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:
| M | cc/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))