selfhost/cmd/wcc: collapse type-kind predicates onto n.type_ (A.6.3b, #46)
The node-keyed kind helpers (typeis8byteprimitive, isstrtype/raw,
isslicetype/raw, istaggedtype/raw, isfloattype, isf32type/raw,
isf64typeraw, isnullabletype) each re-walked TNAME aliases via
aliaslookup and peeled TBANG by hand — duplicating cstage's single-
peel kind predicates at the AST level. After A.6.2 every type-AST
kind these read is tinfo-stamped at check.ww L426-436, and
tinfofornode collapses N_TBANG (check.ww:1145-1152) and the TY_NAMED
chain, so each predicate folds to one tinfo read.
Six new tinfo helpers in lib/ww/typ.ww mirror their cstage SSoT
verbatim:
typeisstr — cstage cgen.c:159 `type_isstr` (TY_STR / TY_UNTYPED_STR)
typeisslice — cstage cgen.c:174 `type_isslice`
typeistagged — cstage cgen.c:516 `type_istagged`
typeisf32 — cstage cgen.c:188 `type_isf32`
typeisnullable — cstage cgen.c:396 `type_isnullable` (reads tinfo.nullable
stamped at check.ww:1309-1318)
typeis8byteprim — cstage cgen.c N_LET sz==8 ladder (slot-pad set)
Rule 9 carve-out per the A.6.3a precedent: each helper has a named
cstage counterpart; the wwstage shape mirrors it directly. The five
dead AST-walking variants (isstrtyperaw, isslicetyperaw,
istaggedtyperaw, isf32typeraw, isf64typeraw) are deleted; the five
remaining callsites (cgenstmt cglet / cgmlet str-routing, cgenexpr
cgdot tuple-field) graduate to the alias-aware isstrtype(c, t).
nullableptrtag stays AST-keyed for now — tinfofornode doesn't
populate TY_TAGGED.params (check.ww:1287-1337 sets size / align /
nullable but not the variant chain), so the tinfo equivalent of
cstage cgen.c:405 `nullable_ptr_tag` can't read params today. WHY
comment at the site cites #50 / A.6.3f as the graduation point,
alongside the variant-index work and the tparam-population glue.
Byte-identity (994/995) is the behavior gate; full `make test` green
at 133/133 confirms.
This commit is contained in:
@@ -336,6 +336,94 @@ export fn typeissigned(t: *tinfo) bool = {
|
||||
return typeisint(t);
|
||||
};
|
||||
|
||||
// typeisstr — TY_STR (and TY_UNTYPED_STR for literals pre-default).
|
||||
// Cite cstage cgen.c:159 `type_isstr` — single TY_NAMED peel, accepts
|
||||
// the same untyped form. ww walks the .under chain so alias-of-alias
|
||||
// (`type s2 = s1; type s1 = str;`) lands the same way.
|
||||
export fn typeisstr(t: *tinfo) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: tykind = t.kind;
|
||||
if (k == tykind.TY_STR) { return true; };
|
||||
if (k == tykind.TY_UNTYPED_STR) { return true; };
|
||||
if (k == tykind.TY_NAMED) { return typeisstr(t.under); };
|
||||
return false;
|
||||
};
|
||||
|
||||
// typeisslice — TY_SLICE. Cite cstage cgen.c:174 `type_isslice`.
|
||||
export fn typeisslice(t: *tinfo) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: tykind = t.kind;
|
||||
if (k == tykind.TY_SLICE) { return true; };
|
||||
if (k == tykind.TY_NAMED) { return typeisslice(t.under); };
|
||||
return false;
|
||||
};
|
||||
|
||||
// typeistagged — TY_TAGGED (alias-aware). Cite cstage cgen.c:516
|
||||
// `type_istagged` — same single-peel shape. The node-keyed wwstage
|
||||
// helper this replaces also unwrapped a leading N_TBANG so
|
||||
// `type error = !(invalid | overflow);` registered as tagged. Post-
|
||||
// A.6.2 the TBANG unwrap is handled by tinfofornode (check.ww:1145-
|
||||
// 1152 returns the inner tinfo unchanged) so we recover the cstage
|
||||
// semantics with the bare kind check + NAMED chase.
|
||||
export fn typeistagged(t: *tinfo) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: tykind = t.kind;
|
||||
if (k == tykind.TY_TAGGED) { return true; };
|
||||
if (k == tykind.TY_NAMED) { return typeistagged(t.under); };
|
||||
return false;
|
||||
};
|
||||
|
||||
// typeisf32 — narrower-than-typeisfloat: only TY_F32 (after alias
|
||||
// chase). Cite cstage cgen.c:188 `type_isf32`. Used to pick MOVSS vs
|
||||
// MOVSD and the SS-variant arithmetic / cast opcodes.
|
||||
export fn typeisf32(t: *tinfo) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: tykind = t.kind;
|
||||
if (k == tykind.TY_F32) { return true; };
|
||||
if (k == tykind.TY_NAMED) { return typeisf32(t.under); };
|
||||
return false;
|
||||
};
|
||||
|
||||
// typeisnullable — TY_TAGGED with the `(*T | void)` one-word fold.
|
||||
// Cite cstage cgen.c:396 `type_isnullable`. The .nullable flag is
|
||||
// stamped by tinfofornode (check.ww:1309-1318) when the two-variant
|
||||
// shape matches.
|
||||
export fn typeisnullable(t: *tinfo) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: tykind = t.kind;
|
||||
if (k == tykind.TY_TAGGED) { return t.nullable != 0; };
|
||||
if (k == tykind.TY_NAMED) { return typeisnullable(t.under); };
|
||||
return false;
|
||||
};
|
||||
|
||||
// typeis8byteprim — does this type take exactly one 8-byte stack
|
||||
// slot (ptr / fn / chan / 64-bit int / scalar primitive padded up to
|
||||
// 8 / `[N]T` whose natural width is 8) rather than a wider aggregate?
|
||||
// Mirrors the ladder cstage's cgen.c N_LET zero-init takes on `sz==8`
|
||||
// (cmd/wcc/check.c sizing + cgen.c N_LET). The node-keyed wwstage
|
||||
// helper this replaces predates tinfo and AST-walked TBANG / TNAME
|
||||
// alias chains; tinfofornode now collapses TBANG (check.ww:1145) and
|
||||
// TY_NAMED.under carries the chain, so the tinfo walk handles every
|
||||
// shape the AST walker did.
|
||||
export fn typeis8byteprim(t: *tinfo) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: tykind = t.kind;
|
||||
if (k == tykind.TY_PTR) { return true; };
|
||||
if (k == tykind.TY_FN) { return true; };
|
||||
if (k == tykind.TY_CHAN) { return true; };
|
||||
if (k == tykind.TY_SLICE) { return false; };
|
||||
if (k == tykind.TY_TUPLE) { return false; };
|
||||
if (k == tykind.TY_TAGGED) { return false; };
|
||||
if (k == tykind.TY_STR) { return false; };
|
||||
if (k == tykind.TY_STRUCT) { return false; };
|
||||
if (k == tykind.TY_ARRAY) { return t.size == 8u64; };
|
||||
if (k == tykind.TY_NAMED) { return typeis8byteprim(t.under); };
|
||||
// Remaining: primitives (i8/u8/.../i64/u64/bool/rune/f32/f64/
|
||||
// int/uint/uintptr) and TY_VOID. All slot-pad to 8 and zero-init
|
||||
// in cstage's `sz==8` branch.
|
||||
return true;
|
||||
};
|
||||
|
||||
export fn typeisuntyped(t: *tinfo) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: tykind = t.kind;
|
||||
|
||||
@@ -6666,6 +6666,94 @@ export fn typeissigned(t: *tinfo) bool = {
|
||||
return typeisint(t);
|
||||
};
|
||||
|
||||
// typeisstr — TY_STR (and TY_UNTYPED_STR for literals pre-default).
|
||||
// Cite cstage cgen.c:159 `type_isstr` — single TY_NAMED peel, accepts
|
||||
// the same untyped form. ww walks the .under chain so alias-of-alias
|
||||
// (`type s2 = s1; type s1 = str;`) lands the same way.
|
||||
export fn typeisstr(t: *tinfo) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: tykind = t.kind;
|
||||
if (k == tykind.TY_STR) { return true; };
|
||||
if (k == tykind.TY_UNTYPED_STR) { return true; };
|
||||
if (k == tykind.TY_NAMED) { return typeisstr(t.under); };
|
||||
return false;
|
||||
};
|
||||
|
||||
// typeisslice — TY_SLICE. Cite cstage cgen.c:174 `type_isslice`.
|
||||
export fn typeisslice(t: *tinfo) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: tykind = t.kind;
|
||||
if (k == tykind.TY_SLICE) { return true; };
|
||||
if (k == tykind.TY_NAMED) { return typeisslice(t.under); };
|
||||
return false;
|
||||
};
|
||||
|
||||
// typeistagged — TY_TAGGED (alias-aware). Cite cstage cgen.c:516
|
||||
// `type_istagged` — same single-peel shape. The node-keyed wwstage
|
||||
// helper this replaces also unwrapped a leading N_TBANG so
|
||||
// `type error = !(invalid | overflow);` registered as tagged. Post-
|
||||
// A.6.2 the TBANG unwrap is handled by tinfofornode (check.ww:1145-
|
||||
// 1152 returns the inner tinfo unchanged) so we recover the cstage
|
||||
// semantics with the bare kind check + NAMED chase.
|
||||
export fn typeistagged(t: *tinfo) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: tykind = t.kind;
|
||||
if (k == tykind.TY_TAGGED) { return true; };
|
||||
if (k == tykind.TY_NAMED) { return typeistagged(t.under); };
|
||||
return false;
|
||||
};
|
||||
|
||||
// typeisf32 — narrower-than-typeisfloat: only TY_F32 (after alias
|
||||
// chase). Cite cstage cgen.c:188 `type_isf32`. Used to pick MOVSS vs
|
||||
// MOVSD and the SS-variant arithmetic / cast opcodes.
|
||||
export fn typeisf32(t: *tinfo) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: tykind = t.kind;
|
||||
if (k == tykind.TY_F32) { return true; };
|
||||
if (k == tykind.TY_NAMED) { return typeisf32(t.under); };
|
||||
return false;
|
||||
};
|
||||
|
||||
// typeisnullable — TY_TAGGED with the `(*T | void)` one-word fold.
|
||||
// Cite cstage cgen.c:396 `type_isnullable`. The .nullable flag is
|
||||
// stamped by tinfofornode (check.ww:1309-1318) when the two-variant
|
||||
// shape matches.
|
||||
export fn typeisnullable(t: *tinfo) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: tykind = t.kind;
|
||||
if (k == tykind.TY_TAGGED) { return t.nullable != 0; };
|
||||
if (k == tykind.TY_NAMED) { return typeisnullable(t.under); };
|
||||
return false;
|
||||
};
|
||||
|
||||
// typeis8byteprim — does this type take exactly one 8-byte stack
|
||||
// slot (ptr / fn / chan / 64-bit int / scalar primitive padded up to
|
||||
// 8 / `[N]T` whose natural width is 8) rather than a wider aggregate?
|
||||
// Mirrors the ladder cstage's cgen.c N_LET zero-init takes on `sz==8`
|
||||
// (cmd/wcc/check.c sizing + cgen.c N_LET). The node-keyed wwstage
|
||||
// helper this replaces predates tinfo and AST-walked TBANG / TNAME
|
||||
// alias chains; tinfofornode now collapses TBANG (check.ww:1145) and
|
||||
// TY_NAMED.under carries the chain, so the tinfo walk handles every
|
||||
// shape the AST walker did.
|
||||
export fn typeis8byteprim(t: *tinfo) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: tykind = t.kind;
|
||||
if (k == tykind.TY_PTR) { return true; };
|
||||
if (k == tykind.TY_FN) { return true; };
|
||||
if (k == tykind.TY_CHAN) { return true; };
|
||||
if (k == tykind.TY_SLICE) { return false; };
|
||||
if (k == tykind.TY_TUPLE) { return false; };
|
||||
if (k == tykind.TY_TAGGED) { return false; };
|
||||
if (k == tykind.TY_STR) { return false; };
|
||||
if (k == tykind.TY_STRUCT) { return false; };
|
||||
if (k == tykind.TY_ARRAY) { return t.size == 8u64; };
|
||||
if (k == tykind.TY_NAMED) { return typeis8byteprim(t.under); };
|
||||
// Remaining: primitives (i8/u8/.../i64/u64/bool/rune/f32/f64/
|
||||
// int/uint/uintptr) and TY_VOID. All slot-pad to 8 and zero-init
|
||||
// in cstage's `sz==8` branch.
|
||||
return true;
|
||||
};
|
||||
|
||||
export fn typeisuntyped(t: *tinfo) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: tykind = t.kind;
|
||||
@@ -11118,72 +11206,13 @@ fn typenameisunsigned(nm: str) bool = {
|
||||
};
|
||||
|
||||
// typeis8byteprimitive — does this type take exactly one 8-byte
|
||||
// slot (pointer / fn-ptr / 64-bit int / chan / scalar primitive
|
||||
// padded up to 8) rather than a wider aggregate? Used by nkind.N_LET
|
||||
// zero-init to mirror C cgen's "only zero if sz == 8 at the type
|
||||
// level" rule. Strings (16), slices (24), tagged unions (>=16),
|
||||
// tuples (16), structs (varies), arrays — all fall through to
|
||||
// false here even when their *slot* rounds up to 8.
|
||||
// slot rather than a wider aggregate? One-liner via typeis8byteprim
|
||||
// (cstage N_LET sz==8 ladder SSoT). t.type_ is stamped at check.ww
|
||||
// L426-436 for every type-AST kind callers reach. Collapsed onto the
|
||||
// tinfo helper per A.6.3b (#46).
|
||||
fn typeis8byteprimitive(c: *cgen, t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: nkind = t.kind;
|
||||
if (k == nkind.N_TPTR) { return true; };
|
||||
if (k == nkind.N_TFN) { return true; };
|
||||
if (k == nkind.N_TCHAN) { return true; };
|
||||
if (k == nkind.N_TSLICE) { return false; };
|
||||
if (k == nkind.N_TARRAY) {
|
||||
// C cgen (cmd/w6c/cgen.c:3317) zero-inits TY_ARRAY whenever
|
||||
// its raw byte size is 8 — e.g. `[8]bool`, `[2]i32`, `[4]i16`,
|
||||
// `[1]i64`. Mirror that here so the wwstage matches.
|
||||
let lenn: *node = t.rhs;
|
||||
let elemn: *node = t.lhs;
|
||||
if (lenn == nil) { return false; };
|
||||
if (lenn.kind != nkind.N_INTLIT) { return false; };
|
||||
let elen: i64 = lenn.uval: i64;
|
||||
let esz: i32 = 8;
|
||||
if (elemn != nil) {
|
||||
if (elemn.kind == nkind.N_TNAME) {
|
||||
let ps: i32 = primsize(elemn.str);
|
||||
if (ps > 0) { esz = ps; };
|
||||
};
|
||||
};
|
||||
return (esz: i64 * elen) == 8i64;
|
||||
};
|
||||
if (k == nkind.N_TTUPLE) { return false; };
|
||||
if (k == nkind.N_TTAGGED){ return false; };
|
||||
// STATUS-3 #22: `!T` carries the error flag on T's underlying
|
||||
// shape (cmd/wcc/check.c:290 resolve_type N_TBANG copies T's
|
||||
// kind, just sets iserror). cstage's N_LET sizes off lu->kind,
|
||||
// so `!void`/`!i32` land in the sz=8 default and `!str`/`!slice`
|
||||
// keep their composite slot. Defer to the inner type so
|
||||
// `let e: !void;` mirrors cstage's MOVQ $0 while `!str` falls
|
||||
// through to the multi-word fill.
|
||||
if (k == nkind.N_TBANG) { return typeis8byteprimitive(c, t.lhs); };
|
||||
if (k == nkind.N_TNAME) {
|
||||
let nm: str = t.str;
|
||||
if (streq(nm, "str")) { return false; };
|
||||
// Plain `void` slot: cstage sz=8 default → MOVQ $0. The let-
|
||||
// decl is a phantom (a tagged-union variant tag carrier), but
|
||||
// the slot is still 8B and zero-inits like any other prim.
|
||||
if (streq(nm, "void")) { return true; };
|
||||
// Struct alias: not a primitive even if the slot is 8B.
|
||||
if (structlookup(c, nm) != nil) { return false; };
|
||||
// Primitive (i8/u8/.../i64/u64/bool/rune/f32/f64/int/...).
|
||||
// All of these get slot-padded to 8 and zero-init in C.
|
||||
if (primsize(nm) > 0) { return true; };
|
||||
// STATUS-3 #22: alias to `!T` or to `void` (Hare-style error
|
||||
// type / phantom variant). cstage resolves the alias and
|
||||
// lands on sz=8 default. Follow through aliaslookup so
|
||||
// `type invalid = !void;` and `type done = void;` zero-init.
|
||||
if (c != nil) {
|
||||
let aliased: *node = aliaslookup(c, nm);
|
||||
if (aliased != nil) {
|
||||
return typeis8byteprimitive(c, aliased);
|
||||
};
|
||||
};
|
||||
return false;
|
||||
};
|
||||
return false;
|
||||
return typeis8byteprim(t.type_: *tinfo);
|
||||
};
|
||||
|
||||
// elemissignedc — given an indexable type-AST (`*T`, `[]T`, `[N]T`),
|
||||
@@ -12419,54 +12448,20 @@ fn collectstructs(c: *cgen, file: *node) void = {
|
||||
};
|
||||
};
|
||||
|
||||
// `type X = str;` aliases) to `str`. Takes *cgen so it can walk the
|
||||
// alias chain registered at file load.
|
||||
fn isstrtyperaw(t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
if (t.kind == nkind.N_TNAME) {
|
||||
let nm: str = t.str;
|
||||
if (streq(nm, "str")) { return true; };
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
// isstrtype — alias-aware. Reads the stamped tinfo so `str`,
|
||||
// `type alias = str`, `!str`, and chained aliases all route to the
|
||||
// str-shaped slot. Cite cstage cgen.c:159 `type_isstr` SSoT.
|
||||
// Collapsed onto typeisstr per A.6.3b (#46); the prior AST walker is
|
||||
// reconstituted by typeisstr's TY_NAMED chase + tinfofornode's
|
||||
// TBANG-unwrap (check.ww:1145).
|
||||
fn isstrtype(c: *cgen, t: *node) bool = {
|
||||
if (isstrtyperaw(t)) { return true; };
|
||||
if (c == nil) { return false; };
|
||||
let r: *node = resolvetype(c, t);
|
||||
if (isstrtyperaw(r)) { return true; };
|
||||
// `parserr = !str` — `!T` aliases shouldn't hide their
|
||||
// underlying type from str-routing. Unwrap and re-check.
|
||||
if (r != nil) {
|
||||
if (r.kind == nkind.N_TBANG) {
|
||||
let inner: *node = r.lhs;
|
||||
if (isstrtyperaw(inner)) { return true; };
|
||||
if (inner != nil) {
|
||||
let r2: *node = resolvetype(c, inner);
|
||||
if (isstrtyperaw(r2)) { return true; };
|
||||
};
|
||||
};
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
fn isslicetyperaw(t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
if (t.kind == nkind.N_TSLICE) { return true; };
|
||||
return false;
|
||||
return typeisstr(t.type_: *tinfo);
|
||||
};
|
||||
|
||||
fn isslicetype(c: *cgen, t: *node) bool = {
|
||||
if (isslicetyperaw(t)) { return true; };
|
||||
if (c == nil) { return false; };
|
||||
let r: *node = resolvetype(c, t);
|
||||
return isslicetyperaw(r);
|
||||
};
|
||||
|
||||
fn istaggedtyperaw(t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
if (t.kind == nkind.N_TTAGGED) { return true; };
|
||||
return false;
|
||||
return typeisslice(t.type_: *tinfo);
|
||||
};
|
||||
|
||||
// resolvetagged — return the underlying N_TTAGGED node for `t`, or nil
|
||||
@@ -12587,64 +12582,31 @@ fn structparamsize(c: *cgen, t: *node) i32 = {
|
||||
return si.totsize;
|
||||
};
|
||||
|
||||
// istaggedtype — alias-aware. Mirrors isstrtype: follow N_TNAME to its
|
||||
// underlying decl, then unwrap a leading N_TBANG so `type error =
|
||||
// !(invalid | overflow);` is still recognised as tagged. Without the
|
||||
// bang unwrap the prologue treats the param as scalar (8B), spilling
|
||||
// only DI and losing the value-word SI; the match read of slot+8 then
|
||||
// trails into saved BP.
|
||||
// istaggedtype — alias-aware. Reads stamped tinfo so `T`,
|
||||
// `type alias = (A|B)`, `type error = !(invalid|overflow)` all
|
||||
// resolve to TY_TAGGED — tinfofornode handles the N_TBANG unwrap
|
||||
// (check.ww:1145) so we don't re-walk it here. Cite cstage cgen.c
|
||||
// (`type_chase_named` + TY_TAGGED). Collapsed per A.6.3b (#46).
|
||||
fn istaggedtype(c: *cgen, t: *node) bool = {
|
||||
if (istaggedtyperaw(t)) { return true; };
|
||||
if (c == nil) { return false; };
|
||||
let r: *node = resolvetype(c, t);
|
||||
if (istaggedtyperaw(r)) { return true; };
|
||||
if (r != nil) {
|
||||
if (r.kind == nkind.N_TBANG) {
|
||||
let inner: *node = r.lhs;
|
||||
if (istaggedtyperaw(inner)) { return true; };
|
||||
if (inner != nil) {
|
||||
let r2: *node = resolvetype(c, inner);
|
||||
if (istaggedtyperaw(r2)) { return true; };
|
||||
};
|
||||
};
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
// isf32typeraw / isf64typeraw — bare TNAME check, no alias resolution.
|
||||
fn isf32typeraw(t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
if (t.kind != nkind.N_TNAME) { return false; };
|
||||
return streq(t.str, "f32");
|
||||
return typeistagged(t.type_: *tinfo);
|
||||
};
|
||||
|
||||
fn isf64typeraw(t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
if (t.kind != nkind.N_TNAME) { return false; };
|
||||
return streq(t.str, "f64");
|
||||
};
|
||||
|
||||
// isfloattype — f32 / f64 (and aliases of those). Used by cglet,
|
||||
// cgident, cgassign, cgbin, cgcast, cgcall, cgreturn, fn-prologue to
|
||||
// dispatch the MOVSS/MOVSD-shaped paths.
|
||||
// isfloattype — f32 / f64 / untyped_float (alias-aware). Cite cstage
|
||||
// cgen.c:117 `cg_isfloat`. Dispatches MOVSS/MOVSD-shaped paths across
|
||||
// cglet, cgident, cgassign, cgbin, cgcast, cgcall, cgreturn, fn-
|
||||
// prologue. Collapsed per A.6.3b (#46).
|
||||
export fn isfloattype(c: *cgen, t: *node) bool = {
|
||||
if (isf32typeraw(t)) { return true; };
|
||||
if (isf64typeraw(t)) { return true; };
|
||||
if (c == nil) { return false; };
|
||||
let r: *node = resolvetype(c, t);
|
||||
if (isf32typeraw(r)) { return true; };
|
||||
if (isf64typeraw(r)) { return true; };
|
||||
return false;
|
||||
if (t == nil) { return false; };
|
||||
return typeisfloat(t.type_: *tinfo);
|
||||
};
|
||||
|
||||
// isf32type — narrower predicate: true only for f32 (after alias
|
||||
// resolution). f64 returns false. Used to pick MOVSS vs MOVSD and
|
||||
// the SS-variant arithmetic / cast opcodes.
|
||||
// isf32type — narrower: true only for f32 (after alias chase). Cite
|
||||
// cstage cgen.c:188 `type_isf32`. Picks MOVSS vs MOVSD and the SS-
|
||||
// variant arithmetic / cast opcodes. Collapsed per A.6.3b (#46).
|
||||
export fn isf32type(c: *cgen, t: *node) bool = {
|
||||
if (isf32typeraw(t)) { return true; };
|
||||
if (c == nil) { return false; };
|
||||
let r: *node = resolvetype(c, t);
|
||||
return isf32typeraw(r);
|
||||
if (t == nil) { return false; };
|
||||
return typeisf32(t.type_: *tinfo);
|
||||
};
|
||||
|
||||
// exprfloatkind — classify an expression's value-class so callers can
|
||||
@@ -12765,30 +12727,23 @@ export fn exprfloatkind(c: *cgen, n: *node) i32 = {
|
||||
return 0;
|
||||
};
|
||||
|
||||
// isnullabletype — nkind.N_TTAGGED with exactly two children, one *T and
|
||||
// one `void`. Folds to a single 8-byte pointer slot per Hare's
|
||||
// `(*T | null)` semantics. Mirrors check.c's resolve_type detection.
|
||||
// isnullabletype — `(*T | void)` one-word fold per Hare's
|
||||
// `(*T | null)` semantics. Cite cstage cgen.c:396 `type_isnullable`;
|
||||
// the .nullable flag lands on tinfo at check.ww:1309-1318 when the
|
||||
// two-variant shape matches. Collapsed per A.6.3b (#46).
|
||||
export fn isnullabletype(t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
if (t.kind != nkind.N_TTAGGED) { return false; };
|
||||
let a: *node = t.list;
|
||||
if (a == nil) { return false; };
|
||||
let b: *node = a.next;
|
||||
if (b == nil) { return false; };
|
||||
if (b.next != nil) { return false; };
|
||||
let aptr: bool = (a.kind == nkind.N_TPTR);
|
||||
let bptr: bool = (b.kind == nkind.N_TPTR);
|
||||
let avoid: bool = (a.kind == nkind.N_TNAME);
|
||||
if (avoid) { avoid = streq(a.str, "void"); };
|
||||
let bvoid: bool = (b.kind == nkind.N_TNAME);
|
||||
if (bvoid) { bvoid = streq(b.str, "void"); };
|
||||
if (aptr) { if (bvoid) { return true; }; };
|
||||
if (avoid) { if (bptr) { return true; }; };
|
||||
return false;
|
||||
return typeisnullable(t.type_: *tinfo);
|
||||
};
|
||||
|
||||
// nullableptrtag — 0-based index of the *T variant in a nullable
|
||||
// union. The void variant takes the other slot (0 or 1).
|
||||
// union. The void variant takes the other slot (0 or 1). AST-keyed
|
||||
// for now: tinfofornode doesn't populate TY_TAGGED.params yet
|
||||
// (check.ww:1287-1337 sets size/align/nullable but not the variant
|
||||
// chain), so the tinfo equivalent of cstage cgen.c:405
|
||||
// `nullable_ptr_tag` can't read params today. Graduates to a pure
|
||||
// tinfo helper in A.6.3f (#50) alongside the variant-index work and
|
||||
// the tparam-population glue.
|
||||
export fn nullableptrtag(t: *node) i32 = {
|
||||
if (t == nil) { return 0; };
|
||||
if (t.kind != nkind.N_TTAGGED) { return 0; };
|
||||
@@ -15663,7 +15618,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
if (tp != nil) {
|
||||
let tpt: *node = tp.lhs;
|
||||
if (isstrtyperaw(tpt)) {
|
||||
if (isstrtype(c, tpt)) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((lc.off + foff + 0): i64);
|
||||
emitline("(BP), AX\n");
|
||||
@@ -20714,8 +20669,8 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
let p1t: *node = nil;
|
||||
if (p0 != nil) { p0t = p0.lhs; };
|
||||
if (p1 != nil) { p1t = p1.lhs; };
|
||||
let s0_is_str: bool = isstrtyperaw(p0t);
|
||||
let s1_is_str: bool = isstrtyperaw(p1t);
|
||||
let s0_is_str: bool = isstrtype(c, p0t);
|
||||
let s1_is_str: bool = isstrtype(c, p1t);
|
||||
if (p0 != nil) {
|
||||
if (p1 != nil) {
|
||||
if (s0_is_str != s1_is_str) {
|
||||
@@ -21257,8 +21212,8 @@ fn cgmlet(c: *cgen, n: *node) void = {
|
||||
if (t0 == nil) { t0 = p0t; };
|
||||
if (t1 == nil) { t1 = p1t; };
|
||||
|
||||
let s0_is_str: bool = isstrtyperaw(t0);
|
||||
let s1_is_str: bool = isstrtyperaw(t1);
|
||||
let s0_is_str: bool = isstrtype(c, t0);
|
||||
let s1_is_str: bool = isstrtype(c, t1);
|
||||
|
||||
cgexpr(c, rhs);
|
||||
|
||||
|
||||
@@ -1485,7 +1485,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
if (tp != nil) {
|
||||
let tpt: *node = tp.lhs;
|
||||
if (isstrtyperaw(tpt)) {
|
||||
if (isstrtype(c, tpt)) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((lc.off + foff + 0): i64);
|
||||
emitline("(BP), AX\n");
|
||||
|
||||
@@ -712,8 +712,8 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
let p1t: *node = nil;
|
||||
if (p0 != nil) { p0t = p0.lhs; };
|
||||
if (p1 != nil) { p1t = p1.lhs; };
|
||||
let s0_is_str: bool = isstrtyperaw(p0t);
|
||||
let s1_is_str: bool = isstrtyperaw(p1t);
|
||||
let s0_is_str: bool = isstrtype(c, p0t);
|
||||
let s1_is_str: bool = isstrtype(c, p1t);
|
||||
if (p0 != nil) {
|
||||
if (p1 != nil) {
|
||||
if (s0_is_str != s1_is_str) {
|
||||
@@ -1255,8 +1255,8 @@ fn cgmlet(c: *cgen, n: *node) void = {
|
||||
if (t0 == nil) { t0 = p0t; };
|
||||
if (t1 == nil) { t1 = p1t; };
|
||||
|
||||
let s0_is_str: bool = isstrtyperaw(t0);
|
||||
let s1_is_str: bool = isstrtyperaw(t1);
|
||||
let s0_is_str: bool = isstrtype(c, t0);
|
||||
let s1_is_str: bool = isstrtype(c, t1);
|
||||
|
||||
cgexpr(c, rhs);
|
||||
|
||||
|
||||
@@ -867,72 +867,13 @@ fn typenameisunsigned(nm: str) bool = {
|
||||
};
|
||||
|
||||
// typeis8byteprimitive — does this type take exactly one 8-byte
|
||||
// slot (pointer / fn-ptr / 64-bit int / chan / scalar primitive
|
||||
// padded up to 8) rather than a wider aggregate? Used by nkind.N_LET
|
||||
// zero-init to mirror C cgen's "only zero if sz == 8 at the type
|
||||
// level" rule. Strings (16), slices (24), tagged unions (>=16),
|
||||
// tuples (16), structs (varies), arrays — all fall through to
|
||||
// false here even when their *slot* rounds up to 8.
|
||||
// slot rather than a wider aggregate? One-liner via typeis8byteprim
|
||||
// (cstage N_LET sz==8 ladder SSoT). t.type_ is stamped at check.ww
|
||||
// L426-436 for every type-AST kind callers reach. Collapsed onto the
|
||||
// tinfo helper per A.6.3b (#46).
|
||||
fn typeis8byteprimitive(c: *cgen, t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: nkind = t.kind;
|
||||
if (k == nkind.N_TPTR) { return true; };
|
||||
if (k == nkind.N_TFN) { return true; };
|
||||
if (k == nkind.N_TCHAN) { return true; };
|
||||
if (k == nkind.N_TSLICE) { return false; };
|
||||
if (k == nkind.N_TARRAY) {
|
||||
// C cgen (cmd/w6c/cgen.c:3317) zero-inits TY_ARRAY whenever
|
||||
// its raw byte size is 8 — e.g. `[8]bool`, `[2]i32`, `[4]i16`,
|
||||
// `[1]i64`. Mirror that here so the wwstage matches.
|
||||
let lenn: *node = t.rhs;
|
||||
let elemn: *node = t.lhs;
|
||||
if (lenn == nil) { return false; };
|
||||
if (lenn.kind != nkind.N_INTLIT) { return false; };
|
||||
let elen: i64 = lenn.uval: i64;
|
||||
let esz: i32 = 8;
|
||||
if (elemn != nil) {
|
||||
if (elemn.kind == nkind.N_TNAME) {
|
||||
let ps: i32 = primsize(elemn.str);
|
||||
if (ps > 0) { esz = ps; };
|
||||
};
|
||||
};
|
||||
return (esz: i64 * elen) == 8i64;
|
||||
};
|
||||
if (k == nkind.N_TTUPLE) { return false; };
|
||||
if (k == nkind.N_TTAGGED){ return false; };
|
||||
// STATUS-3 #22: `!T` carries the error flag on T's underlying
|
||||
// shape (cmd/wcc/check.c:290 resolve_type N_TBANG copies T's
|
||||
// kind, just sets iserror). cstage's N_LET sizes off lu->kind,
|
||||
// so `!void`/`!i32` land in the sz=8 default and `!str`/`!slice`
|
||||
// keep their composite slot. Defer to the inner type so
|
||||
// `let e: !void;` mirrors cstage's MOVQ $0 while `!str` falls
|
||||
// through to the multi-word fill.
|
||||
if (k == nkind.N_TBANG) { return typeis8byteprimitive(c, t.lhs); };
|
||||
if (k == nkind.N_TNAME) {
|
||||
let nm: str = t.str;
|
||||
if (streq(nm, "str")) { return false; };
|
||||
// Plain `void` slot: cstage sz=8 default → MOVQ $0. The let-
|
||||
// decl is a phantom (a tagged-union variant tag carrier), but
|
||||
// the slot is still 8B and zero-inits like any other prim.
|
||||
if (streq(nm, "void")) { return true; };
|
||||
// Struct alias: not a primitive even if the slot is 8B.
|
||||
if (structlookup(c, nm) != nil) { return false; };
|
||||
// Primitive (i8/u8/.../i64/u64/bool/rune/f32/f64/int/...).
|
||||
// All of these get slot-padded to 8 and zero-init in C.
|
||||
if (primsize(nm) > 0) { return true; };
|
||||
// STATUS-3 #22: alias to `!T` or to `void` (Hare-style error
|
||||
// type / phantom variant). cstage resolves the alias and
|
||||
// lands on sz=8 default. Follow through aliaslookup so
|
||||
// `type invalid = !void;` and `type done = void;` zero-init.
|
||||
if (c != nil) {
|
||||
let aliased: *node = aliaslookup(c, nm);
|
||||
if (aliased != nil) {
|
||||
return typeis8byteprimitive(c, aliased);
|
||||
};
|
||||
};
|
||||
return false;
|
||||
};
|
||||
return false;
|
||||
return typeis8byteprim(t.type_: *tinfo);
|
||||
};
|
||||
|
||||
// elemissignedc — given an indexable type-AST (`*T`, `[]T`, `[N]T`),
|
||||
@@ -2168,54 +2109,20 @@ fn collectstructs(c: *cgen, file: *node) void = {
|
||||
};
|
||||
};
|
||||
|
||||
// `type X = str;` aliases) to `str`. Takes *cgen so it can walk the
|
||||
// alias chain registered at file load.
|
||||
fn isstrtyperaw(t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
if (t.kind == nkind.N_TNAME) {
|
||||
let nm: str = t.str;
|
||||
if (streq(nm, "str")) { return true; };
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
// isstrtype — alias-aware. Reads the stamped tinfo so `str`,
|
||||
// `type alias = str`, `!str`, and chained aliases all route to the
|
||||
// str-shaped slot. Cite cstage cgen.c:159 `type_isstr` SSoT.
|
||||
// Collapsed onto typeisstr per A.6.3b (#46); the prior AST walker is
|
||||
// reconstituted by typeisstr's TY_NAMED chase + tinfofornode's
|
||||
// TBANG-unwrap (check.ww:1145).
|
||||
fn isstrtype(c: *cgen, t: *node) bool = {
|
||||
if (isstrtyperaw(t)) { return true; };
|
||||
if (c == nil) { return false; };
|
||||
let r: *node = resolvetype(c, t);
|
||||
if (isstrtyperaw(r)) { return true; };
|
||||
// `parserr = !str` — `!T` aliases shouldn't hide their
|
||||
// underlying type from str-routing. Unwrap and re-check.
|
||||
if (r != nil) {
|
||||
if (r.kind == nkind.N_TBANG) {
|
||||
let inner: *node = r.lhs;
|
||||
if (isstrtyperaw(inner)) { return true; };
|
||||
if (inner != nil) {
|
||||
let r2: *node = resolvetype(c, inner);
|
||||
if (isstrtyperaw(r2)) { return true; };
|
||||
};
|
||||
};
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
fn isslicetyperaw(t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
if (t.kind == nkind.N_TSLICE) { return true; };
|
||||
return false;
|
||||
return typeisstr(t.type_: *tinfo);
|
||||
};
|
||||
|
||||
fn isslicetype(c: *cgen, t: *node) bool = {
|
||||
if (isslicetyperaw(t)) { return true; };
|
||||
if (c == nil) { return false; };
|
||||
let r: *node = resolvetype(c, t);
|
||||
return isslicetyperaw(r);
|
||||
};
|
||||
|
||||
fn istaggedtyperaw(t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
if (t.kind == nkind.N_TTAGGED) { return true; };
|
||||
return false;
|
||||
return typeisslice(t.type_: *tinfo);
|
||||
};
|
||||
|
||||
// resolvetagged — return the underlying N_TTAGGED node for `t`, or nil
|
||||
@@ -2336,64 +2243,31 @@ fn structparamsize(c: *cgen, t: *node) i32 = {
|
||||
return si.totsize;
|
||||
};
|
||||
|
||||
// istaggedtype — alias-aware. Mirrors isstrtype: follow N_TNAME to its
|
||||
// underlying decl, then unwrap a leading N_TBANG so `type error =
|
||||
// !(invalid | overflow);` is still recognised as tagged. Without the
|
||||
// bang unwrap the prologue treats the param as scalar (8B), spilling
|
||||
// only DI and losing the value-word SI; the match read of slot+8 then
|
||||
// trails into saved BP.
|
||||
// istaggedtype — alias-aware. Reads stamped tinfo so `T`,
|
||||
// `type alias = (A|B)`, `type error = !(invalid|overflow)` all
|
||||
// resolve to TY_TAGGED — tinfofornode handles the N_TBANG unwrap
|
||||
// (check.ww:1145) so we don't re-walk it here. Cite cstage cgen.c
|
||||
// (`type_chase_named` + TY_TAGGED). Collapsed per A.6.3b (#46).
|
||||
fn istaggedtype(c: *cgen, t: *node) bool = {
|
||||
if (istaggedtyperaw(t)) { return true; };
|
||||
if (c == nil) { return false; };
|
||||
let r: *node = resolvetype(c, t);
|
||||
if (istaggedtyperaw(r)) { return true; };
|
||||
if (r != nil) {
|
||||
if (r.kind == nkind.N_TBANG) {
|
||||
let inner: *node = r.lhs;
|
||||
if (istaggedtyperaw(inner)) { return true; };
|
||||
if (inner != nil) {
|
||||
let r2: *node = resolvetype(c, inner);
|
||||
if (istaggedtyperaw(r2)) { return true; };
|
||||
};
|
||||
};
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
// isf32typeraw / isf64typeraw — bare TNAME check, no alias resolution.
|
||||
fn isf32typeraw(t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
if (t.kind != nkind.N_TNAME) { return false; };
|
||||
return streq(t.str, "f32");
|
||||
return typeistagged(t.type_: *tinfo);
|
||||
};
|
||||
|
||||
fn isf64typeraw(t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
if (t.kind != nkind.N_TNAME) { return false; };
|
||||
return streq(t.str, "f64");
|
||||
};
|
||||
|
||||
// isfloattype — f32 / f64 (and aliases of those). Used by cglet,
|
||||
// cgident, cgassign, cgbin, cgcast, cgcall, cgreturn, fn-prologue to
|
||||
// dispatch the MOVSS/MOVSD-shaped paths.
|
||||
// isfloattype — f32 / f64 / untyped_float (alias-aware). Cite cstage
|
||||
// cgen.c:117 `cg_isfloat`. Dispatches MOVSS/MOVSD-shaped paths across
|
||||
// cglet, cgident, cgassign, cgbin, cgcast, cgcall, cgreturn, fn-
|
||||
// prologue. Collapsed per A.6.3b (#46).
|
||||
export fn isfloattype(c: *cgen, t: *node) bool = {
|
||||
if (isf32typeraw(t)) { return true; };
|
||||
if (isf64typeraw(t)) { return true; };
|
||||
if (c == nil) { return false; };
|
||||
let r: *node = resolvetype(c, t);
|
||||
if (isf32typeraw(r)) { return true; };
|
||||
if (isf64typeraw(r)) { return true; };
|
||||
return false;
|
||||
if (t == nil) { return false; };
|
||||
return typeisfloat(t.type_: *tinfo);
|
||||
};
|
||||
|
||||
// isf32type — narrower predicate: true only for f32 (after alias
|
||||
// resolution). f64 returns false. Used to pick MOVSS vs MOVSD and
|
||||
// the SS-variant arithmetic / cast opcodes.
|
||||
// isf32type — narrower: true only for f32 (after alias chase). Cite
|
||||
// cstage cgen.c:188 `type_isf32`. Picks MOVSS vs MOVSD and the SS-
|
||||
// variant arithmetic / cast opcodes. Collapsed per A.6.3b (#46).
|
||||
export fn isf32type(c: *cgen, t: *node) bool = {
|
||||
if (isf32typeraw(t)) { return true; };
|
||||
if (c == nil) { return false; };
|
||||
let r: *node = resolvetype(c, t);
|
||||
return isf32typeraw(r);
|
||||
if (t == nil) { return false; };
|
||||
return typeisf32(t.type_: *tinfo);
|
||||
};
|
||||
|
||||
// exprfloatkind — classify an expression's value-class so callers can
|
||||
@@ -2514,30 +2388,23 @@ export fn exprfloatkind(c: *cgen, n: *node) i32 = {
|
||||
return 0;
|
||||
};
|
||||
|
||||
// isnullabletype — nkind.N_TTAGGED with exactly two children, one *T and
|
||||
// one `void`. Folds to a single 8-byte pointer slot per Hare's
|
||||
// `(*T | null)` semantics. Mirrors check.c's resolve_type detection.
|
||||
// isnullabletype — `(*T | void)` one-word fold per Hare's
|
||||
// `(*T | null)` semantics. Cite cstage cgen.c:396 `type_isnullable`;
|
||||
// the .nullable flag lands on tinfo at check.ww:1309-1318 when the
|
||||
// two-variant shape matches. Collapsed per A.6.3b (#46).
|
||||
export fn isnullabletype(t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
if (t.kind != nkind.N_TTAGGED) { return false; };
|
||||
let a: *node = t.list;
|
||||
if (a == nil) { return false; };
|
||||
let b: *node = a.next;
|
||||
if (b == nil) { return false; };
|
||||
if (b.next != nil) { return false; };
|
||||
let aptr: bool = (a.kind == nkind.N_TPTR);
|
||||
let bptr: bool = (b.kind == nkind.N_TPTR);
|
||||
let avoid: bool = (a.kind == nkind.N_TNAME);
|
||||
if (avoid) { avoid = streq(a.str, "void"); };
|
||||
let bvoid: bool = (b.kind == nkind.N_TNAME);
|
||||
if (bvoid) { bvoid = streq(b.str, "void"); };
|
||||
if (aptr) { if (bvoid) { return true; }; };
|
||||
if (avoid) { if (bptr) { return true; }; };
|
||||
return false;
|
||||
return typeisnullable(t.type_: *tinfo);
|
||||
};
|
||||
|
||||
// nullableptrtag — 0-based index of the *T variant in a nullable
|
||||
// union. The void variant takes the other slot (0 or 1).
|
||||
// union. The void variant takes the other slot (0 or 1). AST-keyed
|
||||
// for now: tinfofornode doesn't populate TY_TAGGED.params yet
|
||||
// (check.ww:1287-1337 sets size/align/nullable but not the variant
|
||||
// chain), so the tinfo equivalent of cstage cgen.c:405
|
||||
// `nullable_ptr_tag` can't read params today. Graduates to a pure
|
||||
// tinfo helper in A.6.3f (#50) alongside the variant-index work and
|
||||
// the tparam-population glue.
|
||||
export fn nullableptrtag(t: *node) i32 = {
|
||||
if (t == nil) { return 0; };
|
||||
if (t.kind != nkind.N_TTAGGED) { return 0; };
|
||||
|
||||
@@ -6666,6 +6666,94 @@ export fn typeissigned(t: *tinfo) bool = {
|
||||
return typeisint(t);
|
||||
};
|
||||
|
||||
// typeisstr — TY_STR (and TY_UNTYPED_STR for literals pre-default).
|
||||
// Cite cstage cgen.c:159 `type_isstr` — single TY_NAMED peel, accepts
|
||||
// the same untyped form. ww walks the .under chain so alias-of-alias
|
||||
// (`type s2 = s1; type s1 = str;`) lands the same way.
|
||||
export fn typeisstr(t: *tinfo) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: tykind = t.kind;
|
||||
if (k == tykind.TY_STR) { return true; };
|
||||
if (k == tykind.TY_UNTYPED_STR) { return true; };
|
||||
if (k == tykind.TY_NAMED) { return typeisstr(t.under); };
|
||||
return false;
|
||||
};
|
||||
|
||||
// typeisslice — TY_SLICE. Cite cstage cgen.c:174 `type_isslice`.
|
||||
export fn typeisslice(t: *tinfo) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: tykind = t.kind;
|
||||
if (k == tykind.TY_SLICE) { return true; };
|
||||
if (k == tykind.TY_NAMED) { return typeisslice(t.under); };
|
||||
return false;
|
||||
};
|
||||
|
||||
// typeistagged — TY_TAGGED (alias-aware). Cite cstage cgen.c:516
|
||||
// `type_istagged` — same single-peel shape. The node-keyed wwstage
|
||||
// helper this replaces also unwrapped a leading N_TBANG so
|
||||
// `type error = !(invalid | overflow);` registered as tagged. Post-
|
||||
// A.6.2 the TBANG unwrap is handled by tinfofornode (check.ww:1145-
|
||||
// 1152 returns the inner tinfo unchanged) so we recover the cstage
|
||||
// semantics with the bare kind check + NAMED chase.
|
||||
export fn typeistagged(t: *tinfo) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: tykind = t.kind;
|
||||
if (k == tykind.TY_TAGGED) { return true; };
|
||||
if (k == tykind.TY_NAMED) { return typeistagged(t.under); };
|
||||
return false;
|
||||
};
|
||||
|
||||
// typeisf32 — narrower-than-typeisfloat: only TY_F32 (after alias
|
||||
// chase). Cite cstage cgen.c:188 `type_isf32`. Used to pick MOVSS vs
|
||||
// MOVSD and the SS-variant arithmetic / cast opcodes.
|
||||
export fn typeisf32(t: *tinfo) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: tykind = t.kind;
|
||||
if (k == tykind.TY_F32) { return true; };
|
||||
if (k == tykind.TY_NAMED) { return typeisf32(t.under); };
|
||||
return false;
|
||||
};
|
||||
|
||||
// typeisnullable — TY_TAGGED with the `(*T | void)` one-word fold.
|
||||
// Cite cstage cgen.c:396 `type_isnullable`. The .nullable flag is
|
||||
// stamped by tinfofornode (check.ww:1309-1318) when the two-variant
|
||||
// shape matches.
|
||||
export fn typeisnullable(t: *tinfo) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: tykind = t.kind;
|
||||
if (k == tykind.TY_TAGGED) { return t.nullable != 0; };
|
||||
if (k == tykind.TY_NAMED) { return typeisnullable(t.under); };
|
||||
return false;
|
||||
};
|
||||
|
||||
// typeis8byteprim — does this type take exactly one 8-byte stack
|
||||
// slot (ptr / fn / chan / 64-bit int / scalar primitive padded up to
|
||||
// 8 / `[N]T` whose natural width is 8) rather than a wider aggregate?
|
||||
// Mirrors the ladder cstage's cgen.c N_LET zero-init takes on `sz==8`
|
||||
// (cmd/wcc/check.c sizing + cgen.c N_LET). The node-keyed wwstage
|
||||
// helper this replaces predates tinfo and AST-walked TBANG / TNAME
|
||||
// alias chains; tinfofornode now collapses TBANG (check.ww:1145) and
|
||||
// TY_NAMED.under carries the chain, so the tinfo walk handles every
|
||||
// shape the AST walker did.
|
||||
export fn typeis8byteprim(t: *tinfo) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: tykind = t.kind;
|
||||
if (k == tykind.TY_PTR) { return true; };
|
||||
if (k == tykind.TY_FN) { return true; };
|
||||
if (k == tykind.TY_CHAN) { return true; };
|
||||
if (k == tykind.TY_SLICE) { return false; };
|
||||
if (k == tykind.TY_TUPLE) { return false; };
|
||||
if (k == tykind.TY_TAGGED) { return false; };
|
||||
if (k == tykind.TY_STR) { return false; };
|
||||
if (k == tykind.TY_STRUCT) { return false; };
|
||||
if (k == tykind.TY_ARRAY) { return t.size == 8u64; };
|
||||
if (k == tykind.TY_NAMED) { return typeis8byteprim(t.under); };
|
||||
// Remaining: primitives (i8/u8/.../i64/u64/bool/rune/f32/f64/
|
||||
// int/uint/uintptr) and TY_VOID. All slot-pad to 8 and zero-init
|
||||
// in cstage's `sz==8` branch.
|
||||
return true;
|
||||
};
|
||||
|
||||
export fn typeisuntyped(t: *tinfo) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: tykind = t.kind;
|
||||
@@ -11118,72 +11206,13 @@ fn typenameisunsigned(nm: str) bool = {
|
||||
};
|
||||
|
||||
// typeis8byteprimitive — does this type take exactly one 8-byte
|
||||
// slot (pointer / fn-ptr / 64-bit int / chan / scalar primitive
|
||||
// padded up to 8) rather than a wider aggregate? Used by nkind.N_LET
|
||||
// zero-init to mirror C cgen's "only zero if sz == 8 at the type
|
||||
// level" rule. Strings (16), slices (24), tagged unions (>=16),
|
||||
// tuples (16), structs (varies), arrays — all fall through to
|
||||
// false here even when their *slot* rounds up to 8.
|
||||
// slot rather than a wider aggregate? One-liner via typeis8byteprim
|
||||
// (cstage N_LET sz==8 ladder SSoT). t.type_ is stamped at check.ww
|
||||
// L426-436 for every type-AST kind callers reach. Collapsed onto the
|
||||
// tinfo helper per A.6.3b (#46).
|
||||
fn typeis8byteprimitive(c: *cgen, t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: nkind = t.kind;
|
||||
if (k == nkind.N_TPTR) { return true; };
|
||||
if (k == nkind.N_TFN) { return true; };
|
||||
if (k == nkind.N_TCHAN) { return true; };
|
||||
if (k == nkind.N_TSLICE) { return false; };
|
||||
if (k == nkind.N_TARRAY) {
|
||||
// C cgen (cmd/w6c/cgen.c:3317) zero-inits TY_ARRAY whenever
|
||||
// its raw byte size is 8 — e.g. `[8]bool`, `[2]i32`, `[4]i16`,
|
||||
// `[1]i64`. Mirror that here so the wwstage matches.
|
||||
let lenn: *node = t.rhs;
|
||||
let elemn: *node = t.lhs;
|
||||
if (lenn == nil) { return false; };
|
||||
if (lenn.kind != nkind.N_INTLIT) { return false; };
|
||||
let elen: i64 = lenn.uval: i64;
|
||||
let esz: i32 = 8;
|
||||
if (elemn != nil) {
|
||||
if (elemn.kind == nkind.N_TNAME) {
|
||||
let ps: i32 = primsize(elemn.str);
|
||||
if (ps > 0) { esz = ps; };
|
||||
};
|
||||
};
|
||||
return (esz: i64 * elen) == 8i64;
|
||||
};
|
||||
if (k == nkind.N_TTUPLE) { return false; };
|
||||
if (k == nkind.N_TTAGGED){ return false; };
|
||||
// STATUS-3 #22: `!T` carries the error flag on T's underlying
|
||||
// shape (cmd/wcc/check.c:290 resolve_type N_TBANG copies T's
|
||||
// kind, just sets iserror). cstage's N_LET sizes off lu->kind,
|
||||
// so `!void`/`!i32` land in the sz=8 default and `!str`/`!slice`
|
||||
// keep their composite slot. Defer to the inner type so
|
||||
// `let e: !void;` mirrors cstage's MOVQ $0 while `!str` falls
|
||||
// through to the multi-word fill.
|
||||
if (k == nkind.N_TBANG) { return typeis8byteprimitive(c, t.lhs); };
|
||||
if (k == nkind.N_TNAME) {
|
||||
let nm: str = t.str;
|
||||
if (streq(nm, "str")) { return false; };
|
||||
// Plain `void` slot: cstage sz=8 default → MOVQ $0. The let-
|
||||
// decl is a phantom (a tagged-union variant tag carrier), but
|
||||
// the slot is still 8B and zero-inits like any other prim.
|
||||
if (streq(nm, "void")) { return true; };
|
||||
// Struct alias: not a primitive even if the slot is 8B.
|
||||
if (structlookup(c, nm) != nil) { return false; };
|
||||
// Primitive (i8/u8/.../i64/u64/bool/rune/f32/f64/int/...).
|
||||
// All of these get slot-padded to 8 and zero-init in C.
|
||||
if (primsize(nm) > 0) { return true; };
|
||||
// STATUS-3 #22: alias to `!T` or to `void` (Hare-style error
|
||||
// type / phantom variant). cstage resolves the alias and
|
||||
// lands on sz=8 default. Follow through aliaslookup so
|
||||
// `type invalid = !void;` and `type done = void;` zero-init.
|
||||
if (c != nil) {
|
||||
let aliased: *node = aliaslookup(c, nm);
|
||||
if (aliased != nil) {
|
||||
return typeis8byteprimitive(c, aliased);
|
||||
};
|
||||
};
|
||||
return false;
|
||||
};
|
||||
return false;
|
||||
return typeis8byteprim(t.type_: *tinfo);
|
||||
};
|
||||
|
||||
// elemissignedc — given an indexable type-AST (`*T`, `[]T`, `[N]T`),
|
||||
@@ -12419,54 +12448,20 @@ fn collectstructs(c: *cgen, file: *node) void = {
|
||||
};
|
||||
};
|
||||
|
||||
// `type X = str;` aliases) to `str`. Takes *cgen so it can walk the
|
||||
// alias chain registered at file load.
|
||||
fn isstrtyperaw(t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
if (t.kind == nkind.N_TNAME) {
|
||||
let nm: str = t.str;
|
||||
if (streq(nm, "str")) { return true; };
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
// isstrtype — alias-aware. Reads the stamped tinfo so `str`,
|
||||
// `type alias = str`, `!str`, and chained aliases all route to the
|
||||
// str-shaped slot. Cite cstage cgen.c:159 `type_isstr` SSoT.
|
||||
// Collapsed onto typeisstr per A.6.3b (#46); the prior AST walker is
|
||||
// reconstituted by typeisstr's TY_NAMED chase + tinfofornode's
|
||||
// TBANG-unwrap (check.ww:1145).
|
||||
fn isstrtype(c: *cgen, t: *node) bool = {
|
||||
if (isstrtyperaw(t)) { return true; };
|
||||
if (c == nil) { return false; };
|
||||
let r: *node = resolvetype(c, t);
|
||||
if (isstrtyperaw(r)) { return true; };
|
||||
// `parserr = !str` — `!T` aliases shouldn't hide their
|
||||
// underlying type from str-routing. Unwrap and re-check.
|
||||
if (r != nil) {
|
||||
if (r.kind == nkind.N_TBANG) {
|
||||
let inner: *node = r.lhs;
|
||||
if (isstrtyperaw(inner)) { return true; };
|
||||
if (inner != nil) {
|
||||
let r2: *node = resolvetype(c, inner);
|
||||
if (isstrtyperaw(r2)) { return true; };
|
||||
};
|
||||
};
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
fn isslicetyperaw(t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
if (t.kind == nkind.N_TSLICE) { return true; };
|
||||
return false;
|
||||
return typeisstr(t.type_: *tinfo);
|
||||
};
|
||||
|
||||
fn isslicetype(c: *cgen, t: *node) bool = {
|
||||
if (isslicetyperaw(t)) { return true; };
|
||||
if (c == nil) { return false; };
|
||||
let r: *node = resolvetype(c, t);
|
||||
return isslicetyperaw(r);
|
||||
};
|
||||
|
||||
fn istaggedtyperaw(t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
if (t.kind == nkind.N_TTAGGED) { return true; };
|
||||
return false;
|
||||
return typeisslice(t.type_: *tinfo);
|
||||
};
|
||||
|
||||
// resolvetagged — return the underlying N_TTAGGED node for `t`, or nil
|
||||
@@ -12587,64 +12582,31 @@ fn structparamsize(c: *cgen, t: *node) i32 = {
|
||||
return si.totsize;
|
||||
};
|
||||
|
||||
// istaggedtype — alias-aware. Mirrors isstrtype: follow N_TNAME to its
|
||||
// underlying decl, then unwrap a leading N_TBANG so `type error =
|
||||
// !(invalid | overflow);` is still recognised as tagged. Without the
|
||||
// bang unwrap the prologue treats the param as scalar (8B), spilling
|
||||
// only DI and losing the value-word SI; the match read of slot+8 then
|
||||
// trails into saved BP.
|
||||
// istaggedtype — alias-aware. Reads stamped tinfo so `T`,
|
||||
// `type alias = (A|B)`, `type error = !(invalid|overflow)` all
|
||||
// resolve to TY_TAGGED — tinfofornode handles the N_TBANG unwrap
|
||||
// (check.ww:1145) so we don't re-walk it here. Cite cstage cgen.c
|
||||
// (`type_chase_named` + TY_TAGGED). Collapsed per A.6.3b (#46).
|
||||
fn istaggedtype(c: *cgen, t: *node) bool = {
|
||||
if (istaggedtyperaw(t)) { return true; };
|
||||
if (c == nil) { return false; };
|
||||
let r: *node = resolvetype(c, t);
|
||||
if (istaggedtyperaw(r)) { return true; };
|
||||
if (r != nil) {
|
||||
if (r.kind == nkind.N_TBANG) {
|
||||
let inner: *node = r.lhs;
|
||||
if (istaggedtyperaw(inner)) { return true; };
|
||||
if (inner != nil) {
|
||||
let r2: *node = resolvetype(c, inner);
|
||||
if (istaggedtyperaw(r2)) { return true; };
|
||||
};
|
||||
};
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
// isf32typeraw / isf64typeraw — bare TNAME check, no alias resolution.
|
||||
fn isf32typeraw(t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
if (t.kind != nkind.N_TNAME) { return false; };
|
||||
return streq(t.str, "f32");
|
||||
return typeistagged(t.type_: *tinfo);
|
||||
};
|
||||
|
||||
fn isf64typeraw(t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
if (t.kind != nkind.N_TNAME) { return false; };
|
||||
return streq(t.str, "f64");
|
||||
};
|
||||
|
||||
// isfloattype — f32 / f64 (and aliases of those). Used by cglet,
|
||||
// cgident, cgassign, cgbin, cgcast, cgcall, cgreturn, fn-prologue to
|
||||
// dispatch the MOVSS/MOVSD-shaped paths.
|
||||
// isfloattype — f32 / f64 / untyped_float (alias-aware). Cite cstage
|
||||
// cgen.c:117 `cg_isfloat`. Dispatches MOVSS/MOVSD-shaped paths across
|
||||
// cglet, cgident, cgassign, cgbin, cgcast, cgcall, cgreturn, fn-
|
||||
// prologue. Collapsed per A.6.3b (#46).
|
||||
export fn isfloattype(c: *cgen, t: *node) bool = {
|
||||
if (isf32typeraw(t)) { return true; };
|
||||
if (isf64typeraw(t)) { return true; };
|
||||
if (c == nil) { return false; };
|
||||
let r: *node = resolvetype(c, t);
|
||||
if (isf32typeraw(r)) { return true; };
|
||||
if (isf64typeraw(r)) { return true; };
|
||||
return false;
|
||||
if (t == nil) { return false; };
|
||||
return typeisfloat(t.type_: *tinfo);
|
||||
};
|
||||
|
||||
// isf32type — narrower predicate: true only for f32 (after alias
|
||||
// resolution). f64 returns false. Used to pick MOVSS vs MOVSD and
|
||||
// the SS-variant arithmetic / cast opcodes.
|
||||
// isf32type — narrower: true only for f32 (after alias chase). Cite
|
||||
// cstage cgen.c:188 `type_isf32`. Picks MOVSS vs MOVSD and the SS-
|
||||
// variant arithmetic / cast opcodes. Collapsed per A.6.3b (#46).
|
||||
export fn isf32type(c: *cgen, t: *node) bool = {
|
||||
if (isf32typeraw(t)) { return true; };
|
||||
if (c == nil) { return false; };
|
||||
let r: *node = resolvetype(c, t);
|
||||
return isf32typeraw(r);
|
||||
if (t == nil) { return false; };
|
||||
return typeisf32(t.type_: *tinfo);
|
||||
};
|
||||
|
||||
// exprfloatkind — classify an expression's value-class so callers can
|
||||
@@ -12765,30 +12727,23 @@ export fn exprfloatkind(c: *cgen, n: *node) i32 = {
|
||||
return 0;
|
||||
};
|
||||
|
||||
// isnullabletype — nkind.N_TTAGGED with exactly two children, one *T and
|
||||
// one `void`. Folds to a single 8-byte pointer slot per Hare's
|
||||
// `(*T | null)` semantics. Mirrors check.c's resolve_type detection.
|
||||
// isnullabletype — `(*T | void)` one-word fold per Hare's
|
||||
// `(*T | null)` semantics. Cite cstage cgen.c:396 `type_isnullable`;
|
||||
// the .nullable flag lands on tinfo at check.ww:1309-1318 when the
|
||||
// two-variant shape matches. Collapsed per A.6.3b (#46).
|
||||
export fn isnullabletype(t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
if (t.kind != nkind.N_TTAGGED) { return false; };
|
||||
let a: *node = t.list;
|
||||
if (a == nil) { return false; };
|
||||
let b: *node = a.next;
|
||||
if (b == nil) { return false; };
|
||||
if (b.next != nil) { return false; };
|
||||
let aptr: bool = (a.kind == nkind.N_TPTR);
|
||||
let bptr: bool = (b.kind == nkind.N_TPTR);
|
||||
let avoid: bool = (a.kind == nkind.N_TNAME);
|
||||
if (avoid) { avoid = streq(a.str, "void"); };
|
||||
let bvoid: bool = (b.kind == nkind.N_TNAME);
|
||||
if (bvoid) { bvoid = streq(b.str, "void"); };
|
||||
if (aptr) { if (bvoid) { return true; }; };
|
||||
if (avoid) { if (bptr) { return true; }; };
|
||||
return false;
|
||||
return typeisnullable(t.type_: *tinfo);
|
||||
};
|
||||
|
||||
// nullableptrtag — 0-based index of the *T variant in a nullable
|
||||
// union. The void variant takes the other slot (0 or 1).
|
||||
// union. The void variant takes the other slot (0 or 1). AST-keyed
|
||||
// for now: tinfofornode doesn't populate TY_TAGGED.params yet
|
||||
// (check.ww:1287-1337 sets size/align/nullable but not the variant
|
||||
// chain), so the tinfo equivalent of cstage cgen.c:405
|
||||
// `nullable_ptr_tag` can't read params today. Graduates to a pure
|
||||
// tinfo helper in A.6.3f (#50) alongside the variant-index work and
|
||||
// the tparam-population glue.
|
||||
export fn nullableptrtag(t: *node) i32 = {
|
||||
if (t == nil) { return 0; };
|
||||
if (t.kind != nkind.N_TTAGGED) { return 0; };
|
||||
@@ -15663,7 +15618,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
if (tp != nil) {
|
||||
let tpt: *node = tp.lhs;
|
||||
if (isstrtyperaw(tpt)) {
|
||||
if (isstrtype(c, tpt)) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((lc.off + foff + 0): i64);
|
||||
emitline("(BP), AX\n");
|
||||
@@ -20714,8 +20669,8 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
let p1t: *node = nil;
|
||||
if (p0 != nil) { p0t = p0.lhs; };
|
||||
if (p1 != nil) { p1t = p1.lhs; };
|
||||
let s0_is_str: bool = isstrtyperaw(p0t);
|
||||
let s1_is_str: bool = isstrtyperaw(p1t);
|
||||
let s0_is_str: bool = isstrtype(c, p0t);
|
||||
let s1_is_str: bool = isstrtype(c, p1t);
|
||||
if (p0 != nil) {
|
||||
if (p1 != nil) {
|
||||
if (s0_is_str != s1_is_str) {
|
||||
@@ -21257,8 +21212,8 @@ fn cgmlet(c: *cgen, n: *node) void = {
|
||||
if (t0 == nil) { t0 = p0t; };
|
||||
if (t1 == nil) { t1 = p1t; };
|
||||
|
||||
let s0_is_str: bool = isstrtyperaw(t0);
|
||||
let s1_is_str: bool = isstrtyperaw(t1);
|
||||
let s0_is_str: bool = isstrtype(c, t0);
|
||||
let s1_is_str: bool = isstrtype(c, t1);
|
||||
|
||||
cgexpr(c, rhs);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user