w6c: variant-tag miss is loud, never tag 0 (rule 7)

Every variant-index lookup miss (-1) was silently clamped to tag 0
across both stages: tagged return (value + bare-void), widen store
(struct/slice/str/float/scalar arms + push fast path + field store),
widen tag-remap (identity scan + emit loop), match case compare,
is/as typetest, tryprop error remap, and the alloc-nomem propagation
(clamped to 1). All misses are checker-rejected upstream today, so
the clamps were dead -- but any future checker/cgen seam gap would
mis-tag silently (wrong arm, wrong error, false success). Rule 7:
each site now hard-stops with a per-construct diagnostic; nullable
arms keep their raw -1 by design (a miss encodes the void polarity
for `case null`). Corpus asm byte-unchanged; bootstrap fixed point
holds.
This commit is contained in:
2026-08-09 00:12:21 +09:00
parent 94928470bc
commit 57d33acd1f
4 changed files with 140 additions and 34 deletions

View File

@@ -544,7 +544,11 @@ fn cgtryprop(c: *cgen, n: *syntax.node) void = {
for (p != nil) {
if (variantiserror(u, i)) {
let j: i32 = flatvariantidxt(r, p.type_, false);
if (j < 0) { j = 0; };
if (j < 0) {
let mtp: str = "tryprop: error variant missing from fn return union (rule 7)\n";
os.write(2, mtp.ptr, mtp.len: u64);
os.exit(1);
};
if (j != i) {
let skip: str = mklabel(c, "tryprop_skip");
emitline("\tCMPQ\t$");
@@ -796,7 +800,11 @@ fn cgtypetest(c: *cgen, n: *syntax.node) void = {
emitline("\tJNE\t");
};
} else {
if (want < 0) { want = 0; };
if (want < 0) {
let mtt: str = "tagged typetest: type not in union (rule 7)\n";
os.write(2, mtt.ptr, mtt.len: u64);
os.exit(1);
};
emitline("\tCMPQ\t$");
emitint(want: i64);
emitline(", AX\n");
@@ -982,7 +990,11 @@ fn cgtypeassert(c: *cgen, n: *syntax.node) void = {
emitlabel(okl);
return;
};
if (want < 0) { want = 0; };
if (want < 0) {
let mtt: str = "tagged typetest: type not in union (rule 7)\n";
os.write(2, mtt.ptr, mtt.len: u64);
os.exit(1);
};
emitline("\tMOVQ\t");
emitoff(scrutoff: i64);
emitline("(BP), AX\n");
@@ -3115,7 +3127,12 @@ fn matcharmwant(c: *cgen, scrutt: *syntax.node, pat: *syntax.node) i32 = {
r = flatvariantidxt(scrutt.type_: *syntax.tinfo, pattype, false);
};
};
if (r >= 0) { want = r; };
if (r < 0) {
let mmt: str = "tagged match: case type not in union (rule 7)\n";
os.write(2, mmt.ptr, mmt.len: u64);
os.exit(1);
};
want = r;
};
};
return want;
@@ -10439,7 +10456,11 @@ fn cgassign(c: *cgen, n: *syntax.node) void = {
if (viaptr) { emitline("\tMOVQ\t(BX), BX\n"); };
emitline("\tPOPQ\tAX\n");
let v58tag: i32 = taggedvariantindext(c, fi.tnode.type_: *syntax.tinfo, n.rhs);
if (v58tag < 0) { v58tag = 0; };
if (v58tag < 0) {
let mws: str = "tagged widen: no variant tag for source (rule 7)\n";
os.write(2, mws.ptr, mws.len: u64);
os.exit(1);
};
emitline("\tMOVQ\t$");
emitint(v58tag: i64);
emitline(", ");