boot2

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

commit 8df96c90a922b7aef70f245f4dc22278c4d50eb6
parent 8c3794edc8d382460a98cf293b2ebcd07fd94a37
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Mon, 27 Apr 2026 13:39:21 -0700

cc: compound literals (Stream B)

Implement C99 §6.5.2.5 `(T){ init-list }` as a postfix lvalue.

- parse-cast-or-unary disambiguates `(T){` from `(T)cast` and `(expr)`
  by checking peek for `lbrace` after the typename.
- parse-compound-literal allocates a fresh frame slot sized to T,
  drives the existing parse-init-local-aggregate path (which already
  handles positional, designated, partial-init zero-fill, and trailing
  comma) against it, and pushes a frame lval typed as T.
- Scalar `(T){expr}` gets a small inline initializer since
  parse-init-local-aggregate's brace arm only accepts aggregates.
- File-scope literals die explicitly (no fn-ctx).

No new cg primitives. `&lit`, `lit.field`, `lit[i]`, byval struct
pass, and array decay all chain through cg-take-addr / cg-push-field /
rval!→cg-load→cg-decay-array on the resulting frame lval.

Unblocks tests/cc/117-compound-literal on aarch64.

Diffstat:
Mcc/cc.scm | 76+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 67 insertions(+), 9 deletions(-)

diff --git a/cc/cc.scm b/cc/cc.scm @@ -5303,12 +5303,19 @@ (p (parse-declarator ps (cdr sp))) (ty (cdr p))) (expect-punct ps 'rparen) - (parse-unary ps) - ;; Cast operand undergoes lvalue conversion first (C semantics): - ;; arrays decay to pointers, lvals become rvals. cg-cast then - ;; bit-casts the resulting rval to the target type. - (rval! ps) - (cg-cast (ps-cg ps) ty))) + (cond + ;; (T){ ... } — compound literal (C99 §6.5.2.5). Looks like a + ;; cast at the typename level but disambiguates on the + ;; following `{` and is a postfix lvalue, not a cast operator. + ((at-punct? ps 'lbrace) + (parse-compound-literal ps ty)) + (else + (parse-unary ps) + ;; Cast operand undergoes lvalue conversion first (C semantics): + ;; arrays decay to pointers, lvals become rvals. cg-cast then + ;; bit-casts the resulting rval to the target type. + (rval! ps) + (cg-cast (ps-cg ps) ty))))) (($ tok? (kind IDENT) (value ,n)) (guard (typedef? ps n)) (advance ps) @@ -5316,13 +5323,64 @@ (p (parse-declarator ps (cdr sp))) (ty (cdr p))) (expect-punct ps 'rparen) - (parse-unary ps) - (rval! ps) - (cg-cast (ps-cg ps) ty))) + (cond + ((at-punct? ps 'lbrace) + (parse-compound-literal ps ty)) + (else + (parse-unary ps) + (rval! ps) + (cg-cast (ps-cg ps) ty))))) (else (advance ps) (parse-expr ps) (expect-punct ps 'rparen) (parse-postfix-rest ps)))) +;; -------------------------------------------------------------------- +;; Compound literals (C99 §6.5.2.5): (T){ init-list } +;; +;; Allocate a fresh frame slot sized for T, drive the existing +;; local-aggregate initializer path against it, then push a frame lval +;; typed as T. The literal is an lvalue with automatic storage tied to +;; the enclosing block, so &literal, literal.field, literal[i], byval +;; pass, and array decay all chain through the existing primitives +;; (cg-take-addr / cg-push-field / cg-decay-array via rval!). +;; +;; File-scope literals are out of scope (Stream B, docs/CC-PARALLEL-PLAN.md). +;; -------------------------------------------------------------------- +(define (parse-compound-literal ps ty) + (cond + ((not (ps-fn-ctx ps)) + (die (tok-loc (peek ps)) "compound literal at file scope: unsupported"))) + (let* ((sz (max (ctype-size ty) 1)) + (al (max (ctype-align ty) 1)) + (sl (cg-alloc-slot (ps-cg ps) sz al)) + ;; Synthetic sym: parse-init-local-aggregate only reads + ;; sym-slot at its top-level entry to seed base-off; the + ;; recursive helpers thread `sm` along but never read other + ;; fields. The name is unbound and never enters scope. + (sm (%sym "__cl" 'var 'auto ty sl))) + (cond + ((or (eq? (ctype-kind ty) 'arr) + (eq? (ctype-kind ty) 'struct) + (eq? (ctype-kind ty) 'union)) + (parse-init-local-aggregate ps sm ty)) + (else + ;; Scalar (T){expr [,]} — parse-init-local-aggregate's brace arm + ;; only handles aggregates, so emit the single-element store + ;; here directly. + (expect-punct ps 'lbrace) + (cg-push (ps-cg ps) (%opnd 'frame ty sl #t)) + (parse-expr-bp ps 4) (rval! ps) + (cg-cast (ps-cg ps) ty) + (cg-assign (ps-cg ps)) (cg-pop (ps-cg ps)) + (cond ((at-punct? ps 'comma) (advance ps))) + (expect-punct ps 'rbrace))) + ;; The literal is an lvalue with automatic storage. ctype-size may + ;; have been resolved by parse-init-local-aggregate (e.g. (int[]) + ;; gets its bound fixed in-place); we re-fetch via the slot's type + ;; pointer (ty) which the init code mutated. + (cg-push (ps-cg ps) (%opnd 'frame ty sl #t)) + (parse-postfix-rest ps))) + (define (parse-postfix ps) (parse-primary ps) (parse-postfix-rest ps))