selfhost+cstage+test: graduate deflookup/deflookuprhs same-module-first (#4c)
Class A silent miscompile, latent until two modules export the same str-typed def leaf name and the .ptr/.len field-fold path consumes the wrong-module strlit address/length. Wwstage's deflookuprhs (selfhost/cmd/wcc/cgen.ww) walked c.defs head-first by dname; cgdot's .ptr/.len field-fold handed it the bare leaf from N_IDENT.str, silently inlining the wrong-module strlit. Cstage carries the same shape at cmd/w6c/cgen.c (Sdef walk #3 N_DOT field-fold): Sdef keyed by name only, head-pick on every cross-module collision. No in-tree corpus declares two same-leaf str defs, so 995_self_rebuild stayed green (same surfacing pattern as #4a enumlookup post-strings and #4b structlookup). Sixth leaf of the trio leaf-name lookup graduation (after #27 aliaslookup, #28 fnparams, #31 fnret, #4a enum, #4b struct). Same bundle precedent as #4a (which bundled wwstage enumlookup + enumlookupmod + cstage scope_lookup_prefer sister fix under one structural concern): four sister changes ship together. - defent +dmod field; collectdefs captures d.module. - wwstage deflookup two-pass walk — cosmetic (bool return is invariant under head-pick vs same-module-first), kept for structural symmetry with deflookuprhs. - wwstage deflookuprhs two-pass walk — load-bearing for the .ptr/.len field fold. - cstage Sdef +mod field; sdef_collect captures d->module raw (matches cgfn's raw cur_mod convention); new sdef_mod_match helper handles NULL-safe strcmp; cstage Sdef walk #3 N_DOT field-fold graduation (sister of wwstage deflookuprhs). Two additional cstage Sdef walks (N_IDENT bare load + N_DOT mod- qualified fallback) are DEFERRED. Both consume wwstage's cgenexpr.ww:553 path which is independently broken (str-def bare/ qualified reference emits MOVQ symname(SB) where strlit-inline is required); sentinel rows for those walks fail cs-vs-ws byte-id regardless of the cstage prefer-pass behavior. Per rule 7 the prefer-pass cannot ship without sentinels. Filed: task #11 (cstage walk #2 also needs n->lhs->str as hint source rather than cur_mod, matching #4a/#28/#31's *mod variant pattern) + task #12 (wwstage str-def symbol-load fix that unblocks both deferrals). 735_def_modshadow pins the fix with 1 row: bare-leaf .len of MSG in module alpha must fold against alpha's own def MSG (strlit length 41) even with beta's same-leaf 27-char def MSG at the head of c.defs / sdefs. Asserts the matching immediate inside the right TEXT sym + bad_imm anti-check on both stages plus byte-id between stages.
This commit is contained in:
105
cmd/w6c/cgen.c
105
cmd/w6c/cgen.c
@@ -500,12 +500,27 @@ static Ffi *ffi_map;
|
||||
typedef struct Sdef Sdef;
|
||||
struct Sdef {
|
||||
const char *name;
|
||||
const char *mod; /* raw `// MODULE:` directive on the decl,
|
||||
* or NULL. Mirrors cgfn's c->cur_mod which
|
||||
* stores the same raw form. */
|
||||
const char *bytes;
|
||||
u64 len;
|
||||
Sdef *next;
|
||||
};
|
||||
static Sdef *sdefs;
|
||||
|
||||
/* Same-module-first match for Sdef walks. Mirrors wwstage deflookuprhs's
|
||||
* first pass: returns 1 iff s belongs to the fn we're emitting. Caller
|
||||
* still re-walks for the any-module fallback. */
|
||||
static int
|
||||
sdef_mod_match(Cg *c, Sdef *s)
|
||||
{
|
||||
const char *a = s->mod, *b = c->cur_mod;
|
||||
if (a == b) return 1;
|
||||
if (a == NULL || b == NULL) return 0;
|
||||
return strcmp(a, b) == 0;
|
||||
}
|
||||
|
||||
/* Interned string literals — emitted as DATA directives after all
|
||||
* function bodies, so the linker lays them out alongside .text. */
|
||||
typedef struct Strlit Strlit;
|
||||
@@ -1742,14 +1757,33 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
mafn(c, n->str, c->cur_mod), 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 ident_done;
|
||||
{
|
||||
/* Same-module-first walk over Sdef. Without
|
||||
* the prefer pass two modules with same-leaf
|
||||
* `def MSG: str = "..."` silently fold the
|
||||
* wrong strlit into the caller's bare-ident
|
||||
* load (sister callsite of cgdot's str-def
|
||||
* field fold + wwstage deflookuprhs #4c). */
|
||||
Sdef *s;
|
||||
for (s = sdefs; s; s = s->next) {
|
||||
if (strcmp(s->name, n->str) != 0)
|
||||
continue;
|
||||
if (sdef_mod_match(c, s)) 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 ident_done;
|
||||
}
|
||||
}
|
||||
if (let_islet(n->str)
|
||||
&& (let_isstr(n->type) || let_isslice(n->type))) {
|
||||
@@ -5404,19 +5438,47 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* N_IDENT branch above. Without this we'd
|
||||
* load BP+8 (return-address slot) as the
|
||||
* "len". */
|
||||
for (Sdef *s = sdefs; s; s = s->next) {
|
||||
if (strcmp(s->name, n->lhs->str) != 0)
|
||||
continue;
|
||||
if (ptrfld) {
|
||||
const char *lab = intern_strlit(c,
|
||||
s->bytes, s->len);
|
||||
ins2(c, A_LEAQ, asym(lab), areg(D_AX));
|
||||
} else {
|
||||
ins2(c, A_MOVQ,
|
||||
aimm((long long)s->len),
|
||||
areg(D_AX));
|
||||
{
|
||||
/* Same-module-first walk: two
|
||||
* same-leaf `def MSG: str = ...`
|
||||
* across modules would otherwise
|
||||
* fold the wrong strlit's length /
|
||||
* label into `MSG.len` / `MSG.ptr`
|
||||
* (sister of wwstage deflookuprhs
|
||||
* #4c). */
|
||||
Sdef *s;
|
||||
for (s = sdefs; s; s = s->next) {
|
||||
if (strcmp(s->name,
|
||||
n->lhs->str) != 0)
|
||||
continue;
|
||||
if (sdef_mod_match(c, s))
|
||||
break;
|
||||
}
|
||||
if (s == NULL) {
|
||||
for (s = sdefs; s;
|
||||
s = s->next)
|
||||
if (strcmp(s->name,
|
||||
n->lhs->str)
|
||||
== 0)
|
||||
break;
|
||||
}
|
||||
if (s != NULL) {
|
||||
if (ptrfld) {
|
||||
const char *lab =
|
||||
intern_strlit(c,
|
||||
s->bytes,
|
||||
s->len);
|
||||
ins2(c, A_LEAQ,
|
||||
asym(lab),
|
||||
areg(D_AX));
|
||||
} else {
|
||||
ins2(c, A_MOVQ,
|
||||
aimm((long long)
|
||||
s->len),
|
||||
areg(D_AX));
|
||||
}
|
||||
goto dot_done;
|
||||
}
|
||||
goto dot_done;
|
||||
}
|
||||
/* Top-level str/slice `let` — load
|
||||
* the field through &name(SB). Same
|
||||
@@ -7521,7 +7583,7 @@ emit_defs(Cg *c, FILE *out, Node *file)
|
||||
static void
|
||||
sdef_collect(Cg *c, Node *file)
|
||||
{
|
||||
(void)c;
|
||||
(void)file;
|
||||
sdefs = NULL;
|
||||
for (Node *d = file->list; d; d = d->next) {
|
||||
if (d->kind != N_DEF || d->rhs == NULL) continue;
|
||||
@@ -7530,6 +7592,7 @@ sdef_collect(Cg *c, Node *file)
|
||||
if (r == NULL || r->kind != N_STRLIT) continue;
|
||||
Sdef *s = amalloc(c->a, sizeof *s);
|
||||
s->name = d->str;
|
||||
s->mod = (d->module && d->module[0]) ? d->module : NULL;
|
||||
s->bytes = r->str;
|
||||
s->len = r->strlen;
|
||||
s->next = sdefs;
|
||||
|
||||
Reference in New Issue
Block a user