wwstage: make the cgdot alias-peel struct-break module-aware (#223)
The wwstage cgdot #191 alias-peel loop broke on a name-keyed any-module structlookup, so a receiver whose alias name collides with a struct of the same name in ANOTHER module resolved to the foreign struct and fell through to an undefined `name(SB)` global instead of the field load. The eFinal FLIP renames io's `vstream` -> `stream`, which collides with memio's `stream` struct, so io.read/io.write/io.close's `match (s.reader)` emitted `MOVQ reader(SB), AX` (reader is also a type-alias) -> cs != ww (cstage chases the nominal TY_NAMED.under pointer chain, module-correct). Gate-blind: on master both stages emit the same wrong store so byte-id stays green; the FLIP corpus is the first to put the io-alias/memio-struct collision in one build. Fix (wwstage-only align-down; cstage is the authority and is untouched): make the peel's struct-break MODULE-AWARE — break only on a same-module struct (a genuine struct-value receiver); a same-module alias keeps peeling to its underlying (io.stream -> *vtable -> the pointer field-load arm); a foreign leaf keeps the prior any-module heuristic. New structsamemod / aliassamemod mirror the same-module-first pass already in structlookup / aliaslookup. This is not a naive alias-first reorder (which would reintroduce the mirror collision: a same-module struct plus a foreign same-leaf alias). Peel-only — the direct- struct arm's broader cross-module same-leaf-STRUCT name-keying is filed as #224. #208-family (name-keyed resolution dropping to a wrong global) but in cgen, not the checker; #213 is distinct (cosmetic local-struct-match divergence). test/wcc/784_xmod_alias_struct_collide_run: collision (cross-module alias-vs- struct, same leaf), symmetric (guards the same-module-struct break against a naive reorder), and a no-collision control — branched callee. The discriminating net is cs.s == ww.s (the path is gate-blind and cstage is correct, so byte-id flips when wwstage is fixed); confirmed by source-revert. The FLIP's combined.ww is now cs.s == ww.s byte-identical.
This commit is contained in:
@@ -16142,6 +16142,23 @@ fn structlookup(c: *cgen, name: str) *structinfo = {
|
||||
return nil;
|
||||
};
|
||||
|
||||
// #223: same-module-ONLY struct lookup. structlookup's any-module
|
||||
// fallback returns a foreign same-leaf struct; the cgdot alias-peel
|
||||
// needs to break ONLY on a struct that THIS module defines (a genuine
|
||||
// struct-value receiver), not on a foreign struct that merely shares a
|
||||
// leaf with a same-module alias (io.stream alias vs memio.stream
|
||||
// struct). Returns the struct only when it lives in c.curmod.
|
||||
fn structsamemod(c: *cgen, name: str) *structinfo = {
|
||||
let s: *structinfo = c.structs;
|
||||
for (s != nil) {
|
||||
if (streq(s.sname, name)) {
|
||||
if (streq(s.smod, c.curmod)) { return s; };
|
||||
};
|
||||
s = s.sinext;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// primsize — size in bytes of a primitive type name (or 0 if not
|
||||
// recognised as a primitive — the caller falls back to other paths).
|
||||
// fldnumidx — parse a tuple field name like "0" / "1" / "12" into an
|
||||
@@ -20129,10 +20146,30 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
// builds NAMED chains (project_tinfo_lossy_nominal). Stops
|
||||
// at struct aliases so the existing direct-struct arm below
|
||||
// stays byte-id with pre-fix #22 callers. #191.
|
||||
//
|
||||
// #223: the break is MODULE-AWARE. cstage type_chase_named
|
||||
// follows the resolved NAMED.under pointer (module-correct);
|
||||
// wwstage re-resolves by name (lossy), so a same-module
|
||||
// alias whose leaf collides with a FOREIGN struct of the
|
||||
// same name (io.stream = *vtable vs memio.stream struct)
|
||||
// would wrongly halt the peel at the foreign struct via
|
||||
// structlookup's any-module fallback → field load drops to a
|
||||
// bogus `MOVQ <fld>(SB)`. Sibling of #208 (lossy name-keyed
|
||||
// resolution leaking to a global leaf). Break ONLY on a
|
||||
// same-module struct (a genuine struct-value receiver); a
|
||||
// same-module alias keeps peeling; a foreign leaf (in
|
||||
// neither registry for c.curmod) falls back to the prior
|
||||
// any-module heuristic. The broader cross-module same-leaf
|
||||
// STRUCT name-keying at the direct-struct arm below is
|
||||
// filed separately as #224 (not FLIP-triggered).
|
||||
for (tn != nil && tn.kind == nkind.N_TNAME) {
|
||||
if (structlookup(c, tn.str) != nil) { break; };
|
||||
let nx: *node = aliaslookup(c, tn.str);
|
||||
if (nx == nil) { break; };
|
||||
if (structsamemod(c, tn.str) != nil) { break; };
|
||||
let nx: *node = aliassamemod(c, tn.str);
|
||||
if (nx == nil) {
|
||||
if (structlookup(c, tn.str) != nil) { break; };
|
||||
nx = aliaslookup(c, tn.str);
|
||||
if (nx == nil) { break; };
|
||||
};
|
||||
tn = nx;
|
||||
};
|
||||
if (tn == nil) { return; };
|
||||
@@ -28373,6 +28410,22 @@ fn aliaslookup(c: *cgen, name: str) *node = {
|
||||
return nil;
|
||||
};
|
||||
|
||||
// #223: same-module-ONLY alias resolution. aliaslookup's any-module
|
||||
// fallback can return a foreign same-leaf alias; the alias-peel in
|
||||
// cgdot needs to know whether THIS module defines the name as an alias
|
||||
// (so the peel continues) without that cross-module fallback. Returns
|
||||
// the alias target only when an alias of `name` lives in c.curmod.
|
||||
fn aliassamemod(c: *cgen, name: str) *node = {
|
||||
let a: *aliasent = c.aliases;
|
||||
for (a != nil) {
|
||||
if (streq(a.aname, name)) {
|
||||
if (streq(a.amod, c.curmod)) { return a.target; };
|
||||
};
|
||||
a = a.aanext;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// ---- enum registry --------------------------------------------------
|
||||
//
|
||||
// Mirrors cmd/wcc/check.c's enum resolution at collect time: walk
|
||||
|
||||
@@ -139,6 +139,22 @@ fn aliaslookup(c: *cgen, name: str) *node = {
|
||||
return nil;
|
||||
};
|
||||
|
||||
// #223: same-module-ONLY alias resolution. aliaslookup's any-module
|
||||
// fallback can return a foreign same-leaf alias; the alias-peel in
|
||||
// cgdot needs to know whether THIS module defines the name as an alias
|
||||
// (so the peel continues) without that cross-module fallback. Returns
|
||||
// the alias target only when an alias of `name` lives in c.curmod.
|
||||
fn aliassamemod(c: *cgen, name: str) *node = {
|
||||
let a: *aliasent = c.aliases;
|
||||
for (a != nil) {
|
||||
if (streq(a.aname, name)) {
|
||||
if (streq(a.amod, c.curmod)) { return a.target; };
|
||||
};
|
||||
a = a.aanext;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// ---- enum registry --------------------------------------------------
|
||||
//
|
||||
// Mirrors cmd/wcc/check.c's enum resolution at collect time: walk
|
||||
|
||||
@@ -1766,10 +1766,30 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
// builds NAMED chains (project_tinfo_lossy_nominal). Stops
|
||||
// at struct aliases so the existing direct-struct arm below
|
||||
// stays byte-id with pre-fix #22 callers. #191.
|
||||
//
|
||||
// #223: the break is MODULE-AWARE. cstage type_chase_named
|
||||
// follows the resolved NAMED.under pointer (module-correct);
|
||||
// wwstage re-resolves by name (lossy), so a same-module
|
||||
// alias whose leaf collides with a FOREIGN struct of the
|
||||
// same name (io.stream = *vtable vs memio.stream struct)
|
||||
// would wrongly halt the peel at the foreign struct via
|
||||
// structlookup's any-module fallback → field load drops to a
|
||||
// bogus `MOVQ <fld>(SB)`. Sibling of #208 (lossy name-keyed
|
||||
// resolution leaking to a global leaf). Break ONLY on a
|
||||
// same-module struct (a genuine struct-value receiver); a
|
||||
// same-module alias keeps peeling; a foreign leaf (in
|
||||
// neither registry for c.curmod) falls back to the prior
|
||||
// any-module heuristic. The broader cross-module same-leaf
|
||||
// STRUCT name-keying at the direct-struct arm below is
|
||||
// filed separately as #224 (not FLIP-triggered).
|
||||
for (tn != nil && tn.kind == nkind.N_TNAME) {
|
||||
if (structlookup(c, tn.str) != nil) { break; };
|
||||
let nx: *node = aliaslookup(c, tn.str);
|
||||
if (nx == nil) { break; };
|
||||
if (structsamemod(c, tn.str) != nil) { break; };
|
||||
let nx: *node = aliassamemod(c, tn.str);
|
||||
if (nx == nil) {
|
||||
if (structlookup(c, tn.str) != nil) { break; };
|
||||
nx = aliaslookup(c, tn.str);
|
||||
if (nx == nil) { break; };
|
||||
};
|
||||
tn = nx;
|
||||
};
|
||||
if (tn == nil) { return; };
|
||||
|
||||
@@ -1381,6 +1381,23 @@ fn structlookup(c: *cgen, name: str) *structinfo = {
|
||||
return nil;
|
||||
};
|
||||
|
||||
// #223: same-module-ONLY struct lookup. structlookup's any-module
|
||||
// fallback returns a foreign same-leaf struct; the cgdot alias-peel
|
||||
// needs to break ONLY on a struct that THIS module defines (a genuine
|
||||
// struct-value receiver), not on a foreign struct that merely shares a
|
||||
// leaf with a same-module alias (io.stream alias vs memio.stream
|
||||
// struct). Returns the struct only when it lives in c.curmod.
|
||||
fn structsamemod(c: *cgen, name: str) *structinfo = {
|
||||
let s: *structinfo = c.structs;
|
||||
for (s != nil) {
|
||||
if (streq(s.sname, name)) {
|
||||
if (streq(s.smod, c.curmod)) { return s; };
|
||||
};
|
||||
s = s.sinext;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// primsize — size in bytes of a primitive type name (or 0 if not
|
||||
// recognised as a primitive — the caller falls back to other paths).
|
||||
// fldnumidx — parse a tuple field name like "0" / "1" / "12" into an
|
||||
|
||||
@@ -16142,6 +16142,23 @@ fn structlookup(c: *cgen, name: str) *structinfo = {
|
||||
return nil;
|
||||
};
|
||||
|
||||
// #223: same-module-ONLY struct lookup. structlookup's any-module
|
||||
// fallback returns a foreign same-leaf struct; the cgdot alias-peel
|
||||
// needs to break ONLY on a struct that THIS module defines (a genuine
|
||||
// struct-value receiver), not on a foreign struct that merely shares a
|
||||
// leaf with a same-module alias (io.stream alias vs memio.stream
|
||||
// struct). Returns the struct only when it lives in c.curmod.
|
||||
fn structsamemod(c: *cgen, name: str) *structinfo = {
|
||||
let s: *structinfo = c.structs;
|
||||
for (s != nil) {
|
||||
if (streq(s.sname, name)) {
|
||||
if (streq(s.smod, c.curmod)) { return s; };
|
||||
};
|
||||
s = s.sinext;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// primsize — size in bytes of a primitive type name (or 0 if not
|
||||
// recognised as a primitive — the caller falls back to other paths).
|
||||
// fldnumidx — parse a tuple field name like "0" / "1" / "12" into an
|
||||
@@ -20129,10 +20146,30 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
// builds NAMED chains (project_tinfo_lossy_nominal). Stops
|
||||
// at struct aliases so the existing direct-struct arm below
|
||||
// stays byte-id with pre-fix #22 callers. #191.
|
||||
//
|
||||
// #223: the break is MODULE-AWARE. cstage type_chase_named
|
||||
// follows the resolved NAMED.under pointer (module-correct);
|
||||
// wwstage re-resolves by name (lossy), so a same-module
|
||||
// alias whose leaf collides with a FOREIGN struct of the
|
||||
// same name (io.stream = *vtable vs memio.stream struct)
|
||||
// would wrongly halt the peel at the foreign struct via
|
||||
// structlookup's any-module fallback → field load drops to a
|
||||
// bogus `MOVQ <fld>(SB)`. Sibling of #208 (lossy name-keyed
|
||||
// resolution leaking to a global leaf). Break ONLY on a
|
||||
// same-module struct (a genuine struct-value receiver); a
|
||||
// same-module alias keeps peeling; a foreign leaf (in
|
||||
// neither registry for c.curmod) falls back to the prior
|
||||
// any-module heuristic. The broader cross-module same-leaf
|
||||
// STRUCT name-keying at the direct-struct arm below is
|
||||
// filed separately as #224 (not FLIP-triggered).
|
||||
for (tn != nil && tn.kind == nkind.N_TNAME) {
|
||||
if (structlookup(c, tn.str) != nil) { break; };
|
||||
let nx: *node = aliaslookup(c, tn.str);
|
||||
if (nx == nil) { break; };
|
||||
if (structsamemod(c, tn.str) != nil) { break; };
|
||||
let nx: *node = aliassamemod(c, tn.str);
|
||||
if (nx == nil) {
|
||||
if (structlookup(c, tn.str) != nil) { break; };
|
||||
nx = aliaslookup(c, tn.str);
|
||||
if (nx == nil) { break; };
|
||||
};
|
||||
tn = nx;
|
||||
};
|
||||
if (tn == nil) { return; };
|
||||
@@ -28373,6 +28410,22 @@ fn aliaslookup(c: *cgen, name: str) *node = {
|
||||
return nil;
|
||||
};
|
||||
|
||||
// #223: same-module-ONLY alias resolution. aliaslookup's any-module
|
||||
// fallback can return a foreign same-leaf alias; the alias-peel in
|
||||
// cgdot needs to know whether THIS module defines the name as an alias
|
||||
// (so the peel continues) without that cross-module fallback. Returns
|
||||
// the alias target only when an alias of `name` lives in c.curmod.
|
||||
fn aliassamemod(c: *cgen, name: str) *node = {
|
||||
let a: *aliasent = c.aliases;
|
||||
for (a != nil) {
|
||||
if (streq(a.aname, name)) {
|
||||
if (streq(a.amod, c.curmod)) { return a.target; };
|
||||
};
|
||||
a = a.aanext;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// ---- enum registry --------------------------------------------------
|
||||
//
|
||||
// Mirrors cmd/wcc/check.c's enum resolution at collect time: walk
|
||||
|
||||
Reference in New Issue
Block a user