From 34c8aacf1f9304a1cbd6c420b5991cb6da024435 Mon Sep 17 00:00:00 2001 From: lorettayao Date: Mon, 6 Oct 2025 19:35:07 +0800 Subject: [PATCH 1/6] Fix array compound literal parsing to preserve pointer semantics Implement proper array compound literal handling that emits element writes, counts initializers, and returns the temporary array pointer instead of collapsing to the first element. This restores correct pointer semantics and avoids discarding array literals during parsing. Struct and scalar compound literals are unchanged. The parser now tracks whether the closing brace was already consumed by the array helper to prevent double reads. --- src/parser.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/src/parser.c b/src/parser.c index 3fe0ffc1..085c2d4a 100644 --- a/src/parser.c +++ b/src/parser.c @@ -45,7 +45,10 @@ void parse_array_init(var_t *var, block_t *parent, basic_block_t **bb, bool emit_code); - +void parse_array_compound_literal(var_t *var, + block_t *parent, + basic_block_t **bb, + bool emit_code); label_t *find_label(char *name) { @@ -1283,6 +1286,42 @@ void parse_array_init(var_t *var, } } +void parse_array_compound_literal(var_t *var, + block_t *parent, + basic_block_t **bb, + bool emit_code) +{ + int elem_size = var->type->size; + int count = 0; + var->array_size = 0; + var->init_val = 0; + if (!lex_peek(T_close_curly, NULL)) { + for (;;) { + read_expr(parent, bb); + read_ternary_operation(parent, bb); + var_t *value = opstack_pop(); + if (count == 0) + var->init_val = value->init_val; + if (emit_code) { + var_t target = {0}; + target.type = var->type; + target.ptr_level = 0; + var_t *store_val = resize_var(parent, bb, value, &target); + var_t *elem_addr = compute_element_address( + parent, bb, var, count, elem_size); + add_insn(parent, *bb, OP_write, NULL, elem_addr, store_val, + elem_size, NULL); + } + count++; + if (!lex_accept(T_comma)) + break; + if (lex_peek(T_close_curly, NULL)) + break; + } + } + lex_expect(T_close_curly); + var->array_size = count; +} void read_inner_var_decl(var_t *vd, bool anon, bool is_param) { /* Preserve typedef pointer level - don't reset if already inherited */ @@ -2017,9 +2056,16 @@ void read_expr_operand(block_t *parent, basic_block_t **bb) bool is_array_literal = (cast_ptr_level == -1); if (is_array_literal) cast_ptr_level = 0; /* Reset for normal processing */ - + bool consumed_close_brace = false; /* Check if this is a pointer compound literal */ - if (cast_ptr_level > 0) { + if (is_array_literal) { + compound_var->array_size = 0; + add_insn(parent, *bb, OP_allocat, compound_var, NULL, NULL, 0, + NULL); + parse_array_compound_literal(compound_var, parent, bb, true); + opstack_push(compound_var); + consumed_close_brace = true; + } else if (cast_ptr_level > 0) { /* Pointer compound literal: (int*){&x} */ compound_var->ptr_level = cast_ptr_level; @@ -2187,7 +2233,8 @@ void read_expr_operand(block_t *parent, basic_block_t **bb) } } - lex_expect(T_close_curly); + if (!consumed_close_brace) + lex_expect(T_close_curly); } else { /* Regular parenthesized expression */ read_expr(parent, bb); From a35da48889f371473d92a055a78c861d4f6c44c0 Mon Sep 17 00:00:00 2001 From: lorettayao Date: Mon, 6 Oct 2025 20:53:32 +0800 Subject: [PATCH 2/6] Preserve pointer semantics for array literals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce helpers to centralize array-literal scalar decay. Temporary array compound literals are detected and converted to a scalar only when a scalar is actually required; otherwise the expression yields the temporary array’s address, preserving pointer semantics. Update binary ops, direct/compound assignments, function-call arguments, and ternary results to use the unified helper instead of ad-hoc collapsing. This fixes cases where array literals were reduced to their first element in pointer contexts, while keeping struct and plain scalar behavior unchanged. Addresses #299 (array compound literals). --- src/parser.c | 139 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 102 insertions(+), 37 deletions(-) diff --git a/src/parser.c b/src/parser.c index 085c2d4a..a3493911 100644 --- a/src/parser.c +++ b/src/parser.c @@ -46,9 +46,9 @@ void parse_array_init(var_t *var, basic_block_t **bb, bool emit_code); void parse_array_compound_literal(var_t *var, - block_t *parent, - basic_block_t **bb, - bool emit_code); + block_t *parent, + basic_block_t **bb, + bool emit_code); label_t *find_label(char *name) { @@ -1287,9 +1287,9 @@ void parse_array_init(var_t *var, } void parse_array_compound_literal(var_t *var, - block_t *parent, - basic_block_t **bb, - bool emit_code) + block_t *parent, + basic_block_t **bb, + bool emit_code) { int elem_size = var->type->size; int count = 0; @@ -1307,8 +1307,8 @@ void parse_array_compound_literal(var_t *var, target.type = var->type; target.ptr_level = 0; var_t *store_val = resize_var(parent, bb, value, &target); - var_t *elem_addr = compute_element_address( - parent, bb, var, count, elem_size); + var_t *elem_addr = + compute_element_address(parent, bb, var, count, elem_size); add_insn(parent, *bb, OP_write, NULL, elem_addr, store_val, elem_size, NULL); } @@ -1322,6 +1322,35 @@ void parse_array_compound_literal(var_t *var, lex_expect(T_close_curly); var->array_size = count; } +static bool is_array_literal_temp(var_t *var) +{ + return var && var->array_size > 0 && var->var_name[0] == '.'; +} + +static var_t *scalarize_array_literal(block_t *parent, + basic_block_t **bb, + var_t *array_var, + type_t *hint_type) +{ + if (!is_array_literal_temp(array_var)) + return array_var; + + type_t *elem_type = hint_type ? hint_type : array_var->type; + if (!elem_type) + elem_type = TY_int; + + int elem_size = elem_type->size; + if (elem_size <= 0) + elem_size = TY_int->size; + + var_t *scalar = require_typed_var(parent, elem_type); + scalar->ptr_level = 0; + gen_name_to(scalar->var_name); + scalar->init_val = array_var->init_val; + + add_insn(parent, *bb, OP_read, scalar, array_var, NULL, elem_size, NULL); + return scalar; +} void read_inner_var_decl(var_t *vd, bool anon, bool is_param) { /* Preserve typedef pointer level - don't reset if already inherited */ @@ -1614,7 +1643,16 @@ void read_func_parameters(func_t *func, block_t *parent, basic_block_t **bb) read_ternary_operation(parent, bb); param = opstack_pop(); - + if (func) { + if (param_num < func->num_params) { + var_t *target = &func->param_defs[param_num]; + if (!target->ptr_level && !target->array_size) + param = scalarize_array_literal(parent, bb, param, + target->type); + } else if (func->va_args) { + param = scalarize_array_literal(parent, bb, param, TY_int); + } + } /* Handle parameter type conversion for direct calls. * Indirect calls currently don't provide function instance. */ @@ -2857,6 +2895,16 @@ void read_expr(block_t *parent, basic_block_t **bb) continue; /* skip normal processing */ } + bool rs1_is_ptr_like = rs1 && (rs1->ptr_level || rs1->array_size); + bool rs2_is_ptr_like = rs2 && (rs2->ptr_level || rs2->array_size); + + if (is_array_literal_temp(rs1) && !rs2_is_ptr_like) + rs1 = scalarize_array_literal(parent, bb, rs1, + rs2 && rs2->type ? rs2->type : NULL); + + if (is_array_literal_temp(rs2) && !rs1_is_ptr_like) + rs2 = scalarize_array_literal(parent, bb, rs2, + rs1 && rs1->type ? rs1->type : NULL); /* Constant folding for binary operations */ if (rs1 && rs2 && rs1->init_val && !rs1->ptr_level && !rs1->is_global && rs2->init_val && !rs2->ptr_level && !rs2->is_global) { @@ -3477,7 +3525,7 @@ void finalize_logical(opcode_t op, void read_ternary_operation(block_t *parent, basic_block_t **bb) { - var_t *vd, *rs1; + var_t *vd; if (!lex_accept(T_question)) return; @@ -3502,17 +3550,39 @@ void read_ternary_operation(block_t *parent, basic_block_t **bb) abort(); } - rs1 = opstack_pop(); - vd = require_var(parent); - gen_name_to(vd->var_name); - add_insn(parent, then_, OP_assign, vd, rs1, NULL, 0, NULL); + var_t *true_val = opstack_pop(); /* false branch */ read_expr(parent, &else_); bb_connect(*bb, else_, ELSE); + var_t *false_val = opstack_pop(); + bool true_array = is_array_literal_temp(true_val); + bool false_array = is_array_literal_temp(false_val); - rs1 = opstack_pop(); - add_insn(parent, else_, OP_assign, vd, rs1, NULL, 0, NULL); + if (true_array && !false_array) + true_val = scalarize_array_literal(parent, &then_, true_val, + false_val ? false_val->type : NULL); + + if (false_array && !true_array) + false_val = scalarize_array_literal(parent, &else_, false_val, + true_val ? true_val->type : NULL); + + vd = require_var(parent); + gen_name_to(vd->var_name); + add_insn(parent, then_, OP_assign, vd, true_val, NULL, 0, NULL); + add_insn(parent, else_, OP_assign, vd, false_val, NULL, 0, NULL); + + var_t *array_ref = NULL; + if (is_array_literal_temp(true_val)) + array_ref = true_val; + else if (is_array_literal_temp(false_val)) + array_ref = false_val; + + if (array_ref) { + vd->array_size = array_ref->array_size; + vd->init_val = array_ref->init_val; + vd->type = array_ref->type; + } vd->is_ternary_ret = true; opstack_push(vd); @@ -3661,6 +3731,11 @@ bool read_body_assignment(char *token, read_expr(parent, bb); + var_t *rhs_val = opstack_pop(); + if (!lvalue.ptr_level && !lvalue.is_reference) + rhs_val = scalarize_array_literal(parent, bb, rhs_val, + lvalue.type); + opstack_push(rhs_val); vd = require_var(parent); vd->init_val = increment_size; gen_name_to(vd->var_name); @@ -4403,27 +4478,12 @@ basic_block_t *read_body_statement(block_t *parent, basic_block_t *bb) var_t *expr_result = opstack_pop(); - /* Handle array compound literal to scalar assignment. - * When assigning array compound literals to scalar - * variables, use the first element value rather than array - * address. - */ - if (expr_result && expr_result->array_size > 0 && - !var->ptr_level && var->array_size == 0 && var->type && - (var->type->base_type == TYPE_int || - var->type->base_type == TYPE_short) && - expr_result->var_name[0] == '.') { - var_t *first_elem = require_var(parent); - first_elem->type = var->type; - gen_name_to(first_elem->var_name); - - /* Extract first element from compound literal array */ - add_insn(parent, bb, OP_read, first_elem, expr_result, - NULL, var->type->size, NULL); - expr_result = first_elem; - } + var_t *rhs = expr_result; + if (!var->ptr_level && var->array_size == 0) + rhs = + scalarize_array_literal(parent, bb, rhs, var->type); - rs1 = resize_var(parent, &bb, expr_result, var); + rs1 = resize_var(parent, &bb, rhs, var); add_insn(parent, bb, OP_assign, var, rs1, NULL, 0, NULL); } } @@ -4515,8 +4575,13 @@ basic_block_t *read_body_statement(block_t *parent, basic_block_t *bb) } else { read_expr(parent, &bb); read_ternary_operation(parent, &bb); + var_t *expr_result = opstack_pop(); + var_t *rhs = expr_result; + if (!nv->ptr_level && nv->array_size == 0) + rhs = scalarize_array_literal(parent, bb, rhs, + nv->type); - rs1 = resize_var(parent, &bb, opstack_pop(), nv); + rs1 = resize_var(parent, &bb, rhs, nv); add_insn(parent, bb, OP_assign, nv, rs1, NULL, 0, NULL); } } From b44dc57b64476b8c0b397a19265c0476ef945163 Mon Sep 17 00:00:00 2001 From: lorettayao Date: Mon, 6 Oct 2025 22:26:52 +0800 Subject: [PATCH 3/6] Fix formatting, add testcase, remove static --- src/parser.c | 5 +++-- tests/array_ptr.c | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 tests/array_ptr.c diff --git a/src/parser.c b/src/parser.c index a3493911..54ce7f92 100644 --- a/src/parser.c +++ b/src/parser.c @@ -1319,15 +1319,16 @@ void parse_array_compound_literal(var_t *var, break; } } + lex_expect(T_close_curly); var->array_size = count; } -static bool is_array_literal_temp(var_t *var) +bool is_array_literal_temp(var_t *var) { return var && var->array_size > 0 && var->var_name[0] == '.'; } -static var_t *scalarize_array_literal(block_t *parent, +var_t *scalarize_array_literal(block_t *parent, basic_block_t **bb, var_t *array_var, type_t *hint_type) diff --git a/tests/array_ptr.c b/tests/array_ptr.c new file mode 100644 index 00000000..c49add33 --- /dev/null +++ b/tests/array_ptr.c @@ -0,0 +1,39 @@ +#include + +int add(int a, int b) +{ + return a + b; +} + +int main(void) +{ + int *a = (int[]){1, 2, 3, 4, 5}; + + printf("Testing basic array compound literal:\n"); + for (int i = 0; i < 5; i++) + printf(" a[%d] = %d\n", i, a[i]); + + int sum = a[0] + a[4]; + printf("Sum = %d (expect 6)\n", sum); + + int base = 50; + int combined = base + (int[]){100, 200}; + printf("base + (int[]){100,200} = %d (expect 150)\n", combined); + + int acc = 25; + acc += (int[]){100, 200}; + printf("acc after += array literal = %d (expect 125)\n", acc); + + int flag = 1; + int ternary_val = flag ? (int[]){25, 50} : (int){15}; + printf("ternary true branch = %d (expect 25)\n", ternary_val); + + flag = 0; + ternary_val = flag ? (int[]){25, 50} : (int){15}; + printf("ternary false branch = %d (expect 15)\n", ternary_val); + + int func_val = add((int){5}, (int[]){10, 20, 30}); + printf("add((int){5}, (int[]){10,20,30}) = %d (expect 15)\n", func_val); + + return 0; +} From 1f7bed4f430e328aa36d42460916e949a0c511d9 Mon Sep 17 00:00:00 2001 From: lorettayao Date: Wed, 8 Oct 2025 15:18:56 +0800 Subject: [PATCH 4/6] Fix misleading function naming --- src/parser.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/parser.c b/src/parser.c index 54ce7f92..18168306 100644 --- a/src/parser.c +++ b/src/parser.c @@ -1323,9 +1323,14 @@ void parse_array_compound_literal(var_t *var, lex_expect(T_close_curly); var->array_size = count; } -bool is_array_literal_temp(var_t *var) +/* Identify compiler-emitted temporaries that hold array compound literals. + * Parsing assigns these temporaries synthetic names via gen_name_to (".tN") + * and they keep array metadata without pointer indirection. + */ +bool is_compound_literal_array_temp(var_t *var) { - return var && var->array_size > 0 && var->var_name[0] == '.'; + return var && var->array_size > 0 && !var->ptr_level && + var->var_name[0] == '.'; } var_t *scalarize_array_literal(block_t *parent, @@ -1333,7 +1338,7 @@ var_t *scalarize_array_literal(block_t *parent, var_t *array_var, type_t *hint_type) { - if (!is_array_literal_temp(array_var)) + if (!is_compound_literal_array_temp(array_var)) return array_var; type_t *elem_type = hint_type ? hint_type : array_var->type; @@ -2899,11 +2904,11 @@ void read_expr(block_t *parent, basic_block_t **bb) bool rs1_is_ptr_like = rs1 && (rs1->ptr_level || rs1->array_size); bool rs2_is_ptr_like = rs2 && (rs2->ptr_level || rs2->array_size); - if (is_array_literal_temp(rs1) && !rs2_is_ptr_like) + if (is_compound_literal_array_temp(rs1) && !rs2_is_ptr_like) rs1 = scalarize_array_literal(parent, bb, rs1, rs2 && rs2->type ? rs2->type : NULL); - if (is_array_literal_temp(rs2) && !rs1_is_ptr_like) + if (is_compound_literal_array_temp(rs2) && !rs1_is_ptr_like) rs2 = scalarize_array_literal(parent, bb, rs2, rs1 && rs1->type ? rs1->type : NULL); /* Constant folding for binary operations */ @@ -3557,8 +3562,8 @@ void read_ternary_operation(block_t *parent, basic_block_t **bb) read_expr(parent, &else_); bb_connect(*bb, else_, ELSE); var_t *false_val = opstack_pop(); - bool true_array = is_array_literal_temp(true_val); - bool false_array = is_array_literal_temp(false_val); + bool true_array = is_compound_literal_array_temp(true_val); + bool false_array = is_compound_literal_array_temp(false_val); if (true_array && !false_array) true_val = scalarize_array_literal(parent, &then_, true_val, @@ -3574,9 +3579,9 @@ void read_ternary_operation(block_t *parent, basic_block_t **bb) add_insn(parent, else_, OP_assign, vd, false_val, NULL, 0, NULL); var_t *array_ref = NULL; - if (is_array_literal_temp(true_val)) + if (is_compound_literal_array_temp(true_val)) array_ref = true_val; - else if (is_array_literal_temp(false_val)) + else if (is_compound_literal_array_temp(false_val)) array_ref = false_val; if (array_ref) { From d6b9fbf72dc992311c568f68029fa95f088026f5 Mon Sep 17 00:00:00 2001 From: lorettayao Date: Wed, 8 Oct 2025 15:28:29 +0800 Subject: [PATCH 5/6] Fix data format according to warnings --- src/parser.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/parser.c b/src/parser.c index 18168306..9866936b 100644 --- a/src/parser.c +++ b/src/parser.c @@ -1334,9 +1334,9 @@ bool is_compound_literal_array_temp(var_t *var) } var_t *scalarize_array_literal(block_t *parent, - basic_block_t **bb, - var_t *array_var, - type_t *hint_type) + basic_block_t **bb, + var_t *array_var, + type_t *hint_type) { if (!is_compound_literal_array_temp(array_var)) return array_var; @@ -4486,8 +4486,8 @@ basic_block_t *read_body_statement(block_t *parent, basic_block_t *bb) var_t *rhs = expr_result; if (!var->ptr_level && var->array_size == 0) - rhs = - scalarize_array_literal(parent, bb, rhs, var->type); + rhs = scalarize_array_literal(parent, &bb, rhs, + var->type); rs1 = resize_var(parent, &bb, rhs, var); add_insn(parent, bb, OP_assign, var, rs1, NULL, 0, NULL); @@ -4584,7 +4584,7 @@ basic_block_t *read_body_statement(block_t *parent, basic_block_t *bb) var_t *expr_result = opstack_pop(); var_t *rhs = expr_result; if (!nv->ptr_level && nv->array_size == 0) - rhs = scalarize_array_literal(parent, bb, rhs, + rhs = scalarize_array_literal(parent, &bb, rhs, nv->type); rs1 = resize_var(parent, &bb, rhs, nv); From 272c912c15a5c4275d2612b172992c9f14b7522a Mon Sep 17 00:00:00 2001 From: lorettayao Date: Wed, 8 Oct 2025 17:02:37 +0800 Subject: [PATCH 6/6] add: test case for array compound literals --- tests/driver.sh | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/driver.sh b/tests/driver.sh index 6f63879f..5e3d4cd4 100755 --- a/tests/driver.sh +++ b/tests/driver.sh @@ -724,6 +724,28 @@ int main() { } EOF +# Test: Array compound literal decay to pointer in initializer +try_ 0 << EOF +int main(void) { + int *arr = (int[]){1, 2, 3, 4, 5}; + return arr[0] != 1 || arr[4] != 5; +} +EOF + +# Test: Passing array compound literal as pointer argument +try_ 0 << EOF +int sum(int *p, int n) { + int s = 0; + for (int i = 0; i < n; i++) + s += p[i]; + return s; +} +int main(void) { + int s = sum((int[]){1, 2, 3, 0, 0}, 3); + return s != 6; +} +EOF + # Test: Complex expression with compound literals try_ 77 << EOF int main() {