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);
|
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 = {
|
export fn typeisuntyped(t: *tinfo) bool = {
|
||||||
if (t == nil) { return false; };
|
if (t == nil) { return false; };
|
||||||
let k: tykind = t.kind;
|
let k: tykind = t.kind;
|
||||||
|
|||||||
@@ -6666,6 +6666,94 @@ export fn typeissigned(t: *tinfo) bool = {
|
|||||||
return typeisint(t);
|
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 = {
|
export fn typeisuntyped(t: *tinfo) bool = {
|
||||||
if (t == nil) { return false; };
|
if (t == nil) { return false; };
|
||||||
let k: tykind = t.kind;
|
let k: tykind = t.kind;
|
||||||
@@ -11118,72 +11206,13 @@ fn typenameisunsigned(nm: str) bool = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// typeis8byteprimitive — does this type take exactly one 8-byte
|
// typeis8byteprimitive — does this type take exactly one 8-byte
|
||||||
// slot (pointer / fn-ptr / 64-bit int / chan / scalar primitive
|
// slot rather than a wider aggregate? One-liner via typeis8byteprim
|
||||||
// padded up to 8) rather than a wider aggregate? Used by nkind.N_LET
|
// (cstage N_LET sz==8 ladder SSoT). t.type_ is stamped at check.ww
|
||||||
// zero-init to mirror C cgen's "only zero if sz == 8 at the type
|
// L426-436 for every type-AST kind callers reach. Collapsed onto the
|
||||||
// level" rule. Strings (16), slices (24), tagged unions (>=16),
|
// tinfo helper per A.6.3b (#46).
|
||||||
// tuples (16), structs (varies), arrays — all fall through to
|
|
||||||
// false here even when their *slot* rounds up to 8.
|
|
||||||
fn typeis8byteprimitive(c: *cgen, t: *node) bool = {
|
fn typeis8byteprimitive(c: *cgen, t: *node) bool = {
|
||||||
if (t == nil) { return false; };
|
if (t == nil) { return false; };
|
||||||
let k: nkind = t.kind;
|
return typeis8byteprim(t.type_: *tinfo);
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// elemissignedc — given an indexable type-AST (`*T`, `[]T`, `[N]T`),
|
// 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
|
// isstrtype — alias-aware. Reads the stamped tinfo so `str`,
|
||||||
// alias chain registered at file load.
|
// `type alias = str`, `!str`, and chained aliases all route to the
|
||||||
fn isstrtyperaw(t: *node) bool = {
|
// str-shaped slot. Cite cstage cgen.c:159 `type_isstr` SSoT.
|
||||||
if (t == nil) { return false; };
|
// Collapsed onto typeisstr per A.6.3b (#46); the prior AST walker is
|
||||||
if (t.kind == nkind.N_TNAME) {
|
// reconstituted by typeisstr's TY_NAMED chase + tinfofornode's
|
||||||
let nm: str = t.str;
|
// TBANG-unwrap (check.ww:1145).
|
||||||
if (streq(nm, "str")) { return true; };
|
|
||||||
};
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
fn isstrtype(c: *cgen, t: *node) bool = {
|
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 == nil) { return false; };
|
||||||
if (t.kind == nkind.N_TSLICE) { return true; };
|
return typeisstr(t.type_: *tinfo);
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn isslicetype(c: *cgen, t: *node) bool = {
|
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 == nil) { return false; };
|
||||||
if (t.kind == nkind.N_TTAGGED) { return true; };
|
return typeisslice(t.type_: *tinfo);
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// resolvetagged — return the underlying N_TTAGGED node for `t`, or nil
|
// 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;
|
return si.totsize;
|
||||||
};
|
};
|
||||||
|
|
||||||
// istaggedtype — alias-aware. Mirrors isstrtype: follow N_TNAME to its
|
// istaggedtype — alias-aware. Reads stamped tinfo so `T`,
|
||||||
// underlying decl, then unwrap a leading N_TBANG so `type error =
|
// `type alias = (A|B)`, `type error = !(invalid|overflow)` all
|
||||||
// !(invalid | overflow);` is still recognised as tagged. Without the
|
// resolve to TY_TAGGED — tinfofornode handles the N_TBANG unwrap
|
||||||
// bang unwrap the prologue treats the param as scalar (8B), spilling
|
// (check.ww:1145) so we don't re-walk it here. Cite cstage cgen.c
|
||||||
// only DI and losing the value-word SI; the match read of slot+8 then
|
// (`type_chase_named` + TY_TAGGED). Collapsed per A.6.3b (#46).
|
||||||
// trails into saved BP.
|
|
||||||
fn istaggedtype(c: *cgen, t: *node) bool = {
|
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 == nil) { return false; };
|
||||||
if (t.kind != nkind.N_TNAME) { return false; };
|
return typeistagged(t.type_: *tinfo);
|
||||||
return streq(t.str, "f32");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn isf64typeraw(t: *node) bool = {
|
// isfloattype — f32 / f64 / untyped_float (alias-aware). Cite cstage
|
||||||
if (t == nil) { return false; };
|
// cgen.c:117 `cg_isfloat`. Dispatches MOVSS/MOVSD-shaped paths across
|
||||||
if (t.kind != nkind.N_TNAME) { return false; };
|
// cglet, cgident, cgassign, cgbin, cgcast, cgcall, cgreturn, fn-
|
||||||
return streq(t.str, "f64");
|
// prologue. Collapsed per A.6.3b (#46).
|
||||||
};
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
export fn isfloattype(c: *cgen, t: *node) bool = {
|
export fn isfloattype(c: *cgen, t: *node) bool = {
|
||||||
if (isf32typeraw(t)) { return true; };
|
if (t == nil) { return false; };
|
||||||
if (isf64typeraw(t)) { return true; };
|
return typeisfloat(t.type_: *tinfo);
|
||||||
if (c == nil) { return false; };
|
|
||||||
let r: *node = resolvetype(c, t);
|
|
||||||
if (isf32typeraw(r)) { return true; };
|
|
||||||
if (isf64typeraw(r)) { return true; };
|
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// isf32type — narrower predicate: true only for f32 (after alias
|
// isf32type — narrower: true only for f32 (after alias chase). Cite
|
||||||
// resolution). f64 returns false. Used to pick MOVSS vs MOVSD and
|
// cstage cgen.c:188 `type_isf32`. Picks MOVSS vs MOVSD and the SS-
|
||||||
// the SS-variant arithmetic / cast opcodes.
|
// variant arithmetic / cast opcodes. Collapsed per A.6.3b (#46).
|
||||||
export fn isf32type(c: *cgen, t: *node) bool = {
|
export fn isf32type(c: *cgen, t: *node) bool = {
|
||||||
if (isf32typeraw(t)) { return true; };
|
if (t == nil) { return false; };
|
||||||
if (c == nil) { return false; };
|
return typeisf32(t.type_: *tinfo);
|
||||||
let r: *node = resolvetype(c, t);
|
|
||||||
return isf32typeraw(r);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// exprfloatkind — classify an expression's value-class so callers can
|
// exprfloatkind — classify an expression's value-class so callers can
|
||||||
@@ -12765,30 +12727,23 @@ export fn exprfloatkind(c: *cgen, n: *node) i32 = {
|
|||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// isnullabletype — nkind.N_TTAGGED with exactly two children, one *T and
|
// isnullabletype — `(*T | void)` one-word fold per Hare's
|
||||||
// one `void`. Folds to a single 8-byte pointer slot per Hare's
|
// `(*T | null)` semantics. Cite cstage cgen.c:396 `type_isnullable`;
|
||||||
// `(*T | null)` semantics. Mirrors check.c's resolve_type detection.
|
// 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 = {
|
export fn isnullabletype(t: *node) bool = {
|
||||||
if (t == nil) { return false; };
|
if (t == nil) { return false; };
|
||||||
if (t.kind != nkind.N_TTAGGED) { return false; };
|
return typeisnullable(t.type_: *tinfo);
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// nullableptrtag — 0-based index of the *T variant in a nullable
|
// 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 = {
|
export fn nullableptrtag(t: *node) i32 = {
|
||||||
if (t == nil) { return 0; };
|
if (t == nil) { return 0; };
|
||||||
if (t.kind != nkind.N_TTAGGED) { return 0; };
|
if (t.kind != nkind.N_TTAGGED) { return 0; };
|
||||||
@@ -15663,7 +15618,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
if (tp != nil) {
|
if (tp != nil) {
|
||||||
let tpt: *node = tp.lhs;
|
let tpt: *node = tp.lhs;
|
||||||
if (isstrtyperaw(tpt)) {
|
if (isstrtype(c, tpt)) {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff((lc.off + foff + 0): i64);
|
emitoff((lc.off + foff + 0): i64);
|
||||||
emitline("(BP), AX\n");
|
emitline("(BP), AX\n");
|
||||||
@@ -20714,8 +20669,8 @@ fn cglet(c: *cgen, n: *node) void = {
|
|||||||
let p1t: *node = nil;
|
let p1t: *node = nil;
|
||||||
if (p0 != nil) { p0t = p0.lhs; };
|
if (p0 != nil) { p0t = p0.lhs; };
|
||||||
if (p1 != nil) { p1t = p1.lhs; };
|
if (p1 != nil) { p1t = p1.lhs; };
|
||||||
let s0_is_str: bool = isstrtyperaw(p0t);
|
let s0_is_str: bool = isstrtype(c, p0t);
|
||||||
let s1_is_str: bool = isstrtyperaw(p1t);
|
let s1_is_str: bool = isstrtype(c, p1t);
|
||||||
if (p0 != nil) {
|
if (p0 != nil) {
|
||||||
if (p1 != nil) {
|
if (p1 != nil) {
|
||||||
if (s0_is_str != s1_is_str) {
|
if (s0_is_str != s1_is_str) {
|
||||||
@@ -21257,8 +21212,8 @@ fn cgmlet(c: *cgen, n: *node) void = {
|
|||||||
if (t0 == nil) { t0 = p0t; };
|
if (t0 == nil) { t0 = p0t; };
|
||||||
if (t1 == nil) { t1 = p1t; };
|
if (t1 == nil) { t1 = p1t; };
|
||||||
|
|
||||||
let s0_is_str: bool = isstrtyperaw(t0);
|
let s0_is_str: bool = isstrtype(c, t0);
|
||||||
let s1_is_str: bool = isstrtyperaw(t1);
|
let s1_is_str: bool = isstrtype(c, t1);
|
||||||
|
|
||||||
cgexpr(c, rhs);
|
cgexpr(c, rhs);
|
||||||
|
|
||||||
|
|||||||
@@ -1485,7 +1485,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
if (tp != nil) {
|
if (tp != nil) {
|
||||||
let tpt: *node = tp.lhs;
|
let tpt: *node = tp.lhs;
|
||||||
if (isstrtyperaw(tpt)) {
|
if (isstrtype(c, tpt)) {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff((lc.off + foff + 0): i64);
|
emitoff((lc.off + foff + 0): i64);
|
||||||
emitline("(BP), AX\n");
|
emitline("(BP), AX\n");
|
||||||
|
|||||||
@@ -712,8 +712,8 @@ fn cglet(c: *cgen, n: *node) void = {
|
|||||||
let p1t: *node = nil;
|
let p1t: *node = nil;
|
||||||
if (p0 != nil) { p0t = p0.lhs; };
|
if (p0 != nil) { p0t = p0.lhs; };
|
||||||
if (p1 != nil) { p1t = p1.lhs; };
|
if (p1 != nil) { p1t = p1.lhs; };
|
||||||
let s0_is_str: bool = isstrtyperaw(p0t);
|
let s0_is_str: bool = isstrtype(c, p0t);
|
||||||
let s1_is_str: bool = isstrtyperaw(p1t);
|
let s1_is_str: bool = isstrtype(c, p1t);
|
||||||
if (p0 != nil) {
|
if (p0 != nil) {
|
||||||
if (p1 != nil) {
|
if (p1 != nil) {
|
||||||
if (s0_is_str != s1_is_str) {
|
if (s0_is_str != s1_is_str) {
|
||||||
@@ -1255,8 +1255,8 @@ fn cgmlet(c: *cgen, n: *node) void = {
|
|||||||
if (t0 == nil) { t0 = p0t; };
|
if (t0 == nil) { t0 = p0t; };
|
||||||
if (t1 == nil) { t1 = p1t; };
|
if (t1 == nil) { t1 = p1t; };
|
||||||
|
|
||||||
let s0_is_str: bool = isstrtyperaw(t0);
|
let s0_is_str: bool = isstrtype(c, t0);
|
||||||
let s1_is_str: bool = isstrtyperaw(t1);
|
let s1_is_str: bool = isstrtype(c, t1);
|
||||||
|
|
||||||
cgexpr(c, rhs);
|
cgexpr(c, rhs);
|
||||||
|
|
||||||
|
|||||||
@@ -867,72 +867,13 @@ fn typenameisunsigned(nm: str) bool = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// typeis8byteprimitive — does this type take exactly one 8-byte
|
// typeis8byteprimitive — does this type take exactly one 8-byte
|
||||||
// slot (pointer / fn-ptr / 64-bit int / chan / scalar primitive
|
// slot rather than a wider aggregate? One-liner via typeis8byteprim
|
||||||
// padded up to 8) rather than a wider aggregate? Used by nkind.N_LET
|
// (cstage N_LET sz==8 ladder SSoT). t.type_ is stamped at check.ww
|
||||||
// zero-init to mirror C cgen's "only zero if sz == 8 at the type
|
// L426-436 for every type-AST kind callers reach. Collapsed onto the
|
||||||
// level" rule. Strings (16), slices (24), tagged unions (>=16),
|
// tinfo helper per A.6.3b (#46).
|
||||||
// tuples (16), structs (varies), arrays — all fall through to
|
|
||||||
// false here even when their *slot* rounds up to 8.
|
|
||||||
fn typeis8byteprimitive(c: *cgen, t: *node) bool = {
|
fn typeis8byteprimitive(c: *cgen, t: *node) bool = {
|
||||||
if (t == nil) { return false; };
|
if (t == nil) { return false; };
|
||||||
let k: nkind = t.kind;
|
return typeis8byteprim(t.type_: *tinfo);
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// elemissignedc — given an indexable type-AST (`*T`, `[]T`, `[N]T`),
|
// 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
|
// isstrtype — alias-aware. Reads the stamped tinfo so `str`,
|
||||||
// alias chain registered at file load.
|
// `type alias = str`, `!str`, and chained aliases all route to the
|
||||||
fn isstrtyperaw(t: *node) bool = {
|
// str-shaped slot. Cite cstage cgen.c:159 `type_isstr` SSoT.
|
||||||
if (t == nil) { return false; };
|
// Collapsed onto typeisstr per A.6.3b (#46); the prior AST walker is
|
||||||
if (t.kind == nkind.N_TNAME) {
|
// reconstituted by typeisstr's TY_NAMED chase + tinfofornode's
|
||||||
let nm: str = t.str;
|
// TBANG-unwrap (check.ww:1145).
|
||||||
if (streq(nm, "str")) { return true; };
|
|
||||||
};
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
fn isstrtype(c: *cgen, t: *node) bool = {
|
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 == nil) { return false; };
|
||||||
if (t.kind == nkind.N_TSLICE) { return true; };
|
return typeisstr(t.type_: *tinfo);
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn isslicetype(c: *cgen, t: *node) bool = {
|
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 == nil) { return false; };
|
||||||
if (t.kind == nkind.N_TTAGGED) { return true; };
|
return typeisslice(t.type_: *tinfo);
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// resolvetagged — return the underlying N_TTAGGED node for `t`, or nil
|
// 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;
|
return si.totsize;
|
||||||
};
|
};
|
||||||
|
|
||||||
// istaggedtype — alias-aware. Mirrors isstrtype: follow N_TNAME to its
|
// istaggedtype — alias-aware. Reads stamped tinfo so `T`,
|
||||||
// underlying decl, then unwrap a leading N_TBANG so `type error =
|
// `type alias = (A|B)`, `type error = !(invalid|overflow)` all
|
||||||
// !(invalid | overflow);` is still recognised as tagged. Without the
|
// resolve to TY_TAGGED — tinfofornode handles the N_TBANG unwrap
|
||||||
// bang unwrap the prologue treats the param as scalar (8B), spilling
|
// (check.ww:1145) so we don't re-walk it here. Cite cstage cgen.c
|
||||||
// only DI and losing the value-word SI; the match read of slot+8 then
|
// (`type_chase_named` + TY_TAGGED). Collapsed per A.6.3b (#46).
|
||||||
// trails into saved BP.
|
|
||||||
fn istaggedtype(c: *cgen, t: *node) bool = {
|
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 == nil) { return false; };
|
||||||
if (t.kind != nkind.N_TNAME) { return false; };
|
return typeistagged(t.type_: *tinfo);
|
||||||
return streq(t.str, "f32");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn isf64typeraw(t: *node) bool = {
|
// isfloattype — f32 / f64 / untyped_float (alias-aware). Cite cstage
|
||||||
if (t == nil) { return false; };
|
// cgen.c:117 `cg_isfloat`. Dispatches MOVSS/MOVSD-shaped paths across
|
||||||
if (t.kind != nkind.N_TNAME) { return false; };
|
// cglet, cgident, cgassign, cgbin, cgcast, cgcall, cgreturn, fn-
|
||||||
return streq(t.str, "f64");
|
// prologue. Collapsed per A.6.3b (#46).
|
||||||
};
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
export fn isfloattype(c: *cgen, t: *node) bool = {
|
export fn isfloattype(c: *cgen, t: *node) bool = {
|
||||||
if (isf32typeraw(t)) { return true; };
|
if (t == nil) { return false; };
|
||||||
if (isf64typeraw(t)) { return true; };
|
return typeisfloat(t.type_: *tinfo);
|
||||||
if (c == nil) { return false; };
|
|
||||||
let r: *node = resolvetype(c, t);
|
|
||||||
if (isf32typeraw(r)) { return true; };
|
|
||||||
if (isf64typeraw(r)) { return true; };
|
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// isf32type — narrower predicate: true only for f32 (after alias
|
// isf32type — narrower: true only for f32 (after alias chase). Cite
|
||||||
// resolution). f64 returns false. Used to pick MOVSS vs MOVSD and
|
// cstage cgen.c:188 `type_isf32`. Picks MOVSS vs MOVSD and the SS-
|
||||||
// the SS-variant arithmetic / cast opcodes.
|
// variant arithmetic / cast opcodes. Collapsed per A.6.3b (#46).
|
||||||
export fn isf32type(c: *cgen, t: *node) bool = {
|
export fn isf32type(c: *cgen, t: *node) bool = {
|
||||||
if (isf32typeraw(t)) { return true; };
|
if (t == nil) { return false; };
|
||||||
if (c == nil) { return false; };
|
return typeisf32(t.type_: *tinfo);
|
||||||
let r: *node = resolvetype(c, t);
|
|
||||||
return isf32typeraw(r);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// exprfloatkind — classify an expression's value-class so callers can
|
// exprfloatkind — classify an expression's value-class so callers can
|
||||||
@@ -2514,30 +2388,23 @@ export fn exprfloatkind(c: *cgen, n: *node) i32 = {
|
|||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// isnullabletype — nkind.N_TTAGGED with exactly two children, one *T and
|
// isnullabletype — `(*T | void)` one-word fold per Hare's
|
||||||
// one `void`. Folds to a single 8-byte pointer slot per Hare's
|
// `(*T | null)` semantics. Cite cstage cgen.c:396 `type_isnullable`;
|
||||||
// `(*T | null)` semantics. Mirrors check.c's resolve_type detection.
|
// 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 = {
|
export fn isnullabletype(t: *node) bool = {
|
||||||
if (t == nil) { return false; };
|
if (t == nil) { return false; };
|
||||||
if (t.kind != nkind.N_TTAGGED) { return false; };
|
return typeisnullable(t.type_: *tinfo);
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// nullableptrtag — 0-based index of the *T variant in a nullable
|
// 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 = {
|
export fn nullableptrtag(t: *node) i32 = {
|
||||||
if (t == nil) { return 0; };
|
if (t == nil) { return 0; };
|
||||||
if (t.kind != nkind.N_TTAGGED) { return 0; };
|
if (t.kind != nkind.N_TTAGGED) { return 0; };
|
||||||
|
|||||||
@@ -6666,6 +6666,94 @@ export fn typeissigned(t: *tinfo) bool = {
|
|||||||
return typeisint(t);
|
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 = {
|
export fn typeisuntyped(t: *tinfo) bool = {
|
||||||
if (t == nil) { return false; };
|
if (t == nil) { return false; };
|
||||||
let k: tykind = t.kind;
|
let k: tykind = t.kind;
|
||||||
@@ -11118,72 +11206,13 @@ fn typenameisunsigned(nm: str) bool = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// typeis8byteprimitive — does this type take exactly one 8-byte
|
// typeis8byteprimitive — does this type take exactly one 8-byte
|
||||||
// slot (pointer / fn-ptr / 64-bit int / chan / scalar primitive
|
// slot rather than a wider aggregate? One-liner via typeis8byteprim
|
||||||
// padded up to 8) rather than a wider aggregate? Used by nkind.N_LET
|
// (cstage N_LET sz==8 ladder SSoT). t.type_ is stamped at check.ww
|
||||||
// zero-init to mirror C cgen's "only zero if sz == 8 at the type
|
// L426-436 for every type-AST kind callers reach. Collapsed onto the
|
||||||
// level" rule. Strings (16), slices (24), tagged unions (>=16),
|
// tinfo helper per A.6.3b (#46).
|
||||||
// tuples (16), structs (varies), arrays — all fall through to
|
|
||||||
// false here even when their *slot* rounds up to 8.
|
|
||||||
fn typeis8byteprimitive(c: *cgen, t: *node) bool = {
|
fn typeis8byteprimitive(c: *cgen, t: *node) bool = {
|
||||||
if (t == nil) { return false; };
|
if (t == nil) { return false; };
|
||||||
let k: nkind = t.kind;
|
return typeis8byteprim(t.type_: *tinfo);
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// elemissignedc — given an indexable type-AST (`*T`, `[]T`, `[N]T`),
|
// 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
|
// isstrtype — alias-aware. Reads the stamped tinfo so `str`,
|
||||||
// alias chain registered at file load.
|
// `type alias = str`, `!str`, and chained aliases all route to the
|
||||||
fn isstrtyperaw(t: *node) bool = {
|
// str-shaped slot. Cite cstage cgen.c:159 `type_isstr` SSoT.
|
||||||
if (t == nil) { return false; };
|
// Collapsed onto typeisstr per A.6.3b (#46); the prior AST walker is
|
||||||
if (t.kind == nkind.N_TNAME) {
|
// reconstituted by typeisstr's TY_NAMED chase + tinfofornode's
|
||||||
let nm: str = t.str;
|
// TBANG-unwrap (check.ww:1145).
|
||||||
if (streq(nm, "str")) { return true; };
|
|
||||||
};
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
fn isstrtype(c: *cgen, t: *node) bool = {
|
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 == nil) { return false; };
|
||||||
if (t.kind == nkind.N_TSLICE) { return true; };
|
return typeisstr(t.type_: *tinfo);
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn isslicetype(c: *cgen, t: *node) bool = {
|
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 == nil) { return false; };
|
||||||
if (t.kind == nkind.N_TTAGGED) { return true; };
|
return typeisslice(t.type_: *tinfo);
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// resolvetagged — return the underlying N_TTAGGED node for `t`, or nil
|
// 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;
|
return si.totsize;
|
||||||
};
|
};
|
||||||
|
|
||||||
// istaggedtype — alias-aware. Mirrors isstrtype: follow N_TNAME to its
|
// istaggedtype — alias-aware. Reads stamped tinfo so `T`,
|
||||||
// underlying decl, then unwrap a leading N_TBANG so `type error =
|
// `type alias = (A|B)`, `type error = !(invalid|overflow)` all
|
||||||
// !(invalid | overflow);` is still recognised as tagged. Without the
|
// resolve to TY_TAGGED — tinfofornode handles the N_TBANG unwrap
|
||||||
// bang unwrap the prologue treats the param as scalar (8B), spilling
|
// (check.ww:1145) so we don't re-walk it here. Cite cstage cgen.c
|
||||||
// only DI and losing the value-word SI; the match read of slot+8 then
|
// (`type_chase_named` + TY_TAGGED). Collapsed per A.6.3b (#46).
|
||||||
// trails into saved BP.
|
|
||||||
fn istaggedtype(c: *cgen, t: *node) bool = {
|
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 == nil) { return false; };
|
||||||
if (t.kind != nkind.N_TNAME) { return false; };
|
return typeistagged(t.type_: *tinfo);
|
||||||
return streq(t.str, "f32");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn isf64typeraw(t: *node) bool = {
|
// isfloattype — f32 / f64 / untyped_float (alias-aware). Cite cstage
|
||||||
if (t == nil) { return false; };
|
// cgen.c:117 `cg_isfloat`. Dispatches MOVSS/MOVSD-shaped paths across
|
||||||
if (t.kind != nkind.N_TNAME) { return false; };
|
// cglet, cgident, cgassign, cgbin, cgcast, cgcall, cgreturn, fn-
|
||||||
return streq(t.str, "f64");
|
// prologue. Collapsed per A.6.3b (#46).
|
||||||
};
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
export fn isfloattype(c: *cgen, t: *node) bool = {
|
export fn isfloattype(c: *cgen, t: *node) bool = {
|
||||||
if (isf32typeraw(t)) { return true; };
|
if (t == nil) { return false; };
|
||||||
if (isf64typeraw(t)) { return true; };
|
return typeisfloat(t.type_: *tinfo);
|
||||||
if (c == nil) { return false; };
|
|
||||||
let r: *node = resolvetype(c, t);
|
|
||||||
if (isf32typeraw(r)) { return true; };
|
|
||||||
if (isf64typeraw(r)) { return true; };
|
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// isf32type — narrower predicate: true only for f32 (after alias
|
// isf32type — narrower: true only for f32 (after alias chase). Cite
|
||||||
// resolution). f64 returns false. Used to pick MOVSS vs MOVSD and
|
// cstage cgen.c:188 `type_isf32`. Picks MOVSS vs MOVSD and the SS-
|
||||||
// the SS-variant arithmetic / cast opcodes.
|
// variant arithmetic / cast opcodes. Collapsed per A.6.3b (#46).
|
||||||
export fn isf32type(c: *cgen, t: *node) bool = {
|
export fn isf32type(c: *cgen, t: *node) bool = {
|
||||||
if (isf32typeraw(t)) { return true; };
|
if (t == nil) { return false; };
|
||||||
if (c == nil) { return false; };
|
return typeisf32(t.type_: *tinfo);
|
||||||
let r: *node = resolvetype(c, t);
|
|
||||||
return isf32typeraw(r);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// exprfloatkind — classify an expression's value-class so callers can
|
// exprfloatkind — classify an expression's value-class so callers can
|
||||||
@@ -12765,30 +12727,23 @@ export fn exprfloatkind(c: *cgen, n: *node) i32 = {
|
|||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// isnullabletype — nkind.N_TTAGGED with exactly two children, one *T and
|
// isnullabletype — `(*T | void)` one-word fold per Hare's
|
||||||
// one `void`. Folds to a single 8-byte pointer slot per Hare's
|
// `(*T | null)` semantics. Cite cstage cgen.c:396 `type_isnullable`;
|
||||||
// `(*T | null)` semantics. Mirrors check.c's resolve_type detection.
|
// 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 = {
|
export fn isnullabletype(t: *node) bool = {
|
||||||
if (t == nil) { return false; };
|
if (t == nil) { return false; };
|
||||||
if (t.kind != nkind.N_TTAGGED) { return false; };
|
return typeisnullable(t.type_: *tinfo);
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// nullableptrtag — 0-based index of the *T variant in a nullable
|
// 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 = {
|
export fn nullableptrtag(t: *node) i32 = {
|
||||||
if (t == nil) { return 0; };
|
if (t == nil) { return 0; };
|
||||||
if (t.kind != nkind.N_TTAGGED) { return 0; };
|
if (t.kind != nkind.N_TTAGGED) { return 0; };
|
||||||
@@ -15663,7 +15618,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
if (tp != nil) {
|
if (tp != nil) {
|
||||||
let tpt: *node = tp.lhs;
|
let tpt: *node = tp.lhs;
|
||||||
if (isstrtyperaw(tpt)) {
|
if (isstrtype(c, tpt)) {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff((lc.off + foff + 0): i64);
|
emitoff((lc.off + foff + 0): i64);
|
||||||
emitline("(BP), AX\n");
|
emitline("(BP), AX\n");
|
||||||
@@ -20714,8 +20669,8 @@ fn cglet(c: *cgen, n: *node) void = {
|
|||||||
let p1t: *node = nil;
|
let p1t: *node = nil;
|
||||||
if (p0 != nil) { p0t = p0.lhs; };
|
if (p0 != nil) { p0t = p0.lhs; };
|
||||||
if (p1 != nil) { p1t = p1.lhs; };
|
if (p1 != nil) { p1t = p1.lhs; };
|
||||||
let s0_is_str: bool = isstrtyperaw(p0t);
|
let s0_is_str: bool = isstrtype(c, p0t);
|
||||||
let s1_is_str: bool = isstrtyperaw(p1t);
|
let s1_is_str: bool = isstrtype(c, p1t);
|
||||||
if (p0 != nil) {
|
if (p0 != nil) {
|
||||||
if (p1 != nil) {
|
if (p1 != nil) {
|
||||||
if (s0_is_str != s1_is_str) {
|
if (s0_is_str != s1_is_str) {
|
||||||
@@ -21257,8 +21212,8 @@ fn cgmlet(c: *cgen, n: *node) void = {
|
|||||||
if (t0 == nil) { t0 = p0t; };
|
if (t0 == nil) { t0 = p0t; };
|
||||||
if (t1 == nil) { t1 = p1t; };
|
if (t1 == nil) { t1 = p1t; };
|
||||||
|
|
||||||
let s0_is_str: bool = isstrtyperaw(t0);
|
let s0_is_str: bool = isstrtype(c, t0);
|
||||||
let s1_is_str: bool = isstrtyperaw(t1);
|
let s1_is_str: bool = isstrtype(c, t1);
|
||||||
|
|
||||||
cgexpr(c, rhs);
|
cgexpr(c, rhs);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user