kit

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

commit 15fda5605f98b2ab9473d170d76e01322528cbe0
parent 71b2a2aa5e6017120efb570f33e9c2a15e46e391
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Tue, 19 May 2026 14:30:41 -0700

Fix brace-elided aggregate initializers

Diffstat:
Adoc/BUGS.md | 5+++++
Mlang/c/parse/parse_init.c | 248++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
2 files changed, 202 insertions(+), 51 deletions(-)

diff --git a/doc/BUGS.md b/doc/BUGS.md @@ -0,0 +1,5 @@ +Known bugs with red test cases (test-parse) + +- [ ] K&R old-style function definitions with declaration lists: 6_9_14_kr_function_def_params +- [ ] K&R promoted parameter declarations, e.g. char: 6_9_15_kr_function_def_promoted_char +- [ ] Non-extern block-scope function declarations resolving to external definitions: 6_9_16_block_scope_func_decl diff --git a/lang/c/parse/parse_init.c b/lang/c/parse/parse_init.c @@ -52,8 +52,16 @@ static u64 decode_lit_unit_le(const u8* src, u32 size) { /* Forward declaration for mutual recursion. */ void init_at(Parser* p, FrameSlot slot, const Type* arr_ty, u32 offset, const Type* ty); -static u32 init_elided(Parser* p, FrameSlot slot, const Type* arr_ty, - u32 offset, const Type* ty); + +typedef struct InitDesignatorCont { + const Type* parent_ty; + u32 parent_offset; + u32 next_index; +} InitDesignatorCont; + +static void init_aggregate_remainder(Parser* p, FrameSlot slot, + const Type* arr_ty, u32 offset, + const Type* ty, u32 start_index); /* Push the lvalue of a sub-object at byte offset `offset` within the array * local `slot` (whose type is `arr_ty`), with element type `elem_ty`. */ @@ -254,15 +262,20 @@ static void init_string_at(Parser* p, FrameSlot slot, const Type* arr_ty, /* Parse a designator chain (`[const]` and `.ident` repeats) ending at `=`. */ static void parse_designator_chain(Parser* p, const Type* outer_ty, u32 outer_offset, const Type** sub_ty_out, - u32* sub_offset_out, u32* top_index_out) { + u32* sub_offset_out, u32* top_index_out, + InitDesignatorCont* cont_out) { const Type* cur_ty = outer_ty; u32 cur_off = outer_offset; int first = 1; + InitDesignatorCont cont; + memset(&cont, 0, sizeof cont); for (;;) { if (is_punct(&p->cur, '[')) { i64 idx; u32 esz; SrcLoc cloc = tok_loc_init(&p->cur); + const Type* parent_ty = cur_ty; + u32 parent_off = cur_off; advance(p); idx = eval_const_int(p, cloc); expect_punct(p, ']', "']' after designator index"); @@ -275,6 +288,9 @@ static void parse_designator_chain(Parser* p, const Type* outer_ty, esz = c_abi_sizeof(p->abi, cur_ty->arr.elem); cur_off += (u32)idx * esz; cur_ty = cur_ty->arr.elem; + cont.parent_ty = parent_ty; + cont.parent_offset = parent_off; + cont.next_index = (u32)idx + 1u; if (first) *top_index_out = (u32)idx; first = 0; } else if (is_punct(&p->cur, '.')) { @@ -283,6 +299,10 @@ static void parse_designator_chain(Parser* p, const Type* outer_ty, u32 foff; const Field* ff; u16 fi; + u32 selected_index = 0; + int have_selected_index = 0; + const Type* parent_ty = cur_ty; + u32 parent_off = cur_off; advance(p); if (p->cur.kind != TOK_IDENT || ident_kw_init(p, p->cur.v.ident) != KW_NONE) { @@ -297,26 +317,37 @@ static void parse_designator_chain(Parser* p, const Type* outer_ty, perr(p, "no such field in designator"); } cur_off += foff; - if (first) { - for (fi = 0; fi < cur_ty->rec.nfields; ++fi) { - const Field* g = &cur_ty->rec.fields[fi]; - if (g->name == fname && fname != 0) { + for (fi = 0; fi < cur_ty->rec.nfields; ++fi) { + const Field* g = &cur_ty->rec.fields[fi]; + if (g->name == fname && fname != 0) { + selected_index = fi; + have_selected_index = 1; + if (first) { *top_index_out = fi; - break; } - if ((g->flags & FIELD_ANON) && - (g->type->kind == TY_STRUCT || g->type->kind == TY_UNION)) { - const Type* tmp_ty; - u32 tmp_off; - const Field* tmp_f; - if (find_field(p->abi, p->pool, g->type, fname, &tmp_ty, &tmp_off, - &tmp_f)) { + break; + } + if ((g->flags & FIELD_ANON) && + (g->type->kind == TY_STRUCT || g->type->kind == TY_UNION)) { + const Type* tmp_ty; + u32 tmp_off; + const Field* tmp_f; + if (find_field(p->abi, p->pool, g->type, fname, &tmp_ty, &tmp_off, + &tmp_f)) { + selected_index = fi; + have_selected_index = 1; + if (first) { *top_index_out = fi; - break; } + break; } } } + if (have_selected_index) { + cont.parent_ty = parent_ty; + cont.parent_offset = parent_off; + cont.next_index = selected_index + 1u; + } cur_ty = fty; first = 0; } else { @@ -327,6 +358,28 @@ static void parse_designator_chain(Parser* p, const Type* outer_ty, expect_punct(p, '=', "'=' after designator"); *sub_ty_out = cur_ty; *sub_offset_out = cur_off; + if (cont_out) *cont_out = cont; +} + +static int aggregate_has_index(const Type* ty, u32 index) { + if (!ty) return 0; + if (ty->kind == TY_ARRAY) return index < ty->arr.count; + if (ty->kind == TY_STRUCT) return index < ty->rec.nfields; + return 0; +} + +static int designator_continues_inside(Parser* p, const Type* outer_ty, + u32 outer_offset, + const Type* top_ty, u32 top_offset, + const InitDesignatorCont* cont) { + u32 top_size; + if (!cont || !cont->parent_ty) return 0; + if (cont->parent_ty == outer_ty && cont->parent_offset == outer_offset) + return 0; + if (!aggregate_has_index(cont->parent_ty, cont->next_index)) return 0; + top_size = c_abi_sizeof(p->abi, top_ty); + return cont->parent_offset >= top_offset && + cont->parent_offset - top_offset <= top_size; } static u32 init_struct_fields(Parser* p, FrameSlot slot, const Type* arr_ty, @@ -341,7 +394,9 @@ static u32 init_struct_fields(Parser* p, FrameSlot slot, const Type* arr_ty, const Type* sub_ty; u32 sub_off; u32 top_idx = 0; - parse_designator_chain(p, ty, offset, &sub_ty, &sub_off, &top_idx); + InitDesignatorCont cont; + parse_designator_chain(p, ty, offset, &sub_ty, &sub_off, &top_idx, + &cont); while (zero_lo < top_idx) { const Field* zf = &ty->rec.fields[zero_lo]; if (zf->flags & FIELD_BITFIELD) { @@ -358,6 +413,16 @@ static u32 init_struct_fields(Parser* p, FrameSlot slot, const Type* arr_ty, ++zero_lo; } init_at(p, slot, arr_ty, sub_off, sub_ty); + { + const Field* top_f = &ty->rec.fields[top_idx]; + u32 top_off = offset + L->fields[top_idx].offset; + if (designator_continues_inside(p, ty, offset, top_f->type, top_off, + &cont) && + accept_punct(p, ',') && !is_punct(&p->cur, '}')) { + init_aggregate_remainder(p, slot, arr_ty, cont.parent_offset, + cont.parent_ty, cont.next_index); + } + } i = top_idx; if (zero_lo <= top_idx) zero_lo = top_idx + 1; goto next_item_struct; @@ -398,36 +463,48 @@ static u32 init_struct_fields(Parser* p, FrameSlot slot, const Type* arr_ty, return i; } -static u32 init_elided(Parser* p, FrameSlot slot, const Type* arr_ty, - u32 offset, const Type* ty) { +static void init_aggregate_remainder(Parser* p, FrameSlot slot, + const Type* arr_ty, u32 offset, + const Type* ty, u32 start_index) { if (ty->kind == TY_ARRAY) { u32 esz = c_abi_sizeof(p->abi, ty->arr.elem); - init_at(p, slot, arr_ty, offset, ty->arr.elem); - (void)esz; - return 1; + u32 i; + for (i = start_index; i < ty->arr.count; ++i) { + init_at(p, slot, arr_ty, offset + i * esz, ty->arr.elem); + if (i + 1u >= ty->arr.count) return; + if (!accept_punct(p, ',')) break; + if (is_punct(&p->cur, '}')) break; + } + for (++i; i < ty->arr.count; ++i) { + zero_init_at(p, slot, arr_ty, offset + i * esz, ty->arr.elem); + } + return; } if (ty->kind == TY_STRUCT) { - init_struct_fields(p, slot, arr_ty, offset, ty, 0, /*braced=*/0); - return 1; - } - /* Scalar / pointer / union: consume one assignment-expr. */ - int had_brace = accept_punct(p, '{'); - push_subobject_lv(p, slot, arr_ty, offset, ty); - parse_assign_expr(p); - to_rvalue(p); - { - const Type* rhs = cg_top_type(p->cg); - CSemCheck chk = c_sem_check_assignment(p->pool, ty, rhs, C_SEM_ASSIGN_INIT); - if (!chk.ok) perr(p, "%s", chk.message); - } - coerce_top_to_lvalue(p); - cg_store(p->cg); - cg_drop(p->cg); - if (had_brace) { - accept_punct(p, ','); - expect_punct(p, '}', "'}' after scalar initializer"); + const ABIRecordLayout* L = c_abi_record_layout(p->abi, p->pool, ty); + u32 i; + for (i = start_index; i < ty->rec.nfields; ++i) { + init_field_at(p, slot, arr_ty, offset, ty, i); + if (i + 1u >= ty->rec.nfields) return; + if (!accept_punct(p, ',')) break; + if (is_punct(&p->cur, '}')) break; + } + for (++i; i < ty->rec.nfields; ++i) { + const Field* f = &ty->rec.fields[i]; + if (f->flags & FIELD_BITFIELD) { + if (!(f->flags & FIELD_ZERO_WIDTH)) { + push_record_field_lv(p, slot, arr_ty, offset, ty, i); + cg_push_int(p->cg, 0, f->type); + cg_store(p->cg); + cg_drop(p->cg); + } + } else { + zero_init_at(p, slot, arr_ty, offset + L->fields[i].offset, f->type); + } + } + return; } - return 1; + init_at(p, slot, arr_ty, offset, ty); } void init_at(Parser* p, FrameSlot slot, const Type* arr_ty, u32 offset, @@ -451,7 +528,7 @@ void init_at(Parser* p, FrameSlot slot, const Type* arr_ty, u32 offset, } } if (!is_punct(&p->cur, '{')) { - init_elided(p, slot, arr_ty, offset, elem_ty); + init_aggregate_remainder(p, slot, arr_ty, offset, ty, 0); return; } advance(p); /* '{' */ @@ -464,12 +541,20 @@ void init_at(Parser* p, FrameSlot slot, const Type* arr_ty, u32 offset, const Type* sub_ty; u32 sub_off; u32 top_idx = 0; - parse_designator_chain(p, ty, offset, &sub_ty, &sub_off, &top_idx); + InitDesignatorCont cont; + parse_designator_chain(p, ty, offset, &sub_ty, &sub_off, &top_idx, + &cont); while (zero_lo < top_idx) { zero_init_at(p, slot, arr_ty, offset + zero_lo * esz, elem_ty); ++zero_lo; } init_at(p, slot, arr_ty, sub_off, sub_ty); + if (designator_continues_inside( + p, ty, offset, elem_ty, offset + top_idx * esz, &cont) && + accept_punct(p, ',') && !is_punct(&p->cur, '}')) { + init_aggregate_remainder(p, slot, arr_ty, cont.parent_offset, + cont.parent_ty, cont.next_index); + } i = top_idx + 1; if (zero_lo < i) zero_lo = i; } else { @@ -496,7 +581,7 @@ void init_at(Parser* p, FrameSlot slot, const Type* arr_ty, u32 offset, } if (ty->kind == TY_STRUCT) { if (!is_punct(&p->cur, '{')) { - init_struct_fields(p, slot, arr_ty, offset, ty, 0, /*braced=*/0); + init_aggregate_remainder(p, slot, arr_ty, offset, ty, 0); return; } advance(p); /* '{' */ @@ -514,7 +599,7 @@ void init_at(Parser* p, FrameSlot slot, const Type* arr_ty, u32 offset, const Type* sub_ty; u32 sub_off; u32 top_idx = 0; - parse_designator_chain(p, ty, offset, &sub_ty, &sub_off, &top_idx); + parse_designator_chain(p, ty, offset, &sub_ty, &sub_off, &top_idx, NULL); init_at(p, slot, arr_ty, sub_off, sub_ty); } else { const Field* f = &ty->rec.fields[0]; @@ -1047,6 +1132,44 @@ static void parse_static_bitfield_at(Parser* p, u8* buf, u32 buflen, encode_uint_le(buf + storage_off, storage_size, cur); } +static void parse_static_aggregate_remainder(Parser* p, u8* buf, u32 buflen, + u32 offset, const Type* ty, + u32 start_index) { + if (ty->kind == TY_ARRAY) { + const Type* elem = ty->arr.elem; + u32 esz = c_abi_sizeof(p->abi, elem); + u32 i; + for (i = start_index; i < ty->arr.count; ++i) { + parse_static_init_at(p, buf, buflen, offset + i * esz, elem); + if (i + 1u >= ty->arr.count) return; + if (!accept_punct(p, ',')) break; + if (is_punct(&p->cur, '}')) break; + } + return; + } + if (ty->kind == TY_STRUCT) { + const ABIRecordLayout* L = c_abi_record_layout(p->abi, p->pool, ty); + u32 i; + for (i = start_index; i < ty->rec.nfields; ++i) { + const Field* f = &ty->rec.fields[i]; + if (f->flags & FIELD_BITFIELD) { + if (!(f->flags & FIELD_ZERO_WIDTH)) { + parse_static_bitfield_at(p, buf, buflen, offset, &L->fields[i], + f->type); + } + } else { + parse_static_init_at(p, buf, buflen, offset + L->fields[i].offset, + f->type); + } + if (i + 1u >= ty->rec.nfields) return; + if (!accept_punct(p, ',')) break; + if (is_punct(&p->cur, '}')) break; + } + return; + } + parse_static_init_at(p, buf, buflen, offset, ty); +} + void parse_static_init_at(Parser* p, u8* buf, u32 buflen, u32 offset, const Type* ty) { if (ty->kind == TY_ARRAY) { @@ -1071,7 +1194,8 @@ void parse_static_init_at(Parser* p, u8* buf, u32 buflen, u32 offset, } had_brace = accept_punct(p, '{'); if (!had_brace) { - perr(p, "expected '{' for static-storage array initializer"); + parse_static_aggregate_remainder(p, buf, buflen, offset, ty, 0); + return; } if (!is_punct(&p->cur, '}')) { for (;;) { @@ -1079,8 +1203,16 @@ void parse_static_init_at(Parser* p, u8* buf, u32 buflen, u32 offset, const Type* sub_ty; u32 sub_off; u32 top_idx = 0; - parse_designator_chain(p, ty, offset, &sub_ty, &sub_off, &top_idx); + InitDesignatorCont cont; + parse_designator_chain(p, ty, offset, &sub_ty, &sub_off, &top_idx, + &cont); parse_static_init_at(p, buf, buflen, sub_off, sub_ty); + if (designator_continues_inside( + p, ty, offset, elem, offset + top_idx * esz, &cont) && + accept_punct(p, ',') && !is_punct(&p->cur, '}')) { + parse_static_aggregate_remainder(p, buf, buflen, cont.parent_offset, + cont.parent_ty, cont.next_index); + } i = top_idx + 1; } else { if (i >= ty->arr.count) { @@ -1101,7 +1233,8 @@ void parse_static_init_at(Parser* p, u8* buf, u32 buflen, u32 offset, const ABIRecordLayout* L = c_abi_record_layout(p->abi, p->pool, ty); u32 i = 0; if (!had_brace) { - perr(p, "expected '{' for static-storage struct initializer"); + parse_static_aggregate_remainder(p, buf, buflen, offset, ty, 0); + return; } while (i < ty->rec.nfields && !is_punct(&p->cur, '}')) { const Field* f = &ty->rec.fields[i]; @@ -1109,8 +1242,21 @@ void parse_static_init_at(Parser* p, u8* buf, u32 buflen, u32 offset, const Type* sub_ty; u32 sub_off; u32 top_idx = 0; - parse_designator_chain(p, ty, offset, &sub_ty, &sub_off, &top_idx); + InitDesignatorCont cont; + parse_designator_chain(p, ty, offset, &sub_ty, &sub_off, &top_idx, + &cont); parse_static_init_at(p, buf, buflen, sub_off, sub_ty); + { + const Field* top_f = &ty->rec.fields[top_idx]; + u32 top_off = offset + L->fields[top_idx].offset; + if (designator_continues_inside(p, ty, offset, top_f->type, top_off, + &cont) && + accept_punct(p, ',') && !is_punct(&p->cur, '}')) { + parse_static_aggregate_remainder( + p, buf, buflen, cont.parent_offset, cont.parent_ty, + cont.next_index); + } + } i = top_idx + 1; if (!accept_punct(p, ',')) break; continue; @@ -1140,7 +1286,7 @@ void parse_static_init_at(Parser* p, u8* buf, u32 buflen, u32 offset, const Type* sub_ty; u32 sub_off; u32 top_idx = 0; - parse_designator_chain(p, ty, offset, &sub_ty, &sub_off, &top_idx); + parse_designator_chain(p, ty, offset, &sub_ty, &sub_off, &top_idx, NULL); (void)top_idx; parse_static_init_at(p, buf, buflen, sub_off, sub_ty); } else {