← board

C anonymous struct/union member braced-designated init (-fms-extensions)

Repro

struct S { unsigned char a, b; unsigned char c[2]; };
union UV { struct { unsigned char a, b; }; struct S s; };
union UV g = {{.b = 7, .a = 8}};   // pxx: "expected C expression"

gcc/tcc accept this widely-used extension. Positional {{6,5}} and union-level promoted designators {.b = 8, .a = 7} ALREADY work in pxx — only the braced + designated form into a promoted anonymous member fails.

Root cause

The anonymous struct's fields are PROMOTED flat into the union's field list (so .b/.a resolve directly on the union — that's what makes the promoted-designator form work). But an inner brace {...} is meant to initialise the WHOLE anonymous member as one subaggregate. The recursive init walker (CInitWalkRecord / CInitWalkMember, cparser.inc) treats the union's field[0] as the scalar a, sees the inner { as a braced-scalar { expr } (CInitLeaf's tkBegin branch), then ParseCExpr chokes on the leading . of .b.

Needed

The walker must recognise that field[0..k] belong to a promoted anonymous aggregate and, on a matching inner brace, descend that anonymous sub-struct as one member (honouring designators against its own fields) rather than as flat promoted scalars. Likely wants an anon-group marker on the promoted UFields (parent anon record id + span) so the walker can re-group them for a brace.

Gate

union UV g = {{.b=7,.a=8}} byte-identical to gcc; contributes to unskipping c-testsuite 00216 (with [[bug-c-fullfile-cumulative-parser-desync]]). [[feature-c-compound-literals]]

2026-07-09 — precise location + now the SOLE 00216 blocker

With the form-feed lexer bug fixed ([[bug-c-fullfile-cumulative-parser-desync]], done), this is the ONLY remaining blocker for c-testsuite 00216 (→ 220/220). The promotion happens in ParseCStructInto (cparser.inc ~8213-8246): an anonymous aggregate member's fields are copied FLAT into the parent's bf* field list (name + adjusted offset), with no marker recording the anon-group origin (sub-record id + field span). So the init walker (CInitWalkRecord/CInitWalkMember) cannot tell that field[k..k+n] form one anonymous sub-aggregate to descend on an inner brace. Fix needs a grouping marker (e.g. a parallel UFldAnonRec/UFldAnonSpan on the promoted fields) — a SHARED UField-table change (Track A, self-host-critical; land additive + byte-identical). Then the walker, on an inner { at the first field of a promoted anon group, descends that group as one member honoring its own designators.

RESOLVED 2026-07-09

Implemented via a UFldAnonRec[] group marker (defs.inc): each field promoted from an anonymous member carries its source sub-record id (set in ParseCStructInto's bf* buffer -> emission, defaulted REC_NONE, carried through AddUField relocation). The init walker CInitWalkRecord, at the first field of an anon group with an inner brace, descends the sub-record (field names resolve against the parent's promoted fields, so offsets are correct) and advances past the whole group + overlapping union siblings. Positional {{6,5}}, designated {{.b,.a}}, union-level {.b,.a}, local + global all gcc-verified (b221). Was the last 00216 blocker; 00216 now 220/220. See [[feature-c-compound-literals]].