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

@@ -548,6 +548,20 @@ sdef_mod_match(Cg *c, Sdef *s)
return strcmp(a, b) == 0;
}
/* Explicit-hint variant for `mod.NAME` N_DOT mod-qualified Sdef walks
* (sister of wwstage deflookuprhsmod). Walk #2 needs n->lhs->str — a
* cross-module qualifier from a third module won't match c->cur_mod
* and would fall back to head-pick, possibly inlining the wrong-module
* strlit when both source modules export the same-leaf str def. */
static int
sdef_mod_match_hint(Sdef *s, const char *hint)
{
const char *a = s->mod;
if (a == hint) return 1;
if (a == NULL || hint == NULL) return 0;
return strcmp(a, hint) == 0;
}
/* Interned string literals — emitted as DATA directives after all
* function bodies, so the linker lays them out alongside .text. */
typedef struct Strlit Strlit;
@@ -5317,14 +5331,36 @@ cgexpr(Cg *c, Node *n, Local *locals)
mafn(c, n->str, n->lhs->str), areg(D_AX));
break;
}
for (Sdef *s = sdefs; s; s = s->next) {
if (strcmp(s->name, n->str) != 0) continue;
const char *lab = intern_strlit(c, s->bytes,
s->len);
ins2(c, A_LEAQ, asym(lab), areg(D_AX));
ins2(c, A_MOVQ, aimm((long long)s->len),
areg(D_BX));
goto dot_done;
{
/* Same-module-first walk using n->lhs->str as
* the explicit module hint (sister of wwstage
* deflookuprhsmod). The TY_FN branch above
* already uses n->lhs->str via mafn for the
* cross-module qualifier disambiguation; this
* walk mirrors that polarity so `alpha.MSG`
* from a third module beats a head-of-sdefs
* beta.MSG collision (#11, sister of #4c). */
Sdef *s;
for (s = sdefs; s; s = s->next) {
if (strcmp(s->name, n->str) != 0)
continue;
if (sdef_mod_match_hint(s, n->lhs->str))
break;
}
if (s == NULL) {
for (s = sdefs; s; s = s->next)
if (strcmp(s->name, n->str) == 0)
break;
}
if (s != NULL) {
const char *lab = intern_strlit(c,
s->bytes, s->len);
ins2(c, A_LEAQ, asym(lab), areg(D_AX));
ins2(c, A_MOVQ,
aimm((long long)s->len),
areg(D_BX));
goto dot_done;
}
}
/* Same gating as the bare-ident catch-all: lets route
* through localloadop (their slot can be the target of