fix: allow lexical import shadowing
This commit is contained in:
@@ -1,28 +1,24 @@
|
||||
package rejects_test;
|
||||
|
||||
// Cstage-only compile-reject observers on the `ww` driver. Ports of
|
||||
// the retired native carriers test/wcc/708_param_shadow_mod.c,
|
||||
// 712_redecl.c and 961_opaque_guards.c; every assertion preserved.
|
||||
// Compile-reject observers plus the lexical import-shadowing acceptance
|
||||
// matrix. Ports of the retired native carriers
|
||||
// test/wcc/708_param_shadow_mod.c, 712_redecl.c and
|
||||
// 961_opaque_guards.c.
|
||||
//
|
||||
// ASYMMETRIC polarity, all three families: the reject lives only in
|
||||
// cstage check.c. Wwstage's check.ww is a single-pass resolve walk
|
||||
// with no per-block scoping (#11) and no #108(b) require_sized
|
||||
// construction/binding guards, so it ACCEPTS these programs — the
|
||||
// both-stage //ww:error fixture contract cannot carry them
|
||||
// (residual-carrier-audit.json, 708/712/961 entries). Each row
|
||||
// asserts the CSTAGE reject only; when wwstage gains the rule (#11
|
||||
// per-block scoping; the #108(b) guards), the rows graduate to
|
||||
// //ww:error fixtures and this observer shrinks. The paramshadow neg
|
||||
// rows additionally require the sibling-module fixture tree, so they
|
||||
// stay here until a single-file multi-package repro is validated.
|
||||
// The redeclaration and opaque-guard families below retain their historical
|
||||
// Cstage-only assertions. Import-name shadowing has the opposite polarity:
|
||||
// both stages accept a closer lexical value binding, while the package-name
|
||||
// object remains visible before that binding and after its scope ends. The
|
||||
// fixture tree is retained because the raw-source route needs a real sibling
|
||||
// package to exercise selector use and file-local import ownership.
|
||||
//
|
||||
// paramshadow (#19/#16) — value names and module names are disjoint:
|
||||
// a param/let/mlet/for-range/match-case binding named `shadowmod` in
|
||||
// a file importing module shadowmod is rejected at the decl site; the
|
||||
// renamed binding in a real `package main` builds+runs 42 (no over-trigger); a param named
|
||||
// like a module a SIBLING module imports does not trip (src_imports
|
||||
// filters by the binding's own module — build+run exit 2). The
|
||||
// carrier's neg_selfimp leg is DROPPED here:
|
||||
// paramshadow (#19/#16) — an import qualifier is a file-scope binding.
|
||||
// A param/let/mlet/for-range/match-case binding may shadow it in a closer
|
||||
// lexical scope. Each legacy neg_* fixture now genuinely uses
|
||||
// `shadowmod.say()` before or in the binding declaration and then returns a
|
||||
// value computed from the closer binding. The renamed control still runs 42;
|
||||
// a parameter named like a package imported only by a sibling package still
|
||||
// runs 2. The carrier's neg_selfimp leg remains dropped here:
|
||||
// test/wcc/data/r948_selfimport/case.ww owns the identical claim
|
||||
// (`//ww:error "self-import"`, both frontends).
|
||||
//
|
||||
@@ -67,51 +63,47 @@ fn fixdir() str = {
|
||||
return strings.concat(testenv.repo(), "/test/wcc/data/paramshadowmod");
|
||||
};
|
||||
|
||||
@test fn paramshadow_neg() void = {
|
||||
let tags: []str = ["param", "let", "mlet", "forrange_single",
|
||||
"forrange_tuple", "mcase"];
|
||||
fn paramshadow_case(src: str, label: str, want: i32) void = {
|
||||
let stages: []str = ["ww", "ww_ww"];
|
||||
let tags: []str = ["c", "ww"];
|
||||
let i: i32 = 0;
|
||||
for (i < tags.len) {
|
||||
for (i < stages.len) {
|
||||
let td: str = testenv.fresh();
|
||||
// cwd is the fixture dir so the driver's source-dir-first import
|
||||
// search resolves `import shadowmod;`; -o keeps the binary and
|
||||
// its sepwork out of the tracked tree.
|
||||
let av: []str = [testenv.driver("ww"), "build", "-o",
|
||||
strings.concat(td, "/out"),
|
||||
strings.concat("neg_", tags[i], ".ww")];
|
||||
if (runcode(fixdir(), td, strings.concat("neg_", tags[i]), av)
|
||||
== 0) {
|
||||
fail(strings.concat("neg_", tags[i]),
|
||||
"build unexpectedly succeeded -- shadow rule did not fire");
|
||||
let out: str = strings.concat(td, "/out");
|
||||
// cwd is the fixture dir so source-dir-first import search resolves
|
||||
// `import shadowmod;`; -o keeps every product outside the tree.
|
||||
let av: []str = [testenv.driver(stages[i]), "build", "-o", out, src];
|
||||
let stem: str = strings.concat(label, "_", tags[i]);
|
||||
if (runcode(fixdir(), td, strings.concat("build_", stem), av) != 0) {
|
||||
fail(stem, "build failed -- lexical import shadowing was rejected");
|
||||
};
|
||||
let rav: []str = [out];
|
||||
if (runcode(td, td, strings.concat("run_", stem), rav) != want) {
|
||||
fail(stem, "built binary exit != exact expected value");
|
||||
};
|
||||
testenv.clean(td);
|
||||
i += 1;
|
||||
};
|
||||
};
|
||||
|
||||
fn paramshadow_pos(src: str, label: str, want: i32, why: str) void = {
|
||||
let td: str = testenv.fresh();
|
||||
let out: str = strings.concat(td, "/out");
|
||||
let av: []str = [testenv.driver("ww"), "build", "-o", out, src];
|
||||
if (runcode(fixdir(), td, strings.concat("build_", label), av) != 0) {
|
||||
fail(label, why);
|
||||
@test fn paramshadow_lexical_bindings() void = {
|
||||
let tags: []str = ["param", "let", "mlet", "forrange_single",
|
||||
"forrange_tuple", "mcase"];
|
||||
let wants: []i32 = [44, 42, 45, 107, 45, 42];
|
||||
let i: i32 = 0;
|
||||
for (i < tags.len) {
|
||||
paramshadow_case(strings.concat("neg_", tags[i], ".ww"), tags[i],
|
||||
wants[i]);
|
||||
i += 1;
|
||||
};
|
||||
let rav: []str = [out];
|
||||
if (runcode(td, td, strings.concat("run_", label), rav) != want) {
|
||||
fail(label, "built binary exit != expected");
|
||||
};
|
||||
testenv.clean(td);
|
||||
};
|
||||
|
||||
@test fn paramshadow_pos_rename() void = {
|
||||
paramshadow_pos("pos_rename.ww", "pos_rename", 42,
|
||||
"build failed -- rule over-triggered after the rename");
|
||||
paramshadow_case("pos_rename.ww", "pos_rename", 42);
|
||||
};
|
||||
|
||||
@test fn paramshadow_pos_crossmod() void = {
|
||||
paramshadow_pos("pos_crossmod.ww", "pos_crossmod", 2, strings.concat(
|
||||
"build failed -- shadow rule over-triggered on a ",
|
||||
"cross-module param"));
|
||||
paramshadow_case("pos_crossmod.ww", "pos_crossmod", 2);
|
||||
};
|
||||
|
||||
fn rejectrows(family: str, labels: []str, srcs: []str) void = {
|
||||
|
||||
Reference in New Issue
Block a user