build: close package variant identity gaps

This commit is contained in:
2026-08-12 22:09:55 +09:00
parent 1724ea086f
commit 2dd6b8a250
5 changed files with 57 additions and 7 deletions

View File

@@ -3060,7 +3060,9 @@ lookup_visible_type(Checker *c, const char *name)
for (Scope *p = c->cur; p; p = p->parent) { for (Scope *p = c->cur; p; p = p->parent) {
for (Sym *b = p->first; b; b = b->next) for (Sym *b = p->first; b; b = b->next)
if (b->kind == SK_TYPE && strcmp(b->name, name) == 0 if (b->kind == SK_TYPE && strcmp(b->name, name) == 0
&& direct_module_visible(c, b->mod)) && direct_module_visible(c, b->mod)
&& (b->decl == NULL || !b->decl->imported
|| b->decl->export))
return b; return b;
} }
return NULL; return NULL;

View File

@@ -1182,6 +1182,13 @@ sep_add_generated_main(struct sepgraph *g, struct sepproduct *product,
fprintf(stderr, "ww: generated test-main identity is too long\n"); fprintf(stderr, "ww: generated test-main identity is too long\n");
return -1; return -1;
} }
for (int i = 0; i < g->n; i++) {
if (strcmp(g->pkg[i].path, p->path) != 0) continue;
fprintf(stderr,
"ww: generated test-main package identity collides with source import %s\n",
p->path);
return -1;
}
snprintf(p->entry, sizeof p->entry, "%s", variantentry); snprintf(p->entry, sizeof p->entry, "%s", variantentry);
snprintf(p->name, sizeof p->name, "main"); snprintf(p->name, sizeof p->name, "main");
p->variant = SEP_VARIANT_TEST_MAIN; p->variant = SEP_VARIANT_TEST_MAIN;
@@ -1361,6 +1368,21 @@ sep_topo_visit(struct sepgraph *g, int pi, int *order, int *no,
* with an unrelated normal action in the command-global universe, but never * with an unrelated normal action in the command-global universe, but never
* in one product. A single link closure must remain an unambiguous package * in one product. A single link closure must remain an unambiguous package
* namespace. */ * namespace. */
static int
sep_internal_replaces_production(const struct sepgraph *g, int a, int b)
{
const struct seppkg *internal = &g->pkg[a];
const struct seppkg *production = &g->pkg[b];
if (internal->variant != SEP_VARIANT_SAME_TEST) {
internal = &g->pkg[b];
production = &g->pkg[a];
}
return internal->variant == SEP_VARIANT_SAME_TEST
&& production->variant == SEP_VARIANT_PRODUCTION
&& production->role != SEP_ROLE_TEST_SUPPORT
&& strcmp(internal->canon, production->canon) == 0;
}
static int static int
sep_validate_module_closure(struct sepgraph *g, const int *order, int n, sep_validate_module_closure(struct sepgraph *g, const int *order, int n,
int include_root) int include_root)
@@ -1372,7 +1394,8 @@ sep_validate_module_closure(struct sepgraph *g, const int *order, int n,
for (int j = i + 1; j < n; j++) { for (int j = i + 1; j < n; j++) {
int b = order[j]; int b = order[j];
if (!include_root && g->pkg[b].root) continue; if (!include_root && g->pkg[b].root) continue;
if (strcmp(g->pkg[a].path, g->pkg[b].path) == 0) { if (strcmp(g->pkg[a].path, g->pkg[b].path) == 0
&& !sep_internal_replaces_production(g, a, b)) {
fprintf(stderr, fprintf(stderr,
"ww: product closure contains multiple packages named %s\n", "ww: product closure contains multiple packages named %s\n",
g->pkg[a].path); g->pkg[a].path);

View File

@@ -312,7 +312,9 @@ fn lookupvisibletype(c: *checker, name: str) *syntax.sym = {
for (b != nil) { for (b != nil) {
if (b.skind == syntax.skind.SK_TYPE if (b.skind == syntax.skind.SK_TYPE
&& syntax.streq(b.name, name) && syntax.streq(b.name, name)
&& directmodvisible(c, b.mod)) { return b; }; && directmodvisible(c, b.mod)
&& (b.decl == nil || b.decl.imported == 0
|| b.decl.exported != 0)) { return b; };
b = b.snext; b = b.snext;
}; };
p = p.parent; p = p.parent;

View File

@@ -1397,6 +1397,15 @@ fn sepaddgeneratedmain(g: *sepgraph, product: *sepproduct, ordinal: i32,
if (variant < 0 || variant >= g.n) { return -1; }; if (variant < 0 || variant >= g.n) { return -1; };
let p: *seppkg = &g.pkg[g.n]; let p: *seppkg = &g.pkg[g.n];
p.path = generatedmainpath(ordinal); p.path = generatedmainpath(ordinal);
let pathi: i32 = 0;
for (pathi < g.n) {
if (cstreq(g.pkg[pathi].path, p.path)) {
cerr("ww: generated test-main package identity collides with source import ");
cerr(pathstr(p.path)); cerr("\n");
return -1;
};
pathi += 1;
};
p.entry = g.pkg[variant].entry; p.entry = g.pkg[variant].entry;
p.artifact = productartifact(ordinal, SEP_VARIANT_TEST_MAIN); p.artifact = productartifact(ordinal, SEP_VARIANT_TEST_MAIN);
p.name = arenadupcstr("main\0".ptr, 4u64); p.name = arenadupcstr("main\0".ptr, 4u64);
@@ -1613,6 +1622,20 @@ fn septopovisit(g: *sepgraph, pi: i32, order: []i32, no: *i32,
return 0; return 0;
}; };
fn sepinternalreplacesproduction(g: *sepgraph, a: i32, b: i32) bool = {
let internal: i32 = a;
let production: i32 = b;
if (g.pkg[internal].variant != SEP_VARIANT_SAME_TEST) {
internal = b;
production = a;
};
return g.pkg[internal].variant == SEP_VARIANT_SAME_TEST
&& g.pkg[production].variant == SEP_VARIANT_PRODUCTION
&& g.pkg[production].role != SEP_ROLE_TEST_SUPPORT
&& os.samefile(pathstr(g.pkg[internal].entry),
pathstr(g.pkg[production].entry));
};
fn sepvalidatemoduleclosure(g: *sepgraph, order: []i32, n: i32, fn sepvalidatemoduleclosure(g: *sepgraph, order: []i32, n: i32,
includeroot: bool) i32 = { includeroot: bool) i32 = {
let i: i32 = 0; let i: i32 = 0;
@@ -1624,7 +1647,8 @@ fn sepvalidatemoduleclosure(g: *sepgraph, order: []i32, n: i32,
for (j < n) { for (j < n) {
let b: i32 = order[j]; let b: i32 = order[j];
if ((includeroot || !g.pkg[b].root) if ((includeroot || !g.pkg[b].root)
&& cstreq(g.pkg[a].path, g.pkg[b].path)) { && cstreq(g.pkg[a].path, g.pkg[b].path)
&& !sepinternalreplacesproduction(g, a, b)) {
cerr("ww: product closure contains multiple packages named "); cerr("ww: product closure contains multiple packages named ");
cerr(pathstr(g.pkg[a].path)); cerr("\n"); cerr(pathstr(g.pkg[a].path)); cerr("\n");
return -1; return -1;

View File

@@ -302,7 +302,7 @@ fn m2positive(pkg: str) void = {
}; };
testenv.writefile(strings.concat(td, "/consumer.ww"), strings.concat( testenv.writefile(strings.concat(td, "/consumer.ww"), strings.concat(
"package consumer;\nimport leaktest;\n", "package consumer;\nimport leaktest;\n",
"fn forbidden(s: leaktest.secret) void = {};\n")); "fn forbidden(s: secret) void = {};\n"));
let ccav: []str = [testenv.driver("w6c"), "-c", "--import", let ccav: []str = [testenv.driver("w6c"), "-c", "--import",
"leaktest", "cs.wwi", "-o", "cs.s", "consumer.ww"]; "leaktest", "cs.wwi", "-o", "cs.s", "consumer.ww"];
let wcav: []str = [testenv.driver("w6c_ww"), "-c", "--import", let wcav: []str = [testenv.driver("w6c_ww"), "-c", "--import",
@@ -314,8 +314,7 @@ fn m2positive(pkg: str) void = {
if (cco.termination != exec.termination.EXIT || cco.code == 0 if (cco.termination != exec.termination.EXIT || cco.code == 0
|| wco.termination != exec.termination.EXIT || wco.code == 0 || wco.termination != exec.termination.EXIT || wco.code == 0
|| !testenv.same(cco.stderr, wco.stderr) || !testenv.same(cco.stderr, wco.stderr)
|| !testenv.has(cco.stderr, || !testenv.has(cco.stderr, "unknown type 'secret'")) {
"package 'leaktest' has no exported declaration 'secret'")) {
fail("private-closure", "private nominal became source-visible"); fail("private-closure", "private nominal became source-visible");
}; };
testenv.clean(td); testenv.clean(td);