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:
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