selfhost+cstage+test: graduate deflookup mod-qualified same-module-first (#11)

cstage Sdef walk #2 N_DOT branch used c->cur_mod where n->lhs->str is
the correct module hint. Sister of #4c wwstage graduation; same shape
as the TY_FN branch which already uses mafn(c, n->str, n->lhs->str).

cmd/w6c/cgen.c: add sdef_mod_match_hint(s, hint); walk #2 routes hint
first then head-pick fallback, matching #4a/#28/#31/#34 *mod variant
pattern. selfhost: add deflookuprhsmod(c, name, mod); cgdot N_DOT
mod-qualified str-def value-load routes through it. Rule-10 symmetric
stages: both stages now share the lhs.str polarity (was: both used
cur_mod / cur-module hint).

747_def_modqual_modshadow: table-driven sentinel — gamma calls
alpha.MSG with beta.MSG (same-leaf-name) at head of c.defs/sdefs.
want_imm "$38," (alpha strlit len), bad_imm "$27," (beta strlit len),
plus cs-vs-ws byte-id. Reverting cstage walk #2 to head-pick → fails
$38 on cstage + diverges cs-vs-ws; reverting wwstage cgdot to plain
deflookuprhs → fails $38 on wwstage.

make test 121/121; ww2==ww3==ww4 byte-id holds.
This commit is contained in:
2026-05-19 00:58:28 +09:00
parent c9cf25be28
commit 7a278c1a2d
7 changed files with 354 additions and 14 deletions

View File

@@ -12673,8 +12673,11 @@ fn cgdot(c: *cgen, n: *node) void = {
// strlit-inline matches cstage Sdef walk #2 in
// cmd/w6c/cgen.c N_DOT mod-qualified. Without this
// the MOVQ leaf(SB) fallback emits a bogus ref
// (`alpha.MSG(SB)`, never DATAW-defined). Filed #12.
let drhs: *node = deflookuprhs(c, fld);
// (`alpha.MSG(SB)`, never DATAW-defined). lhs.str is
// the explicit module hint — a 3rd-module qualifier
// `alpha.MSG` from gamma needs alpha (not c.curmod)
// to beat a head-of-c.defs beta.MSG collision (#11).
let drhs: *node = deflookuprhsmod(c, fld, lhs.str);
if (drhs != nil) {
if (drhs.kind == nkind.N_STRLIT) {
let bytes: str = drhs.str;
@@ -20880,6 +20883,29 @@ fn deflookuprhs(c: *cgen, name: str) *node = {
return nil;
};
// deflookuprhsmod — same-module-first walk for `mod.NAME` references.
// Trio-leaf *mod variant mirroring fnretlookupmod (#31) / fnparamslookupmod
// (#28) / enumlookupmod (#4a). Module-qualified `alpha.MSG` from a third
// module needs the explicit alpha hint; deflookuprhs prefers c.curmod
// (which doesn't match either source module on a 3rd-module qualifier)
// and falls back to head-pick, possibly inlining beta.MSG's strlit when
// both alpha and beta declare same-leaf str defs. cgdot's mod-qualified
// str-def value-load routes here so a cross-module N_DOT collision
// resolves to the explicit module. Falls back to deflookuprhs's bare-
// leaf two-pass when no module matches.
fn deflookuprhsmod(c: *cgen, name: str, mod: str) *node = {
if (mod.len > 0) {
let e: *defent = c.defs;
for (e != nil) {
if (streq(e.dname, name)) {
if (streq(e.dmod, mod)) { return e.drhs; };
};
e = e.dnext;
};
};
return deflookuprhs(c, name);
};
// ---- module-private symbol map --------------------------------------
//
// Every non-FFI top-level fn decl lives in its module's namespace —

View File

@@ -1730,6 +1730,29 @@ fn deflookuprhs(c: *cgen, name: str) *node = {
return nil;
};
// deflookuprhsmod — same-module-first walk for `mod.NAME` references.
// Trio-leaf *mod variant mirroring fnretlookupmod (#31) / fnparamslookupmod
// (#28) / enumlookupmod (#4a). Module-qualified `alpha.MSG` from a third
// module needs the explicit alpha hint; deflookuprhs prefers c.curmod
// (which doesn't match either source module on a 3rd-module qualifier)
// and falls back to head-pick, possibly inlining beta.MSG's strlit when
// both alpha and beta declare same-leaf str defs. cgdot's mod-qualified
// str-def value-load routes here so a cross-module N_DOT collision
// resolves to the explicit module. Falls back to deflookuprhs's bare-
// leaf two-pass when no module matches.
fn deflookuprhsmod(c: *cgen, name: str, mod: str) *node = {
if (mod.len > 0) {
let e: *defent = c.defs;
for (e != nil) {
if (streq(e.dname, name)) {
if (streq(e.dmod, mod)) { return e.drhs; };
};
e = e.dnext;
};
};
return deflookuprhs(c, name);
};
// ---- module-private symbol map --------------------------------------
//
// Every non-FFI top-level fn decl lives in its module's namespace —

View File

@@ -1744,8 +1744,11 @@ fn cgdot(c: *cgen, n: *node) void = {
// strlit-inline matches cstage Sdef walk #2 in
// cmd/w6c/cgen.c N_DOT mod-qualified. Without this
// the MOVQ leaf(SB) fallback emits a bogus ref
// (`alpha.MSG(SB)`, never DATAW-defined). Filed #12.
let drhs: *node = deflookuprhs(c, fld);
// (`alpha.MSG(SB)`, never DATAW-defined). lhs.str is
// the explicit module hint — a 3rd-module qualifier
// `alpha.MSG` from gamma needs alpha (not c.curmod)
// to beat a head-of-c.defs beta.MSG collision (#11).
let drhs: *node = deflookuprhsmod(c, fld, lhs.str);
if (drhs != nil) {
if (drhs.kind == nkind.N_STRLIT) {
let bytes: str = drhs.str;

View File

@@ -12673,8 +12673,11 @@ fn cgdot(c: *cgen, n: *node) void = {
// strlit-inline matches cstage Sdef walk #2 in
// cmd/w6c/cgen.c N_DOT mod-qualified. Without this
// the MOVQ leaf(SB) fallback emits a bogus ref
// (`alpha.MSG(SB)`, never DATAW-defined). Filed #12.
let drhs: *node = deflookuprhs(c, fld);
// (`alpha.MSG(SB)`, never DATAW-defined). lhs.str is
// the explicit module hint — a 3rd-module qualifier
// `alpha.MSG` from gamma needs alpha (not c.curmod)
// to beat a head-of-c.defs beta.MSG collision (#11).
let drhs: *node = deflookuprhsmod(c, fld, lhs.str);
if (drhs != nil) {
if (drhs.kind == nkind.N_STRLIT) {
let bytes: str = drhs.str;
@@ -20880,6 +20883,29 @@ fn deflookuprhs(c: *cgen, name: str) *node = {
return nil;
};
// deflookuprhsmod — same-module-first walk for `mod.NAME` references.
// Trio-leaf *mod variant mirroring fnretlookupmod (#31) / fnparamslookupmod
// (#28) / enumlookupmod (#4a). Module-qualified `alpha.MSG` from a third
// module needs the explicit alpha hint; deflookuprhs prefers c.curmod
// (which doesn't match either source module on a 3rd-module qualifier)
// and falls back to head-pick, possibly inlining beta.MSG's strlit when
// both alpha and beta declare same-leaf str defs. cgdot's mod-qualified
// str-def value-load routes here so a cross-module N_DOT collision
// resolves to the explicit module. Falls back to deflookuprhs's bare-
// leaf two-pass when no module matches.
fn deflookuprhsmod(c: *cgen, name: str, mod: str) *node = {
if (mod.len > 0) {
let e: *defent = c.defs;
for (e != nil) {
if (streq(e.dname, name)) {
if (streq(e.dmod, mod)) { return e.drhs; };
};
e = e.dnext;
};
};
return deflookuprhs(c, name);
};
// ---- module-private symbol map --------------------------------------
//
// Every non-FFI top-level fn decl lives in its module's namespace —