Skip to content

Commit 83716cd

Browse files
committed
Add tests for array literal pointer semantics
Add regression tests that verify pointer and decay semantics of array compound literals. The tests cover decay during initialization, passing array literals to functions that expect pointer arguments, pointer-typed ternary expressions involving literal branches, and correctness for char[] and short[] literals in simple computations. Add a driver test to ensure array compound literals decay to pointers (int *p = (int[]){42,43,44}) and fix parsing to track compound literals explicitly and keep them pointer-like for varargs and pointer initializers. These tests confirm the behavior introduced by the refined compound literal semantics implemented in the preceding commit. Parser cleanup: flattened the nested parameter-handling conditionals in parser.c so array-literal parameters are scalarized and promoted/resized without deep if-nesting, improving readability while preserving the decay semantics enforced by the new tests
1 parent 11c6b59 commit 83716cd

File tree

7 files changed

+155
-52
lines changed

7 files changed

+155
-52
lines changed

src/defs.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,11 @@ struct var {
410410
* the variable is based on the top of the local stack.
411411
*/
412412
bool ofs_based_on_stack_top;
413+
414+
/* True when this variable was synthesized to hold a compound literal
415+
* (e.g., array or struct literal temporaries).
416+
*/
417+
bool is_compound_literal;
413418
};
414419

