test: port 696/697/699 resolver carriers to corpus fixtures

696_modtype_leaf_collision.c -> r696_modtype_leaf_pos (run-exit 26),
  r696_modtype_leaf_neg (staged error; w6c 'no field', w6c_ww
  asserttyped: dot — the old no-wwstage-checkfile comment is stale)
697_samemod_prefer.c -> r697_samemod_prefer (compile)
699_use_promote_alias.c -> r699_usepromote_{type,fn,def,var}
  (run-exit 42 each)

Single-file multi-package form folds the modcollision/samemodprefer/
usepromote data trees into the fixtures; the trees retire with their
carriers. The dir-tree .wwi transport of colliding type decls is owned
by test/byteid/wwi_test.ww. Coverage upgrades: 697 and the 699 rows
gain the wwstage leg the carriers gated or skipped, and all positive
rows enter the blanket test-data-byteid net.
This commit is contained in:
2026-08-08 14:28:50 +09:00
parent 08cae16a0a
commit f45360bbec
26 changed files with 131 additions and 710 deletions

View File

@@ -1,13 +1,13 @@
package wwfixture; package wwfixture;
def protocolversion: i32 = 1; def protocolversion: i32 = 1;
def corpuscount: i32 = 1634; def corpuscount: i32 = 1641;
def errorcount: i32 = 336; def errorcount: i32 = 337;
def compilecount: i32 = 17; def compilecount: i32 = 18;
def runcount: i32 = 198; def runcount: i32 = 198;
def runexitcount: i32 = 1083; def runexitcount: i32 = 1088;
def nativecount: i32 = 3268; def nativecount: i32 = 3282;
def corpushash: str = "cb7cf09decc2f64b40c1f06b836c5b2aec0a4f1b9b2a78665f228606a0245a1f"; def corpushash: str = "d518a094a11081af773549c53a6830c510685ef44548c8ea01400e0661c37e65";
type directive = enum i32 { type directive = enum i32 {
ERROR = 0, ERROR = 0,

View File

@@ -1,178 +0,0 @@
/*
* 696_modtype_leaf_collision — same-leaf-name cross-module type
* disambiguation. Two modules each export `type stream` with
* intentionally distinct field sets; the consumer pins both via
* `mod1.stream` / `mod2.stream`.
*
* Pre-fix (before sym.mod tagging + scope_lookup_in_module): the
* second `type stream` registration in pass-1 failed with
* "duplicate type stream", or when both did survive they collapsed
* to whichever sym was first in the bucket chain. The negative case
* (`b: mod1.stream; return b.c;`) used to silently bind through the
* wrong type and the bogus field read would either crash at run
* time or return the wrong byte. Post-fix it must surface as a
* compile-time "no field 'c' in stream" error.
*
* Fixtures live in test/wcc/data/modcollision/. mod1/mod1.ww and
* mod2/mod2.ww are deliberately *new* source files so the test
* stays self-contained — independent of lib/bufio (now that
* `bufio.stream` and `io.stream` coexist on the post-#15 surface).
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
static int
run_pos(const char *driver, const char *fixdir, const char *tag)
{
char td[1024], bin[1024], sepwork[1100];
snprintf(td, sizeof td, "/tmp/modcol_pos_%d_%s", getpid(), tag);
if (mkdir(td, 0755) != 0) {
fprintf(stderr, "modtype_leaf[%s]: mkdir %s failed\n", tag, td);
return 1;
}
snprintf(bin, sizeof bin, "%s/pos", td);
snprintf(sepwork, sizeof sepwork, "%s/pos.sepwork", td);
char cmd[2048];
/* cd into the fixture dir so the driver's source-dir-first import
* search resolves `use mod1; use mod2;`; -o puts the binary and the
* caller-owned .sepwork under the directory acquired above. */
snprintf(cmd, sizeof cmd,
"cd %s && %s build -o %s/pos pos.ww >/dev/null 2>&1",
fixdir, driver, td);
int fail = 0;
if (runwait(cmd) != 0) {
fprintf(stderr, "modtype_leaf[%s]: pos.ww build failed\n", tag);
fail = 1;
} else {
int got = runwait(bin);
/* 3 + 5 (mod1.stream) + 7 + 11 (mod2.stream) = 26. */
if (got != 26) {
fprintf(stderr,
"modtype_leaf[%s]: pos.ww exit=%d want=26\n", tag, got);
fail = 1;
}
}
snprintf(cmd, sizeof cmd, "rm -rf %s", sepwork);
if (runwait(cmd) != 0) {
fprintf(stderr, "modtype_leaf[%s]: cleanup %s failed\n",
tag, sepwork);
fail = 1;
}
if (unlink(bin) != 0 && errno != ENOENT) {
fprintf(stderr, "modtype_leaf[%s]: cleanup %s failed\n", tag, bin);
fail = 1;
}
if (rmdir(td) != 0) {
fprintf(stderr, "modtype_leaf[%s]: cleanup %s failed\n", tag, td);
fail = 1;
}
return fail;
}
static int
run_neg(const char *driver, const char *fixdir, const char *tag)
{
char td[1024], bin[1024], sepwork[1100];
snprintf(td, sizeof td, "/tmp/modcol_neg_%d_%s", getpid(), tag);
if (mkdir(td, 0755) != 0) {
fprintf(stderr, "modtype_leaf[%s]: mkdir %s failed\n", tag, td);
return 1;
}
snprintf(bin, sizeof bin, "%s/neg", td);
snprintf(sepwork, sizeof sepwork, "%s/neg.sepwork", td);
char cmd[2048];
/* Build must fail with a field-resolution error: the `c` field
* lives on mod2.stream, not mod1.stream. If pre-fix sym-binding
* smashed mod1.stream's identity into mod2.stream's slot, this
* would silently succeed. -o puts the binary and its caller-owned
* .sepwork (created even on the expected build failure) under the
* directory acquired above. */
snprintf(cmd, sizeof cmd,
"cd %s && %s build -o %s/neg neg.ww >/dev/null 2>&1",
fixdir, driver, td);
int rc = runwait(cmd);
int fail = 0;
if (rc == 0) {
fprintf(stderr,
"modtype_leaf[%s]: neg.ww built but should have errored\n",
tag);
fail = 1;
}
snprintf(cmd, sizeof cmd, "rm -rf %s", sepwork);
if (runwait(cmd) != 0) {
fprintf(stderr, "modtype_leaf[%s]: cleanup %s failed\n",
tag, sepwork);
fail = 1;
}
if (unlink(bin) != 0 && errno != ENOENT) {
fprintf(stderr, "modtype_leaf[%s]: cleanup %s failed\n", tag, bin);
fail = 1;
}
if (rmdir(td) != 0) {
fprintf(stderr, "modtype_leaf[%s]: cleanup %s failed\n", tag, td);
fail = 1;
}
return fail;
}
int
main(void)
{
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
char absbin[1024];
if (bin[0] != '/') {
char cwd[1024];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
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;
size_t cwd_n = strlen(fixdir);
const char *rel = "/test/wcc/data/modcollision";
if (cwd_n + strlen(rel) + 1 >= sizeof fixdir) return 1;
memcpy(fixdir + cwd_n, rel, strlen(rel) + 1);
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: 3/3 ok\n");
return 0;
}

View File

@@ -1,119 +0,0 @@
/*
* 697_samemod_prefer — same-module preference for bare-leaf lookup.
*
* Two modules each export `read` with intentionally distinct
* signatures (mod1: i32 → i32, mod2: str → i32). Each module's
* `caller()` invokes bare `read(...)` with its own arg type. The
* bare-leaf lookup must resolve to the SAME-MODULE `read`; otherwise
* the call is a check-time signature mismatch.
*
* Pre-fix (flat scope, first-wins by hash order): one of the bare
* lookups picked the wrong module's `read` and the build failed with
* "argument type not assignable". Post-fix (scope_lookup_prefer): each
* bare-leaf inside its own module binds to the same-module entry, both
* calls type-check, and the build succeeds.
*
* Runtime exit codes aren't asserted here because cgen still emits
* unmangled `TEXT read` labels for both modules and the linker
* collapses them. Mangling fn labels by module is a separate codegen
* sweep; this test pins the resolver fix alone.
*
* Fixtures live in test/wcc/data/samemodprefer/. Same shape as
* 696_modtype_leaf_collision.c.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
static int
run_pos(const char *driver, const char *fixdir, const char *tag)
{
/* -o puts the binary and caller-owned `<stem>.sepwork/` under the
* acquired directory; the cd keeps source-dir-first import search
* resolving `use mod1; use mod2;`. */
char td[1024], outbin[1024], sepwork[1100];
snprintf(td, sizeof td, "/tmp/smprefer_%d_%s", getpid(), tag);
if (mkdir(td, 0755) != 0) {
fprintf(stderr, "samemod_prefer[%s]: mkdir %s failed\n", tag, td);
return 1;
}
snprintf(outbin, sizeof outbin, "%s/pos", td);
snprintf(sepwork, sizeof sepwork, "%s/pos.sepwork", td);
char cmd[2048];
snprintf(cmd, sizeof cmd,
"cd %s && %s build -o %s/pos pos.ww >/dev/null 2>&1",
fixdir, driver, td);
int fail = 0;
if (runwait(cmd) != 0) {
fprintf(stderr,
"samemod_prefer[%s]: pos.ww build failed — bare-leaf "
"`read` lookup likely cross-bound\n", tag);
fail = 1;
}
snprintf(cmd, sizeof cmd, "rm -rf %s", sepwork);
if (runwait(cmd) != 0) {
fprintf(stderr, "samemod_prefer[%s]: cleanup %s failed\n",
tag, sepwork);
fail = 1;
}
if (unlink(outbin) != 0 && errno != ENOENT) {
fprintf(stderr, "samemod_prefer[%s]: cleanup %s failed\n",
tag, outbin);
fail = 1;
}
if (rmdir(td) != 0) {
fprintf(stderr, "samemod_prefer[%s]: cleanup %s failed\n", tag, td);
fail = 1;
}
return fail;
}
int
main(void)
{
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
char absbin[1024];
if (bin[0] != '/') {
char cwd[1024];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
char cdrv[1024];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
char fixdir[1024];
if (getcwd(fixdir, sizeof fixdir) == NULL) return 1;
size_t cwd_n = strlen(fixdir);
const char *rel = "/test/wcc/data/samemodprefer";
if (cwd_n + strlen(rel) + 1 >= sizeof fixdir) return 1;
memcpy(fixdir + cwd_n, rel, strlen(rel) + 1);
int fail = 0;
fail += run_pos(cdrv, fixdir, "cstage");
if (fail) {
fprintf(stderr,
"samemod_prefer: %d case(s) failed\n", fail);
return 1;
}
printf("samemod_prefer: 1/1 ok\n");
return 0;
}

View File

@@ -1,152 +0,0 @@
/*
* 699_use_promote_alias — SK_USE→SK_X promotion preserves use_alias.
*
* Sweep of every reachable promotion site in cmd/wcc/check.c where an
* SK_USE leaf gets in-place-promoted to a same-named decl. With driver
* concatenation, the imported module's top-level decls appear in
* file.list BEFORE the primary's `use foo;`, so pass-1 (N_USE +
* N_TYPEDECL only) installs an SK_USE("foo") that pass-1.5 later
* promotes when it walks the imported N_DEF / N_FNDECL / N_LET of the
* same name. Each promotion must set `prev->use_alias = 1` so the
* dot-prefixed lookups in resolve_typename (L77) and N_DOT (L709)
* continue to walk the leaf as a module head.
*
* The fnmatch.flag-style "unknown type" failure that surfaced this is
* the SK_FN row; the SK_TYPE row pins the random.random precedent
* against regression; SK_DEF / SK_VAR are the same structural shape
* with `def` / top-level `let` standing in for `fn`.
*
* row | imported decl shape | path | check.c line
* -----+-----------------------------+----------+-------------
* type | `type typmod = struct {...}`| L1660/77 | regression pin
* fn | `fn fnmod() i32 = ...` | L1722 | the bug
* def | `def defmod: i32 = 0` | L1709 | same shape
* var | `let varmod: i32 = 0` | L1736 | same shape
*
* Fixtures: test/wcc/data/usepromote/{type,fn,def,var}mod/<name>.ww
* primary: test/wcc/data/usepromote/pos_{type,fn,def,var}.ww
*
* Each primary returns `flag.A` (= 42) so a successful build + run +
* exit=42 proves the lookup chain end-to-end. Cstage-only: wwstage
* uses a coexistence-not-promotion model in selfhost/cmd/wcc/check.ww
* (separate sym entries differentiated by `mod`, no use_alias flag at
* all), so the bug is structurally non-reachable on that side — see
* the architectural note at installdecl in check.ww. Task #11 will
* revisit when wwstage grows a real check pass on the cgen path.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
static int
run_row(const char *driver, const char *fixdir, const char *tag)
{
/* Keep the output and its caller-owned `<stem>.sepwork/` in one
* invocation-owned directory outside the repository. */
char td[64];
snprintf(td, sizeof td, "/tmp/usepr_%d_%s", getpid(), tag);
if (mkdir(td, 0755) != 0) {
fprintf(stderr, "use_promote_alias[%s]: mkdir %s: %s\n",
tag, td, strerror(errno));
return 1;
}
char src[64];
snprintf(src, sizeof src, "pos_%s.ww", tag);
char bin[2048], sepdir[2048], rmcmd[2112];
snprintf(bin, sizeof bin, "%s/out", td);
snprintf(sepdir, sizeof sepdir, "%s.sepwork", bin);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepdir);
char cmd[2048];
int failed = 0, got;
/* cd into the fixture dir so the driver's source-dir-first
* import search resolves `use <tag>mod;`; -o points OUT of the repo. */
snprintf(cmd, sizeof cmd,
"cd %s && %s build -o %s %s >/dev/null 2>&1", fixdir, driver, bin, src);
if (runwait(cmd) != 0) {
fprintf(stderr,
"use_promote_alias[%s]: build failed — SK_USE→SK_X "
"promotion likely dropped use_alias\n", tag);
failed = 1;
goto cleanup;
}
got = runwait(bin);
if (got != 42) {
fprintf(stderr,
"use_promote_alias[%s]: exit=%d want=42\n", tag, got);
failed = 1;
}
cleanup:
{
int cleanup_failed = 0;
if (runwait(rmcmd) != 0) {
fprintf(stderr, "use_promote_alias[%s]: remove %s failed\n",
tag, sepdir);
cleanup_failed = 1;
}
if (unlink(bin) != 0 && errno != ENOENT) {
fprintf(stderr, "use_promote_alias[%s]: unlink %s: %s\n",
tag, bin, strerror(errno));
cleanup_failed = 1;
}
if (rmdir(td) != 0) {
fprintf(stderr, "use_promote_alias[%s]: rmdir %s: %s\n",
tag, td, strerror(errno));
cleanup_failed = 1;
}
if (!failed && cleanup_failed) failed = 1;
}
return failed;
}
int
main(void)
{
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
char absbin[1024];
if (bin[0] != '/') {
char cwd[1024];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
char cdrv[1024];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
char fixdir[1024];
if (getcwd(fixdir, sizeof fixdir) == NULL) return 1;
size_t cwd_n = strlen(fixdir);
const char *rel = "/test/wcc/data/usepromote";
if (cwd_n + strlen(rel) + 1 >= sizeof fixdir) return 1;
memcpy(fixdir + cwd_n, rel, strlen(rel) + 1);
int fail = 0;
fail += run_row(cdrv, fixdir, "type");
fail += run_row(cdrv, fixdir, "fn");
fail += run_row(cdrv, fixdir, "def");
fail += run_row(cdrv, fixdir, "var");
if (fail) {
fprintf(stderr,
"use_promote_alias: %d row(s) failed\n", fail);
return 1;
}
printf("use_promote_alias: 4/4 ok\n");
return 0;
}

View File

@@ -1,10 +0,0 @@
// modcollision/mod1 — exports `type stream` with mod1-shaped fields.
// Paired with mod2/mod2.ww to exercise same-leaf-name cross-module
// type disambiguation. Test driver: 696_modtype_leaf_collision.c.
package mod1;
export type stream = struct {
a: i32,
b: i32,
};

View File

@@ -1,14 +0,0 @@
// modcollision/mod2 — exports `type stream` with mod2-shaped fields.
// Paired with mod1/mod1.ww to exercise same-leaf-name cross-module
// type disambiguation. Test driver: 696_modtype_leaf_collision.c.
//
// Fields are intentionally named distinctly from mod1's (c/d vs a/b)
// so the negative test (`b: mod1.stream` accessed via mod2-only field
// 'c') surfaces as a compile-time field-resolution error.
package mod2;
export type stream = struct {
c: i32,
d: i32,
};

View File

@@ -1,21 +0,0 @@
// Negative case: declare `b: mod1.stream` then access a mod2-only
// field. With the mod-tagged scope lookup `b` resolves to mod1.stream
// (fields a, b), so the field access `b.c` must be a compile-time
// error rather than silently binding to mod2.stream and succeeding.
//
// No cross-module call is made: a missing mod1.make would itself
// trigger a link-time error that could mask the field-resolution
// error this test is actually pinning. `let b: mod1.stream;` is
// enough — we read b.c before initializing it, which is fine because
// the field-resolution error fires at check, long before any reach
// analysis or codegen runs.
package modcollision;
import mod1;
import mod2;
fn main() i32 = {
let b: mod1.stream;
return b.c; // mod1.stream has no `c`; expect a compile error.
};

View File

@@ -1,20 +0,0 @@
// Positive case: two modules each export `type stream`. The consumer
// imports both and disambiguates via the module qualifier. mod1.stream
// has fields (a, b); mod2.stream has fields (c, d). The exit code
// encodes a sum of all four fields, so any cross-binding would either
// fail to compile or return the wrong value.
package modcollision;
import mod1;
import mod2;
fn main() i32 = {
let s1: mod1.stream;
s1.a = 3: i32;
s1.b = 5: i32;
let s2: mod2.stream;
s2.c = 7: i32;
s2.d = 11: i32;
return s1.a + s1.b + s2.c + s2.d;
};

View File

@@ -0,0 +1,19 @@
//ww:error c "no field 'c' in stream" ww "asserttyped: dot"
// migrated from test/wcc/696_modtype_leaf_collision.c: field `c` lives only on mod2.stream; pre-fix the read silently bound through the wrong type.
package mod1;
export type stream = struct {
a: i32,
b: i32,
};
package mod2;
export type stream = struct {
c: i32,
d: i32,
};
package main;
import mod1;
import mod2;
fn main() i32 = {
let b: mod1.stream;
return b.c;
};

View File

@@ -0,0 +1,24 @@
//ww:run-exit 26
// migrated from test/wcc/696_modtype_leaf_collision.c: same-leaf cross-module `type stream` must stay mod-tagged (pre-fix sym smash cross-bound the fields).
package mod1;
export type stream = struct {
a: i32,
b: i32,
};
package mod2;
export type stream = struct {
c: i32,
d: i32,
};
package main;
import mod1;
import mod2;
fn main() i32 = {
let s1: mod1.stream;
s1.a = 3: i32;
s1.b = 5: i32;
let s2: mod2.stream;
s2.c = 7: i32;
s2.d = 11: i32;
return s1.a + s1.b + s2.c + s2.d;
};

View File

@@ -0,0 +1,22 @@
//ww:compile
// migrated from test/wcc/697_samemod_prefer.c: bare-leaf `read` binds same-module first; compile-only (unmangled TEXT read labels still collapse in the linker).
package mod1;
export fn read(x: i32) i32 = {
return x + 100i32;
};
export fn caller() i32 = {
return read(1i32);
};
package mod2;
export fn read(x: str) i32 = {
return x.len + 200i32;
};
export fn caller() i32 = {
return read("ok");
};
package main;
import mod1;
import mod2;
fn main() i32 = {
return 0i32;
};

View File

@@ -0,0 +1,14 @@
//ww:run-exit 42
// migrated from test/wcc/699_use_promote_alias.c: SK_USE->SK_DEF promotion must keep use_alias so `defmod.flag` resolves.
package defmod;
export def defmod: i32 = 0i32;
export type flag = enum i32 {
NONE = 0,
A = 42,
};
package main;
import defmod;
fn main() i32 = {
let m: defmod.flag = defmod.flag.A;
return m: i32;
};

View File

@@ -0,0 +1,16 @@
//ww:run-exit 42
// migrated from test/wcc/699_use_promote_alias.c: SK_USE->SK_FN promotion (the original bug) must keep use_alias or `fnmod.flag` fails "unknown type".
package fnmod;
export type flag = enum i32 {
NONE = 0,
A = 42,
};
export fn fnmod() i32 = {
return 0i32;
};
package main;
import fnmod;
fn main() i32 = {
let m: fnmod.flag = fnmod.flag.A;
return m: i32;
};

View File

@@ -0,0 +1,16 @@
//ww:run-exit 42
// migrated from test/wcc/699_use_promote_alias.c: SK_USE->SK_TYPE in-place promotion keeps use_alias so `typmod.flag` resolves (check.c self-import branch).
package typmod;
export type typmod = struct {
x: i32,
};
export type flag = enum i32 {
NONE = 0,
A = 42,
};
package main;
import typmod;
fn main() i32 = {
let m: typmod.flag = typmod.flag.A;
return m: i32;
};

View File

@@ -0,0 +1,14 @@
//ww:run-exit 42
// migrated from test/wcc/699_use_promote_alias.c: SK_USE->SK_VAR promotion must keep use_alias so `varmod.flag` resolves.
package varmod;
export let varmod: i32 = 0i32;
export type flag = enum i32 {
NONE = 0,
A = 42,
};
package main;
import varmod;
fn main() i32 = {
let m: varmod.flag = varmod.flag.A;
return m: i32;
};

View File

@@ -1,20 +0,0 @@
// samemodprefer/mod1 — exports a `read` that takes an i32 plus a
// `caller` that invokes bare `read(1i32)`. The bare-leaf lookup must
// resolve to mod1.read (i32 → i32) rather than mod2.read (str → i32),
// which coexists in the flat scope. Pre-fix the lookup picked
// whichever entry hashed earliest into the bucket; a wrong bind would
// surface as a check-time signature mismatch. Paired with mod2/mod2.ww
// and test/wcc/697_samemod_prefer.c.
package mod1;
export fn read(x: i32) i32 = {
return x + 100i32;
};
export fn caller() i32 = {
// Bare-leaf call inside mod1: must bind to mod1.read (i32 arg).
// If preference doesn't kick in this fails check: mod2.read
// expects a str, so `read(1i32)` would be a signature mismatch.
return read(1i32);
};

View File

@@ -1,20 +0,0 @@
// samemodprefer/mod2 — exports a `read` that takes a str plus a
// `caller` that invokes bare `read("ok")`. The bare-leaf lookup must
// resolve to mod2.read (str → i32) rather than mod1.read (i32 → i32),
// which coexists in the flat scope. Pre-fix the lookup picked
// whichever entry hashed earliest into the bucket; a wrong bind would
// surface as a check-time signature mismatch. Paired with mod1/mod1.ww
// and test/wcc/697_samemod_prefer.c.
package mod2;
export fn read(x: str) i32 = {
return x.len + 200i32;
};
export fn caller() i32 = {
// Bare-leaf call inside mod2: must bind to mod2.read (str arg).
// If preference doesn't kick in this fails check: mod1.read
// expects an i32, so `read("ok")` would be a signature mismatch.
return read("ok");
};

View File

@@ -1,22 +0,0 @@
// Positive case: two modules each export `read` with DIFFERENT
// signatures (mod1: i32 → i32, mod2: str → i32). Each module also
// exports a `caller` that invokes bare `read(...)` with its own arg
// type. The bare-leaf inside mod1 must bind to mod1.read; inside mod2
// to mod2.read. If preference doesn't kick in and the bare lookup
// picks the other module's `read`, the build fails with a signature
// mismatch at check-time. The driver test asserts that this fixture
// builds successfully.
//
// We don't assert runtime exit codes here because cgen still emits
// unmangled `TEXT read` labels for both modules and the linker
// collapses them — that's a separate codegen sweep, orthogonal to the
// resolver fix this test pins.
package samemodprefer;
import mod1;
import mod2;
fn main() i32 = {
return 0i32;
};

View File

@@ -1,15 +0,0 @@
// usepromote/defmod — exports a `def defmod` with the same leaf as
// the module name. Paired with pos_def.ww to exercise the SK_USE→
// SK_DEF promotion (cmd/wcc/check.c L1709). Mirrors the SK_FN bug
// shape: without `use_alias = 1`, the consumer's `defmod.flag` lookup
// fails because the promoted-in-place SK_DEF leaf no longer advertises
// itself as a module head.
package defmod;
export def defmod: i32 = 0i32;
export type flag = enum i32 {
NONE = 0,
A = 42,
};

View File

@@ -1,19 +0,0 @@
// usepromote/fnmod — exports a `fn fnmod()` with the same leaf as the
// module name. Paired with pos_fn.ww to exercise the bug fix at
// cmd/wcc/check.c L1722 (SK_USE→SK_FN promotion was missing the
// `use_alias = 1` line, so the consumer's `fnmod.flag` lookup walked
// resolve_typename's dot branch, found head `fnmod` as SK_FN, and
// skipped the module-filtered leaf path → "unknown type fnmod.flag".
//
// lib/fnmatch is the real-world instance that surfaced this.
package fnmod;
export type flag = enum i32 {
NONE = 0,
A = 42,
};
export fn fnmod() i32 = {
return 0i32;
};

View File

@@ -1,14 +0,0 @@
// SK_USE→SK_DEF promotion. Same shape as pos_fn.ww but the imported
// module exports `def defmod: i32 = 0` instead of `fn defmod()`.
// Pass-1.5 N_DEF case (check.c L1709) promotes the SK_USE leaf to
// SK_DEF. Pre-fix it forgot use_alias=1, so `defmod.flag` resolution
// failed. Post-fix the build succeeds and exit code = flag.A = 42.
package usepromote;
import defmod;
fn main() i32 = {
let m: defmod.flag = defmod.flag.A;
return m: i32;
};

View File

@@ -1,17 +0,0 @@
// SK_USE→SK_FN promotion (the bug). fnmod exports `fn fnmod()` and
// `type flag = enum`. Pass-1 installs SK_TYPE("flag", mod="fnmod") and
// SK_USE("fnmod") (the `fn fnmod` isn't installed in pass-1; pass-1
// only handles N_USE + N_TYPEDECL). Pass-1.5 then sees the N_FNDECL
// "fnmod", finds the SK_USE local, and promotes in place to SK_FN.
// Pre-fix that promotion forgot `use_alias = 1`, so `fnmod.flag`
// resolution failed with "unknown type fnmod.flag". Post-fix the
// build succeeds and exit code = flag.A = 42.
package usepromote;
import fnmod;
fn main() i32 = {
let m: fnmod.flag = fnmod.flag.A;
return m: i32;
};

View File

@@ -1,16 +0,0 @@
// SK_USE→SK_TYPE regression pin. typmod exports `type typmod = ...`
// AND `type flag = enum`. Driver concatenation puts imported decls
// first, so pass-1 installs SK_TYPE("typmod") first; the primary's
// `use typmod` then hits the self-import branch (check.c L1660) and
// sets use_alias=1 on the SK_TYPE entry. We rely on use_alias=1 here
// so the dot-prefixed `typmod.flag` lookup resolves to mod="typmod"'s
// flag entry. Exit code = flag.A = 42 verifies end-to-end.
package usepromote;
import typmod;
fn main() i32 = {
let m: typmod.flag = typmod.flag.A;
return m: i32;
};

View File

@@ -1,14 +0,0 @@
// SK_USE→SK_VAR promotion. Imported module exports a top-level
// `let varmod: i32 = 0`. Pass-1.5 N_LET case (check.c L1736) promotes
// the SK_USE leaf to SK_VAR. Pre-fix it forgot use_alias=1, so
// `varmod.flag` resolution failed. Post-fix the build succeeds and
// exit code = flag.A = 42.
package usepromote;
import varmod;
fn main() i32 = {
let m: varmod.flag = varmod.flag.A;
return m: i32;
};

View File

@@ -1,19 +0,0 @@
// usepromote/typmod — exports a `type typmod` with the same leaf as
// the module name. Paired with pos_type.ww to pin the SK_USE→SK_TYPE
// promotion's use_alias=1 behaviour (cmd/wcc/check.c L1660 / L1677):
// the consumer's `typmod.flag` lookup must walk through the alias
// even though the leaf sym is SK_TYPE, not SK_USE.
//
// random.random in lib/ is the canonical real-world instance of this
// shape; this fixture replays it as a regression pin.
package typmod;
export type typmod = struct {
x: i32,
};
export type flag = enum i32 {
NONE = 0,
A = 42,
};

View File

@@ -1,14 +0,0 @@
// usepromote/varmod — exports a top-level `let varmod` with the same
// leaf as the module name. Paired with pos_var.ww to exercise the
// SK_USE→SK_VAR promotion at cmd/wcc/check.c L1736. Same shape as
// SK_DEF / SK_FN — without `use_alias = 1`, the consumer's
// `varmod.flag` lookup fails.
package varmod;
export let varmod: i32 = 0i32;
export type flag = enum i32 {
NONE = 0,
A = 42,
};