boot2

Playing with the boostrap
git clone https://git.ryansepassi.com/git/boot2.git
Log | Files | Refs | README

commit 5947945ef1b00beb0744635095b6e1a6259a6a2a
parent 0fd7206cab421b0c14cad970c764ebe3514e46c9
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Fri,  1 May 2026 17:31:17 -0700

cc/parse: support brace elision in nested aggregate initializers

The aggregate-init parsers (%parse-init-array-list and
%parse-init-local-array-list) only handled nested aggregates when the
inner braces were explicit. Without them, e.g.
`struct S arr[2] = {1,2,3,4}` or `int a[2][2] = {1,2,3,4}`, the loop
fell into the scalar branch and treated each integer as the entire
aggregate slot — initializing only the first element of each
sub-aggregate and leaving the rest at whatever zero-fill happened to
land. C99 §6.7.8 ¶22 requires the contents of an aggregate without
its own braces to be drawn from the parent list, deep enough to
fill it.

Add %parse-init-{array,struct}-noBrace and their local-scope
counterparts that consume one aggregate's worth of children without
expecting an enclosing `{`/`}`. Apply them in both the global and
local array/struct list element loops when the elem type is an
aggregate and the next token is not `{`.

Test: 241-init-brace-elision-struct-array

Diffstat:
Mcc/cc.scm | 211+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/cc/241-init-brace-elision-struct-array.c | 6++++++
Atests/cc/241-init-brace-elision-struct-array.expected-exit | 1+
3 files changed, 218 insertions(+), 0 deletions(-)