415420
typedef struct {

src/parser.c

Lines changed: 86 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,6 @@ void parse_array_init(var_t *var,
4545
block_t *parent,
4646
basic_block_t **bb,
4747
bool emit_code);
48-
void parse_array_compound_literal(var_t *var,
49-
block_t *parent,
50-
basic_block_t **bb,
51-
bool emit_code);
5248

5349
label_t *find_label(char *name)
5450
{
@@ -877,6 +873,7 @@ void parse_array_literal_expr(block_t *parent, basic_block_t **bb)
877873
{
878874
var_t *array_var = require_var(parent);
879875
gen_name_to(array_var->var_name);
876+
array_var->is_compound_literal = true;
880877

881878
int element_count = 0;
882879
var_t *first_element = NULL;
@@ -1335,8 +1332,7 @@ void parse_array_init(var_t *var,
13351332

13361333
void parse_array_compound_literal(var_t *var,
13371334
block_t *parent,
1338-
basic_block_t **bb,
1339-
bool emit_code)
1335+
basic_block_t **bb)
13401336
{
13411337
int elem_size = var->type->size;
13421338
int count = 0;
@@ -1349,16 +1345,16 @@ void parse_array_compound_literal(var_t *var,
13491345
var_t *value = opstack_pop();
13501346
if (count == 0)
13511347
var->init_val = value->init_val;
1352-
if (emit_code) {
1353-
var_t target = {0};
1354-
target.type = var->type;
1355-
target.ptr_level = 0;
1356-
var_t *store_val = resize_var(parent, bb, value, &target);
1357-
var_t *elem_addr =
1358-
compute_element_address(parent, bb, var, count, elem_size);
1359-
add_insn(parent, *bb, OP_write, NULL, elem_addr, store_val,
1360-
elem_size, NULL);
1361-
}
1348+
1349+
var_t target = {0};
1350+
target.type = var->type;
1351+
target.ptr_level = 0;
1352+
var_t *store_val = resize_var(parent, bb, value, &target);
1353+
var_t *elem_addr =
1354+
compute_element_address(parent, bb, var, count, elem_size);
1355+
add_insn(parent, *bb, OP_write, NULL, elem_addr, store_val,
1356+
elem_size, NULL);
1357+
13621358
count++;
13631359
if (!lex_accept(T_comma))
13641360
break;
@@ -1371,13 +1367,13 @@ void parse_array_compound_literal(var_t *var,
13711367
var->array_size = count;
13721368
}
13731369
/* Identify compiler-emitted temporaries that hold array compound literals.
1374-
* Parsing assigns these temporaries synthetic names via gen_name_to (".tN")
1375-
* and they keep array metadata without pointer indirection.
1370+
* They keep array metadata without pointer indirection and are marked via
1371+
* is_compound_literal when synthesized.
13761372
*/
13771373
bool is_array_literal_placeholder(var_t *var)
13781374
{
13791375
return var && var->array_size > 0 && !var->ptr_level &&
1380-
var->var_name[0] == '.';
1376+
var->is_compound_literal;
13811377
}
13821378

13831379
bool is_pointer_like_value(var_t *var)
@@ -1386,6 +1382,11 @@ bool is_pointer_like_value(var_t *var)
13861382
(var->type && var->type->ptr_level > 0));
13871383
}
13881384

1385+
/* Lower a compiler-emitted array literal placeholder (marked via
1386+
* is_compound_literal) into a scalar temporary when later IR expects a plain
1387+
* value instead of addressable storage. This keeps SSA joins uniform when only
1388+
* one branch originates from an array literal.
1389+
*/
13891390
var_t *scalarize_array_literal(block_t *parent,
13901391
basic_block_t **bb,
13911392
var_t *array_var,
@@ -1394,24 +1395,50 @@ var_t *scalarize_array_literal(block_t *parent,
13941395
if (!is_array_literal_placeholder(array_var))
13951396
return array_var;
13961397

1398+
/* Array literal placeholders carry the literal's natural type; default to
1399+
* int when the parser left the type unset.
1400+
*/
13971401
type_t *literal_type = array_var->type ? array_var->type : TY_int;
13981402
int literal_size = literal_type->size;
13991403
if (literal_size <= 0)
14001404
literal_size = TY_int->size;
14011405

1406+
/* A caller-provided hint (e.g., assignment target) dictates the result
1407+
* type when available so we reuse wider/narrower scalar destinations.
1408+
*/
14021409
type_t *result_type = hint_type ? hint_type : literal_type;
14031410
if (!result_type)
14041411
result_type = TY_int;
14051412

1413+
/* Create a new scalar temporary, giving it a unique name and copying over
1414+
* the literal data so downstream code can treat it like a normal value.
1415+
*/
14061416
var_t *scalar = require_typed_var(parent, result_type);
14071417
scalar->ptr_level = 0;
14081418
gen_name_to(scalar->var_name);
14091419
scalar->init_val = array_var->init_val;
14101420

1421+
/* Materialize the literal data into the scalar temporary via an OP_read. */
14111422
add_insn(parent, *bb, OP_read, scalar, array_var, NULL, literal_size, NULL);
14121423

14131424
return scalar;
14141425
}
1426+
1427+
/* Centralized guard for lowering array literal placeholders when a scalar
1428+
* value is expected, keeping the scattered special cases consistent.
1429+
*/
1430+
var_t *scalarize_array_literal_if_needed(block_t *parent,
1431+
basic_block_t **bb,
1432+
var_t *value,
1433+
type_t *hint_type,
1434+
bool needs_scalar)
1435+
{
1436+
if (!needs_scalar)
1437+
return value;
1438+
1439+
return scalarize_array_literal(parent, bb, value, hint_type);
1440+
}
1441+
14151442
void read_inner_var_decl(var_t *vd, bool anon, bool is_param)
14161443
{
14171444
/* Preserve typedef pointer level - don't reset if already inherited */
@@ -1704,24 +1731,24 @@ void read_func_parameters(func_t *func, block_t *parent, basic_block_t **bb)
17041731
read_ternary_operation(parent, bb);
17051732

17061733
param = opstack_pop();
1707-
if (func) {
1708-
if (param_num < func->num_params) {
1709-
var_t *target = &func->param_defs[param_num];
1710-
if (!target->ptr_level && !target->array_size)
1711-
param = scalarize_array_literal(parent, bb, param,
1712-
target->type);
1713-
}
1734+
if (func && param_num < func->num_params) {
1735+
var_t *target = &func->param_defs[param_num];
1736+
if (!target->ptr_level && !target->array_size)
1737+
param =
1738+
scalarize_array_literal(parent, bb, param, target->type);
17141739
}
17151740
/* Handle parameter type conversion for direct calls.
17161741
* Indirect calls currently don't provide function instance.
17171742
*/
1718-
if (func) {
1719-
if (param_num >= func->num_params && func->va_args) {
1743+
if (func && param_num >= func->num_params && func->va_args) {
1744+
/* Default promotions apply to scalar varargs, but pointer-like
1745+
* values (including array literals) must flow through unchanged so
1746+
* "%p" and friends see an address rather than a scalarized value.
1747+
*/
1748+
if (!is_pointer_like_value(param))
17201749
param = promote(parent, bb, param, TY_int, 0);
1721-
} else {
1722-
param =
1723-
resize_var(parent, bb, param, &func->param_defs[param_num]);
1724-
}
1750+
} else if (func) {
1751+
param = resize_var(parent, bb, param, &func->param_defs[param_num]);
17251752
}
17261753

17271754
params[param_num++] = param;
@@ -2148,6 +2175,7 @@ void read_expr_operand(block_t *parent, basic_block_t **bb)
21482175
var_t *compound_var =
21492176
require_typed_var(parent, cast_or_literal_type);
21502177
gen_name_to(compound_var->var_name);
2178+
compound_var->is_compound_literal = true;
21512179

21522180
/* Check if this is an array compound literal (int[]){...} */
21532181
bool is_array_literal = (cast_ptr_level == -1);
@@ -2159,7 +2187,7 @@ void read_expr_operand(block_t *parent, basic_block_t **bb)
21592187
compound_var->array_size = 0;
21602188
add_insn(parent, *bb, OP_allocat, compound_var, NULL, NULL, 0,
21612189
NULL);
2162-
parse_array_compound_literal(compound_var, parent, bb, true);
2190+
parse_array_compound_literal(compound_var, parent, bb);
21632191

21642192
if (compound_var->array_size == 0) {
21652193
compound_var->init_val = 0;
@@ -3628,13 +3656,22 @@ void read_ternary_operation(block_t *parent, basic_block_t **bb)
36283656
bool true_ptr_like = is_pointer_like_value(true_val);
36293657
bool false_ptr_like = is_pointer_like_value(false_val);
36303658

3631-
if (true_array && !false_ptr_like)
3632-
true_val = scalarize_array_literal(parent, &then_, true_val,
3633-
false_val ? false_val->type : NULL);
3659+
/* The ternary result must look like whichever side is pointer-like. If the
3660+
* "true" expression is still a raw array literal but the "false" side is a
3661+
* plain scalar, materialize the literal now so both branches produce
3662+
* comparable scalar SSA values.
3663+
*/
3664+
true_val = scalarize_array_literal_if_needed(
3665+
parent, &then_, true_val, false_val ? false_val->type : NULL,
3666+
true_array && !false_ptr_like);
36343667

3635-
if (false_array && !true_ptr_like)
3636-
false_val = scalarize_array_literal(parent, &else_, false_val,
3637-
true_val ? true_val->type : NULL);
3668+
/* Apply the same conversion symmetrically when only the false branch is a
3669+
* literal array. This prevents OP_assign from trying to move array storage
3670+
* into a scalar destination later in code generation.
3671+
*/
3672+
false_val = scalarize_array_literal_if_needed(
3673+
parent, &else_, false_val, true_val ? true_val->type : NULL,
3674+
false_array && !true_ptr_like);
36383675

36393676
vd = require_var(parent);
36403677
gen_name_to(vd->var_name);
@@ -3801,9 +3838,9 @@ bool read_body_assignment(char *token,
38013838
read_expr(parent, bb);
38023839

38033840
var_t *rhs_val = opstack_pop();
3804-
if (!lvalue.ptr_level && !lvalue.is_reference)
3805-
rhs_val = scalarize_array_literal(parent, bb, rhs_val,
3806-
lvalue.type);
3841+
rhs_val = scalarize_array_literal_if_needed(
3842+
parent, bb, rhs_val, lvalue.type,
3843+
!lvalue.ptr_level && !lvalue.is_reference);
38073844
opstack_push(rhs_val);
38083845
vd = require_var(parent);
38093846
vd->init_val = increment_size;
@@ -4546,9 +4583,9 @@ basic_block_t *read_body_statement(block_t *parent, basic_block_t *bb)
45464583
read_ternary_operation(parent, &bb);
45474584

45484585
var_t *rhs = opstack_pop();
4549-
if (!var->ptr_level && var->array_size == 0)
4550-
rhs = scalarize_array_literal(parent, &bb, rhs,
4551-
var->type);
4586+
rhs = scalarize_array_literal_if_needed(
4587+
parent, &bb, rhs, var->type,
4588+
!var->ptr_level && var->array_size == 0);
45524589

45534590
rs1 = resize_var(parent, &bb, rhs, var);
45544591
add_insn(parent, bb, OP_assign, var, rs1, NULL, 0, NULL);
@@ -4643,9 +4680,9 @@ basic_block_t *read_body_statement(block_t *parent, basic_block_t *bb)
46434680
read_expr(parent, &bb);
46444681
read_ternary_operation(parent, &bb);
46454682
var_t *rhs = opstack_pop();
4646-
if (!nv->ptr_level && nv->array_size == 0)
4647-
rhs = scalarize_array_literal(parent, &bb, rhs,
4648-
nv->type);
4683+
rhs = scalarize_array_literal_if_needed(
4684+
parent, &bb, rhs, nv->type,
4685+
!nv->ptr_level && nv->array_size == 0);
46494686

46504687
rs1 = resize_var(parent, &bb, rhs, nv);
46514688
add_insn(parent, bb, OP_assign, nv, rs1, NULL, 0, NULL);
@@ -5303,6 +5340,7 @@ void initialize_struct_field(var_t *nv, var_t *v, int offset)
53035340
nv->base = NULL;
53045341
nv->subscript = 0;
53055342
nv->subscripts_idx = 0;
5343+
nv->is_compound_literal = false;
53065344
}
53075345

53085346
void read_global_statement(void)

tests/driver.sh

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,28 @@ int main() {
733733
}
734734
EOF
735735

736+
# Test: Array compound literal decay to pointer in initializer
737+
try_ 0 << EOF
738+
int main(void) {
739+
int *arr = (int[]){1, 2, 3, 4, 5};
740+
return arr[0] != 1 || arr[4] != 5;
741+
}
742+
EOF
743+
744+
# Test: Passing array compound literal as pointer argument
745+
try_ 0 << EOF
746+
int sum(int *p, int n) {
747+
int s = 0;
748+
for (int i = 0; i < n; i++)
749+
s += p[i];
750+
return s;
751+
}
752+
int main(void) {
753+
int s = sum((int[]){1, 2, 3, 0, 0}, 3);
754+
return s != 6;
755+
}
756+
EOF
757+
736758
# Test: Complex expression with compound literals
737759
try_ 77 << EOF
738760
int main() {
@@ -4737,6 +4759,36 @@ int main() {
47374759
}
47384760
EOF
47394761

4762+
try_ 200 << EOF
4763+
int main(void) {
4764+
char *s = (char[]){'A', 'B', 'C', 'D', 'E'};
4765+
return s[0] + s[1] + s[4]; /* 65 + 66 + 69 */
4766+
}
4767+
EOF
4768+
4769+
try_ 6 << EOF
4770+
int main(void) {
4771+
short *s = (short[]){1, 2, 3, 4, 5};
4772+
return s[0] + s[4];
4773+
}
4774+
EOF
4775+
4776+
try_ 60 << EOF
4777+
int main(void) {
4778+
int arr[] = {10, 20, 30, 40, 50};
4779+
int *selected = 1 ? arr : (int[]){1, 2, 3, 4, 5};
4780+
return selected[0] + selected[4];
4781+
}
4782+
EOF
4783+
4784+
try_ 6 << EOF
4785+
int main(void) {
4786+
int arr[] = {10, 20, 30, 40, 50};
4787+
int *selected = 0 ? arr : (int[]){1, 2, 3, 4, 5};
4788+
return selected[0] + selected[4];
4789+
}
4790+
EOF
4791+
47404792
try_ 120 << EOF
47414793
int main() {
47424794
/* Complex expression with mixed compound literals */
@@ -4789,6 +4841,14 @@ int main() {
47894841
}
47904842
EOF
47914843

4844+
# Array literal decay in initializer for pointer variable
4845+
try_ 0 << EOF
4846+
int main(void) {
4847+
int *p = (int[]){42, 43, 44};
4848+
return *p == 42 ? 0 : 1;
4849+
}
4850+
EOF
4851+
47924852
# Test pointer compound literals
47934853
try_ 0 << EOF
47944854
int main()

tests/snapshots/fib-arm-static.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

tests/snapshots/fib-riscv-static.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

tests/snapshots/hello-arm-static.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

tests/snapshots/hello-riscv-static.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)