boot2

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

commit cd42b163a2272e058d1b41ca6a13e28d12e7d4aa
parent 07aa6dbc034fab8c9f43999169b2192e7569e0d5
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Mon, 27 Apr 2026 12:04:53 -0700

tests/cc: add compound-literal and const-expr fixtures

Pin the intended surface for two C99 features the parser does not
yet accept:

- 117: (T){...} as expression — scalar, array, struct, designated,
  used as rval, arg, address, indexed, member-accessed.
- 118: integer constant expressions in array bounds, enum inits,
  case labels, and static/file-scope inits — covering sizeof,
  arithmetic, bitwise, shift, comparison, logical, ternary, cast.

Both pass under host cc; both currently die under cc.scm.

Diffstat:
Atests/cc/117-compound-literal.c | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/cc/117-compound-literal.expected-exit | 1+
Atests/cc/118-const-expr.c | 144+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/cc/118-const-expr.expected-exit | 1+
4 files changed, 252 insertions(+), 0 deletions(-)

diff --git a/tests/cc/117-compound-literal.c b/tests/cc/117-compound-literal.c @@ -0,0 +1,106 @@ +/* Compound literals (C99 §6.5.2.5): `(T){ init-list }` as a postfix + * expression. Scope: block-scope only. Initializer grammar reuses the + * existing local-aggregate path (positional, designated, trailing + * commas, partial-init zero-fill). + * + * A compound literal is an lvalue with automatic storage tied to the + * enclosing block, so it can be addressed, indexed, member-accessed, + * and passed to functions whose parameters expect a pointer. + */ + +struct Point { int x; int y; }; + +int sum_pt(struct Point p) { return p.x + p.y; } +int sum_pt_ptr(struct Point *p) { return p->x + p->y; } +int sum_arr3(int *a) { return a[0] + a[1] + a[2]; } + +int test_scalar(void) { + int v = (int){42}; + if (v != 42) return 1; + return 0; +} + +int test_array_index(void) { + /* Direct index on the literal — array decays to pointer. */ + if ((int[]){10, 20, 30}[1] != 20) return 1; + if ((int[3]){5, 6, 7}[2] != 7) return 2; + return 0; +} + +int test_array_decay(void) { + /* Bound to a pointer; outlives current full-expression because + * compound literals at block scope live until the block ends. */ + int *p = (int[]){100, 200, 300}; + if (p[0] + p[1] + p[2] != 600) return 1; + return 0; +} + +int test_array_as_arg(void) { + return sum_arr3((int[]){1, 2, 4}) == 7 ? 0 : 1; +} + +int test_struct_positional(void) { + struct Point q = (struct Point){3, 4}; + if (q.x != 3) return 1; + if (q.y != 4) return 2; + return 0; +} + +int test_struct_designated(void) { + struct Point q = (struct Point){.y = 5, .x = 7}; + if (q.x != 7) return 1; + if (q.y != 5) return 2; + /* Partial designated init: unmentioned field is zeroed. */ + struct Point r = (struct Point){.x = 9}; + if (r.x != 9) return 3; + if (r.y != 0) return 4; + return 0; +} + +int test_struct_as_arg_byval(void) { + return sum_pt((struct Point){10, 20}) == 30 ? 0 : 1; +} + +int test_struct_addr(void) { + /* &literal — compound literals are lvalues. */ + return sum_pt_ptr(&(struct Point){11, 22}) == 33 ? 0 : 1; +} + +int test_struct_member_direct(void) { + /* Member access directly on the literal. */ + if ((struct Point){50, 60}.x != 50) return 1; + if ((struct Point){50, 60}.y != 60) return 2; + return 0; +} + +int test_trailing_comma(void) { + int *p = (int[]){1, 2, 3,}; + return (p[0] + p[1] + p[2]) == 6 ? 0 : 1; +} + +int test_partial_array(void) { + /* Trailing zeros for unspecified elements. */ + int *p = (int[5]){1, 2}; + if (p[0] != 1) return 1; + if (p[1] != 2) return 2; + if (p[2] != 0) return 3; + if (p[3] != 0) return 4; + if (p[4] != 0) return 5; + return 0; +} + +int main(int argc, char **argv) { + int r; + if ((r = test_scalar())) return 1 * 10 + r; + if ((r = test_array_index())) return 2 * 10 + r; + if ((r = test_array_decay())) return 3 * 10 + r; + if ((r = test_array_as_arg())) return 4 * 10 + r; + if ((r = test_struct_positional())) return 5 * 10 + r; + if ((r = test_struct_designated())) return 6 * 10 + r; + if ((r = test_struct_as_arg_byval()))return 7 * 10 + r; + if ((r = test_struct_addr())) return 8 * 10 + r; + if ((r = test_struct_member_direct()))return 9 * 10 + r; + if ((r = test_trailing_comma())) return 10 * 10 + r; + if ((r = test_partial_array())) return 11 * 10 + r; + return 0; +} diff --git a/tests/cc/117-compound-literal.expected-exit b/tests/cc/117-compound-literal.expected-exit @@ -0,0 +1 @@ +0 diff --git a/tests/cc/118-const-expr.c b/tests/cc/118-const-expr.c @@ -0,0 +1,144 @@ +/* Integer constant expressions (C99 §6.6). The four contexts that + * demand one: array bounds, enum initializers, case labels, and + * static/file-scope initializers. The accepted operand surface: + * + * integer / character literals, enum constants + * sizeof T / sizeof expr (non-VLA — operand not evaluated) + * unary + - ~ ! + * binary + - * / % << >> & | ^ + * compare < <= > >= == != + * logical && || + * ternary ?: + * cast to integer type + * parenthesization + * + * Floats, function calls, address-of, non-const identifiers, VLAs: + * out of scope. + */ + +enum { + A = 1, + B = A + 1, /* arithmetic on enum-const */ + C = B << 2, /* shift */ + D = sizeof(int), /* sizeof in enum init */ + E = (1 < 2) ? 100 : 200, /* comparison + ternary */ + F = ~0, /* unary bnot */ + G = 'a', /* char constant promotes to int */ + H = (A && B) + (F || A), /* short-circuit logical: 1+1 = 2 */ + I = 1 + ~0, /* mixed unary + binary */ +}; + +/* Array bounds at file scope. */ +int g_arr_sizeof[sizeof(int)]; /* 4 */ +int g_arr_arith[2 + 3 * 4]; /* 14 */ +int g_arr_shift[1 << 3]; /* 8 */ +int g_arr_enum[B + 1]; /* 3 */ +int g_arr_paren[(1 + 2) * (3 + 1)]; /* 12 */ + +/* File-scope scalar initializers — must be constant expressions. */ +int g_init_arith = 1 + 2 * 3; /* 7 */ +int g_init_shift = (1 << 4) | 1; /* 17 */ +int g_init_sizeof = sizeof(long); /* 8 */ +int g_init_ternary = (5 > 3) ? 42 : 0; /* 42 */ +int g_init_enum = B * 10 + C; /* 2*10 + 8 = 28 */ +int g_init_cast = (int)(unsigned char)257; /* 257 & 0xff = 1 */ +int g_init_neg = -100 + 50; /* -50 */ +int g_init_char = 'A'; /* 65 */ + +int test_enum_values(void) { + if (A != 1) return 1; + if (B != 2) return 2; + if (C != 8) return 3; + if (D != 4) return 4; + if (E != 100) return 5; + if (F != -1) return 6; + if (G != 97) return 7; + if (H != 2) return 8; + if (I != 0) return 9; + return 0; +} + +int test_global_array_bounds(void) { + if (sizeof(g_arr_sizeof) != sizeof(int) * 4) return 1; + if (sizeof(g_arr_arith) != sizeof(int) * 14) return 2; + if (sizeof(g_arr_shift) != sizeof(int) * 8) return 3; + if (sizeof(g_arr_enum) != sizeof(int) * 3) return 4; + if (sizeof(g_arr_paren) != sizeof(int) * 12) return 5; + return 0; +} + +int test_global_inits(void) { + if (g_init_arith != 7) return 1; + if (g_init_shift != 17) return 2; + if (g_init_sizeof != 8) return 3; + if (g_init_ternary != 42) return 4; + if (g_init_enum != 28) return 5; + if (g_init_cast != 1) return 6; + if (g_init_neg !=-50) return 7; + if (g_init_char != 65) return 8; + return 0; +} + +int test_local_array_bounds(void) { + /* Block-scope arrays use the same const-expr grammar. */ + int a[2 + 3]; /* 5 */ + int b[sizeof(int) * 2]; /* 8 */ + int c[B + C]; /* 2 + 8 = 10 */ + if (sizeof(a) != sizeof(int) * 5) return 1; + if (sizeof(b) != sizeof(int) * 8) return 2; + if (sizeof(c) != sizeof(int) * 10) return 3; + return 0; +} + +int test_static_local_init(void) { + /* Block-scope static needs a constant expression too. */ + static int s_arith = 100 - 1; + static int s_sizeof = sizeof(int *); + static int s_enum = C * D; /* 8 * 4 = 32 */ + static int s_logic = (1 || 0) && (2 < 3); /* 1 */ + if (s_arith != 99) return 1; + if (s_sizeof != 8) return 2; + if (s_enum != 32) return 3; + if (s_logic != 1) return 4; + return 0; +} + +int test_case_labels(void) { + int r = 0; + int i; + for (i = 0; i < 5; i = i + 1) { + switch (i) { + case 0: r = r + 1; break; + case A: r = r + 2; break; /* 1: enum-const */ + case 1 + 1: r = r + 4; break; /* 2: arith */ + case sizeof(int)-1: r = r + 8; break; /* 3: sizeof+arith */ + case B << 1: r = r + 16; break; /* 4: shift */ + default: r = r + 64; break; + } + } + return r == (1 + 2 + 4 + 8 + 16) ? 0 : 1; +} + +int test_char_in_const_expr(void) { + /* Character constant participates in const arithmetic. */ + static int diff = 'z' - 'a'; /* 25 */ + int x = 'a'; + switch (x) { + case 'a': break; + default: return 1; + } + if (diff != 25) return 2; + return 0; +} + +int main(int argc, char **argv) { + int r; + if ((r = test_enum_values())) return 1 * 10 + r; + if ((r = test_global_array_bounds()))return 2 * 10 + r; + if ((r = test_global_inits())) return 3 * 10 + r; + if ((r = test_local_array_bounds())) return 4 * 10 + r; + if ((r = test_static_local_init())) return 5 * 10 + r; + if ((r = test_case_labels())) return 6 * 10 + r; + if ((r = test_char_in_const_expr())) return 7 * 10 + r; + return 0; +} diff --git a/tests/cc/118-const-expr.expected-exit b/tests/cc/118-const-expr.expected-exit @@ -0,0 +1 @@ +0