kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

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:
Msrc/parse/parse.c | 43++++++++++++++++++++++---------------------
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;