diff --git a/cc/cc.scm b/cc/cc.scm @@ -5692,12 +5692,107 @@ (cond ((at-punct? ps 'comma) (advance ps))) (expect-punct ps 'rbrace) (list p))))) + ;; Brace elision: when elem is itself an + ;; aggregate and no inner `{` was given, + ;; consume that aggregate's worth of + ;; child initializers from the parent list + ;; (C99 §6.7.8 ¶22). Otherwise, treat the + ;; next expression as a scalar piece. + ((eq? (ctype-kind elem) 'arr) + (%parse-init-array-noBrace ps elem)) + ((or (eq? (ctype-kind elem) 'struct) + (eq? (ctype-kind elem) 'union)) + (%parse-init-struct-noBrace ps elem)) (else (list (%const-init-piece ps elem)))))) (cond ((at-punct? ps 'comma) (advance ps))) p))))) (lp (%init-main-prepend-reversed piece acc) (+ count 1)))))))) +;; Brace-elision helpers: consume one aggregate's children without +;; expecting an enclosing `{` `}`. Used when an aggregate-typed element +;; appears in an outer list without inner braces, e.g. +;; `struct S a[2] = {1,2,3,4}`. +(define (%parse-init-array-noBrace ps ty) + (let* ((elem (%init-array-elem-type ty)) + (esize (ctype-size elem)) + (decl (%init-array-decl-len ty)) + (final (cond ((< decl 0) 0) (else decl)))) + (let lp ((acc '()) (count 0)) + (cond + ((>= count final) + ;; Pad to declared length if shorter (count==final-no-pad). + (reverse acc)) + ;; Stop early if we hit `}` of an enclosing list. + ((at-punct? ps 'rbrace) (reverse acc)) + (else + (let ((p (cond + ((at-punct? ps 'lbrace) + (advance ps) + (cond + ((eq? (ctype-kind elem) 'arr) + (let-values (((p _c) + (%parse-init-array-list ps elem))) p)) + ((or (eq? (ctype-kind elem) 'struct) + (eq? (ctype-kind elem) 'union)) + (%parse-init-struct-list ps elem)) + (else + (let ((p (%const-init-piece ps elem))) + (cond ((at-punct? ps 'comma) (advance ps))) + (expect-punct ps 'rbrace) + (list p))))) + ((eq? (ctype-kind elem) 'arr) + (%parse-init-array-noBrace ps elem)) + ((or (eq? (ctype-kind elem) 'struct) + (eq? (ctype-kind elem) 'union)) + (%parse-init-struct-noBrace ps elem)) + (else (list (%const-init-piece ps elem)))))) + (cond ((at-punct? ps 'comma) (advance ps))) + (lp (append (reverse p) acc) (+ count 1)))))))) + +(define (%parse-init-struct-noBrace ps ty) + ;; Brace-elided struct: consume positional initializers for each + ;; field in declaration order, returning a flat piece list (with + ;; offset-merge so trailing fields get padded). + (let* ((fields (%init-struct-fields ty)) + (size (ctype-size ty))) + (let lp ((entries '()) (rest fields)) + (cond + ((null? rest) + (%merge-init-entries (reverse entries) size)) + ((at-punct? ps 'rbrace) + (%merge-init-entries (reverse entries) size)) + (else + (let* ((target (car rest)) + (fname (car target)) + (fty (car (cdr target))) + (foff (car (cddr target))) + (piece-list + (cond + ((at-punct? ps 'lbrace) + (advance ps) + (cond + ((eq? (ctype-kind fty) 'arr) + (let-values (((p _c) + (%parse-init-array-list ps fty))) p)) + ((or (eq? (ctype-kind fty) 'struct) + (eq? (ctype-kind fty) 'union)) + (%parse-init-struct-list ps fty)) + (else + (let ((p (%const-init-piece ps fty))) + (cond ((at-punct? ps 'comma) (advance ps))) + (expect-punct ps 'rbrace) + (list p))))) + ((eq? (ctype-kind fty) 'arr) + (%parse-init-array-noBrace ps fty)) + ((or (eq? (ctype-kind fty) 'struct) + (eq? (ctype-kind fty) 'union)) + (%parse-init-struct-noBrace ps fty)) + (else + (list (%const-init-piece ps fty)))))) + (cond ((at-punct? ps 'comma) (advance ps))) + (lp (cons (cons foff piece-list) entries) (cdr rest)))))))) + (define (%piece-bytesize p) ;; Output width of one piece (cf. %cg-init-piece->bv): a bv emits ;; one byte per element; a (label-ref . _) emits an 8-byte slot. @@ -5931,6 +6026,15 @@ (cg-assign (ps-cg ps)) (cg-pop (ps-cg ps)) (cond ((at-punct? ps 'comma) (advance ps))) (expect-punct ps 'rbrace)))) + ;; Brace elision: aggregate elem without inner `{` — + ;; consume that aggregate's worth of children directly + ;; (C99 §6.7.8 ¶22). Without this, `int a[2][2] = {1,2,3,4}` + ;; would treat each integer as an entire `int[2]`. + ((eq? (ctype-kind elem) 'arr) + (%parse-init-local-array-noBrace ps sm eoff elem)) + ((or (eq? (ctype-kind elem) 'struct) + (eq? (ctype-kind elem) 'union)) + (%parse-init-local-struct-noBrace ps sm eoff elem)) (else (%push-frame-elem-lval ps eoff elem) (parse-expr-bp ps 4) (rval! ps) @@ -5939,6 +6043,113 @@ (cond ((at-punct? ps 'comma) (advance ps))) (lp (+ i 1)))))))) +(define (%parse-init-local-array-noBrace ps sm base-off ty) + ;; Like %parse-init-local-array-list but consumes a fixed number of + ;; children (= declared length) without expecting an enclosing `}`. + ;; Pads remaining slots with zero. Caller is the parent list whose + ;; `}` (or further siblings) follow. + (let* ((elem (%init-array-elem-type ty)) + (esize (ctype-size elem)) + (decl (%init-array-decl-len ty)) + (final (cond ((< decl 0) 0) (else decl)))) + (let lp ((i 0)) + (cond + ((>= i final) #t) + ((at-punct? ps 'rbrace) + ;; Hit parent `}` early; zero-fill remaining slots. + (let zlp ((k i)) + (cond + ((>= k final) #t) + (else + (let ((off (+ base-off (* k esize)))) + (let zb ((j 0)) + (cond + ((>= j esize) #t) + (else + (%push-frame-elem-lval ps (+ off j) %t-u8) + (cg-push-imm (ps-cg ps) %t-u8 0) + (cg-assign (ps-cg ps)) + (cg-pop (ps-cg ps)) + (zb (+ j 1))))) + (zlp (+ k 1))))))) + (else + (let ((eoff (+ base-off (* i esize)))) + (cond + ((at-punct? ps 'lbrace) + (advance ps) + (cond + ((eq? (ctype-kind elem) 'arr) + (%parse-init-local-array-list ps sm eoff elem)) + ((or (eq? (ctype-kind elem) 'struct) + (eq? (ctype-kind elem) 'union)) + (%parse-init-local-struct-list ps sm eoff elem)) + (else + (%push-frame-elem-lval ps eoff elem) + (parse-expr-bp ps 4) (rval! ps) + (cg-cast (ps-cg ps) elem) + (cg-assign (ps-cg ps)) (cg-pop (ps-cg ps)) + (cond ((at-punct? ps 'comma) (advance ps))) + (expect-punct ps 'rbrace)))) + ((eq? (ctype-kind elem) 'arr) + (%parse-init-local-array-noBrace ps sm eoff elem)) + ((or (eq? (ctype-kind elem) 'struct) + (eq? (ctype-kind elem) 'union)) + (%parse-init-local-struct-noBrace ps sm eoff elem)) + (else + (%push-frame-elem-lval ps eoff elem) + (parse-expr-bp ps 4) (rval! ps) + (cg-cast (ps-cg ps) elem) + (cg-assign (ps-cg ps)) (cg-pop (ps-cg ps)))) + (cond ((at-punct? ps 'comma) (advance ps))) + (lp (+ i 1)))))))) + +(define (%parse-init-local-struct-noBrace ps sm base-off ty) + ;; Brace-elided struct in auto context: positional inits for each + ;; field; pad unmentioned tail fields with zero. + (let ((fields (%init-struct-fields ty))) + (let lp ((rest fields) (seen '())) + (cond + ((or (null? rest) (at-punct? ps 'rbrace)) + (for-each + (lambda (f) + (cond ((not (%anon-touched? f seen)) + (%emit-zero-field ps base-off f)))) + fields)) + (else + (let* ((target (car rest)) + (fname (car target)) + (fty (car (cdr target))) + (foff (car (cddr target))) + (eoff (+ base-off foff))) + (cond + ((at-punct? ps 'lbrace) + (advance ps) + (cond + ((eq? (ctype-kind fty) 'arr) + (%parse-init-local-array-list ps sm eoff fty)) + ((or (eq? (ctype-kind fty) 'struct) + (eq? (ctype-kind fty) 'union)) + (%parse-init-local-struct-list ps sm eoff fty)) + (else + (%push-frame-elem-lval ps eoff fty) + (parse-expr-bp ps 4) (rval! ps) + (cg-cast (ps-cg ps) fty) + (cg-assign (ps-cg ps)) (cg-pop (ps-cg ps)) + (cond ((at-punct? ps 'comma) (advance ps))) + (expect-punct ps 'rbrace)))) + ((eq? (ctype-kind fty) 'arr) + (%parse-init-local-array-noBrace ps sm eoff fty)) + ((or (eq? (ctype-kind fty) 'struct) + (eq? (ctype-kind fty) 'union)) + (%parse-init-local-struct-noBrace ps sm eoff fty)) + (else + (%push-frame-elem-lval ps eoff fty) + (parse-expr-bp ps 4) (rval! ps) + (cg-cast (ps-cg ps) fty) + (cg-assign (ps-cg ps)) (cg-pop (ps-cg ps)))) + (cond ((at-punct? ps 'comma) (advance ps))) + (lp (cdr rest) (cons fname seen)))))))) + (define (%bv-in-list? bv xs) (cond ((null? xs) #f) ((equal? bv (car xs)) #t) diff --git a/tests/cc/241-init-brace-elision-struct-array.c b/tests/cc/241-init-brace-elision-struct-array.c @@ -0,0 +1,6 @@ +/* Brace elision in nested aggregate init */ +struct S { int a; int b; }; +int main(void) { + struct S arr[2] = { 1, 2, 3, 4 }; /* no inner braces */ + return arr[0].a + arr[0].b + arr[1].a + arr[1].b; /* 10 */ +} diff --git a/tests/cc/241-init-brace-elision-struct-array.expected-exit b/tests/cc/241-init-brace-elision-struct-array.expected-exit @@ -0,0 +1 @@ +10