selfhost: structlookup/enumlookup mod-filter (mirror aliaslookup)

Tags structinfo/enumtype with originating module; exact-match first,
then split pkg.X and filter by smod/emod. Without this, two modules
with same-leaf-name struct/enum types collapsed to whichever entry
appeared first in the chain.

Wired into 696_modtype_leaf_collision via a wwstage run_pos using
ww_ww (negative case omitted: w6c_ww has no checkfile pass). Updated
the test's Makefile deps to include the wwstage binaries.

Audited the rest of the lookup family — fnretlookup, fnparamslookup,
deflookup don't need the same treatment: the parser emits N_DOT.str
(call/field name) as the leaf only, and fnparamslookup is only
invoked with N_IDENT.str. Dotted module-qualified function calls go
through the module-mangling path instead.
This commit is contained in:
2026-05-15 13:58:56 +09:00
parent 0ef94eef04
commit 9d85aa4142
6 changed files with 202 additions and 46 deletions

View File

@@ -6878,12 +6878,41 @@ fn nodeprimwidth(c: *cgen, n: *node) i32 = {
// ---- type-driven slot sizing ----------------------------------------
fn structlookup(c: *cgen, name: str) *structinfo = {
// Exact match first: bare-from-source struct names and already-
// leafed lookups hit here directly.
let s: *structinfo = c.structs;
for (s != nil) {
let sn: str = s.sname;
if (streq(sn, name)) { return s; };
s = s.sinext;
};
// Module-qualified form: `pkg.S` → match the leaf scoped to its
// originating module. Mirrors aliaslookup's mod-filter; the
// `smod == pkg` guard is what prevents two modules with same-
// leaf-name structs from collapsing into whichever entry appears
// first in the chain.
let i: i32 = name.len - 1;
for (i >= 0) {
if (name[i] == 46u8) { // '.'
let pkg: str;
pkg.ptr = name.ptr;
pkg.len = i;
let leaf: str;
leaf.ptr = name.ptr + ((i + 1): u64);
leaf.len = name.len - (i + 1);
let b: *structinfo = c.structs;
for (b != nil) {
if (streq(b.sname, leaf)) {
if (streq(b.smod, pkg)) {
return b;
};
};
b = b.sinext;
};
return nil;
};
i -= 1;
};
return nil;
};
@@ -7229,9 +7258,10 @@ fn fieldsize(c: *cgen, tnode: *node) i32 = {
return 8;
};
fn registerstruct(c: *cgen, name: str, tstruct: *node) void = {
let si: *structinfo = amalloc(c.a, 64u64): *structinfo;
fn registerstruct(c: *cgen, name: str, module: str, tstruct: *node) void = {
let si: *structinfo = amalloc(c.a, 80u64): *structinfo;
si.sname = name;
si.smod = module;
si.fields = nil;
si.totsize = 0;
let head: *fieldinfo = nil;
@@ -7279,7 +7309,7 @@ fn collectstructs(c: *cgen, file: *node) void = {
let body: *node = d.lhs;
if (body != nil) {
if (body.kind == nkind.N_TSTRUCT) {
registerstruct(c, d.str, body);
registerstruct(c, d.str, d.module, body);
};
};
};
@@ -15529,8 +15559,9 @@ fn collectenums(c: *cgen, file: *node) void = {
let body: *node = d.lhs;
if (body != nil) {
if (body.kind == nkind.N_TENUM) {
let et: *enumtype = amalloc(c.a, 48u64): *enumtype;
let et: *enumtype = amalloc(c.a, 64u64): *enumtype;
et.ename = d.str;
et.emod = d.module;
et.storage = body.lhs;
et.members = nil;
let prev: u64 = (-1i64): u64;
@@ -15566,24 +15597,40 @@ fn collectenums(c: *cgen, file: *node) void = {
};
fn enumlookup(c: *cgen, name: str) *enumtype = {
// Strip any `pkg.` prefix and key off the leaf — driver-side
// concatenation flattens the namespace, so `os.whence` and
// `whence` refer to the same registered enum.
let leaf: str = name;
// Exact match first: bare-from-source idents and already-leafed
// names hit here directly.
let e: *enumtype = c.enums;
for (e != nil) {
if (streq(e.ename, name)) { return e; };
e = e.etnext;
};
// Module-qualified form: `pkg.enum` → match the leaf scoped to
// its originating module. Mirrors aliaslookup's mod-filter; the
// `emod == pkg` guard is what prevents two modules with same-
// leaf-name enums from collapsing into whichever entry appears
// first in the chain.
let i: i32 = name.len - 1;
for (i >= 0) {
if (name[i] == 46u8) { // '.'
leaf.ptr = name.ptr + (i + 1): u64;
let pkg: str;
pkg.ptr = name.ptr;
pkg.len = i;
let leaf: str;
leaf.ptr = name.ptr + ((i + 1): u64);
leaf.len = name.len - (i + 1);
break;
let b: *enumtype = c.enums;
for (b != nil) {
if (streq(b.ename, leaf)) {
if (streq(b.emod, pkg)) {
return b;
};
};
b = b.etnext;
};
return nil;
};
i -= 1;
};
let e: *enumtype = c.enums;
for (e != nil) {
if (streq(e.ename, leaf)) { return e; };
e = e.etnext;
};
return nil;
};
@@ -15633,6 +15680,7 @@ type fieldinfo = struct {
type structinfo = struct {
sname: str,
smod: str, // originating module (`// MODULE: foo`), or empty
fields: *fieldinfo,
totsize: i32,
sinext: *structinfo,
@@ -15676,6 +15724,7 @@ type enummember = struct {
type enumtype = struct {
ename: str,
emod: str, // originating module (`// MODULE: foo`), or empty
storage: *node, // AST type expr for the storage type (i32 by default)
members: *enummember,
etnext: *enumtype,

View File

@@ -179,8 +179,9 @@ fn collectenums(c: *cgen, file: *node) void = {
let body: *node = d.lhs;
if (body != nil) {
if (body.kind == nkind.N_TENUM) {
let et: *enumtype = amalloc(c.a, 48u64): *enumtype;
let et: *enumtype = amalloc(c.a, 64u64): *enumtype;
et.ename = d.str;
et.emod = d.module;
et.storage = body.lhs;
et.members = nil;
let prev: u64 = (-1i64): u64;
@@ -216,24 +217,40 @@ fn collectenums(c: *cgen, file: *node) void = {
};
fn enumlookup(c: *cgen, name: str) *enumtype = {
// Strip any `pkg.` prefix and key off the leaf — driver-side
// concatenation flattens the namespace, so `os.whence` and
// `whence` refer to the same registered enum.
let leaf: str = name;
// Exact match first: bare-from-source idents and already-leafed
// names hit here directly.
let e: *enumtype = c.enums;
for (e != nil) {
if (streq(e.ename, name)) { return e; };
e = e.etnext;
};
// Module-qualified form: `pkg.enum` → match the leaf scoped to
// its originating module. Mirrors aliaslookup's mod-filter; the
// `emod == pkg` guard is what prevents two modules with same-
// leaf-name enums from collapsing into whichever entry appears
// first in the chain.
let i: i32 = name.len - 1;
for (i >= 0) {
if (name[i] == 46u8) { // '.'
leaf.ptr = name.ptr + (i + 1): u64;
let pkg: str;
pkg.ptr = name.ptr;
pkg.len = i;
let leaf: str;
leaf.ptr = name.ptr + ((i + 1): u64);
leaf.len = name.len - (i + 1);
break;
let b: *enumtype = c.enums;
for (b != nil) {
if (streq(b.ename, leaf)) {
if (streq(b.emod, pkg)) {
return b;
};
};
b = b.etnext;
};
return nil;
};
i -= 1;
};
let e: *enumtype = c.enums;
for (e != nil) {
if (streq(e.ename, leaf)) { return e; };
e = e.etnext;
};
return nil;
};
@@ -283,6 +300,7 @@ type fieldinfo = struct {
type structinfo = struct {
sname: str,
smod: str, // originating module (`// MODULE: foo`), or empty
fields: *fieldinfo,
totsize: i32,
sinext: *structinfo,
@@ -326,6 +344,7 @@ type enummember = struct {
type enumtype = struct {
ename: str,
emod: str, // originating module (`// MODULE: foo`), or empty
storage: *node, // AST type expr for the storage type (i32 by default)
members: *enummember,
etnext: *enumtype,

View File

@@ -1149,12 +1149,41 @@ fn nodeprimwidth(c: *cgen, n: *node) i32 = {
// ---- type-driven slot sizing ----------------------------------------
fn structlookup(c: *cgen, name: str) *structinfo = {
// Exact match first: bare-from-source struct names and already-
// leafed lookups hit here directly.
let s: *structinfo = c.structs;
for (s != nil) {
let sn: str = s.sname;
if (streq(sn, name)) { return s; };
s = s.sinext;
};
// Module-qualified form: `pkg.S` → match the leaf scoped to its
// originating module. Mirrors aliaslookup's mod-filter; the
// `smod == pkg` guard is what prevents two modules with same-
// leaf-name structs from collapsing into whichever entry appears
// first in the chain.
let i: i32 = name.len - 1;
for (i >= 0) {
if (name[i] == 46u8) { // '.'
let pkg: str;
pkg.ptr = name.ptr;
pkg.len = i;
let leaf: str;
leaf.ptr = name.ptr + ((i + 1): u64);
leaf.len = name.len - (i + 1);
let b: *structinfo = c.structs;
for (b != nil) {
if (streq(b.sname, leaf)) {
if (streq(b.smod, pkg)) {
return b;
};
};
b = b.sinext;
};
return nil;
};
i -= 1;
};
return nil;
};
@@ -1500,9 +1529,10 @@ fn fieldsize(c: *cgen, tnode: *node) i32 = {
return 8;
};
fn registerstruct(c: *cgen, name: str, tstruct: *node) void = {
let si: *structinfo = amalloc(c.a, 64u64): *structinfo;
fn registerstruct(c: *cgen, name: str, module: str, tstruct: *node) void = {
let si: *structinfo = amalloc(c.a, 80u64): *structinfo;
si.sname = name;
si.smod = module;
si.fields = nil;
si.totsize = 0;
let head: *fieldinfo = nil;
@@ -1550,7 +1580,7 @@ fn collectstructs(c: *cgen, file: *node) void = {
let body: *node = d.lhs;
if (body != nil) {
if (body.kind == nkind.N_TSTRUCT) {
registerstruct(c, d.str, body);
registerstruct(c, d.str, d.module, body);
};
};
};

View File

@@ -6878,12 +6878,41 @@ fn nodeprimwidth(c: *cgen, n: *node) i32 = {
// ---- type-driven slot sizing ----------------------------------------
fn structlookup(c: *cgen, name: str) *structinfo = {
// Exact match first: bare-from-source struct names and already-
// leafed lookups hit here directly.
let s: *structinfo = c.structs;
for (s != nil) {
let sn: str = s.sname;
if (streq(sn, name)) { return s; };
s = s.sinext;
};
// Module-qualified form: `pkg.S` → match the leaf scoped to its
// originating module. Mirrors aliaslookup's mod-filter; the
// `smod == pkg` guard is what prevents two modules with same-
// leaf-name structs from collapsing into whichever entry appears
// first in the chain.
let i: i32 = name.len - 1;
for (i >= 0) {
if (name[i] == 46u8) { // '.'
let pkg: str;
pkg.ptr = name.ptr;
pkg.len = i;
let leaf: str;
leaf.ptr = name.ptr + ((i + 1): u64);
leaf.len = name.len - (i + 1);
let b: *structinfo = c.structs;
for (b != nil) {
if (streq(b.sname, leaf)) {
if (streq(b.smod, pkg)) {
return b;
};
};
b = b.sinext;
};
return nil;
};
i -= 1;
};
return nil;
};
@@ -7229,9 +7258,10 @@ fn fieldsize(c: *cgen, tnode: *node) i32 = {
return 8;
};
fn registerstruct(c: *cgen, name: str, tstruct: *node) void = {
let si: *structinfo = amalloc(c.a, 64u64): *structinfo;
fn registerstruct(c: *cgen, name: str, module: str, tstruct: *node) void = {
let si: *structinfo = amalloc(c.a, 80u64): *structinfo;
si.sname = name;
si.smod = module;
si.fields = nil;
si.totsize = 0;
let head: *fieldinfo = nil;
@@ -7279,7 +7309,7 @@ fn collectstructs(c: *cgen, file: *node) void = {
let body: *node = d.lhs;
if (body != nil) {
if (body.kind == nkind.N_TSTRUCT) {
registerstruct(c, d.str, body);
registerstruct(c, d.str, d.module, body);
};
};
};
@@ -15529,8 +15559,9 @@ fn collectenums(c: *cgen, file: *node) void = {
let body: *node = d.lhs;
if (body != nil) {
if (body.kind == nkind.N_TENUM) {
let et: *enumtype = amalloc(c.a, 48u64): *enumtype;
let et: *enumtype = amalloc(c.a, 64u64): *enumtype;
et.ename = d.str;
et.emod = d.module;
et.storage = body.lhs;
et.members = nil;
let prev: u64 = (-1i64): u64;
@@ -15566,24 +15597,40 @@ fn collectenums(c: *cgen, file: *node) void = {
};
fn enumlookup(c: *cgen, name: str) *enumtype = {
// Strip any `pkg.` prefix and key off the leaf — driver-side
// concatenation flattens the namespace, so `os.whence` and
// `whence` refer to the same registered enum.
let leaf: str = name;
// Exact match first: bare-from-source idents and already-leafed
// names hit here directly.
let e: *enumtype = c.enums;
for (e != nil) {
if (streq(e.ename, name)) { return e; };
e = e.etnext;
};
// Module-qualified form: `pkg.enum` → match the leaf scoped to
// its originating module. Mirrors aliaslookup's mod-filter; the
// `emod == pkg` guard is what prevents two modules with same-
// leaf-name enums from collapsing into whichever entry appears
// first in the chain.
let i: i32 = name.len - 1;
for (i >= 0) {
if (name[i] == 46u8) { // '.'
leaf.ptr = name.ptr + (i + 1): u64;
let pkg: str;
pkg.ptr = name.ptr;
pkg.len = i;
let leaf: str;
leaf.ptr = name.ptr + ((i + 1): u64);
leaf.len = name.len - (i + 1);
break;
let b: *enumtype = c.enums;
for (b != nil) {
if (streq(b.ename, leaf)) {
if (streq(b.emod, pkg)) {
return b;
};
};
b = b.etnext;
};
return nil;
};
i -= 1;
};
let e: *enumtype = c.enums;
for (e != nil) {
if (streq(e.ename, leaf)) { return e; };
e = e.etnext;
};
return nil;
};
@@ -15633,6 +15680,7 @@ type fieldinfo = struct {
type structinfo = struct {
sname: str,
smod: str, // originating module (`// MODULE: foo`), or empty
fields: *fieldinfo,
totsize: i32,
sinext: *structinfo,
@@ -15676,6 +15724,7 @@ type enummember = struct {
type enumtype = struct {
ename: str,
emod: str, // originating module (`// MODULE: foo`), or empty
storage: *node, // AST type expr for the storage type (i32 by default)
members: *enummember,
etnext: *enumtype,