diff --git a/Makefile b/Makefile index 1d1c5897..3451bbe6 100644 --- a/Makefile +++ b/Makefile @@ -356,6 +356,7 @@ $(BIN)/test_match_bind_struct: test/wcc/695_match_bind_struct.c $(BIN)/ww \ $(BIN)/test_modtype_leaf_collision: test/wcc/696_modtype_leaf_collision.c \ $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ + $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index d254e15c..5c1b3597 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -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, diff --git a/selfhost/cmd/wcc/cgen.ww b/selfhost/cmd/wcc/cgen.ww index 5733f11e..281a388f 100644 --- a/selfhost/cmd/wcc/cgen.ww +++ b/selfhost/cmd/wcc/cgen.ww @@ -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, diff --git a/selfhost/cmd/wcc/cgenutil.ww b/selfhost/cmd/wcc/cgenutil.ww index 13befef3..c7027400 100644 --- a/selfhost/cmd/wcc/cgenutil.ww +++ b/selfhost/cmd/wcc/cgenutil.ww @@ -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); }; }; }; diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index cbf651a2..6d00f404 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -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, diff --git a/test/wcc/696_modtype_leaf_collision.c b/test/wcc/696_modtype_leaf_collision.c index f9da1f4f..12b6b84f 100644 --- a/test/wcc/696_modtype_leaf_collision.c +++ b/test/wcc/696_modtype_leaf_collision.c @@ -97,6 +97,8 @@ main(void) char cdrv[1024]; snprintf(cdrv, sizeof cdrv, "%s/ww", bin); + char wdrv[1024]; + snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); char fixdir[1024]; if (getcwd(fixdir, sizeof fixdir) == NULL) return 1; @@ -108,12 +110,18 @@ main(void) int fail = 0; fail += run_pos(cdrv, fixdir, "cstage"); fail += run_neg(cdrv, fixdir, "cstage"); + fail += run_pos(wdrv, fixdir, "wwstage"); + /* No wwstage neg case: w6c_ww has no checkfile pass, so a + * missing-field reference doesn't surface as a build error. + * The positive case alone pins the structlookup mod-filter + * (without it, `mod2.stream` would either fail to resolve or + * silently bind to mod1.stream and miscompute the exit). */ if (fail) { fprintf(stderr, "modtype_leaf_collision: %d case(s) failed\n", fail); return 1; } - printf("modtype_leaf_collision: 2/2 ok\n"); + printf("modtype_leaf_collision: 3/3 ok\n"); return 0; }