wcc/ww: cgtypeassert gains the nullable arm

'as' on a nullable value compared the pointer itself to a tag (the
missing arm). Mirror cgtypetest's nullable fold and the cstage twin
(cgen.c:10694). Review item #17.
This commit is contained in:
2026-06-12 19:22:51 +09:00
parent 6f3c896fea
commit 02eb867036
5 changed files with 324 additions and 3 deletions

View File

@@ -24019,8 +24019,32 @@ fn cgtypeassert(c: *cgen, n: *node) void = {
};
};
let want: i32 = cgtagvariantidx(c, scrutt, n.rhs);
if (want < 0) { want = 0; };
let okl: str = mklabel(c, "asrt_ok");
// Nullable `(*T | void)`: the slot word IS the pointer, not a tag.
// The *T variant asserts non-null, the void variant asserts null;
// AX keeps the pointer on the ok path (no slot+8 unwrap — the 8B
// nullable slot has no second word). The pre-fix path compared the
// POINTER against `want` (so a real pointer aborted, null passed)
// and unwrapped a frame word past the slot. `want` stays RAW (no
// clamp), mirroring cstage cgen.c N_TYPEASSERT nullable arm and
// ww's own cgtypetest nullable fold (cgenexpr.ww). (task #17/F4)
if (isnullabletype(scrutt)) {
let ptrtag: i32 = nullableptrtag(scrutt);
emitline("\tMOVQ\t");
emitoff(scrutoff: i64);
emitline("(BP), AX\n");
emitline("\tCMPQ\t$0, AX\n");
if (want == ptrtag) {
emitline("\tJNE\t");
} else {
emitline("\tJE\t");
};
emitline(okl);
emitline("\n\tMOVQ\t$1, DI\n\tMOVQ\t$60, AX\n\tSYSCALL\n");
emitlabel(okl);
return;
};
if (want < 0) { want = 0; };
emitline("\tMOVQ\t");
emitoff(scrutoff: i64);
emitline("(BP), AX\n");

View File

@@ -868,8 +868,32 @@ fn cgtypeassert(c: *cgen, n: *node) void = {
};
};
let want: i32 = cgtagvariantidx(c, scrutt, n.rhs);
if (want < 0) { want = 0; };
let okl: str = mklabel(c, "asrt_ok");
// Nullable `(*T | void)`: the slot word IS the pointer, not a tag.
// The *T variant asserts non-null, the void variant asserts null;
// AX keeps the pointer on the ok path (no slot+8 unwrap — the 8B
// nullable slot has no second word). The pre-fix path compared the
// POINTER against `want` (so a real pointer aborted, null passed)
// and unwrapped a frame word past the slot. `want` stays RAW (no
// clamp), mirroring cstage cgen.c N_TYPEASSERT nullable arm and
// ww's own cgtypetest nullable fold (cgenexpr.ww). (task #17/F4)
if (isnullabletype(scrutt)) {
let ptrtag: i32 = nullableptrtag(scrutt);
emitline("\tMOVQ\t");
emitoff(scrutoff: i64);
emitline("(BP), AX\n");
emitline("\tCMPQ\t$0, AX\n");
if (want == ptrtag) {
emitline("\tJNE\t");
} else {
emitline("\tJE\t");
};
emitline(okl);
emitline("\n\tMOVQ\t$1, DI\n\tMOVQ\t$60, AX\n\tSYSCALL\n");
emitlabel(okl);
return;
};
if (want < 0) { want = 0; };
emitline("\tMOVQ\t");
emitoff(scrutoff: i64);
emitline("(BP), AX\n");

View File

@@ -24019,8 +24019,32 @@ fn cgtypeassert(c: *cgen, n: *node) void = {
};
};
let want: i32 = cgtagvariantidx(c, scrutt, n.rhs);
if (want < 0) { want = 0; };
let okl: str = mklabel(c, "asrt_ok");
// Nullable `(*T | void)`: the slot word IS the pointer, not a tag.
// The *T variant asserts non-null, the void variant asserts null;
// AX keeps the pointer on the ok path (no slot+8 unwrap — the 8B
// nullable slot has no second word). The pre-fix path compared the
// POINTER against `want` (so a real pointer aborted, null passed)
// and unwrapped a frame word past the slot. `want` stays RAW (no
// clamp), mirroring cstage cgen.c N_TYPEASSERT nullable arm and
// ww's own cgtypetest nullable fold (cgenexpr.ww). (task #17/F4)
if (isnullabletype(scrutt)) {
let ptrtag: i32 = nullableptrtag(scrutt);
emitline("\tMOVQ\t");
emitoff(scrutoff: i64);
emitline("(BP), AX\n");
emitline("\tCMPQ\t$0, AX\n");
if (want == ptrtag) {
emitline("\tJNE\t");
} else {
emitline("\tJE\t");
};
emitline(okl);
emitline("\n\tMOVQ\t$1, DI\n\tMOVQ\t$60, AX\n\tSYSCALL\n");
emitlabel(okl);
return;
};
if (want < 0) { want = 0; };
emitline("\tMOVQ\t");
emitoff(scrutoff: i64);
emitline("(BP), AX\n");