check: reject an array-literal borrow into a union slice success variant (#13)

A stack array literal returned into a tagged-union slice success variant (fn mk() ([]i32|e) = { return [10,20,30]; }) slipped past reject_arrlit_borrow — it bailed when the dst was not TY_SLICE, but a union dst is TY_TAGGED — so cgen built an all-zeros slice header: a silent both-stage miscompile (and the .ptr would dangle anyway, no outliving backing). Extend the reject to chase a TY_TAGGED dst to its slice success variant, then apply the existing reject; this is the #25/#31 treatment seen through the union, and matches Hare (rule-9). Reached by all acceptance sites (return/assign/call-arg) so the class closes by construction. Both stages converge on an identical accept/reject decision (an array literal assignable to a union is assignable to a slice or array variant; neither stage can accept it). Full support — promoting the literal to an outliving backing — is the separate #33 arc. Compile-error fixture + a positive over-reach guard (a real slice into the union still compiles). No asm emitted by a reject, so byte-id is unchanged (no floor ratchet).
This commit is contained in:
2026-06-28 02:44:23 +09:00
parent 01649598f4
commit 6b36b050d7
4 changed files with 61 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
//ww:error "cannot borrow as a slice here"
// #13 carrier: an array LITERAL returned into a tagged-union SLICE success
// variant ([]i32 | e) has no outliving backing (the maker's frame dies) — the
// .ptr would dangle, the same #31/#33 no-backing reject as a bare-slice return.
// Both stages must REJECT: the borrow gate now sees THROUGH the union to its
// []i32 success variant (the silent all-zeros-header miscompile gap). Full
// non-let support (an outliving backing) is #33.
package main;
type e = !i32;
fn mk() ([]i32 | e) = { return [10i32, 20i32, 30i32]; };
export fn main() i32 = {
let s: []i32 = mk()!;
return s[0];
};

View File

@@ -0,0 +1,14 @@
//ww:run-exit 10
// #13 over-reach guard: a REAL slice returned into the same tagged-union
// success variant ([]i32 | e) has a live backing (the caller's array `a`), so
// both stages must still ACCEPT and build the correct header (tag=0, byte-id).
// Discriminates the array-LITERAL reject (sibling carrier) from a blanket
// union-slice-return reject — proves the #13 fix is surgical. Returns s[0]=10.
package main;
type e = !i32;
fn mk(src: []i32) ([]i32 | e) = { return src; };
export fn main() i32 = {
let a: [3]i32 = [10i32, 20i32, 30i32];
let s: []i32 = mk(a)!;
return s[0];
};