C anonymous struct/union member braced-designated init (-fms-extensions)
- Type: feature/bug. Track C (C frontend).
- Found: 2026-07-09, isolating c-testsuite 00216 after compound literals landed.
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]].