commit ab2d4c8ad60e56bba34ca8d3b407980ba8d93004
parent 1237b5860034481d9e40836011e4e781f6a47ad4
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Sun, 24 May 2026 05:42:48 -0700
parse: accept compound-literal static initializers; fix bootstrap driver -Ilang
Self-compilation (make bootstrap) choked on the slice rework's CFREE_SLICE_LIT,
which expands to a parenthesized struct compound literal `((CfreeSlice){...})`.
- parse_static_init_at: an aggregate (struct/union/array) object may be
initialized by a compound literal of its own type, optionally wrapped in
grouping parens (`static T x = (T){...};` / `((T){...})`). Strip the
`(type-name)` cast and peel grouping parens by recursing.
- parse_static_const (pointer path): peel grouping parens around any pointer
initializer (`("str")`, `(&x)`), not just a nested `((`.
- stage2_link.sh: driver build needs -Ilang so cc.c finds "c/c.h", mirroring
the Makefile's DRIVER_CFLAGS.
Bootstrap now reaches a bitwise-identical stage2/stage3.
Diffstat:
2 files changed, 28 insertions(+), 2 deletions(-)
diff --git a/lang/c/parse/parse_init.c b/lang/c/parse/parse_init.c
@@ -1239,7 +1239,11 @@ static CStaticConst parse_static_const(Parser* p, const Type* ty, SrcLoc loc) {
if (ty && ty->kind == TY_PTR) {
if (is_punct(&p->cur, '(')) {
Tok n = peek1(p);
- if (is_punct(&n, '(')) {
+ /* Grouping parens around the pointer initializer, e.g. `("str")`,
+ * `(&x)`, or `((expr))`. A `(type-name)` is a cast or compound literal
+ * and is handled below, so peel only when the inner token does not start
+ * a type-name. */
+ if (!starts_type_name(p, &n)) {
advance(p);
r = parse_static_const(p, ty, loc);
expect_punct(p, ')', "')' in static pointer initializer");
@@ -1369,6 +1373,27 @@ static void parse_static_aggregate_remainder(Parser* p, u8* buf, u32 buflen,
void parse_static_init_at(Parser* p, u8* buf, u32 buflen, u32 offset,
const Type* ty) {
+ /* An aggregate object may be initialized by a (possibly parenthesized)
+ * compound literal of its own type: `static T x = (T){...};` or, after macro
+ * expansion, `static T x = ((T){...});`. Strip the `(type-name)` cast so the
+ * brace body below initializes the object in place; peel grouping parens by
+ * recursing and matching the closing ')'. Pointer/scalar targets keep their
+ * own handling (parse_static_const / eval_const), where a compound literal
+ * can instead yield an address or value. */
+ if ((ty->kind == TY_STRUCT || ty->kind == TY_UNION || ty->kind == TY_ARRAY) &&
+ is_punct(&p->cur, '(')) {
+ Tok n = peek1(p);
+ if (starts_type_name(p, &n)) {
+ advance(p);
+ parse_type_name(p);
+ expect_punct(p, ')', "')' after compound literal type-name");
+ } else if (is_punct(&n, '(')) {
+ advance(p);
+ parse_static_init_at(p, buf, buflen, offset, ty);
+ expect_punct(p, ')', "')' after parenthesized initializer");
+ return;
+ }
+ }
if (ty->kind == TY_ARRAY) {
const Type* elem = ty->arr.elem;
u32 esz = c_abi_sizeof(p->abi, elem);
diff --git a/scripts/stage2_link.sh b/scripts/stage2_link.sh
@@ -38,7 +38,8 @@ LANG_CPP_FLAGS="--support-dir $ROOT -Iinclude -Ilang/cpp"
LANG_C_FLAGS="--support-dir $ROOT -Iinclude -Ilang/cpp -Ilang/c"
LANG_WASM_FLAGS="--support-dir $ROOT -Iinclude -Ilang/wasm"
LANG_TOY_FLAGS="--support-dir $ROOT -Iinclude"
-DRIVER_FLAGS="--support-dir $ROOT -Iinclude -I."
+# `cc` reaches the C frontend header as "c/c.h"; -Ilang mirrors DRIVER_CFLAGS.
+DRIVER_FLAGS="--support-dir $ROOT -Iinclude -I. -Ilang"
cfree_objs=()
fail_src=()