Repository navigation
gen-c: a function returning a fixed array returns the address of a stack local #3445
Description
What
fn probe(v: i32) -> [4]u8 { var a : [4]u8 = [0,0,0,0]; return a; }
uint8_t* probe(int32_t v) {
uint8_t a[4] = { 0, 0, 0, 0 };
return a;
}warning: address of stack memory associated with local variable 'a' returned [-Wreturn-stack-address]
Apple clang 21. The caller receives a pointer into a frame that no longer exists.
Population
18 sites in 14 files, over 582 generated C translation units. Counted by clang, not by a grep.
specs/base/ternary_encoding.t27 specs/github/comments.t27
specs/cloud/railway_deploy.t27 specs/github/issues.t27
specs/compiler/mod_structure.t27 specs/github/prs.t27
specs/config/schema.t27 specs/github/tests/e2e_full_flow.t27
specs/demos/jones/topology_*.t27 specs/memory/formula_embed.t27
specs/server/mdns.t27
The mechanism already exists, and the spelling cannot reach it
The other array spelling works completely:
fn probe(v: i32) -> [u8; 4] { var a : [u8; 4] = [0,0,0,0]; a[0] = 7; return a; }
typedef struct { uint8_t v[4]; } t27_arr_uint8_t_4;
t27_arr_uint8_t_4 probe(int32_t v) {
t27_arr_uint8_t_4 a = { 0, 0, 0, 0 };
a.v[0] = 7;
return a;
}Zero diagnostics. c_array_info requires a ;, so [N]T never reaches any of it.
The obvious fix was tried and it is WRONG — measured
Normalising [N]T → [T; N] inside c_array_info (guarding size 0, and leaving parameter position to c_static_array_param so [static N] survives — verified) makes the corpus worse:
| before | after | |
|---|---|---|
| errors | 3849 | 3878 |
| files that compile | 296 | 287 |
-Wreturn-stack-address |
18 | 15 |
The target warning barely moves and nine files stop compiling. The new errors say why:
+56 warning: incompatible pointer types passing 't27_arr_int32_t_3 *' to parameter
+18 't27_arr_int32_t_5 *' +18 't27_arr_int32_t_27 *' +13 't27_arr_int32_t_10 *'
+15 error: subscripted value is not an array, pointer, or vector
The type becomes a struct while the indexing (a[i] rather than a.v[i]) and the argument passing at call sites do not. The two spellings have divergent downstream handling, and normalising only the type is not a drop-in.
This change is not in a branch. It was built, measured, and reverted.
What a real fix needs
Three things travelling together, which is why this is an issue and not a PR:
- the type (done above, and easy),
- every index of such a value becoming
.v[i], - every call site passing one, since callees take
T x[static N]— a struct is not that.
Item 3 interacts with #3435: parameters deliberately keep T x[static N], so a struct return feeding a call would need .v at the boundary. Whether returns and parameters should use the same representation is a design question, and it is the one worth answering before any code moves.
Boundary
specs/base/ternary_encoding.t27specs/cloud/railway_deploy.t27specs/compiler/mod_structure.t27specs/config/schema.t27specs/github/comments.t27specs/github/issues.t27specs/github/prs.t27specs/github/tests/e2e_full_flow.t27specs/memory/formula_embed.t27specs/server/mdns.t27
Activity
gen-c: a fixed-array struct field is storage, not a pointer (#3447)
gen-c: an array literal argument is a compound literal (#3476)
gen-c: a slice field takes the compound literal, except in a return (#…
gHashTag commentedon Sep 23, 2026
A boundary drafted from the paths this issue already names, so a bee can take it. Nothing is reserved until this is in the issue body - the Queen reads the body, not comments. Check the paths first: a wrong boundary reserves files the work does not own.
Boundary
specs/base/ternary_encoding.t27specs/cloud/railway_deploy.t27specs/compiler/mod_structure.t27specs/config/schema.t27specs/github/comments.t27specs/github/issues.t27specs/github/prs.t27specs/github/tests/e2e_full_flow.t27specs/memory/formula_embed.t27specs/server/mdns.t27github-actions commentedon Oct 3, 2026
Re-filed as #5710.
An attempt claimed this issue on 2026-09-24 and left an empty branch. A dispatch row that has spent its retry ceiling is released once and then keeps its issue for good, which is the right rule for a third identical failure and the wrong outcome for an issue nobody ever attempted twice. The new issue carries the same work, the same boundary, and today's brief.
Closing this one so the swarm can take the work again.
salvage(queen-3445): commit what the turn left uncommitted