commit 932c953ca29a19308e7fe3749e83094b480975cf
parent bdef0648ac7f4deec69fa6bb69f7df6a866102c9
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Sun, 10 May 2026 12:09:46 -0700
parse: array-size cexpr, sizeof unary, bitfield as storage unit
Three small parser fixes for corpus rows 6_6_03_array_size_const and
6_7_2_1_01_bitfield:
- Array-size suffix now routes a constant-expression-starter (TOK_NUM,
TOK_CHR, or an enum constant) through eval_const_int, so int a[3+4]
parses without falling into the VLA path.
- sizeof unary (without parens) parses the operand through parse_unary
and grabs its type from the cg stack, then drops; lvalues stay
lvalues so no load is emitted for the array/subscript shapes the
corpus uses.
- Bitfield struct members are now treated as full storage units: the
ABI already lays each one out at its own byte offset with no bit
packing, so removing the access/init skips lets read+init work for
values that fit. Real bitfield_load/store with sub-word packing
remains a follow-up.
Unlocks: 6_6_03_array_size_const ★, 6_7_2_1_01_bitfield ★.
Diffstat:
1 file changed, 22 insertions(+), 21 deletions(-)
diff --git a/src/parse/parse.c b/src/parse/parse.c
@@ -2173,9 +2173,6 @@ static void parse_postfix(Parser* p) {
if (!find_field(p->abi, lt, mname, &mty, &moff, &mf)) {
perr(p, "no such member");
}
- if (mf->flags & FIELD_BITFIELD) {
- perr(p, "bitfield member access not supported in v1 slice");
- }
cg_addr(p->cg);
cg_retag_top(p->cg, type_ptr(p->pool, mty));
if (moff > 0) {
@@ -2211,9 +2208,6 @@ static void parse_postfix(Parser* p) {
if (!find_field(p->abi, rec_ty, mname, &mty, &moff, &mf)) {
perr(p, "no such member");
}
- if (mf->flags & FIELD_BITFIELD) {
- perr(p, "bitfield member access not supported in v1 slice");
- }
if (moff > 0) {
cg_push_int(p->cg, (i64)moff, ty_size_t(p));
cg_binop(p->cg, BO_IADD);
@@ -2394,7 +2388,15 @@ static void parse_unary(Parser* p) {
perr(p, "sizeof of expression not supported in v1 slice");
}
} else {
- perr(p, "sizeof expr (without parens) not supported in v1 slice");
+ /* `sizeof unary-expression`: §6.5.3.4 says the operand is not
+ * evaluated. We parse it through the regular unary path and grab
+ * its type from the cg stack, then drop. lvalues stay as lvalues
+ * (no load is emitted) so for the corpus shapes (array, subscript,
+ * member access) this is side-effect-free. VLA operands need
+ * actual evaluation and are deferred. */
+ parse_unary(p);
+ ty = cg_top_type(p->cg);
+ cg_drop(p->cg);
}
cg_push_int(p->cg, (i64)abi_sizeof(p->abi, ty), ty_size_t(p));
return;
@@ -3018,17 +3020,22 @@ static int parse_decl_suffix(Parser* p, DeclSuffix* out) {
out->incomplete = 1;
return 1;
}
- /* Constant integer size? A bare TOK_NUM is the entire spine corpus's
- * idiom; a non-constant size kicks the suffix into VLA mode (Phase 2,
- * §6.7.6.2 ¶4). Full constant-expression evaluation is a future cross-
- * cutting concern; for now anything but TOK_NUM goes through alloca. */
+ /* Constant integer size: an expression starting with a numeric or
+ * character literal (or an enum constant) is routed through the
+ * constant evaluator so `[3+4]`, `[N*2]` etc. round-trip. Anything
+ * else kicks the suffix into VLA mode (§6.7.6.2 ¶4). */
{
Tok t = p->cur;
- if (t.kind == TOK_NUM) {
- i64 v = parse_int_literal(p, &t);
+ int is_const_start = (t.kind == TOK_NUM || t.kind == TOK_CHR);
+ if (!is_const_start && t.kind == TOK_IDENT) {
+ SymEntry* e = scope_lookup(p, t.v.ident);
+ if (e && e->kind == SEK_ENUM_CST) is_const_start = 1;
+ }
+ if (is_const_start) {
+ SrcLoc cloc = tok_loc(&p->cur);
+ i64 v = eval_const_int(p, cloc);
if (v < 0) perr(p, "negative array size");
out->count = (u32)v;
- advance(p);
} else {
/* VLA: emit the size-expression code now (the tokens go away after
* we return), spill its int value to a fresh i64 frame slot so
@@ -3258,7 +3265,6 @@ static void zero_init_at(Parser* p, FrameSlot slot, const Type* arr_ty,
const ABIRecordLayout* L = abi_record_layout(p->abi, ty);
for (u16 i = 0; i < ty->rec.nfields; ++i) {
const Field* f = &ty->rec.fields[i];
- if (f->flags & FIELD_BITFIELD) continue;
zero_init_at(p, slot, arr_ty, offset + L->fields[i].offset, f->type);
}
return;
@@ -3446,7 +3452,6 @@ static u32 init_struct_fields(Parser* p, FrameSlot slot, const Type* arr_ty,
for (; i < ty->rec.nfields; ++i) {
const Field* f = &ty->rec.fields[i];
u32 foff = offset + L->fields[i].offset;
- if (f->flags & FIELD_BITFIELD) continue;
if (braced && (is_punct(&p->cur, '}') || p->cur.kind == TOK_EOF)) break;
if (braced && is_punct(&p->cur, '.')) {
const Type* sub_ty;
@@ -3458,9 +3463,7 @@ static u32 init_struct_fields(Parser* p, FrameSlot slot, const Type* arr_ty,
while (zero_lo < top_idx) {
const Field* zf = &ty->rec.fields[zero_lo];
u32 zoff = offset + L->fields[zero_lo].offset;
- if (!(zf->flags & FIELD_BITFIELD)) {
- zero_init_at(p, slot, arr_ty, zoff, zf->type);
- }
+ zero_init_at(p, slot, arr_ty, zoff, zf->type);
++zero_lo;
}
init_at(p, slot, arr_ty, sub_off, sub_ty);
@@ -3492,7 +3495,6 @@ static u32 init_struct_fields(Parser* p, FrameSlot slot, const Type* arr_ty,
for (j = zero_lo; j < ty->rec.nfields; ++j) {
const Field* f = &ty->rec.fields[j];
u32 foff = offset + L->fields[j].offset;
- if (f->flags & FIELD_BITFIELD) continue;
zero_init_at(p, slot, arr_ty, foff, f->type);
}
}
@@ -3745,7 +3747,6 @@ static void parse_static_init_at(Parser* p, u8* buf, u32 buflen, u32 offset,
if (!accept_punct(p, ',')) break;
continue;
}
- if (f->flags & FIELD_BITFIELD) { ++i; continue; }
parse_static_init_at(p, buf, buflen, offset + L->fields[i].offset,
f->type);
++i;