selfhost/cmd/wcc/check: enum<->int reinterpret bypasses tagged check
Wwstage checkisas fell straight through to the tagged-union arm on `enum_val as i32` reinterprets, false-positiving on every enum→int cast in lib/ (lib/time/instant.ww, lib/os, lib/os/lseek). Cstage admits these at cmd/wcc/check.c:1346-1357: when N_TYPEASSERT has LHS-or-RHS enum AND both ends are integer-typed, the target type returns without the tagged check. `is` (TYPETEST) stays rejected — cstage gates only N_TYPEASSERT. isinttypeast helper covers N_TENUM + i8..i64/u8..u64/int/uint/ uintptr/rune. Excludes floats so `enum as f64` still rejects. N_TYPEASSERT branch in checkisas detects enum on either side via resolvealias-unwrap, gates on both-ends-int, returns target type before the tagged-union check. 4/5 selfhost main.combined.ww files now resolve clean via wwdump_ww -r. Residual on selfhost/cmd/ww tracked as #53 (separate checkletassign u64→i32 path).
This commit is contained in:
@@ -7722,6 +7722,31 @@ fn isuntypednil(t: *node) bool = {
|
|||||||
return streq(t.str, "untyped_nil");
|
return streq(t.str, "untyped_nil");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// isinttypeast — int-typed AST node. Either a primitive int name
|
||||||
|
// (i8..i64/u8..u64/int/uint/uintptr/rune) or an N_TENUM. Floats are
|
||||||
|
// excluded so the enum↔int reinterpret in checkisas (#52) refuses a
|
||||||
|
// surprise `enum as f64` shape. Mirrors cstage's type_isint
|
||||||
|
// (cmd/wcc/type.c) restricted to the kinds reachable from AST.
|
||||||
|
fn isinttypeast(t: *node) bool = {
|
||||||
|
if (t == nil) { return false; };
|
||||||
|
if (t.kind == nkind.N_TENUM) { return true; };
|
||||||
|
if (t.kind != nkind.N_TNAME) { return false; };
|
||||||
|
let s: str = t.str;
|
||||||
|
if (streq(s, "i8")) { return true; };
|
||||||
|
if (streq(s, "i16")) { return true; };
|
||||||
|
if (streq(s, "i32")) { return true; };
|
||||||
|
if (streq(s, "i64")) { return true; };
|
||||||
|
if (streq(s, "u8")) { return true; };
|
||||||
|
if (streq(s, "u16")) { return true; };
|
||||||
|
if (streq(s, "u32")) { return true; };
|
||||||
|
if (streq(s, "u64")) { return true; };
|
||||||
|
if (streq(s, "int")) { return true; };
|
||||||
|
if (streq(s, "uint")) { return true; };
|
||||||
|
if (streq(s, "uintptr")) { return true; };
|
||||||
|
if (streq(s, "rune")) { return true; };
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
fn isnumerictname(t: *node) bool = {
|
fn isnumerictname(t: *node) bool = {
|
||||||
if (t == nil) { return false; };
|
if (t == nil) { return false; };
|
||||||
if (t.kind != nkind.N_TNAME) { return false; };
|
if (t.kind != nkind.N_TNAME) { return false; };
|
||||||
@@ -8095,6 +8120,24 @@ fn checkisas(c: *checker, n: *node) void = {
|
|||||||
let st: *node = scruttype(c, n.lhs);
|
let st: *node = scruttype(c, n.lhs);
|
||||||
let u: *node = resolvealias(c, unwrapbang(st));
|
let u: *node = resolvealias(c, unwrapbang(st));
|
||||||
if (u == nil) { return; };
|
if (u == nil) { return; };
|
||||||
|
// #52: enum ↔ int reinterpret (`enum as intT` / `intT as enum`).
|
||||||
|
// Mirrors cstage cmd/wcc/check.c:1346-1357 — N_TYPEASSERT with an
|
||||||
|
// enum on either side and integer types on both reinterprets in
|
||||||
|
// the same register, no tag check involved. Returns early before
|
||||||
|
// the tagged-union gate so lib/time/instant.ww `(c as i32)` and
|
||||||
|
// the lib/os syscall casts stop false-positiving. `is` (TYPETEST)
|
||||||
|
// stays rejected on non-tagged operands — cstage cmd/wcc/check.c
|
||||||
|
// gates the bypass on N_TYPEASSERT only.
|
||||||
|
if (n.kind == nkind.N_TYPEASSERT) {
|
||||||
|
let v: *node = resolvealias(c, unwrapbang(n.rhs));
|
||||||
|
let lhsenum: bool = false;
|
||||||
|
let rhsenum: bool = false;
|
||||||
|
if (u != nil) { if (u.kind == nkind.N_TENUM) { lhsenum = true; }; };
|
||||||
|
if (v != nil) { if (v.kind == nkind.N_TENUM) { rhsenum = true; }; };
|
||||||
|
if (lhsenum || rhsenum) {
|
||||||
|
if (isinttypeast(u)) { if (isinttypeast(v)) { return; }; };
|
||||||
|
};
|
||||||
|
};
|
||||||
if (u.kind != nkind.N_TTAGGED) {
|
if (u.kind != nkind.N_TTAGGED) {
|
||||||
os.write(2, "is/as: operand is not a tagged union\n".ptr, 37u64);
|
os.write(2, "is/as: operand is not a tagged union\n".ptr, 37u64);
|
||||||
c.errs += 1;
|
c.errs += 1;
|
||||||
|
|||||||
@@ -728,6 +728,31 @@ fn isuntypednil(t: *node) bool = {
|
|||||||
return streq(t.str, "untyped_nil");
|
return streq(t.str, "untyped_nil");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// isinttypeast — int-typed AST node. Either a primitive int name
|
||||||
|
// (i8..i64/u8..u64/int/uint/uintptr/rune) or an N_TENUM. Floats are
|
||||||
|
// excluded so the enum↔int reinterpret in checkisas (#52) refuses a
|
||||||
|
// surprise `enum as f64` shape. Mirrors cstage's type_isint
|
||||||
|
// (cmd/wcc/type.c) restricted to the kinds reachable from AST.
|
||||||
|
fn isinttypeast(t: *node) bool = {
|
||||||
|
if (t == nil) { return false; };
|
||||||
|
if (t.kind == nkind.N_TENUM) { return true; };
|
||||||
|
if (t.kind != nkind.N_TNAME) { return false; };
|
||||||
|
let s: str = t.str;
|
||||||
|
if (streq(s, "i8")) { return true; };
|
||||||
|
if (streq(s, "i16")) { return true; };
|
||||||
|
if (streq(s, "i32")) { return true; };
|
||||||
|
if (streq(s, "i64")) { return true; };
|
||||||
|
if (streq(s, "u8")) { return true; };
|
||||||
|
if (streq(s, "u16")) { return true; };
|
||||||
|
if (streq(s, "u32")) { return true; };
|
||||||
|
if (streq(s, "u64")) { return true; };
|
||||||
|
if (streq(s, "int")) { return true; };
|
||||||
|
if (streq(s, "uint")) { return true; };
|
||||||
|
if (streq(s, "uintptr")) { return true; };
|
||||||
|
if (streq(s, "rune")) { return true; };
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
fn isnumerictname(t: *node) bool = {
|
fn isnumerictname(t: *node) bool = {
|
||||||
if (t == nil) { return false; };
|
if (t == nil) { return false; };
|
||||||
if (t.kind != nkind.N_TNAME) { return false; };
|
if (t.kind != nkind.N_TNAME) { return false; };
|
||||||
@@ -1101,6 +1126,24 @@ fn checkisas(c: *checker, n: *node) void = {
|
|||||||
let st: *node = scruttype(c, n.lhs);
|
let st: *node = scruttype(c, n.lhs);
|
||||||
let u: *node = resolvealias(c, unwrapbang(st));
|
let u: *node = resolvealias(c, unwrapbang(st));
|
||||||
if (u == nil) { return; };
|
if (u == nil) { return; };
|
||||||
|
// #52: enum ↔ int reinterpret (`enum as intT` / `intT as enum`).
|
||||||
|
// Mirrors cstage cmd/wcc/check.c:1346-1357 — N_TYPEASSERT with an
|
||||||
|
// enum on either side and integer types on both reinterprets in
|
||||||
|
// the same register, no tag check involved. Returns early before
|
||||||
|
// the tagged-union gate so lib/time/instant.ww `(c as i32)` and
|
||||||
|
// the lib/os syscall casts stop false-positiving. `is` (TYPETEST)
|
||||||
|
// stays rejected on non-tagged operands — cstage cmd/wcc/check.c
|
||||||
|
// gates the bypass on N_TYPEASSERT only.
|
||||||
|
if (n.kind == nkind.N_TYPEASSERT) {
|
||||||
|
let v: *node = resolvealias(c, unwrapbang(n.rhs));
|
||||||
|
let lhsenum: bool = false;
|
||||||
|
let rhsenum: bool = false;
|
||||||
|
if (u != nil) { if (u.kind == nkind.N_TENUM) { lhsenum = true; }; };
|
||||||
|
if (v != nil) { if (v.kind == nkind.N_TENUM) { rhsenum = true; }; };
|
||||||
|
if (lhsenum || rhsenum) {
|
||||||
|
if (isinttypeast(u)) { if (isinttypeast(v)) { return; }; };
|
||||||
|
};
|
||||||
|
};
|
||||||
if (u.kind != nkind.N_TTAGGED) {
|
if (u.kind != nkind.N_TTAGGED) {
|
||||||
os.write(2, "is/as: operand is not a tagged union\n".ptr, 37u64);
|
os.write(2, "is/as: operand is not a tagged union\n".ptr, 37u64);
|
||||||
c.errs += 1;
|
c.errs += 1;
|
||||||
|
|||||||
@@ -7722,6 +7722,31 @@ fn isuntypednil(t: *node) bool = {
|
|||||||
return streq(t.str, "untyped_nil");
|
return streq(t.str, "untyped_nil");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// isinttypeast — int-typed AST node. Either a primitive int name
|
||||||
|
// (i8..i64/u8..u64/int/uint/uintptr/rune) or an N_TENUM. Floats are
|
||||||
|
// excluded so the enum↔int reinterpret in checkisas (#52) refuses a
|
||||||
|
// surprise `enum as f64` shape. Mirrors cstage's type_isint
|
||||||
|
// (cmd/wcc/type.c) restricted to the kinds reachable from AST.
|
||||||
|
fn isinttypeast(t: *node) bool = {
|
||||||
|
if (t == nil) { return false; };
|
||||||
|
if (t.kind == nkind.N_TENUM) { return true; };
|
||||||
|
if (t.kind != nkind.N_TNAME) { return false; };
|
||||||
|
let s: str = t.str;
|
||||||
|
if (streq(s, "i8")) { return true; };
|
||||||
|
if (streq(s, "i16")) { return true; };
|
||||||
|
if (streq(s, "i32")) { return true; };
|
||||||
|
if (streq(s, "i64")) { return true; };
|
||||||
|
if (streq(s, "u8")) { return true; };
|
||||||
|
if (streq(s, "u16")) { return true; };
|
||||||
|
if (streq(s, "u32")) { return true; };
|
||||||
|
if (streq(s, "u64")) { return true; };
|
||||||
|
if (streq(s, "int")) { return true; };
|
||||||
|
if (streq(s, "uint")) { return true; };
|
||||||
|
if (streq(s, "uintptr")) { return true; };
|
||||||
|
if (streq(s, "rune")) { return true; };
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
fn isnumerictname(t: *node) bool = {
|
fn isnumerictname(t: *node) bool = {
|
||||||
if (t == nil) { return false; };
|
if (t == nil) { return false; };
|
||||||
if (t.kind != nkind.N_TNAME) { return false; };
|
if (t.kind != nkind.N_TNAME) { return false; };
|
||||||
@@ -8095,6 +8120,24 @@ fn checkisas(c: *checker, n: *node) void = {
|
|||||||
let st: *node = scruttype(c, n.lhs);
|
let st: *node = scruttype(c, n.lhs);
|
||||||
let u: *node = resolvealias(c, unwrapbang(st));
|
let u: *node = resolvealias(c, unwrapbang(st));
|
||||||
if (u == nil) { return; };
|
if (u == nil) { return; };
|
||||||
|
// #52: enum ↔ int reinterpret (`enum as intT` / `intT as enum`).
|
||||||
|
// Mirrors cstage cmd/wcc/check.c:1346-1357 — N_TYPEASSERT with an
|
||||||
|
// enum on either side and integer types on both reinterprets in
|
||||||
|
// the same register, no tag check involved. Returns early before
|
||||||
|
// the tagged-union gate so lib/time/instant.ww `(c as i32)` and
|
||||||
|
// the lib/os syscall casts stop false-positiving. `is` (TYPETEST)
|
||||||
|
// stays rejected on non-tagged operands — cstage cmd/wcc/check.c
|
||||||
|
// gates the bypass on N_TYPEASSERT only.
|
||||||
|
if (n.kind == nkind.N_TYPEASSERT) {
|
||||||
|
let v: *node = resolvealias(c, unwrapbang(n.rhs));
|
||||||
|
let lhsenum: bool = false;
|
||||||
|
let rhsenum: bool = false;
|
||||||
|
if (u != nil) { if (u.kind == nkind.N_TENUM) { lhsenum = true; }; };
|
||||||
|
if (v != nil) { if (v.kind == nkind.N_TENUM) { rhsenum = true; }; };
|
||||||
|
if (lhsenum || rhsenum) {
|
||||||
|
if (isinttypeast(u)) { if (isinttypeast(v)) { return; }; };
|
||||||
|
};
|
||||||
|
};
|
||||||
if (u.kind != nkind.N_TTAGGED) {
|
if (u.kind != nkind.N_TTAGGED) {
|
||||||
os.write(2, "is/as: operand is not a tagged union\n".ptr, 37u64);
|
os.write(2, "is/as: operand is not a tagged union\n".ptr, 37u64);
|
||||||
c.errs += 1;
|
c.errs += 1;
|
||||||
|
|||||||
Reference in New Issue
Block a user