selfhost/cmd/wcc/cgenutil: collapse slice+str N_DOT arms (#56, A.6.3h)

nodeisslice + nodeisstr each walked base->struct->field on N_DOT,
gated on `base.kind == nkind.N_IDENT` (with dotinnerstructptr and
dotchainresolve fallbacks for chained / value-struct cases) --
duplicating cstage at the AST level while silently dropping
multi-level chains rooted at non-IDENT/DOT bases (e.g. `t.1.field`
for a tuple positional). After A.6.2 the checker stamps n.type_ on
every N_DOT (check.ww:1947-1996 pseudo-field, struct-field, and
tuple-positional arms), and A.6.3b landed typeisstr / typeisslice.
Both arms collapse to one tinfo read.

Polarity DOWN per rule 10: cstage was already aligned. cgen.c:168-170
node_isstr = type_isstr(n->type); cgen.c:182-184 node_isslice =
type_isslice(n->type). Wwstage was the laggard; this brings wwstage
to cstage's leaner shape, mirroring A.6.3g (8da1414).

Silent-false coverage gain: multi-level chains and tuple-positional
bases (`t.1.field` for a str element) now resolve via the checker
stamp instead of returning false. The "Not covered: tuple-positional"
bullet in nodeisstr's docstring is removed accordingly. dotchainresolve
itself is untouched -- still used by cgenexpr (cgdot read-path, &-of
address-of, structlit BP write-back). Byte-identity (994/995) is the
gate.

make test 133/133 ok. Net cgenutil.ww -110.
This commit is contained in:
2026-05-23 14:39:05 +09:00
parent 8da14147b3
commit 8d4a9700a0
3 changed files with 39 additions and 408 deletions

View File

@@ -10947,74 +10947,13 @@ fn nodeisslice(c: *cgen, n: *node) bool = {
}; };
return false; return false;
}; };
// N_DOT to a slice field: resolve the field through the struct // N_DOT: read the checker-stamped n.type_. Struct field, nested
// (or *struct) the base ident / inner chain lands on, then check // dot, value-struct hops, and pseudo-fields (.ptr/.len/.cap) all
// the field tnode. Mirrors nodeisstr's N_DOT branch so call-arg // resolve to the right tinfo via check.ww:1947-1978 (pseudo-field
// push/pop counts 3 words for `p.sl` and `p.inner.sl` shapes. // + struct-field stamps). Cstage cgen.c:182-184 node_isslice =
// `.ptr` / `.len` / `.cap` are pseudo-fields — they yield ptr // type_isslice(n->type) — same shape. Collapsed per A.6.3h (#56).
// (*u8) and i32, not a slice — so we exclude them up front.
if (k == nkind.N_DOT) { if (k == nkind.N_DOT) {
let base: *node = n.lhs; return typeisslice(n.type_: *tinfo);
let fld: str = n.str;
if (streq(fld, "ptr")) { return false; };
if (streq(fld, "len")) { return false; };
if (streq(fld, "cap")) { return false; };
if (base != nil) {
let sname: str;
sname.ptr = nil; sname.len = 0;
if (base.kind == nkind.N_IDENT) {
let lc: *local = localfindnode(c, base.str);
if (lc != nil) {
let tn: *node = lc.tnode;
let lkind: nkind = nkind.N_NONE;
if (tn != nil) { lkind = tn.kind; };
if (lkind == nkind.N_TNAME) { sname = tn.str; };
if (lkind == nkind.N_TPTR) {
let inner: *node = tn.lhs;
if (inner != nil) {
if (inner.kind == nkind.N_TNAME) { sname = inner.str; };
};
};
};
};
if (base.kind == nkind.N_DOT) {
let innert: *node = dotinnerstructptr(c, base);
if (innert != nil) {
if (innert.kind == nkind.N_TNAME) { sname = innert.str; };
};
};
if (sname.len > 0) {
let si: *structinfo = structlookup(c, sname);
if (si != nil) {
let fi: *fieldinfo = si.fields;
for (fi != nil) {
if (streq(fi.fname, fld)) {
return isslicetype(c, fi.tnode);
};
fi = fi.finext;
};
};
};
// Chained dot through value-struct hops (`o.inner.sl`,
// `p.inner.sl`): dotinnerstructptr above only walks
// *struct fields, so a value-struct chain falls through.
// dotchainresolve handles arbitrary depth through value
// struct AND `*T` root, returning the leaf fieldinfo.
let rootnm: str = "";
let rootoff: i32 = 0;
let totaloff: i32 = 0;
let lfi: *fieldinfo = nil;
let sdelta: i32 = -1;
let isglobal: bool = false;
let ptrroot: bool = false;
let ok: bool = dotchainresolve(c, n,
&rootnm, &rootoff, &totaloff,
&lfi, &sdelta, &isglobal, &ptrroot);
if (ok && sdelta < 0 && lfi != nil) {
return isslicetype(c, lfi.tnode);
};
};
return false;
}; };
return false; return false;
}; };
@@ -11030,13 +10969,11 @@ fn nodeisslice(c: *cgen, n: *node) bool = {
// A typed AST check (cstage reads n->type) would replace this whole // A typed AST check (cstage reads n->type) would replace this whole
// function. Covered arms below: N_STRLIT, N_IDENT (local/let-typed), // function. Covered arms below: N_STRLIT, N_IDENT (local/let-typed),
// N_CALL (return type), N_INDEX (element type of [N]T / []T / *T base), // N_CALL (return type), N_INDEX (element type of [N]T / []T / *T base),
// N_DOT (struct field / chained / pseudo-fields excluded), N_CAST. // N_DOT (reads checker-stamped n.type_ — #56 A.6.3h), N_CAST.
// Not covered (separate bugs / out of scope): // Not covered (separate bugs / out of scope):
// - N_UN(TK_STAR) of `*str` — cgun itself emits only `MOVQ (AX), AX` // - N_UN(TK_STAR) of `*str` — cgun itself emits only `MOVQ (AX), AX`
// and never loads .len into BX; fixing the recognizer alone won't // and never loads .len into BX; fixing the recognizer alone won't
// help. Tracked alongside the broader cgun-load-shape gap. // help. Tracked alongside the broader cgun-load-shape gap.
// - N_DOT to a tuple positional `t.1` of a str element — wwstage's
// cgdot loads (AX, BX) but tuple-as-arg has independent issues.
fn nodeisstr(c: *cgen, n: *node) bool = { fn nodeisstr(c: *cgen, n: *node) bool = {
if (n == nil) { return false; }; if (n == nil) { return false; };
let k: nkind = n.kind; let k: nkind = n.kind;
@@ -11159,73 +11096,13 @@ fn nodeisstr(c: *cgen, n: *node) bool = {
}; };
return false; return false;
}; };
// N_DOT: read the checker-stamped n.type_. Struct field, nested
// dot, value-struct hops, and pseudo-fields (.ptr/.len/.cap) all
// resolve to the right tinfo via check.ww:1947-1978. Cstage
// cgen.c:168-170 node_isstr = type_isstr(n->type) — same shape.
// Collapsed per A.6.3h (#56).
if (k == nkind.N_DOT) { if (k == nkind.N_DOT) {
let base: *node = n.lhs; return typeisstr(n.type_: *tinfo);
let fld: str = n.str;
// `<expr>.ptr` is *u8 not str; `<expr>.len` is i32 not str.
if (streq(fld, "ptr")) { return false; };
if (streq(fld, "len")) { return false; };
if (streq(fld, "cap")) { return false; };
if (base != nil) {
let sname: str;
sname.ptr = nil; sname.len = 0;
if (base.kind == nkind.N_IDENT) {
let lc: *local = localfindnode(c, base.str);
if (lc != nil) {
let tn: *node = lc.tnode;
let lkind: nkind = nkind.N_NONE;
if (tn != nil) { lkind = tn.kind; };
if (lkind == nkind.N_TNAME) { sname = tn.str; };
if (lkind == nkind.N_TPTR) {
let inner: *node = tn.lhs;
if (inner != nil) {
if (inner.kind == nkind.N_TNAME) { sname = inner.str; };
};
};
};
};
// Chained dot (`p.foo.bar`): use dotinnerstructptr
// to resolve the inner chain to the *struct it lands
// on, then look up `fld` in that struct.
if (base.kind == nkind.N_DOT) {
let innert: *node = dotinnerstructptr(c, base);
if (innert != nil) {
if (innert.kind == nkind.N_TNAME) { sname = innert.str; };
};
};
if (sname.len > 0) {
let si: *structinfo = structlookup(c, sname);
if (si != nil) {
let fi: *fieldinfo = si.fields;
for (fi != nil) {
let fn_: str = fi.fname;
if (streq(fn_, fld)) {
return isstrtype(c, fi.tnode);
};
fi = fi.finext;
};
};
};
// Chained dot through value-struct hops (`p.inner.s`):
// dotinnerstructptr above only walks *struct fields;
// dotchainresolve handles arbitrary depth through
// value struct AND `*T` root. Mirror of the nodeisslice
// fallback so chained str-field args also push 2 words.
let rootnm: str = "";
let rootoff: i32 = 0;
let totaloff: i32 = 0;
let lfi: *fieldinfo = nil;
let sdelta: i32 = -1;
let isglobal: bool = false;
let ptrroot: bool = false;
let ok: bool = dotchainresolve(c, n,
&rootnm, &rootoff, &totaloff,
&lfi, &sdelta, &isglobal, &ptrroot);
if (ok && sdelta < 0 && lfi != nil) {
return isstrtype(c, lfi.tnode);
};
};
return false;
}; };
if (k == nkind.N_CAST) { if (k == nkind.N_CAST) {
return isstrtype(c, n.rhs); return isstrtype(c, n.rhs);

View File

@@ -563,74 +563,13 @@ fn nodeisslice(c: *cgen, n: *node) bool = {
}; };
return false; return false;
}; };
// N_DOT to a slice field: resolve the field through the struct // N_DOT: read the checker-stamped n.type_. Struct field, nested
// (or *struct) the base ident / inner chain lands on, then check // dot, value-struct hops, and pseudo-fields (.ptr/.len/.cap) all
// the field tnode. Mirrors nodeisstr's N_DOT branch so call-arg // resolve to the right tinfo via check.ww:1947-1978 (pseudo-field
// push/pop counts 3 words for `p.sl` and `p.inner.sl` shapes. // + struct-field stamps). Cstage cgen.c:182-184 node_isslice =
// `.ptr` / `.len` / `.cap` are pseudo-fields — they yield ptr // type_isslice(n->type) — same shape. Collapsed per A.6.3h (#56).
// (*u8) and i32, not a slice — so we exclude them up front.
if (k == nkind.N_DOT) { if (k == nkind.N_DOT) {
let base: *node = n.lhs; return typeisslice(n.type_: *tinfo);
let fld: str = n.str;
if (streq(fld, "ptr")) { return false; };
if (streq(fld, "len")) { return false; };
if (streq(fld, "cap")) { return false; };
if (base != nil) {
let sname: str;
sname.ptr = nil; sname.len = 0;
if (base.kind == nkind.N_IDENT) {
let lc: *local = localfindnode(c, base.str);
if (lc != nil) {
let tn: *node = lc.tnode;
let lkind: nkind = nkind.N_NONE;
if (tn != nil) { lkind = tn.kind; };
if (lkind == nkind.N_TNAME) { sname = tn.str; };
if (lkind == nkind.N_TPTR) {
let inner: *node = tn.lhs;
if (inner != nil) {
if (inner.kind == nkind.N_TNAME) { sname = inner.str; };
};
};
};
};
if (base.kind == nkind.N_DOT) {
let innert: *node = dotinnerstructptr(c, base);
if (innert != nil) {
if (innert.kind == nkind.N_TNAME) { sname = innert.str; };
};
};
if (sname.len > 0) {
let si: *structinfo = structlookup(c, sname);
if (si != nil) {
let fi: *fieldinfo = si.fields;
for (fi != nil) {
if (streq(fi.fname, fld)) {
return isslicetype(c, fi.tnode);
};
fi = fi.finext;
};
};
};
// Chained dot through value-struct hops (`o.inner.sl`,
// `p.inner.sl`): dotinnerstructptr above only walks
// *struct fields, so a value-struct chain falls through.
// dotchainresolve handles arbitrary depth through value
// struct AND `*T` root, returning the leaf fieldinfo.
let rootnm: str = "";
let rootoff: i32 = 0;
let totaloff: i32 = 0;
let lfi: *fieldinfo = nil;
let sdelta: i32 = -1;
let isglobal: bool = false;
let ptrroot: bool = false;
let ok: bool = dotchainresolve(c, n,
&rootnm, &rootoff, &totaloff,
&lfi, &sdelta, &isglobal, &ptrroot);
if (ok && sdelta < 0 && lfi != nil) {
return isslicetype(c, lfi.tnode);
};
};
return false;
}; };
return false; return false;
}; };
@@ -646,13 +585,11 @@ fn nodeisslice(c: *cgen, n: *node) bool = {
// A typed AST check (cstage reads n->type) would replace this whole // A typed AST check (cstage reads n->type) would replace this whole
// function. Covered arms below: N_STRLIT, N_IDENT (local/let-typed), // function. Covered arms below: N_STRLIT, N_IDENT (local/let-typed),
// N_CALL (return type), N_INDEX (element type of [N]T / []T / *T base), // N_CALL (return type), N_INDEX (element type of [N]T / []T / *T base),
// N_DOT (struct field / chained / pseudo-fields excluded), N_CAST. // N_DOT (reads checker-stamped n.type_ — #56 A.6.3h), N_CAST.
// Not covered (separate bugs / out of scope): // Not covered (separate bugs / out of scope):
// - N_UN(TK_STAR) of `*str` — cgun itself emits only `MOVQ (AX), AX` // - N_UN(TK_STAR) of `*str` — cgun itself emits only `MOVQ (AX), AX`
// and never loads .len into BX; fixing the recognizer alone won't // and never loads .len into BX; fixing the recognizer alone won't
// help. Tracked alongside the broader cgun-load-shape gap. // help. Tracked alongside the broader cgun-load-shape gap.
// - N_DOT to a tuple positional `t.1` of a str element — wwstage's
// cgdot loads (AX, BX) but tuple-as-arg has independent issues.
fn nodeisstr(c: *cgen, n: *node) bool = { fn nodeisstr(c: *cgen, n: *node) bool = {
if (n == nil) { return false; }; if (n == nil) { return false; };
let k: nkind = n.kind; let k: nkind = n.kind;
@@ -775,73 +712,13 @@ fn nodeisstr(c: *cgen, n: *node) bool = {
}; };
return false; return false;
}; };
// N_DOT: read the checker-stamped n.type_. Struct field, nested
// dot, value-struct hops, and pseudo-fields (.ptr/.len/.cap) all
// resolve to the right tinfo via check.ww:1947-1978. Cstage
// cgen.c:168-170 node_isstr = type_isstr(n->type) — same shape.
// Collapsed per A.6.3h (#56).
if (k == nkind.N_DOT) { if (k == nkind.N_DOT) {
let base: *node = n.lhs; return typeisstr(n.type_: *tinfo);
let fld: str = n.str;
// `<expr>.ptr` is *u8 not str; `<expr>.len` is i32 not str.
if (streq(fld, "ptr")) { return false; };
if (streq(fld, "len")) { return false; };
if (streq(fld, "cap")) { return false; };
if (base != nil) {
let sname: str;
sname.ptr = nil; sname.len = 0;
if (base.kind == nkind.N_IDENT) {
let lc: *local = localfindnode(c, base.str);
if (lc != nil) {
let tn: *node = lc.tnode;
let lkind: nkind = nkind.N_NONE;
if (tn != nil) { lkind = tn.kind; };
if (lkind == nkind.N_TNAME) { sname = tn.str; };
if (lkind == nkind.N_TPTR) {
let inner: *node = tn.lhs;
if (inner != nil) {
if (inner.kind == nkind.N_TNAME) { sname = inner.str; };
};
};
};
};
// Chained dot (`p.foo.bar`): use dotinnerstructptr
// to resolve the inner chain to the *struct it lands
// on, then look up `fld` in that struct.
if (base.kind == nkind.N_DOT) {
let innert: *node = dotinnerstructptr(c, base);
if (innert != nil) {
if (innert.kind == nkind.N_TNAME) { sname = innert.str; };
};
};
if (sname.len > 0) {
let si: *structinfo = structlookup(c, sname);
if (si != nil) {
let fi: *fieldinfo = si.fields;
for (fi != nil) {
let fn_: str = fi.fname;
if (streq(fn_, fld)) {
return isstrtype(c, fi.tnode);
};
fi = fi.finext;
};
};
};
// Chained dot through value-struct hops (`p.inner.s`):
// dotinnerstructptr above only walks *struct fields;
// dotchainresolve handles arbitrary depth through
// value struct AND `*T` root. Mirror of the nodeisslice
// fallback so chained str-field args also push 2 words.
let rootnm: str = "";
let rootoff: i32 = 0;
let totaloff: i32 = 0;
let lfi: *fieldinfo = nil;
let sdelta: i32 = -1;
let isglobal: bool = false;
let ptrroot: bool = false;
let ok: bool = dotchainresolve(c, n,
&rootnm, &rootoff, &totaloff,
&lfi, &sdelta, &isglobal, &ptrroot);
if (ok && sdelta < 0 && lfi != nil) {
return isstrtype(c, lfi.tnode);
};
};
return false;
}; };
if (k == nkind.N_CAST) { if (k == nkind.N_CAST) {
return isstrtype(c, n.rhs); return isstrtype(c, n.rhs);

View File

@@ -10947,74 +10947,13 @@ fn nodeisslice(c: *cgen, n: *node) bool = {
}; };
return false; return false;
}; };
// N_DOT to a slice field: resolve the field through the struct // N_DOT: read the checker-stamped n.type_. Struct field, nested
// (or *struct) the base ident / inner chain lands on, then check // dot, value-struct hops, and pseudo-fields (.ptr/.len/.cap) all
// the field tnode. Mirrors nodeisstr's N_DOT branch so call-arg // resolve to the right tinfo via check.ww:1947-1978 (pseudo-field
// push/pop counts 3 words for `p.sl` and `p.inner.sl` shapes. // + struct-field stamps). Cstage cgen.c:182-184 node_isslice =
// `.ptr` / `.len` / `.cap` are pseudo-fields — they yield ptr // type_isslice(n->type) — same shape. Collapsed per A.6.3h (#56).
// (*u8) and i32, not a slice — so we exclude them up front.
if (k == nkind.N_DOT) { if (k == nkind.N_DOT) {
let base: *node = n.lhs; return typeisslice(n.type_: *tinfo);
let fld: str = n.str;
if (streq(fld, "ptr")) { return false; };
if (streq(fld, "len")) { return false; };
if (streq(fld, "cap")) { return false; };
if (base != nil) {
let sname: str;
sname.ptr = nil; sname.len = 0;
if (base.kind == nkind.N_IDENT) {
let lc: *local = localfindnode(c, base.str);
if (lc != nil) {
let tn: *node = lc.tnode;
let lkind: nkind = nkind.N_NONE;
if (tn != nil) { lkind = tn.kind; };
if (lkind == nkind.N_TNAME) { sname = tn.str; };
if (lkind == nkind.N_TPTR) {
let inner: *node = tn.lhs;
if (inner != nil) {
if (inner.kind == nkind.N_TNAME) { sname = inner.str; };
};
};
};
};
if (base.kind == nkind.N_DOT) {
let innert: *node = dotinnerstructptr(c, base);
if (innert != nil) {
if (innert.kind == nkind.N_TNAME) { sname = innert.str; };
};
};
if (sname.len > 0) {
let si: *structinfo = structlookup(c, sname);
if (si != nil) {
let fi: *fieldinfo = si.fields;
for (fi != nil) {
if (streq(fi.fname, fld)) {
return isslicetype(c, fi.tnode);
};
fi = fi.finext;
};
};
};
// Chained dot through value-struct hops (`o.inner.sl`,
// `p.inner.sl`): dotinnerstructptr above only walks
// *struct fields, so a value-struct chain falls through.
// dotchainresolve handles arbitrary depth through value
// struct AND `*T` root, returning the leaf fieldinfo.
let rootnm: str = "";
let rootoff: i32 = 0;
let totaloff: i32 = 0;
let lfi: *fieldinfo = nil;
let sdelta: i32 = -1;
let isglobal: bool = false;
let ptrroot: bool = false;
let ok: bool = dotchainresolve(c, n,
&rootnm, &rootoff, &totaloff,
&lfi, &sdelta, &isglobal, &ptrroot);
if (ok && sdelta < 0 && lfi != nil) {
return isslicetype(c, lfi.tnode);
};
};
return false;
}; };
return false; return false;
}; };
@@ -11030,13 +10969,11 @@ fn nodeisslice(c: *cgen, n: *node) bool = {
// A typed AST check (cstage reads n->type) would replace this whole // A typed AST check (cstage reads n->type) would replace this whole
// function. Covered arms below: N_STRLIT, N_IDENT (local/let-typed), // function. Covered arms below: N_STRLIT, N_IDENT (local/let-typed),
// N_CALL (return type), N_INDEX (element type of [N]T / []T / *T base), // N_CALL (return type), N_INDEX (element type of [N]T / []T / *T base),
// N_DOT (struct field / chained / pseudo-fields excluded), N_CAST. // N_DOT (reads checker-stamped n.type_ — #56 A.6.3h), N_CAST.
// Not covered (separate bugs / out of scope): // Not covered (separate bugs / out of scope):
// - N_UN(TK_STAR) of `*str` — cgun itself emits only `MOVQ (AX), AX` // - N_UN(TK_STAR) of `*str` — cgun itself emits only `MOVQ (AX), AX`
// and never loads .len into BX; fixing the recognizer alone won't // and never loads .len into BX; fixing the recognizer alone won't
// help. Tracked alongside the broader cgun-load-shape gap. // help. Tracked alongside the broader cgun-load-shape gap.
// - N_DOT to a tuple positional `t.1` of a str element — wwstage's
// cgdot loads (AX, BX) but tuple-as-arg has independent issues.
fn nodeisstr(c: *cgen, n: *node) bool = { fn nodeisstr(c: *cgen, n: *node) bool = {
if (n == nil) { return false; }; if (n == nil) { return false; };
let k: nkind = n.kind; let k: nkind = n.kind;
@@ -11159,73 +11096,13 @@ fn nodeisstr(c: *cgen, n: *node) bool = {
}; };
return false; return false;
}; };
// N_DOT: read the checker-stamped n.type_. Struct field, nested
// dot, value-struct hops, and pseudo-fields (.ptr/.len/.cap) all
// resolve to the right tinfo via check.ww:1947-1978. Cstage
// cgen.c:168-170 node_isstr = type_isstr(n->type) — same shape.
// Collapsed per A.6.3h (#56).
if (k == nkind.N_DOT) { if (k == nkind.N_DOT) {
let base: *node = n.lhs; return typeisstr(n.type_: *tinfo);
let fld: str = n.str;
// `<expr>.ptr` is *u8 not str; `<expr>.len` is i32 not str.
if (streq(fld, "ptr")) { return false; };
if (streq(fld, "len")) { return false; };
if (streq(fld, "cap")) { return false; };
if (base != nil) {
let sname: str;
sname.ptr = nil; sname.len = 0;
if (base.kind == nkind.N_IDENT) {
let lc: *local = localfindnode(c, base.str);
if (lc != nil) {
let tn: *node = lc.tnode;
let lkind: nkind = nkind.N_NONE;
if (tn != nil) { lkind = tn.kind; };
if (lkind == nkind.N_TNAME) { sname = tn.str; };
if (lkind == nkind.N_TPTR) {
let inner: *node = tn.lhs;
if (inner != nil) {
if (inner.kind == nkind.N_TNAME) { sname = inner.str; };
};
};
};
};
// Chained dot (`p.foo.bar`): use dotinnerstructptr
// to resolve the inner chain to the *struct it lands
// on, then look up `fld` in that struct.
if (base.kind == nkind.N_DOT) {
let innert: *node = dotinnerstructptr(c, base);
if (innert != nil) {
if (innert.kind == nkind.N_TNAME) { sname = innert.str; };
};
};
if (sname.len > 0) {
let si: *structinfo = structlookup(c, sname);
if (si != nil) {
let fi: *fieldinfo = si.fields;
for (fi != nil) {
let fn_: str = fi.fname;
if (streq(fn_, fld)) {
return isstrtype(c, fi.tnode);
};
fi = fi.finext;
};
};
};
// Chained dot through value-struct hops (`p.inner.s`):
// dotinnerstructptr above only walks *struct fields;
// dotchainresolve handles arbitrary depth through
// value struct AND `*T` root. Mirror of the nodeisslice
// fallback so chained str-field args also push 2 words.
let rootnm: str = "";
let rootoff: i32 = 0;
let totaloff: i32 = 0;
let lfi: *fieldinfo = nil;
let sdelta: i32 = -1;
let isglobal: bool = false;
let ptrroot: bool = false;
let ok: bool = dotchainresolve(c, n,
&rootnm, &rootoff, &totaloff,
&lfi, &sdelta, &isglobal, &ptrroot);
if (ok && sdelta < 0 && lfi != nil) {
return isstrtype(c, lfi.tnode);
};
};
return false;
}; };
if (k == nkind.N_CAST) { if (k == nkind.N_CAST) {
return isstrtype(c, n.rhs); return isstrtype(c, n.rhs);