wcc/check+w6c+w6c_ww: materialize array-literal slice-borrow base into per-fn scratch (fix #25 + #31)
A one-step `let xs: []T = [e0,e1,..]` had two faults. #31 (silent, cs!=ww): the #258 array→slice borrow wrapped the un-addressable N_ARRLIT directly as the N_SLICE base and cgen never spilled it to a stack slot, so .ptr dangled (`let xs:[]i32=[10,20,30]; xs[1]` returned the un-stored header 1; []u8/[]str segfaulted). #25 (over-strict): a slice target fell through to the exact- element type_eq borrow gate, rejecting bare-int-width ([]u8=[1,2,3]) and str elements the array-init path coerces. Fix (re-stamp + per-borrow scratch; both stages byte-identical asm): - Checker re-stamps the slice arrlit as [count]T, reusing the array-init per-element coercion + range-check (#25): in-range accepts, out-of-range loud-rejects. cstage arrlit_init_fits gains a TY_SLICE arm; wwstage checkletassign mirrors it and stashes the synthesized [count]T tnode on arrlit.lhs (free for N_ARRLIT) so cgen can size the backing NODE-wise (elemsizeofc) and count from the tnode's .rhs intlit — the arrlit's own value tinfo carries the literal's untyped element (unsized), so node-first sizing is required (a cstage/wwstage representation divergence; cstage's Type IS sized and reads base->type). - cgen materialises the N_ARRLIT borrow base into a FRESH per-borrow @slicescr stack slot (distinct slot per borrow: a borrow's backing must outlive the lowering, so it can't share a cached @aggargscr/@tagscr-style slot — two live borrows would alias one backing; localalloc/local_alloc is always-fresh), filled by REUSING the array-init element fill extracted from the N_LET path (cstage cg_arrlit_fill_bp, wwstage cgarrlitfillbp — same store sequence the byte-id-green `let a:[N]T=[..]` uses, the frame-order + store-op guarantee), then LEAQ'd as the base. Supported ONLY at a `let` init. In call-arg / return / assign position there is no addressable backing, so both stages LOUD-REJECT ("bind it to a `let` first") — aligning cstage DOWN to wwstage (which already refused the untyped arrlit element) per rule-10; this closes #31's silent call-arg segfault as a compile error. Full non-let support is deferred (#33). Escape (rule-8 WHY): a `let xs:[]T=[..]; return xs;` returns a slice into a freed frame slot = dangling, IDENTICAL to the pre-existing named-array borrow and Hare-consistent (no escape analysis / GC / heap promotion). Test 953_arrlit_slice_run: 8 accept rows (cstage runtime readback + cs==ww byte-id, frame-size canary incl.) covering the #31 i32 pin, bare-int→u8 coercion, str readback, the multi-live soundness pin (xs[0]+ys[0]=5, not 8 — proves fresh-per-borrow), and a mutate-through-borrow proof; 4 reject rows (out-of-range element + the three non-let contexts, loud in both stages). Tuple-element slices stay blocked by the pre-existing #30 array-init FATAL.
This commit is contained in:
369
cmd/w6c/cgen.c
369
cmd/w6c/cgen.c
@@ -2984,6 +2984,175 @@ cg_tagged_tuple_payload_shift(Cg *c, Type *tup)
|
||||
ins2(c, A_MOVQ, areg(seq[i + 1]), areg(seq[i]));
|
||||
}
|
||||
|
||||
/* cg_arrlit_fill_bp — #31: fill the [count]T destination at BP-relative
|
||||
* `off` from an N_ARRLIT, extracted verbatim from the N_LET array-init
|
||||
* path so the slice-borrow base materialisation (the N_SLICE-over-
|
||||
* N_ARRLIT arm) reuses the identical element-store sequence. `lu` is the
|
||||
* [count]T array type the checker re-stamped (#25); `arrlit` the literal. */
|
||||
static void
|
||||
cg_arrlit_fill_bp(Cg *c, Local **locals, Type *lu, Node *arrlit, int off)
|
||||
{
|
||||
Type *esub = lu->sub;
|
||||
int esz = esub ? (int)esub->size : 1;
|
||||
/* #270-1c: an AGGREGATE (struct/array/tuple) element
|
||||
* of an array literal — the scalar per-element MOVQ
|
||||
* below stores only the first 8 bytes (unpopulated
|
||||
* tail). Fill each element slot from its literal
|
||||
* (cg_structlit_fill_bp) or source ident (word-copy). */
|
||||
Type *esubu = type_chase_named(esub);
|
||||
int is_agg = esubu && (esubu->kind == TY_STRUCT
|
||||
|| esubu->kind == TY_ARRAY
|
||||
|| esubu->kind == TY_TUPLE);
|
||||
/* #12: a tagged-union element. NOT folded into is_agg —
|
||||
* is_agg's body does N_STRUCTLIT/N_IDENT word-copy and
|
||||
* FATALs on the literal/scalar case, never boxing the
|
||||
* tag+payload. Route each element through the same
|
||||
* cg_widen_tagged_store choke-point every other tagged
|
||||
* store uses (let-init, vararg gather, struct-field). */
|
||||
int is_tagged_el = esubu && esubu->kind == TY_TAGGED;
|
||||
int is_str_el = type_isstr(esub);
|
||||
/* #20/#270 str-slice arm: a slice element is a 24B
|
||||
* {ptr,len,cap} header just like str; cgexpr lowers it
|
||||
* into AX/BX/CX. Both must store all three words — the
|
||||
* scalar 1-word MOVQ below drops .len and .cap. */
|
||||
int is_slice_el = type_isslice(esub);
|
||||
/* float element → store FROM X0; the AX path stores
|
||||
* raw double low-bits, garbage for f32 (#122, twin of
|
||||
* the arr[i]= store fix and the cgen.c:6423 read). */
|
||||
int is_float_el = type_isfloat(esub);
|
||||
int fmov = type_isf32(esub) ? A_MOVSS : A_MOVSD;
|
||||
int op = A_MOVQ;
|
||||
if (!is_str_el) {
|
||||
if (esz == 1) op = A_MOVB;
|
||||
else if (esz == 2) op = A_MOVW;
|
||||
else if (esz == 4) op = A_MOVL;
|
||||
/* #128a: esz==2 routes to MOVW (A_MOVW landed in
|
||||
* both stages' w6a). Pre-fix the 2-byte case fell
|
||||
* through to MOVQ, over-writing 6B into the next
|
||||
* element's slot; sequential adjacent writes
|
||||
* accident-corrected fully-init arrays but
|
||||
* partial inits clobbered neighbours. */
|
||||
}
|
||||
int idx = 0;
|
||||
Node *last = NULL;
|
||||
int repeat = 0;
|
||||
for (Node *e = arrlit->list; e; e = e->next) {
|
||||
if (e->kind == N_FIELD && e->str &&
|
||||
strcmp(e->str, "...") == 0) {
|
||||
repeat = 1;
|
||||
break;
|
||||
}
|
||||
int base = off + idx * esz;
|
||||
if (is_agg) {
|
||||
if (e->kind == N_STRUCTLIT) {
|
||||
cg_structlit_fill_bp(c, locals,
|
||||
esubu, e, base);
|
||||
} else if (e->kind == N_IDENT) {
|
||||
int soff = localfind(*locals,
|
||||
e->str);
|
||||
int k = 0;
|
||||
for (; k + 8 <= esz; k += 8) {
|
||||
ins2(c, A_MOVQ,
|
||||
amem(D_BP, soff + k),
|
||||
areg(D_AX));
|
||||
ins2(c, A_MOVQ,
|
||||
areg(D_AX),
|
||||
amem(D_BP, base + k));
|
||||
}
|
||||
if (k + 4 <= esz) {
|
||||
ins2(c, A_MOVL,
|
||||
amem(D_BP, soff + k),
|
||||
areg(D_AX));
|
||||
ins2(c, A_MOVL,
|
||||
areg(D_AX),
|
||||
amem(D_BP, base + k));
|
||||
k += 4;
|
||||
}
|
||||
if (k + 2 <= esz) {
|
||||
ins2(c, A_MOVW,
|
||||
amem(D_BP, soff + k),
|
||||
areg(D_AX));
|
||||
ins2(c, A_MOVW,
|
||||
areg(D_AX),
|
||||
amem(D_BP, base + k));
|
||||
k += 2;
|
||||
}
|
||||
if (k + 1 <= esz) {
|
||||
ins2(c, A_MOVB,
|
||||
amem(D_BP, soff + k),
|
||||
areg(D_AX));
|
||||
ins2(c, A_MOVB,
|
||||
areg(D_AX),
|
||||
amem(D_BP, base + k));
|
||||
k += 1;
|
||||
}
|
||||
} else {
|
||||
fatal("#270-1c: array-literal "
|
||||
"aggregate element shape "
|
||||
"unsupported (rule-7)");
|
||||
}
|
||||
last = e;
|
||||
idx++;
|
||||
continue;
|
||||
}
|
||||
if (is_tagged_el) {
|
||||
cg_widen_tagged_store(c, locals, esub,
|
||||
e, D_BP, base, esz);
|
||||
last = e;
|
||||
idx++;
|
||||
continue;
|
||||
}
|
||||
cgexpr(c, e, *locals);
|
||||
if (is_str_el || is_slice_el) {
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, base));
|
||||
ins2(c, A_MOVQ, areg(D_BX),
|
||||
amem(D_BP, base + 8));
|
||||
ins2(c, A_MOVQ, areg(D_CX),
|
||||
amem(D_BP, base + 16));
|
||||
} else if (is_float_el) {
|
||||
ins2(c, fmov, areg(D_X0),
|
||||
amem(D_BP, base));
|
||||
} else {
|
||||
ins2(c, op, areg(D_AX),
|
||||
amem(D_BP, base));
|
||||
}
|
||||
last = e;
|
||||
idx++;
|
||||
}
|
||||
if (repeat && is_agg)
|
||||
fatal("#270-1c: `...` repeat of an aggregate "
|
||||
"array-literal element not wired (rule-7)");
|
||||
/* #12: `...` re-stores from AX, but cg_widen_tagged_store
|
||||
* consumed the node and trashed AX — a repeat-fill would
|
||||
* write garbage. No consumer needs `[N]tagged=[x,...]`. */
|
||||
if (repeat && is_tagged_el)
|
||||
fatal("#12: `...` repeat of a tagged-union "
|
||||
"array-literal element not wired (rule-7)");
|
||||
if (repeat && last) {
|
||||
/* fill remaining slots with the value still in
|
||||
* AX (and BX for str). */
|
||||
while (idx < (int)lu->alen) {
|
||||
int base = off + idx * esz;
|
||||
if (is_str_el || is_slice_el) {
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, base));
|
||||
ins2(c, A_MOVQ, areg(D_BX),
|
||||
amem(D_BP, base + 8));
|
||||
ins2(c, A_MOVQ, areg(D_CX),
|
||||
amem(D_BP, base + 16));
|
||||
} else if (is_float_el) {
|
||||
ins2(c, fmov, areg(D_X0),
|
||||
amem(D_BP, base));
|
||||
} else {
|
||||
ins2(c, op, areg(D_AX),
|
||||
amem(D_BP, base));
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cgexpr(Cg *c, Node *n, Local *locals)
|
||||
{
|
||||
@@ -6428,7 +6597,8 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* silently wrong for non-u8). Other non-ident
|
||||
* bases stay esz=1 (unscaled). */
|
||||
int esz = (base && (base->kind == N_IDENT
|
||||
|| base->kind == N_DOT)
|
||||
|| base->kind == N_DOT
|
||||
|| base->kind == N_ARRLIT)
|
||||
&& bu && bu->sub)
|
||||
? (int)bu->sub->size : 1;
|
||||
/* base addr → push */
|
||||
@@ -8685,7 +8855,8 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* non-ident bases stay esz=1 (unscaled) -- #76 residual,
|
||||
* non-ident cluster #74. */
|
||||
int esz = (base && (base->kind == N_IDENT
|
||||
|| base->kind == N_DOT) && bu && bu->sub)
|
||||
|| base->kind == N_DOT || base->kind == N_ARRLIT)
|
||||
&& bu && bu->sub)
|
||||
? (int)bu->sub->size : 1;
|
||||
if (base && base->kind == N_IDENT) {
|
||||
int boff = localfind(locals, base->str);
|
||||
@@ -8701,6 +8872,40 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
} else {
|
||||
ins2(c, A_MOVQ, amem(D_BP, boff), areg(D_AX));
|
||||
}
|
||||
} else if (base && base->kind == N_ARRLIT && bu
|
||||
&& bu->kind == TY_ARRAY) {
|
||||
/* #31: an array LITERAL base — the desugared one-step
|
||||
* `let xs: []T = [..]` borrow (the ONLY context that
|
||||
* reaches here; call-arg/return/assign loud-reject at the
|
||||
* checker, reject_arrlit_borrow, deferred to #33). The
|
||||
* literal has no storage address — cgexpr would leave
|
||||
* AX=garbage and the borrow's .ptr would dangle.
|
||||
* Materialise it into a FRESH per-borrow @slicescr stack
|
||||
* slot (distinct slot per borrow: a borrow's backing must
|
||||
* stay live for the slice's lifetime, so it can't share a
|
||||
* cached SSoT slot the way @aggargscr/@tagscr — drained/
|
||||
* consumed in place — do; two live borrows would otherwise
|
||||
* alias one backing). Reuses local_alloc + the shared
|
||||
* array-init fill; the checker re-stamped base->type to
|
||||
* [count]T (#25) so the fill stores at the declared
|
||||
* element width.
|
||||
*
|
||||
* Escape (WHY, rob): a `let xs: []T = [..]; return xs;`
|
||||
* returns a slice pointing at this frame slot, freed on
|
||||
* return = dangling. This is IDENTICAL to the pre-existing
|
||||
* named-array borrow (`let a: [N]T = [..]; return a;`) and
|
||||
* is Hare-consistent: ww has no escape analysis, no GC, no
|
||||
* heap promotion — borrowing a local past its frame is a
|
||||
* programmer footgun, not promoted. Don't "fix" this
|
||||
* expecting heap promotion; ww deliberately doesn't, same
|
||||
* as Hare. */
|
||||
int cnt = (int)bu->alen;
|
||||
int bsz = (bu->sub ? (int)bu->sub->size : 1) * cnt;
|
||||
if (bsz < 1) bsz = 1;
|
||||
int scr = local_alloc(c, &locals, "@slicescr", bsz,
|
||||
cg_frame);
|
||||
cg_arrlit_fill_bp(c, &locals, bu, base, scr);
|
||||
ins2(c, A_LEAQ, amem(D_BP, scr), areg(D_AX));
|
||||
} else if (base) {
|
||||
/* #252: N_DOT `[N]T`-field base → field ADDRESS via
|
||||
* cg_dotbase_addr (LEAQ), not the auto-deref VALUE load
|
||||
@@ -9071,165 +9276,7 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
* (is_agg excludes TY_TAGGED) — tracked as task #12. */
|
||||
if (n->rhs && n->rhs->kind == N_ARRLIT && lu
|
||||
&& lu->kind == TY_ARRAY) {
|
||||
Type *esub = lu->sub;
|
||||
int esz = esub ? (int)esub->size : 1;
|
||||
/* #270-1c: an AGGREGATE (struct/array/tuple) element
|
||||
* of an array literal — the scalar per-element MOVQ
|
||||
* below stores only the first 8 bytes (unpopulated
|
||||
* tail). Fill each element slot from its literal
|
||||
* (cg_structlit_fill_bp) or source ident (word-copy). */
|
||||
Type *esubu = type_chase_named(esub);
|
||||
int is_agg = esubu && (esubu->kind == TY_STRUCT
|
||||
|| esubu->kind == TY_ARRAY
|
||||
|| esubu->kind == TY_TUPLE);
|
||||
/* #12: a tagged-union element. NOT folded into is_agg —
|
||||
* is_agg's body does N_STRUCTLIT/N_IDENT word-copy and
|
||||
* FATALs on the literal/scalar case, never boxing the
|
||||
* tag+payload. Route each element through the same
|
||||
* cg_widen_tagged_store choke-point every other tagged
|
||||
* store uses (let-init, vararg gather, struct-field). */
|
||||
int is_tagged_el = esubu && esubu->kind == TY_TAGGED;
|
||||
int is_str_el = type_isstr(esub);
|
||||
/* #20/#270 str-slice arm: a slice element is a 24B
|
||||
* {ptr,len,cap} header just like str; cgexpr lowers it
|
||||
* into AX/BX/CX. Both must store all three words — the
|
||||
* scalar 1-word MOVQ below drops .len and .cap. */
|
||||
int is_slice_el = type_isslice(esub);
|
||||
/* float element → store FROM X0; the AX path stores
|
||||
* raw double low-bits, garbage for f32 (#122, twin of
|
||||
* the arr[i]= store fix and the cgen.c:6423 read). */
|
||||
int is_float_el = type_isfloat(esub);
|
||||
int fmov = type_isf32(esub) ? A_MOVSS : A_MOVSD;
|
||||
int op = A_MOVQ;
|
||||
if (!is_str_el) {
|
||||
if (esz == 1) op = A_MOVB;
|
||||
else if (esz == 2) op = A_MOVW;
|
||||
else if (esz == 4) op = A_MOVL;
|
||||
/* #128a: esz==2 routes to MOVW (A_MOVW landed in
|
||||
* both stages' w6a). Pre-fix the 2-byte case fell
|
||||
* through to MOVQ, over-writing 6B into the next
|
||||
* element's slot; sequential adjacent writes
|
||||
* accident-corrected fully-init arrays but
|
||||
* partial inits clobbered neighbours. */
|
||||
}
|
||||
int idx = 0;
|
||||
Node *last = NULL;
|
||||
int repeat = 0;
|
||||
for (Node *e = n->rhs->list; e; e = e->next) {
|
||||
if (e->kind == N_FIELD && e->str &&
|
||||
strcmp(e->str, "...") == 0) {
|
||||
repeat = 1;
|
||||
break;
|
||||
}
|
||||
int base = off + idx * esz;
|
||||
if (is_agg) {
|
||||
if (e->kind == N_STRUCTLIT) {
|
||||
cg_structlit_fill_bp(c, locals,
|
||||
esubu, e, base);
|
||||
} else if (e->kind == N_IDENT) {
|
||||
int soff = localfind(*locals,
|
||||
e->str);
|
||||
int k = 0;
|
||||
for (; k + 8 <= esz; k += 8) {
|
||||
ins2(c, A_MOVQ,
|
||||
amem(D_BP, soff + k),
|
||||
areg(D_AX));
|
||||
ins2(c, A_MOVQ,
|
||||
areg(D_AX),
|
||||
amem(D_BP, base + k));
|
||||
}
|
||||
if (k + 4 <= esz) {
|
||||
ins2(c, A_MOVL,
|
||||
amem(D_BP, soff + k),
|
||||
areg(D_AX));
|
||||
ins2(c, A_MOVL,
|
||||
areg(D_AX),
|
||||
amem(D_BP, base + k));
|
||||
k += 4;
|
||||
}
|
||||
if (k + 2 <= esz) {
|
||||
ins2(c, A_MOVW,
|
||||
amem(D_BP, soff + k),
|
||||
areg(D_AX));
|
||||
ins2(c, A_MOVW,
|
||||
areg(D_AX),
|
||||
amem(D_BP, base + k));
|
||||
k += 2;
|
||||
}
|
||||
if (k + 1 <= esz) {
|
||||
ins2(c, A_MOVB,
|
||||
amem(D_BP, soff + k),
|
||||
areg(D_AX));
|
||||
ins2(c, A_MOVB,
|
||||
areg(D_AX),
|
||||
amem(D_BP, base + k));
|
||||
k += 1;
|
||||
}
|
||||
} else {
|
||||
fatal("#270-1c: array-literal "
|
||||
"aggregate element shape "
|
||||
"unsupported (rule-7)");
|
||||
}
|
||||
last = e;
|
||||
idx++;
|
||||
continue;
|
||||
}
|
||||
if (is_tagged_el) {
|
||||
cg_widen_tagged_store(c, locals, esub,
|
||||
e, D_BP, base, esz);
|
||||
last = e;
|
||||
idx++;
|
||||
continue;
|
||||
}
|
||||
cgexpr(c, e, *locals);
|
||||
if (is_str_el || is_slice_el) {
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, base));
|
||||
ins2(c, A_MOVQ, areg(D_BX),
|
||||
amem(D_BP, base + 8));
|
||||
ins2(c, A_MOVQ, areg(D_CX),
|
||||
amem(D_BP, base + 16));
|
||||
} else if (is_float_el) {
|
||||
ins2(c, fmov, areg(D_X0),
|
||||
amem(D_BP, base));
|
||||
} else {
|
||||
ins2(c, op, areg(D_AX),
|
||||
amem(D_BP, base));
|
||||
}
|
||||
last = e;
|
||||
idx++;
|
||||
}
|
||||
if (repeat && is_agg)
|
||||
fatal("#270-1c: `...` repeat of an aggregate "
|
||||
"array-literal element not wired (rule-7)");
|
||||
/* #12: `...` re-stores from AX, but cg_widen_tagged_store
|
||||
* consumed the node and trashed AX — a repeat-fill would
|
||||
* write garbage. No consumer needs `[N]tagged=[x,...]`. */
|
||||
if (repeat && is_tagged_el)
|
||||
fatal("#12: `...` repeat of a tagged-union "
|
||||
"array-literal element not wired (rule-7)");
|
||||
if (repeat && last) {
|
||||
/* fill remaining slots with the value still in
|
||||
* AX (and BX for str). */
|
||||
while (idx < (int)lu->alen) {
|
||||
int base = off + idx * esz;
|
||||
if (is_str_el || is_slice_el) {
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, base));
|
||||
ins2(c, A_MOVQ, areg(D_BX),
|
||||
amem(D_BP, base + 8));
|
||||
ins2(c, A_MOVQ, areg(D_CX),
|
||||
amem(D_BP, base + 16));
|
||||
} else if (is_float_el) {
|
||||
ins2(c, fmov, areg(D_X0),
|
||||
amem(D_BP, base));
|
||||
} else {
|
||||
ins2(c, op, areg(D_AX),
|
||||
amem(D_BP, base));
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
cg_arrlit_fill_bp(c, locals, lu, n->rhs, off);
|
||||
break;
|
||||
}
|
||||
/* Struct ident copy: `let p2: T = p1;` where T is a struct
|
||||
|
||||
@@ -396,13 +396,21 @@ arrlit_init_fits(Checker *c, Type *dt, Node *rhs)
|
||||
{
|
||||
if (rhs == NULL || rhs->kind != N_ARRLIT) return 0;
|
||||
Type *u = (dt && dt->kind == TY_NAMED) ? dt->under : dt;
|
||||
if (u == NULL || u->kind != TY_ARRAY) return 0;
|
||||
/* #25: a SLICE target is admitted via the same per-element coercion
|
||||
* as the array path — the #258 borrow demands an exact element
|
||||
* type_eq, which an arrlit's self-stamped [N]<default> can't meet for
|
||||
* untyped_str / bare-int-width elements. Peel to the slice element T
|
||||
* and run the array-element coercion against it. */
|
||||
if (u == NULL || (u->kind != TY_ARRAY && u->kind != TY_SLICE))
|
||||
return 0;
|
||||
Type *et = u->sub;
|
||||
Type *eu = (et && et->kind == TY_NAMED) ? et->under : et;
|
||||
u64 count = 0;
|
||||
for (Node *e = rhs->list; e; e = e->next) {
|
||||
if (e->kind == N_FIELD && e->str
|
||||
&& strcmp(e->str, "...") == 0)
|
||||
continue;
|
||||
count++;
|
||||
Node *ev = e;
|
||||
while (ev && ev->kind == N_CAST) ev = ev->lhs;
|
||||
u64 v;
|
||||
@@ -417,6 +425,12 @@ arrlit_init_fits(Checker *c, Type *dt, Node *rhs)
|
||||
if (!type_assignable(et, e->type) && !assignable_addrfn(c, et, e))
|
||||
return 0;
|
||||
}
|
||||
/* #25/#31: re-stamp the literal as [count]T so desugar_arrayslice keys
|
||||
* on an exact-element-eq array and the cgen N_SLICE-over-N_ARRLIT arm
|
||||
* (#31) materialises the borrow backing at the DECLARED element width.
|
||||
* The array path keeps the declared array type, so this is slice-only. */
|
||||
if (u->kind == TY_SLICE)
|
||||
rhs->type = type_array(c->a, et, count);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -939,6 +953,26 @@ coerce_floatlit(Node *n, Type *target)
|
||||
* the four acceptance sites can call it unconditionally; it no-ops unless
|
||||
* the dst is a slice and the src an array with an exactly-matching
|
||||
* element. */
|
||||
/* reject_arrlit_borrow — #31/#33: the array-literal → slice borrow is
|
||||
* supported only at a `let` init, where clet spills the literal to a
|
||||
* per-borrow backing slot (#31). In call-arg / return / assign position
|
||||
* there is no addressable backing — the borrow's .ptr would dangle (the
|
||||
* original #31 silent segfault). Reject loudly here so the gap is a
|
||||
* compile error, not a miscompile. rule-10: wwstage rejects the same
|
||||
* source (its untyped-arrlit element fails the borrow's typeeq); aligning
|
||||
* cstage DOWN keeps both stages loud-identical. Full non-let support is
|
||||
* #33. Returns 1 (and emits the error) when it refuses the borrow. */
|
||||
static int
|
||||
reject_arrlit_borrow(Checker *c, Type *dst, Node *expr)
|
||||
{
|
||||
if (expr == NULL || expr->kind != N_ARRLIT) return 0;
|
||||
Type *du = (dst && dst->kind == TY_NAMED) ? dst->under : dst;
|
||||
if (du == NULL || du->kind != TY_SLICE) return 0;
|
||||
err(c, expr->pos, "array literal cannot borrow as a slice here; "
|
||||
"bind it to a `let` first");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
desugar_arrayslice(Checker *c, Type *dst, Node *expr)
|
||||
{
|
||||
@@ -1539,8 +1573,11 @@ cexpr(Checker *c, Node *n)
|
||||
&& !assignable_addrfn(c, p->type, a))
|
||||
err(c, a->pos, "argument type %s not assignable to %s",
|
||||
type_name(c->a, at), type_name(c->a, p->type));
|
||||
/* #258: `f(arr)` borrows the array as a full slice. */
|
||||
desugar_arrayslice(c, p->type, a);
|
||||
/* #258: `f(arr)` borrows the array as a full slice.
|
||||
* #31/#33: a bare array LITERAL arg has no backing —
|
||||
* loud-reject (supported only at a `let`). */
|
||||
if (!reject_arrlit_borrow(c, p->type, a))
|
||||
desugar_arrayslice(c, p->type, a);
|
||||
p = p->next;
|
||||
}
|
||||
if (p != NULL && !p->variadic)
|
||||
@@ -1568,8 +1605,11 @@ cexpr(Checker *c, Node *n)
|
||||
!assignable_addrfn(c, l, n->rhs))
|
||||
err(c, n->pos, "cannot assign %s to %s",
|
||||
type_name(c->a, r), type_name(c->a, l));
|
||||
/* #258: `s = arr` borrows the array as a full slice. */
|
||||
desugar_arrayslice(c, l, n->rhs);
|
||||
/* #258: `s = arr` borrows the array as a full slice.
|
||||
* #31/#33: a bare array LITERAL rhs has no backing —
|
||||
* loud-reject (supported only at a `let`). */
|
||||
if (!reject_arrlit_borrow(c, l, n->rhs))
|
||||
desugar_arrayslice(c, l, n->rhs);
|
||||
return n->type = l;
|
||||
}
|
||||
case N_STRUCTLIT: {
|
||||
@@ -2032,8 +2072,11 @@ cstmt(Checker *c, Node *n)
|
||||
type_name(c->a, rt), type_name(c->a, c->ret));
|
||||
/* #104 fold-2: `fn g() f32 = { return 1.0; }` — narrow to f32. */
|
||||
coerce_floatlit(n->lhs, c->ret);
|
||||
/* #258: `return arr` borrows the array as a full slice. */
|
||||
desugar_arrayslice(c, c->ret, n->lhs);
|
||||
/* #258: `return arr` borrows the array as a full slice.
|
||||
* #31/#33: a bare array LITERAL has no backing — loud-reject
|
||||
* (supported only at a `let`). */
|
||||
if (!reject_arrlit_borrow(c, c->ret, n->lhs))
|
||||
desugar_arrayslice(c, c->ret, n->lhs);
|
||||
break;
|
||||
}
|
||||
case N_IF: {
|
||||
|
||||
Reference in New Issue
Block a user