From 1ce3a4e9e08e105b4e1423eb0b02641d9589a5db Mon Sep 17 00:00:00 2001 From: agra Date: Tue, 9 Jun 2026 10:29:23 +0300 Subject: [PATCH 1/2] =?UTF-8?q?docs(fork-c/S0):=20setup=20contract=20?= =?UTF-8?q?=E2=80=94=20byte-baseline=20+=20commit-discipline,=20E6b=20disp?= =?UTF-8?q?osition=20+=20two-corpus=20partition,=20A=E2=80=93E6=20reuse/de?= =?UTF-8?q?lete=20ledger?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit S0 of the ratified Fork C plan (zero-legacy name-resolution redesign, S0→S6). Pure setup/documentation: NO production code change, NO behavior change. Single-author output byte-identical to wt-stdlib-base by construction. Deliverables under docs/fork-c/ (docs/, not current/, because current/ is gitignored and the contract must be committed): S0.1 — byte-baseline + commit-discipline: the committed examples/expected/* snapshots are the single-author byte-identity reference; the zero-diff repro is `zig build && zig build test && bash tests/run_examples.sh`. Resolver-target set explicitly excluded + listed. Commit-classification rule: mirror | consumer-cutover | deletion. S0.2 — E6b disposition + two-corpus partition: transitional E6b src NOT merged (grep-clean: no resolveRegistrationSigTypeInSource / sig_registration_mode / e6br_gate.test.zig on baseline). Harvested 0811–0829 trees + goldens (never the src), empirically partitioned by running each through the base compiler vs the E6b target: - baseline-green (mirror-equivalence): 0795–0798 (merged) + 0823, 0828 — given examples/expected/ markers, locked into the S0 baseline. - resolver-target (known-wrong old behavior): 0811–0822, 0824–0827, 0829 + the re-filed E6BR-5 nested-pattern regression — a listed xfail harness under tests/resolver-target/ (manifest + TARGET goldens, NO active marker), flips active+green at S3.9. 0811/0829 noted as old-selector-wrong on the E6b-unmerged base; E6BR-5 subsumed by the whole-AST resolver, NOT an E6b attempt-6. S0.3 — A–E6 reuse/delete ledger: every load-bearing A–E6 artifact mapped REUSED (Fork C home) or DELETED/TRANSITIONAL (S3/S6 phase); E6c/d/e dropped, F/H/I/K absorbed/superseded. Gate over the baseline-green corpus: zig build + zig build test (LSP corpus sweep 574 files, no crash) + bash tests/run_examples.sh (540 passed, 0 failed) all exit 0. --- docs/fork-c/README.md | 57 +++++++ ...0.1-byte-baseline-and-commit-discipline.md | 102 +++++++++++++ ...6b-disposition-and-two-corpus-partition.md | 143 ++++++++++++++++++ docs/fork-c/S0.3-reuse-delete-ledger.md | 72 +++++++++ ...1-modules-same-name-error-set-ambiguous.sx | 44 ++++++ .../a.sx | 4 + .../b.sx | 4 + ...12-modules-same-name-error-set-own-wins.sx | 28 ++++ .../dep.sx | 10 ++ ...les-same-name-error-set-lambda-own-wins.sx | 30 ++++ .../dep.sx | 11 ++ ...es-same-name-error-set-lambda-ambiguous.sx | 27 ++++ .../a.sx | 4 + .../b.sx | 4 + .../0815-route-all-new-surfaces-ambiguous.sx | 35 +++++ .../a.sx | 6 + .../b.sx | 4 + .../0816-route-all-new-surfaces-own-wins.sx | 29 ++++ .../dep.sx | 11 ++ ...lified-annotation-single-import-resolve.sx | 20 +++ .../dep.sx | 3 + ...8-modules-qualified-annotation-own-wins.sx | 21 +++ .../dep.sx | 3 + ...qualified-annotation-error-set-own-wins.sx | 19 +++ .../dep.sx | 4 + ...820-protocols-same-name-method-own-wins.sx | 37 +++++ .../dep.sx | 10 ++ ...21-protocols-same-name-method-ambiguous.sx | 18 +++ .../a.sx | 3 + .../b.sx | 3 + examples/0822-route-all-own-wins-surfaces.sx | 39 +++++ .../0822-route-all-own-wins-surfaces/dep.sx | 10 ++ ...823-route-all-own-wins-subform-wrappers.sx | 32 ++++ .../dep.sx | 10 ++ ...ocols-same-name-method-wrapped-own-wins.sx | 78 ++++++++++ .../dep.sx | 11 ++ ...cols-same-name-method-wrapped-ambiguous.sx | 23 +++ .../a.sx | 9 ++ .../b.sx | 8 + ...cols-param-impl-source-wrapped-own-wins.sx | 32 ++++ .../dep.sx | 11 ++ ...ols-param-impl-source-wrapped-ambiguous.sx | 27 ++++ .../a.sx | 9 ++ .../b.sx | 8 + ...otocols-param-impl-arg-wrapped-own-wins.sx | 31 ++++ .../dep.sx | 11 ++ ...-param-impl-mixed-pack-source-ambiguous.sx | 36 +++++ .../a.sx | 9 ++ .../b.sx | 8 + ...3-route-all-own-wins-subform-wrappers.exit | 1 + ...route-all-own-wins-subform-wrappers.stderr | 1 + ...route-all-own-wins-subform-wrappers.stdout | 1 + ...ocols-param-impl-arg-wrapped-own-wins.exit | 1 + ...ols-param-impl-arg-wrapped-own-wins.stderr | 1 + ...ols-param-impl-arg-wrapped-own-wins.stdout | 1 + tests/resolver-target/README.md | 49 ++++++ .../e6br5-nested-pack-source-ambiguous.sx | 48 ++++++ .../e6br5-nested-pack-source-ambiguous/a.sx | 4 + .../e6br5-nested-pack-source-ambiguous/b.sx | 3 + ...modules-same-name-error-set-ambiguous.exit | 1 + ...dules-same-name-error-set-ambiguous.stderr | 29 ++++ ...dules-same-name-error-set-ambiguous.stdout | 1 + ...-modules-same-name-error-set-own-wins.exit | 1 + ...odules-same-name-error-set-own-wins.stderr | 1 + ...odules-same-name-error-set-own-wins.stdout | 1 + ...s-same-name-error-set-lambda-own-wins.exit | 1 + ...same-name-error-set-lambda-own-wins.stderr | 0 ...same-name-error-set-lambda-own-wins.stdout | 2 + ...-same-name-error-set-lambda-ambiguous.exit | 1 + ...ame-name-error-set-lambda-ambiguous.stderr | 5 + ...ame-name-error-set-lambda-ambiguous.stdout | 0 ...0815-route-all-new-surfaces-ambiguous.exit | 1 + ...15-route-all-new-surfaces-ambiguous.stderr | 29 ++++ ...15-route-all-new-surfaces-ambiguous.stdout | 0 .../0816-route-all-new-surfaces-own-wins.exit | 1 + ...816-route-all-new-surfaces-own-wins.stderr | 0 ...816-route-all-new-surfaces-own-wins.stdout | 1 + ...fied-annotation-single-import-resolve.exit | 1 + ...ed-annotation-single-import-resolve.stderr | 1 + ...ed-annotation-single-import-resolve.stdout | 1 + ...modules-qualified-annotation-own-wins.exit | 1 + ...dules-qualified-annotation-own-wins.stderr | 1 + ...dules-qualified-annotation-own-wins.stdout | 1 + ...alified-annotation-error-set-own-wins.exit | 1 + ...ified-annotation-error-set-own-wins.stderr | 1 + ...ified-annotation-error-set-own-wins.stdout | 1 + ...0-protocols-same-name-method-own-wins.exit | 1 + ...protocols-same-name-method-own-wins.stderr | 1 + ...protocols-same-name-method-own-wins.stdout | 1 + ...-protocols-same-name-method-ambiguous.exit | 1 + ...rotocols-same-name-method-ambiguous.stderr | 5 + ...rotocols-same-name-method-ambiguous.stdout | 1 + .../0822-route-all-own-wins-surfaces.exit | 1 + .../0822-route-all-own-wins-surfaces.stderr | 1 + .../0822-route-all-own-wins-surfaces.stdout | 1 + ...ols-same-name-method-wrapped-own-wins.exit | 1 + ...s-same-name-method-wrapped-own-wins.stderr | 1 + ...s-same-name-method-wrapped-own-wins.stdout | 1 + ...ls-same-name-method-wrapped-ambiguous.exit | 1 + ...-same-name-method-wrapped-ambiguous.stderr | 5 + ...-same-name-method-wrapped-ambiguous.stdout | 1 + ...ls-param-impl-source-wrapped-own-wins.exit | 1 + ...-param-impl-source-wrapped-own-wins.stderr | 1 + ...-param-impl-source-wrapped-own-wins.stdout | 1 + ...s-param-impl-source-wrapped-ambiguous.exit | 1 + ...param-impl-source-wrapped-ambiguous.stderr | 5 + ...param-impl-source-wrapped-ambiguous.stdout | 1 + ...aram-impl-mixed-pack-source-ambiguous.exit | 1 + ...am-impl-mixed-pack-source-ambiguous.stderr | 5 + ...am-impl-mixed-pack-source-ambiguous.stdout | 1 + .../e6br5-nested-pack-source-ambiguous.exit | 1 + ...br5-nested-pack-source-ambiguous.target.md | 22 +++ tests/resolver-target/manifest.md | 70 +++++++++ tests/resolver-target/run_resolver_target.sh | 89 +++++++++++ 114 files changed, 1584 insertions(+) create mode 100644 docs/fork-c/README.md create mode 100644 docs/fork-c/S0.1-byte-baseline-and-commit-discipline.md create mode 100644 docs/fork-c/S0.2-e6b-disposition-and-two-corpus-partition.md create mode 100644 docs/fork-c/S0.3-reuse-delete-ledger.md create mode 100644 examples/0811-modules-same-name-error-set-ambiguous.sx create mode 100644 examples/0811-modules-same-name-error-set-ambiguous/a.sx create mode 100644 examples/0811-modules-same-name-error-set-ambiguous/b.sx create mode 100644 examples/0812-modules-same-name-error-set-own-wins.sx create mode 100644 examples/0812-modules-same-name-error-set-own-wins/dep.sx create mode 100644 examples/0813-modules-same-name-error-set-lambda-own-wins.sx create mode 100644 examples/0813-modules-same-name-error-set-lambda-own-wins/dep.sx create mode 100644 examples/0814-modules-same-name-error-set-lambda-ambiguous.sx create mode 100644 examples/0814-modules-same-name-error-set-lambda-ambiguous/a.sx create mode 100644 examples/0814-modules-same-name-error-set-lambda-ambiguous/b.sx create mode 100644 examples/0815-route-all-new-surfaces-ambiguous.sx create mode 100644 examples/0815-route-all-new-surfaces-ambiguous/a.sx create mode 100644 examples/0815-route-all-new-surfaces-ambiguous/b.sx create mode 100644 examples/0816-route-all-new-surfaces-own-wins.sx create mode 100644 examples/0816-route-all-new-surfaces-own-wins/dep.sx create mode 100644 examples/0817-modules-qualified-annotation-single-import-resolve.sx create mode 100644 examples/0817-modules-qualified-annotation-single-import-resolve/dep.sx create mode 100644 examples/0818-modules-qualified-annotation-own-wins.sx create mode 100644 examples/0818-modules-qualified-annotation-own-wins/dep.sx create mode 100644 examples/0819-modules-qualified-annotation-error-set-own-wins.sx create mode 100644 examples/0819-modules-qualified-annotation-error-set-own-wins/dep.sx create mode 100644 examples/0820-protocols-same-name-method-own-wins.sx create mode 100644 examples/0820-protocols-same-name-method-own-wins/dep.sx create mode 100644 examples/0821-protocols-same-name-method-ambiguous.sx create mode 100644 examples/0821-protocols-same-name-method-ambiguous/a.sx create mode 100644 examples/0821-protocols-same-name-method-ambiguous/b.sx create mode 100644 examples/0822-route-all-own-wins-surfaces.sx create mode 100644 examples/0822-route-all-own-wins-surfaces/dep.sx create mode 100644 examples/0823-route-all-own-wins-subform-wrappers.sx create mode 100644 examples/0823-route-all-own-wins-subform-wrappers/dep.sx create mode 100644 examples/0824-protocols-same-name-method-wrapped-own-wins.sx create mode 100644 examples/0824-protocols-same-name-method-wrapped-own-wins/dep.sx create mode 100644 examples/0825-protocols-same-name-method-wrapped-ambiguous.sx create mode 100644 examples/0825-protocols-same-name-method-wrapped-ambiguous/a.sx create mode 100644 examples/0825-protocols-same-name-method-wrapped-ambiguous/b.sx create mode 100644 examples/0826-protocols-param-impl-source-wrapped-own-wins.sx create mode 100644 examples/0826-protocols-param-impl-source-wrapped-own-wins/dep.sx create mode 100644 examples/0827-protocols-param-impl-source-wrapped-ambiguous.sx create mode 100644 examples/0827-protocols-param-impl-source-wrapped-ambiguous/a.sx create mode 100644 examples/0827-protocols-param-impl-source-wrapped-ambiguous/b.sx create mode 100644 examples/0828-protocols-param-impl-arg-wrapped-own-wins.sx create mode 100644 examples/0828-protocols-param-impl-arg-wrapped-own-wins/dep.sx create mode 100644 examples/0829-packs-param-impl-mixed-pack-source-ambiguous.sx create mode 100644 examples/0829-packs-param-impl-mixed-pack-source-ambiguous/a.sx create mode 100644 examples/0829-packs-param-impl-mixed-pack-source-ambiguous/b.sx create mode 100644 examples/expected/0823-route-all-own-wins-subform-wrappers.exit create mode 100644 examples/expected/0823-route-all-own-wins-subform-wrappers.stderr create mode 100644 examples/expected/0823-route-all-own-wins-subform-wrappers.stdout create mode 100644 examples/expected/0828-protocols-param-impl-arg-wrapped-own-wins.exit create mode 100644 examples/expected/0828-protocols-param-impl-arg-wrapped-own-wins.stderr create mode 100644 examples/expected/0828-protocols-param-impl-arg-wrapped-own-wins.stdout create mode 100644 tests/resolver-target/README.md create mode 100644 tests/resolver-target/cases/e6br5-nested-pack-source-ambiguous.sx create mode 100644 tests/resolver-target/cases/e6br5-nested-pack-source-ambiguous/a.sx create mode 100644 tests/resolver-target/cases/e6br5-nested-pack-source-ambiguous/b.sx create mode 100644 tests/resolver-target/expected/0811-modules-same-name-error-set-ambiguous.exit create mode 100644 tests/resolver-target/expected/0811-modules-same-name-error-set-ambiguous.stderr create mode 100644 tests/resolver-target/expected/0811-modules-same-name-error-set-ambiguous.stdout create mode 100644 tests/resolver-target/expected/0812-modules-same-name-error-set-own-wins.exit create mode 100644 tests/resolver-target/expected/0812-modules-same-name-error-set-own-wins.stderr create mode 100644 tests/resolver-target/expected/0812-modules-same-name-error-set-own-wins.stdout create mode 100644 tests/resolver-target/expected/0813-modules-same-name-error-set-lambda-own-wins.exit create mode 100644 tests/resolver-target/expected/0813-modules-same-name-error-set-lambda-own-wins.stderr create mode 100644 tests/resolver-target/expected/0813-modules-same-name-error-set-lambda-own-wins.stdout create mode 100644 tests/resolver-target/expected/0814-modules-same-name-error-set-lambda-ambiguous.exit create mode 100644 tests/resolver-target/expected/0814-modules-same-name-error-set-lambda-ambiguous.stderr create mode 100644 tests/resolver-target/expected/0814-modules-same-name-error-set-lambda-ambiguous.stdout create mode 100644 tests/resolver-target/expected/0815-route-all-new-surfaces-ambiguous.exit create mode 100644 tests/resolver-target/expected/0815-route-all-new-surfaces-ambiguous.stderr create mode 100644 tests/resolver-target/expected/0815-route-all-new-surfaces-ambiguous.stdout create mode 100644 tests/resolver-target/expected/0816-route-all-new-surfaces-own-wins.exit create mode 100644 tests/resolver-target/expected/0816-route-all-new-surfaces-own-wins.stderr create mode 100644 tests/resolver-target/expected/0816-route-all-new-surfaces-own-wins.stdout create mode 100644 tests/resolver-target/expected/0817-modules-qualified-annotation-single-import-resolve.exit create mode 100644 tests/resolver-target/expected/0817-modules-qualified-annotation-single-import-resolve.stderr create mode 100644 tests/resolver-target/expected/0817-modules-qualified-annotation-single-import-resolve.stdout create mode 100644 tests/resolver-target/expected/0818-modules-qualified-annotation-own-wins.exit create mode 100644 tests/resolver-target/expected/0818-modules-qualified-annotation-own-wins.stderr create mode 100644 tests/resolver-target/expected/0818-modules-qualified-annotation-own-wins.stdout create mode 100644 tests/resolver-target/expected/0819-modules-qualified-annotation-error-set-own-wins.exit create mode 100644 tests/resolver-target/expected/0819-modules-qualified-annotation-error-set-own-wins.stderr create mode 100644 tests/resolver-target/expected/0819-modules-qualified-annotation-error-set-own-wins.stdout create mode 100644 tests/resolver-target/expected/0820-protocols-same-name-method-own-wins.exit create mode 100644 tests/resolver-target/expected/0820-protocols-same-name-method-own-wins.stderr create mode 100644 tests/resolver-target/expected/0820-protocols-same-name-method-own-wins.stdout create mode 100644 tests/resolver-target/expected/0821-protocols-same-name-method-ambiguous.exit create mode 100644 tests/resolver-target/expected/0821-protocols-same-name-method-ambiguous.stderr create mode 100644 tests/resolver-target/expected/0821-protocols-same-name-method-ambiguous.stdout create mode 100644 tests/resolver-target/expected/0822-route-all-own-wins-surfaces.exit create mode 100644 tests/resolver-target/expected/0822-route-all-own-wins-surfaces.stderr create mode 100644 tests/resolver-target/expected/0822-route-all-own-wins-surfaces.stdout create mode 100644 tests/resolver-target/expected/0824-protocols-same-name-method-wrapped-own-wins.exit create mode 100644 tests/resolver-target/expected/0824-protocols-same-name-method-wrapped-own-wins.stderr create mode 100644 tests/resolver-target/expected/0824-protocols-same-name-method-wrapped-own-wins.stdout create mode 100644 tests/resolver-target/expected/0825-protocols-same-name-method-wrapped-ambiguous.exit create mode 100644 tests/resolver-target/expected/0825-protocols-same-name-method-wrapped-ambiguous.stderr create mode 100644 tests/resolver-target/expected/0825-protocols-same-name-method-wrapped-ambiguous.stdout create mode 100644 tests/resolver-target/expected/0826-protocols-param-impl-source-wrapped-own-wins.exit create mode 100644 tests/resolver-target/expected/0826-protocols-param-impl-source-wrapped-own-wins.stderr create mode 100644 tests/resolver-target/expected/0826-protocols-param-impl-source-wrapped-own-wins.stdout create mode 100644 tests/resolver-target/expected/0827-protocols-param-impl-source-wrapped-ambiguous.exit create mode 100644 tests/resolver-target/expected/0827-protocols-param-impl-source-wrapped-ambiguous.stderr create mode 100644 tests/resolver-target/expected/0827-protocols-param-impl-source-wrapped-ambiguous.stdout create mode 100644 tests/resolver-target/expected/0829-packs-param-impl-mixed-pack-source-ambiguous.exit create mode 100644 tests/resolver-target/expected/0829-packs-param-impl-mixed-pack-source-ambiguous.stderr create mode 100644 tests/resolver-target/expected/0829-packs-param-impl-mixed-pack-source-ambiguous.stdout create mode 100644 tests/resolver-target/expected/e6br5-nested-pack-source-ambiguous.exit create mode 100644 tests/resolver-target/expected/e6br5-nested-pack-source-ambiguous.target.md create mode 100644 tests/resolver-target/manifest.md create mode 100755 tests/resolver-target/run_resolver_target.sh diff --git a/docs/fork-c/README.md b/docs/fork-c/README.md new file mode 100644 index 0000000..ff403a3 --- /dev/null +++ b/docs/fork-c/README.md @@ -0,0 +1,57 @@ +# Fork C — setup contract (S0) + +Zero-legacy name-resolution redesign (S0→S6), replacing E6c–K. This directory is the +**committed S0 contract** the remaining 26 steps (S1→S6) execute against. It is pure +setup/documentation — **S0 introduces no production code change and no behavior +change**; single-author output is byte-identical to `wt-stdlib-base` by construction. + +**Authority:** `runs/stdlib/design/fork-c-deepdive/reconciled.md` (§5 by-construction +audit, §6 staged roadmap + deletion lists, §8 fold-in) and +`runs/stdlib/design/fork-c-plan/planspec-r3.json` (the S0.1/S0.2/S0.3 intent + +acceptance, Adi-confirmed — they govern). + +## Doc-area decision + +Deliverables live under **`docs/fork-c/`**, NOT `current/fork-c/`. Reason: `current/` +is **gitignored** in this repo (`.gitignore` → `current/`), so a new `current/fork-c/` +tree would not be committed; the S0 contract must be committed. `docs/` is tracked. + +## Baseline + +- **Base:** `wt-stdlib-base @ 1f755284d98c6e8ebba953045c06e35d8cbe6278` (A–E6a merged). +- **E6b:** `flow/stdlib/E6b @ af737b0` — PAUSED, **unmerged**, all transitional, destined + for S3/S6 deletion. Its semantics goldens are harvested; its src is never merged. +- **This branch:** `flow/stdlib/S0` (branched from the base; S0 HEAD == base). + +## Contents + +| file | sub-step | what | +|---|---|---| +| `S0.1-byte-baseline-and-commit-discipline.md` | S0.1 | the byte-identity reference + the zero-diff reproduction command + resolver-target exclusion + the `mirror \| consumer-cutover \| deletion` commit-classification discipline | +| `S0.2-e6b-disposition-and-two-corpus-partition.md` | S0.2 | E6b src not merged (grep-clean) + the harvested corpus partitioned baseline-green vs resolver-target + 0811/0829 placement + the E6BR-5 re-file + the mirror/flip statement | +| `S0.3-reuse-delete-ledger.md` | S0.3 | every load-bearing A–E6 artifact mapped REUSED (Fork C home) or DELETED/TRANSITIONAL (S3/S6 phase); E6c/d/e dropped, F/H/I/K absorbed/superseded | +| `../../tests/resolver-target/` | S0.2 | the listed resolver-target harness: `manifest.md` (18 cases), `expected/` TARGET goldens, the E6BR-5 reproducer under `cases/`, and `run_resolver_target.sh` (xfail runner — NOT part of the gate) | + +## The two-corpus law (the one thing the next 26 steps must never conflate) + +1. **BASELINE-GREEN / mirror-equivalence corpus** — tests where the old selector is + already correct today (A–E6a merged + the 6 harvested baseline-green cases + FFI + 12xx–14xx + the LSP smoke). Stays **green and single-author byte-identical at every + step S0→S6**, and is the S2 assert-only Debug mirror's **only** oracle. +2. **RESOLVER-TARGET corpus** — harvested goldens that encode **known-wrong old + behavior** (silent resolve / under-diagnose / wrong author / own-wins-fails) + the + E6BR-5 regression. Held **inactive/xfail** (listed, never silently dropped) from S0 + through S3.8, then **flips to active + green at S3.9** against its TARGET output — + never against the old selectors (a wrong oracle for it on the E6b-unmerged base). + +## Gate (this branch) + +```sh +export PATH="$HOME/.zvm/bin:$PATH" +zig build && zig build test && bash tests/run_examples.sh # exit 0 over the baseline-green corpus +``` + +Since S0 changes no production code, single-author output is byte-identical by +construction. The resolver-target xfail diagnostic +(`bash tests/resolver-target/run_resolver_target.sh`) is separate and not part of the +gate. diff --git a/docs/fork-c/S0.1-byte-baseline-and-commit-discipline.md b/docs/fork-c/S0.1-byte-baseline-and-commit-discipline.md new file mode 100644 index 0000000..0fd50dc --- /dev/null +++ b/docs/fork-c/S0.1-byte-baseline-and-commit-discipline.md @@ -0,0 +1,102 @@ +# S0.1 — Byte-baseline (baseline-green only) + commit-classification discipline + +Authority: `runs/stdlib/design/fork-c-plan/planspec-r3.json` (S0.1) + +`runs/stdlib/design/fork-c-deepdive/reconciled.md` (§6 green-lock). Base: +`wt-stdlib-base @ 1f755284d98c6e8ebba953045c06e35d8cbe6278` (A–E6a merged). This is +documentation only — no production code change, no behavior change. + +## 1. The byte-identity reference + +The single-author byte-identity reference that **every later Fork C commit is checked +against** is the committed `examples/expected/*` snapshot set on the baseline commit +above. We do **not** copy every file into this doc; the snapshots ARE the reference, +and the reproduction is a documented zero-diff command (§2). Single-author byte +identity is held structurally by `nominal_id == 0 ≡ structural intern` +(`src/ir/types.zig` `internNominal`), which S1–S2 keep additive and S3 preserves +through ordinal-0 materialization. + +The baseline-green corpus that the reference covers: + +| segment | what | how it is exercised | count @ S0 | +|---|---|---|---| +| baseline-green examples | every `examples/.sx` with an active `examples/expected/.exit` marker (incl. the 6 harvested baseline-green cases `0795–0798`, `0823`, `0828`) | `bash tests/run_examples.sh` | **540** active markers | +| FFI corpus | `examples/12xx–14xx` (96 entry trees; 95 with active markers; ~418 files incl. module/`.c`/`.h`/`.m` companions) | same runner (markers) | 95 active markers | +| LSP completion/hover smoke | LSP unit tests under `zig build test` — `analyzeDocument` flat/namespaced import + the `lsp corpus sweep: every examples/*.sx analyzes without panicking` sweep + definition/references/inlayHint | `zig build test` | — | + +> Count note: `reconciled.md §1` cites "116 files in `examples/12xx–14xx`". The live +> tree has **96 entry `.sx` trees** (95 with active markers); the "116" is a stale +> historical figure. What is load-bearing is the invariant — *all FFI 12xx–14xx +> examples stay byte-stable* — which `run_examples.sh` enforces via their markers, +> not the exact historical count. + +## 2. The zero-diff reproduction method + +`tests/run_examples.sh` runs `sx run ` for every `.sx` that has an +`expected/.exit` marker, normalizes stdout/stderr identically for expected and +actual (address hashing → `0xADDR`; absolute `…/examples|issues/` paths → repo-relative), +and diffs exit + stdout + stderr (+ optional `.ir`). A **zero-diff** run is the +byte-identity check: + +```sh +export PATH="$HOME/.zvm/bin:$PATH" +zig build # build the compiler under test +zig build test # unit tests incl. the LSP completion/hover smoke + corpus sweep +bash tests/run_examples.sh # must print " passed, 0 failed, 0 skipped*, 0 timed out" +``` + +Any later commit whose single-author output drifts shows up as a `FAIL` with a printed +diff. `tests/run_examples.sh --update` regenerates the snapshots — it must **only** be +run intentionally when a commit's classification (§3) authorizes an output change, and +the diff reviewed. (* `skipped` counts `.exit` markers whose `.sx` is absent; it is 0 +on a clean tree.) + +## 3. Resolver-target EXCLUSION (recorded, not silently absent) + +The **resolver-target set** is **excluded from both** the byte-baseline AND the active +`run_examples.sh` set, because it encodes known-wrong old behavior (silent resolve / +exit 0 / under-diagnosis / wrong author) — a stale old-selector verdict must never +enter the baseline as if it were correct. The exclusion is explicit and **listed**, not +absent: + +- the 17 harvested `08xx` resolver-target cases (`0811, 0812, 0813, 0814, 0815, 0816, + 0817, 0818, 0819, 0820, 0821, 0822, 0824, 0825, 0826, 0827, 0829`) + the re-filed + **E6BR-5** regression; +- enumerated in `tests/resolver-target/manifest.md`, with TARGET goldens in + `tests/resolver-target/expected/`, held inactive (no `examples/expected/` marker) and + asserted currently-failing by `tests/resolver-target/run_resolver_target.sh`; +- full disposition in `S0.2-e6b-disposition-and-two-corpus-partition.md`. + +They flip to active + green at **S3.9** and only then join the baseline. + +## 4. Commit-classification discipline + +Every future Fork C migration commit (S1→S6) is tagged with exactly one of three +classes, stated in the commit subject/body so a reviewer knows what byte-effect to +expect: + +| tag | meaning | expected byte-effect on the baseline-green corpus | +|---|---|---| +| **`mirror`** | builds new facts / a new resolver path **in parallel**, while lowering still consumes the old path (S1–S2; the assert-only Debug mirror) | **zero** — single-author output byte-identical; provably zero byte-risk | +| **`consumer-cutover`** | switches a consumer from the old path to the resolved facts (S3 materializer / calls / consts / protocol-registration / `#using`; S5 LSP) | **zero on baseline-green** — byte-identical by ordinal-0 materialization + payload-preserving facts; the only commits that may change resolver-target (the S3.9 flip is a cutover) | +| **`deletion`** | removes a now-dead artifact (old name selectors, `*_by_source` mirrors, `type_bridge`, `findByName`, the grep gate, the S2 mirror) | **zero** — the deleted code had no live readers after its cutover; a surviving reader fails to compile | + +Rules: +- A commit is exactly one class; a cutover that also deletes its now-dead source is + still a `consumer-cutover` if the delete is the same atomic cutover (e.g. S3.10 + removes the last old selector **and** the S2 mirror in the cutover commit). +- `mirror` and `deletion` commits MUST be byte-zero on baseline-green; if a `mirror` + commit changes a byte, it was not actually parallel — stop. +- Only `consumer-cutover` may legitimately change output, and only the **resolver-target** + corpus (never baseline-green) — that is the S3.9 flip. + +## 5. Acceptance (S0.1) — self-check + +- ✅ Byte-baseline of all baseline-green examples + FFI 12xx–14xx + LSP smoke captured + and reproducible via the documented zero-diff command (§1–§2); the reference is the + committed `examples/expected/*` at the baseline commit, re-checked by a zero-`FAIL` + `run_examples.sh`. +- ✅ Resolver-target set explicitly excluded from the byte-baseline AND the active + `run_examples.sh` set, and recorded/listed (§3) — not silently absent. +- ✅ The `mirror | consumer-cutover | deletion` classification rule is written (§4). +- ✅ `zig build && zig build test && bash tests/run_examples.sh` green over the + baseline-green corpus; no behavior change (S0 adds no production code). diff --git a/docs/fork-c/S0.2-e6b-disposition-and-two-corpus-partition.md b/docs/fork-c/S0.2-e6b-disposition-and-two-corpus-partition.md new file mode 100644 index 0000000..665e6c7 --- /dev/null +++ b/docs/fork-c/S0.2-e6b-disposition-and-two-corpus-partition.md @@ -0,0 +1,143 @@ +# S0.2 — E6b disposition, the two-corpus partition, and the E6BR-5 re-file + +Authority: `runs/stdlib/design/fork-c-plan/planspec-r3.json` (S0.2) + +`runs/stdlib/design/fork-c-deepdive/reconciled.md` (§3 one-resolver-two-timings, §6 +roadmap, §8 fold-in). Base: `wt-stdlib-base @ 1f75528` (A–E6a merged). E6b: +`flow/stdlib/E6b @ af737b0` (PAUSED, unmerged). This is documentation only — no +production code change. + +## 1. E6b transitional src is NOT merged + +The transitional E6b source on `flow/stdlib/E6b @ af737b0` is **not** brought onto the +Fork C baseline. All of it is destined for **S3/S6 deletion** under Fork C: + +- the **route-all engine** + the type-reference **choke-point** + (`resolveRegistrationSigTypeInSource` / `sig_registration_mode`) — `lower.zig`, + `protocols.zig`; +- the executable **grep gate** `src/ir/e6br_gate.test.zig` (+ its `ir.zig` import); +- the supporting `calls` / `lower` / `protocols` / `type_bridge` / `type_resolver` / + `types` changes. + +Grounded reasons it is transitional, not load-bearing: the whole-AST resolver walks +**every** reference position, so it closes the protocol/param-impl signature surface +the choke-point policed (reconciled §3/§4 T4); and the grep gate is unnecessary once +the leaf it polices no longer exists (reconciled §5). A–E6a stays merged purely as +**proof-of-semantics + per-decl nominal infra** (`internNominal` / `nominal_id==0` +byte-identity rule / `RawDeclRef` facts → the `DeclId` seed) — see +`S0.3-reuse-delete-ledger.md`. + +**Grep-clean (verified on this branch, S0 == base):** + +| check | base / S0 | E6b | +|---|---|---| +| `resolveRegistrationSigTypeInSource` in `src/` | **absent** | present (`lower.zig`) | +| `sig_registration_mode` in `src/` | **absent** | present (`lower.zig`, `protocols.zig`) | +| `src/ir/e6br_gate.test.zig` | **absent** | present | +| `walkConcreteSigArgs` in `src/ir/lower.zig` | **absent** | present (the E6BR-5 site) | + +Because S0 introduces **no production code change**, these remain absent on the Fork C +baseline by construction. + +## 2. Harvested resolver-acceptance corpus + the two-corpus partition + +Harvested by copying the example **trees + their expected goldens ONLY** (never the +transitional src): + +- `examples/0811–0829` (19 trees) from `flow/stdlib/E6b @ af737b0` — error-set / + protocol / param-impl / route-all same-name ambiguity & own-wins surfaces; +- `examples/0795–0798` (already merged on the base) — the enum/union ambiguous/own-wins + pair. + +The corpus is **partitioned by empirically running each harvested case through the +current base compiler and comparing its output to the E6b TARGET golden** (the exact +method: build, then `sx run examples/.sx`, normalize with the +`run_examples.sh` rules, diff against the E6b golden): + +### (a) BASELINE-GREEN — the mirror-equivalence corpus + +Harvested cases whose **old selector is already CORRECT on `wt-stdlib-base` today** +(actual == E6b target, byte-for-byte). They get standard +`examples/expected/.{exit,stdout,stderr}` markers, are run by +`tests/run_examples.sh`, and are **locked into the S0 baseline**. This set is the S2 +assert-only Debug mirror's **ONLY** oracle. + +| case | exit | proof it is already-correct on base | +|---|---|---| +| `0795-modules-same-name-enum-ambiguous` | 1 | already merged + green (E6a) | +| `0796-modules-same-name-enum-own-wins` | 0 | already merged + green | +| `0797-modules-same-name-union-ambiguous` | 1 | already merged + green | +| `0798-modules-same-name-union-own-wins` | 0 | already merged + green | +| `0823-route-all-own-wins-subform-wrappers` | 0 | base output == E6b target (`opt=1 arr=2 sl=3 dep=99`) | +| `0828-protocols-param-impl-arg-wrapped-own-wins` | 0 | base output == E6b target (`tag=7 dep=9`) | + +### (b) RESOLVER-TARGET — known-wrong old behavior + +Harvested cases (and the re-filed E6BR-5) where the **old selector is wrong on the +E6b-unmerged base** (silently resolves last-wins / under-diagnoses / picks the wrong +author / fails own-author resolution). They are held in a **documented, listed, +separate harness** at `tests/resolver-target/` with **NO active `examples/expected/` +marker** (so `run_examples.sh` does not run them), but **WITH** their TARGET output +recorded and an **enumerated manifest** (`tests/resolver-target/manifest.md`, 18 +cases). The sibling xfail runner `run_resolver_target.sh` runs each and asserts it +currently FAILS — so the set is **never silently dropped**. It flips to active + green +at **S3.9**. + +The 17 harvested `08xx` resolver-target cases: `0811, 0812, 0813, 0814, 0815, 0816, +0817, 0818, 0819, 0820, 0821, 0822, 0824, 0825, 0826, 0827, 0829`. Per-case class, +base-now behavior, and target are in the manifest. + +**0811 and 0829 placement (required one-liners):** + +- **0811 → resolver-target.** *Old selector is wrong here on the E6b-unmerged base:* + the five bare error-set forms (`size_of` / annotation / type-as-value / match-arm / + `!IoErr` channel) each silently resolved one global last-wins `IoErr` via the + `type_bridge.resolveInlineErrorSet` `findByName` short-circuit and the program exited + 0; the target is five loud "type 'IoErr' is ambiguous" diagnostics + exit 1. +- **0829 → resolver-target.** *Old selector is wrong here on the E6b-unmerged base:* + the concrete `*Box` prefix of the mixed pack-closure source fell to the no-author + `type_bridge.resolveTemplateSignatureType` wrapper (global last-wins) and registered + silently (exit 0); the target is a loud `Box` ambiguity + exit 1. + +## 3. E6BR-5 re-filed into resolver-target + +**E6BR-5** (the open nested-pattern ambiguity hole that paused E6b) is re-filed into +the resolver-target set as a regression — **explicitly NOT an E6b attempt-6.** + +- *What it is:* `walkConcreteSigArgs` (E6b `lower.zig:14686`) **skips** any direct arg + that has an unbound part instead of **recursing** into it, so a nested concrete leaf + (`*Box` inside `Closure(Closure(*Box, ..$inner) -> $IR, ..$args) -> $R`) is never + ambiguity-checked; the impl compiled rc=0. On `wt-stdlib-base` (E6BR-4 unmerged) both + the direct and nested concrete leaf silently resolve via the pre-E6BR-4 no-author + wrapper path, so the reproducer exits 0 with no diagnostic. +- *Subsumption (one line):* the whole-AST resolver walks **every** reference position — + including nested parameterized-pattern leaves — so the nested `Box` is + ambiguity-checked by construction and the "skip a nested arg" hole cannot exist. +- *Form:* an authored reproducer lives at + `tests/resolver-target/cases/e6br5-nested-pack-source-ambiguous.sx` (self-contained; + verified to exit 0 silently on the base — the fail-before). Its target is a **spec** + (exit 1 + `Box` ambiguous), with exact bytes produced by the resolver at S3.9 + (`expected/e6br5-…target.md`). + +## 4. The mirror / flip statement (locked) + +- **S2's mirror asserts `resolver == old-selector` over the BASELINE-GREEN corpus + ONLY.** It never asserts new == old over the resolver-target corpus (the old selector + is a wrong oracle there), and it never selects for lowering (assert-only, Debug-only, + deleted in the S3.10 commit that completes the cutover). +- **S3.9 flips the resolver-target corpus to active + green**, validated against each + case's TARGET output (not against the old selectors). After the flip, the goldens + move from `tests/resolver-target/expected/` to `examples/expected/`, the harness goes + empty, and these cases stay green through S6. + +## 5. Acceptance (S0.2) — self-check + +- ✅ Written disposition: E6b transitional src not merged (§1, grep-clean table). +- ✅ Harvested corpus recorded + partitioned: baseline-green (markers, locked, + mirror-equivalence) vs resolver-target (enumerated manifest, separate listed harness, + no active marker, TARGET goldens present, currently xfail, never dropped) — §2. +- ✅ 0811 and 0829 each in resolver-target with the "old selector is wrong on the + E6b-unmerged base" one-liner — §2. +- ✅ E6BR-5 re-filed into resolver-target with the subsumption one-liner, NOT an E6b + attempt-6 — §3. +- ✅ Mirror/flip statement recorded — §4. +- ✅ No transitional E6b src on the Fork C baseline (grep-clean) — §1. diff --git a/docs/fork-c/S0.3-reuse-delete-ledger.md b/docs/fork-c/S0.3-reuse-delete-ledger.md new file mode 100644 index 0000000..babae16 --- /dev/null +++ b/docs/fork-c/S0.3-reuse-delete-ledger.md @@ -0,0 +1,72 @@ +# S0.3 — A–E6 reuse / delete ledger + +Authority: `runs/stdlib/design/fork-c-deepdive/reconciled.md` (§6 roadmap + per-phase +deletion lists, §8 fold-in) + `runs/stdlib/design/migration.md` ("what happens to +already-merged A–E6 work") + `planspec-r3.json` (S0.3). Base: `wt-stdlib-base @ +1f75528`. Symbol/file refs are grounded against the base tree. This is documentation +only — no code change. + +This ledger is the contract the later phases execute against: every load-bearing A–E6 +artifact is mapped to **REUSED** (with its Fork C home) or **DELETED/TRANSITIONAL** +(with the S3/S6 phase that removes it). A–E6a stays merged; the transitional E6b src +is never merged (see `S0.2-…`). + +## A. REUSED — A–E6 work that becomes Fork C infrastructure + +| A–E6 artifact (base location) | Fork C home | phase | +|---|---|---| +| **Phase A import facts** — `RawDeclRef` / `RawAuthor` / `ModuleDecls` / `NamespaceEdges` (`src/imports.zig`), built in `resolveImports` (`core.zig`) | **seed `DeclId` construction** — `DeclTable` keys every `RawDeclRef` into a stable `DeclId` (source + name + AST ptr + `DeclKind`); namespace members get ids | S1 | +| **Phase B visibility** — `collectVisibleAuthors` / `collectNamespaceAuthors` (`src/ir/resolver.zig`), "the one graph iterator" over `flat_import_graph`/`namespace_edges` | **resolver internals** — become the resolver's visibility walk, with own-wins / single-flat-visible / ≥2-ambiguous **verdicts above them**, producing `ResolvedRef` | S2 | +| **Phase C callable selection** | **`ResolvedRef.function` / `.type_function`** keyed by `DeclId` | S2 (select) → S3 (consume) | +| **Phase D nominal identity** — `internNominal` / `updatePreservingKey` + the **`nominal_id == 0 ≡ structural intern` ordinal-0 byte-identity rule** (`src/ir/types.zig`, `lower.zig`) | **reused inside materialization** — `materializeType(ResolvedTypeNode)` interns in old scan order with ordinal 0 for non-colliding decls ⇒ byte-identical single-author output | S3 (+ green-lock every phase) | +| **E-series selection rules** — own-wins / not-visible / ambiguity / direct-flat (the E1–E6a behaviors) | **resolver behavior + regression tests** (the baseline-green corpus is the mirror oracle) | S2 behavior; regressions locked S0 | +| **CP rule** — body-author == layout-author | **keyed by `InstantiationId{template_decl, resolved_args}`** in the fact store | S4 | +| **E6BR routed-signature cases** (the E6BR-1…4 behavioral cells) | **resolver-signature regressions** — the resolver walks every signature reference position; cases live in the resolver-target corpus, flip at S3.9 | S3.9 | +| **FFI `foreign_class_map` consumers + 116-class corpus** | parallel `DeclId`s land at S1 (map still the consumer); foreign classes keyed by `DeclId` at S4; runtime names stay **payload strings on facts** | S1 → S4 | + +## B. DELETED / TRANSITIONAL — removed in S3/S6 + +| artifact (base location) | why it goes | removal phase | +|---|---|---| +| **Stateless `type_bridge` leaves** — `resolveAstType` / `resolveTypeName` / `resolveInline{Enum,Union,Struct,ErrorSet}` / `resolveParameterizedType` / `resolveErrorType` (`src/ir/type_bridge.zig`); the whole `type_bridge.zig` (E6b renamed the entry leaf `resolveAstTypeNoAuthorSelection`) | no-author `findByName` leaves — replaced by `materializeType(ResolvedTypeNode)` which cannot take a name | selectors S3; file S6 | +| **`TypeResolver.resolveName` / `resolveNamed`** (`src/ir/type_resolver.zig`) (E6b: `resolveNamedGlobalNoAuthorSelection`) | name→global resolution; superseded by the resolver | S3 (name-selection) / S6 (`resolveName*`) | +| **Old name selectors** — `selectNominalLeaf`, `resolveNominalLeaf`, `moduleTypeAuthor`, `namedRefTid`, `flatTypeAuthorCount`, `nameAuthoredAsTypeAnywhere`, `selectModuleConst` (+ const-source pins), `selectGenericStructHead`, `headTypeGate`, `headFnLeak`, `flatFnAuthor*`, the name-selection in `resolveTypeCallWithBindings`/`resolveParameterizedWithBindings` (`src/ir/lower.zig`) | the duality leak — replaced by `ResolvedRef`/`ResolvedTypeNode` consumed in lowering | S3 | +| **`*_by_source` mirrors + source pins** (`src/ir/program_index.zig`) + their writers + the `lower.zig` unified writers | dual-write mirrors of the global maps — superseded by the `DeclId`-keyed fact store | S4 | +| **`type_decl_tids`** (`src/ir/types.zig`, `lower.zig`) | name→TypeId mirror — superseded by `DeclId` facts | S4/S6 | +| **`ShadowTypeDecl` / shadow-slot reservation helpers** (`src/ir/lower.zig`) + lower-side nominal selectors | shadow reservation is a name-keyed pre-pass artifact — subsumed by `DeclId` pre-pass | S3/S4 | +| **`TypeTable.findByName` / `findUniqueByName`** (`src/ir/types.zig`) | the global name table — deleted **last** (after the ~15 category-(b) stdlib lookups are re-homed to resolved-once `DeclId`s, per the §6 critical ordering constraint) | S6 | +| **the type-reference choke-point + route-all engine** (`resolveRegistrationSigTypeInSource` / `sig_registration_mode`) | **transitional E6b src — never merged**; destined for deletion under Fork C | S3/S6 (already off-baseline) | +| **the grep gate `e6br_gate.test.zig`** (+ its `ir.zig` import) | **transitional E6b src — never merged**; unnecessary once the leaf it polices is gone | S6 (already off-baseline) | +| **the S2→S3 assert-only Debug mirror** | a test oracle, not a code path — must be deleted in the **same S3.10 commit** that removes the last old selector | S3.10 | + +## C. Dropped / absorbed / superseded plan items + +- **E6c / E6d / E6e** (protocol / foreign / type-fn per-kind identity): **DROPPED as + steps.** They become resolver behavior + regression tests — a whole-AST resolver + walks every reference position (annotation, `size_of`, dispatch head, `Self`, + vtable), closing the protocol surface the per-kind patch structurally could not + (reconciled §4 T4). +- **F** (namespace resolver + 0104): **ABSORBED** into S2 as resolver internals + (`namespace_edges` → `ResolvedRef.namespace` / member). +- **H** (constructor heads): **ABSORBED** into S3 `materializeType` over resolved + generic/protocol/type-fn heads. +- **I** (protocol + foreign selection, loud-on-≥2): **ABSORBED** into S2 selection + S4 + `DeclId` facts. +- **K** (delete dead readers): **SUPERSEDED** by the S4 `DeclId`-keyed fact store + the + S6 deletions — "just delete the maps" is upgraded to "replace with `DeclId` facts." +- **the per-kind taxonomy**: **REIFIED** as the `ResolvedRef` union itself; the + exhaustive `switch` is the live taxonomy check (the grep gate is gone because the leaf + it policed no longer exists). + +## D. Acceptance (S0.3) — self-check + +- ✅ Every load-bearing A–E6 artifact mapped to REUSED (with Fork C home) or + DELETED/TRANSITIONAL (with the S3/S6 phase that removes it) — tables A & B; covers + Phase A import facts → `DeclId` seed; B `collectVisibleAuthors` → resolver internals; + C callable selection → `ResolvedRef.function/type_function`; D `internNominal` + + ordinal-0 byte-identity → materialization; E-series rules → resolver behavior + + regressions; CP → `InstantiationId`; the stateless `type_bridge`/`type_resolver` + leaves, `*_by_source` mirrors + source pins, `type_decl_tids`/shadow helpers + + lower-side selectors, the choke-point + route-all + grep gate → S3/S6 deletion. +- ✅ States E6c/d/e dropped and F/H/I/K absorbed/superseded — table C. +- ✅ No code change. diff --git a/examples/0811-modules-same-name-error-set-ambiguous.sx b/examples/0811-modules-same-name-error-set-ambiguous.sx new file mode 100644 index 0000000..5a5ad32 --- /dev/null +++ b/examples/0811-modules-same-name-error-set-ambiguous.sx @@ -0,0 +1,44 @@ +// E6b — per-decl nominal identity for ERROR-SET decls. A bare ERROR-SET reference +// is non-transitive AND ambiguity-checked at every site, exactly like the struct +// leaf (0755) and the enum/union leaves (0795/0797). `main` flat-imports two +// modules that each author a same-name `IoErr` error set and authors none itself, +// so EACH of the following bare ERROR-SET forms is a genuine collision the source +// cannot disambiguate — and each must emit the LOUD "type 'IoErr' is ambiguous" +// diagnostic and poison the result, NEVER silently pick a global `findByName` +// last-wins author: +// +// - reflection / type-arg slot `size_of(IoErr)` +// - typed error-value annotation `e : IoErr = error.Disk` +// - type-as-value `t : Type = IoErr` +// - type-category match arm `case IoErr:` +// - `!Named` failable channel `-> !IoErr` (E6b audited use surface) +// +// Fail-before (pre-E6b): the stateless `type_bridge.resolveInlineErrorSet` +// `findByName` short-circuit interned ONE global last-wins `IoErr`, so every bare +// form silently resolved to it; the `!IoErr` channel resolved through +// `resolveErrorType -> resolveTypeName` straight to that global slot. The program +// exited 0. + +#import "modules/std.sx"; +#import "0811-modules-same-name-error-set-ambiguous/a.sx"; +#import "0811-modules-same-name-error-set-ambiguous/b.sx"; + +describe :: ($T: Type) -> s32 { + r := if T == { + case IoErr: 1; + else: 0; + } + r +} + +fail_io :: () -> !IoErr { + raise error.Disk; +} + +main :: () -> s32 { + sz := size_of(IoErr); + e : IoErr = error.Disk; + t : Type = IoErr; + k := describe(s64); + 0 +} diff --git a/examples/0811-modules-same-name-error-set-ambiguous/a.sx b/examples/0811-modules-same-name-error-set-ambiguous/a.sx new file mode 100644 index 0000000..7076e93 --- /dev/null +++ b/examples/0811-modules-same-name-error-set-ambiguous/a.sx @@ -0,0 +1,4 @@ +// One of two flat-imported authors of a same-name `IoErr` error set. With both +// modules flat-visible from a file that authors none itself, every bare reference +// to the name is genuinely ambiguous. +IoErr :: error { Disk, Net } diff --git a/examples/0811-modules-same-name-error-set-ambiguous/b.sx b/examples/0811-modules-same-name-error-set-ambiguous/b.sx new file mode 100644 index 0000000..dc75d5c --- /dev/null +++ b/examples/0811-modules-same-name-error-set-ambiguous/b.sx @@ -0,0 +1,4 @@ +// The second flat-imported author of a same-name `IoErr` error set. A separate +// nominal identity from a.sx's `IoErr`, so each bare reference is a real collision +// the importing source cannot disambiguate. +IoErr :: error { Disk, Net } diff --git a/examples/0812-modules-same-name-error-set-own-wins.sx b/examples/0812-modules-same-name-error-set-own-wins.sx new file mode 100644 index 0000000..65bb834 --- /dev/null +++ b/examples/0812-modules-same-name-error-set-own-wins.sx @@ -0,0 +1,28 @@ +// E6b — own-wins-over-flat for ERROR-SET per-decl nominal identity. `main` +// flat-imports `dep.sx` (which authors `IoErr { Net }`) AND authors its OWN +// `IoErr { Disk }`. A bare `IoErr` reference in `main` resolves to `main`'s OWN +// author, not the flat-imported one (the querying source's author wins outright — +// no ambiguity), so `e : IoErr = error.Disk` binds `main`'s set (whose `Disk` tag +// dep's `IoErr` lacks) while `dep_err()` returns dep's DISTINCT `IoErr`. +// +// Fail-before (pre-E6b): the stateless `type_bridge.resolveInlineErrorSet` +// `findByName` short-circuit interned ONE global last-wins `IoErr`, so `main`'s +// `IoErr` and dep's `IoErr` collapsed to a single nominal. Whichever author won +// the global slot, the OTHER module's tag is not in it — so exactly one of +// `error.Disk` (main) / `error.Net` (dep, in `dep_err`'s body) fails its +// membership check and the program exits 1. +// +// Pass-after: distinct nominal TypeIds — `error.Disk` validates against main's +// `{ Disk }` and `error.Net` against dep's `{ Net }`, both pass, exit 0. + +#import "modules/std.sx"; +#import "0812-modules-same-name-error-set-own-wins/dep.sx"; + +IoErr :: error { Disk } + +main :: () -> s32 { + e : IoErr = error.Disk; + d := dep_err(); + print("own={} dep={}\n", e, d); + 0 +} diff --git a/examples/0812-modules-same-name-error-set-own-wins/dep.sx b/examples/0812-modules-same-name-error-set-own-wins/dep.sx new file mode 100644 index 0000000..2b9b65a --- /dev/null +++ b/examples/0812-modules-same-name-error-set-own-wins/dep.sx @@ -0,0 +1,10 @@ +// A flat-imported module authors its OWN `IoErr { Net }`. The importing file +// (`main`) ALSO authors an `IoErr` — its own author must win there (own-wins), +// while this module's `IoErr` stays a DISTINCT nominal type used by `dep_err`. +// The tag sets are disjoint, so a cross-binding to the wrong `IoErr` is a hard +// compile error (`Net` is not in main's `{ Disk }`, and vice-versa). +IoErr :: error { Net } + +dep_err :: () -> IoErr { + return error.Net; +} diff --git a/examples/0813-modules-same-name-error-set-lambda-own-wins.sx b/examples/0813-modules-same-name-error-set-lambda-own-wins.sx new file mode 100644 index 0000000..088ed55 --- /dev/null +++ b/examples/0813-modules-same-name-error-set-lambda-own-wins.sx @@ -0,0 +1,30 @@ +// E6b — own-wins for an ERROR-SET name in a CLOSURE-LITERAL return annotation +// (`closure(() -> !IoErr { … })`). `main` flat-imports `dep.sx` (which authors +// `IoErr { Net }`) AND authors its OWN `IoErr { Disk }`. The closure literal's +// `-> !IoErr` channel must resolve to `main`'s OWN author (own-wins), so the +// `raise error.Disk` inside the closure body validates against main's `{ Disk }`; +// meanwhile `dep_err` keeps dep's DISTINCT `IoErr { Net }`. +// +// Fail-before (attempt-1): `lowerLambda` resolved the explicit `lam.return_type` +// through the STATELESS `type_bridge.resolveAstType`, so the closure's `!IoErr` +// channel bound the global last-wins author (dep's `{ Net }`); `error.Disk` was +// then "not in error set 'IoErr'" and the program exited 1. +// +// Pass-after: the lambda return annotation routes through the source-aware +// `resolveTypeWithBindings` (same path as the regular function-return channel), +// main's own `IoErr` wins, `error.Disk` validates, exit 0. + +#import "modules/std.sx"; +#import "0813-modules-same-name-error-set-lambda-own-wins/dep.sx"; + +IoErr :: error { Disk } + +main :: () -> s32 { + fail_own := closure(() -> !IoErr { raise error.Disk; }); + fail_own() catch e { + if e == error.Disk { print("own=Disk\n"); } + }; + d := dep_err(); + print("dep={}\n", d); + return 0; +} diff --git a/examples/0813-modules-same-name-error-set-lambda-own-wins/dep.sx b/examples/0813-modules-same-name-error-set-lambda-own-wins/dep.sx new file mode 100644 index 0000000..05f0330 --- /dev/null +++ b/examples/0813-modules-same-name-error-set-lambda-own-wins/dep.sx @@ -0,0 +1,11 @@ +// A flat-imported module authors its OWN `IoErr { Net }`. The importing file +// (`main`) ALSO authors an `IoErr` AND uses it as a closure-literal return +// annotation (`-> !IoErr`); main's author must win THERE too (own-wins), +// while this module's `IoErr` stays a DISTINCT nominal used by `dep_err`. +// The tag sets are disjoint, so binding the wrong `IoErr` is a hard compile +// error (`Net` is not in main's `{ Disk }`, and vice-versa). +IoErr :: error { Net } + +dep_err :: () -> IoErr { + return error.Net; +} diff --git a/examples/0814-modules-same-name-error-set-lambda-ambiguous.sx b/examples/0814-modules-same-name-error-set-lambda-ambiguous.sx new file mode 100644 index 0000000..a9157d7 --- /dev/null +++ b/examples/0814-modules-same-name-error-set-lambda-ambiguous.sx @@ -0,0 +1,27 @@ +// E6b — ambiguity guard for an ERROR-SET name in a CLOSURE-LITERAL return +// annotation (`closure(() -> !IoErr { … })`). `main` flat-imports two modules +// that each author a same-name `IoErr` and authors none itself, so the closure +// literal's `-> !IoErr` channel is a genuine collision the source cannot +// disambiguate. It must emit the LOUD "type 'IoErr' is ambiguous" diagnostic and +// poison the channel — NEVER silently pick a global `findByName` last-wins author. +// +// NOTE on fail-before/pass-after: unlike the OWN-WINS sibling (0813), this +// AMBIGUITY case does NOT fail-before on attempt-1's binary. The pre-lowering +// closure-shape pass (`convergeClosureShapeSets` → `recordClosureShape`) resolves +// every closure literal's return annotation through the source-aware +// `resolveType` (`resolveTypeWithBindings`), which attempt-1 already routed +// error-set authors through — so the ambiguity is detected there and reported +// regardless of `lowerLambda`. (For a `-> !Named` set that pass bails early after +// resolving, so the OWN-WINS membership in 0813 still flowed through the stateless +// `lowerLambda` path that attempt-2 fixes.) This example is the standing GUARD +// that the lambda `-> !Named` ambiguity stays reported across both source-aware +// sites; 0813 is the one that exercises attempt-2's `lowerLambda` channel fix. + +#import "modules/std.sx"; +#import "0814-modules-same-name-error-set-lambda-ambiguous/a.sx"; +#import "0814-modules-same-name-error-set-lambda-ambiguous/b.sx"; + +main :: () -> s32 { + fail_io := closure(() -> !IoErr { return; }); + return 0; +} diff --git a/examples/0814-modules-same-name-error-set-lambda-ambiguous/a.sx b/examples/0814-modules-same-name-error-set-lambda-ambiguous/a.sx new file mode 100644 index 0000000..b8f327b --- /dev/null +++ b/examples/0814-modules-same-name-error-set-lambda-ambiguous/a.sx @@ -0,0 +1,4 @@ +// One of two flat-imported authors of a same-name `IoErr` error set. With both +// modules flat-visible from a file that authors none itself, the `-> !IoErr` +// closure-literal channel is genuinely ambiguous. +IoErr :: error { Disk, Net } diff --git a/examples/0814-modules-same-name-error-set-lambda-ambiguous/b.sx b/examples/0814-modules-same-name-error-set-lambda-ambiguous/b.sx new file mode 100644 index 0000000..b21b506 --- /dev/null +++ b/examples/0814-modules-same-name-error-set-lambda-ambiguous/b.sx @@ -0,0 +1,4 @@ +// The second flat-imported author of a same-name `IoErr` error set. A separate +// nominal identity from a.sx's `IoErr`, so the `-> !IoErr` closure-literal channel +// is a real collision the importing source cannot disambiguate. +IoErr :: error { Disk, Net } diff --git a/examples/0815-route-all-new-surfaces-ambiguous.sx b/examples/0815-route-all-new-surfaces-ambiguous.sx new file mode 100644 index 0000000..078e69b --- /dev/null +++ b/examples/0815-route-all-new-surfaces-ambiguous.sx @@ -0,0 +1,35 @@ +// E6b-R — route-all: the NEW use surfaces a same-name type reaches now funnel +// through the source-aware engine, so an ambiguous bare name poisons LOUDLY at +// each of them instead of silently picking a global `findByName` last-wins author. +// `main` flat-imports two same-name `Box` struct authors and authors none itself, +// so every bare `Box` below is a genuine collision. `Box` is the kind-agnostic +// canary (the bare-leaf forms — annotations / size_of / match / `!Named` — are +// already locked per kind by 0755/0795/0797/0811); these cells exercise the +// surfaces E6b-R added: +// +// - wrapper-alias element `BoxPtr :: *Box` (engine wrapper aliasing) +// - union body-builder child `WrapU :: union { b: Box }` (C1, registerUnionDecl) +// - enum body-builder child `WrapE :: enum { V: Box }` (C1, registerEnumDecl) +// - tuple-literal element `size_of((Box, s32))` (O1/K5) +// - inline-anonymous body child `x : union { b: Box }` (inline-anon engine arm) +// +// Fail-before (pre-E6b-R): each of these resolved `Box` through the stateless +// `type_bridge.resolveAstType` global last-wins, silently binding one arbitrary +// author with NO diagnostic. Pass-after: every surface emits the LOUD +// "type 'Box' is ambiguous" and the build exits 1. + +#import "modules/std.sx"; +#import "0815-route-all-new-surfaces-ambiguous/a.sx"; +#import "0815-route-all-new-surfaces-ambiguous/b.sx"; + +BoxPtr :: *Box; + +WrapU :: union { b: Box; n: s32; } + +WrapE :: enum { V: Box; } + +main :: () -> s32 { + sz := size_of((Box, s32)); + x : union { b: Box; n: s32 } = ---; + 0 +} diff --git a/examples/0815-route-all-new-surfaces-ambiguous/a.sx b/examples/0815-route-all-new-surfaces-ambiguous/a.sx new file mode 100644 index 0000000..0942b88 --- /dev/null +++ b/examples/0815-route-all-new-surfaces-ambiguous/a.sx @@ -0,0 +1,6 @@ +// One of two flat-imported authors of a same-name `Box` struct. With both modules +// flat-visible from a file that authors none itself, every bare reference to the +// name is genuinely ambiguous — at EVERY use surface, including the ones E6b-R +// newly routed through the source-aware engine (wrapper aliases, tuple-literal +// elements, enum/union body-builder child types, inline-anonymous types). +Box :: struct { x: s32; } diff --git a/examples/0815-route-all-new-surfaces-ambiguous/b.sx b/examples/0815-route-all-new-surfaces-ambiguous/b.sx new file mode 100644 index 0000000..2c8e08f --- /dev/null +++ b/examples/0815-route-all-new-surfaces-ambiguous/b.sx @@ -0,0 +1,4 @@ +// The second flat-imported author of a same-name `Box` struct. A separate nominal +// identity from a.sx's `Box`, so each bare reference is a real collision the +// importing source cannot disambiguate. +Box :: struct { x: s32; } diff --git a/examples/0816-route-all-new-surfaces-own-wins.sx b/examples/0816-route-all-new-surfaces-own-wins.sx new file mode 100644 index 0000000..4d0bf7b --- /dev/null +++ b/examples/0816-route-all-new-surfaces-own-wins.sx @@ -0,0 +1,29 @@ +// E6b-R — own-wins-over-flat at the NEW use surfaces route-all funnels through the +// source-aware engine. `main` flat-imports `dep.sx` (which authors `Box { a }`) AND +// authors its OWN `Box { m }`; the field sets are DISJOINT, so the observable is +// field access: `.m` typechecks only against main's `Box`, `.a` only against dep's. +// +// `WrapU` is authored only by `main`, so its union field type `Box` resolves at +// registration (`registerUnionDecl` → the source-aware body builder, E6b-R C1) to +// MAIN's `Box`. Accessing `w.b.m` therefore typechecks, while `dep_box` uses dep's +// DISTINCT `Box { a }`. +// +// Fail-before (pre-E6b-R): `registerUnionDecl` built the union body through the +// stateless `type_bridge.buildUnionInfo`, whose `findByName` short-circuit bound +// the global last-wins `Box`. If dep's `Box { a }` won the slot, `WrapU.b` was +// dep's `Box` and `w.b.m` was a hard "field not found" — silently wrong nominal. +// Pass-after: the union field is main's own `Box`, `w.b.m` resolves, exit 0. + +#import "modules/std.sx"; +#import "0816-route-all-new-surfaces-own-wins/dep.sx"; + +Box :: struct { m: s32; } + +WrapU :: union { b: Box; n: s32; } + +main :: () -> s32 { + w : WrapU = ---; + w.b.m = 5; + print("own={} dep={}\n", w.b.m, dep_box()); + 0 +} diff --git a/examples/0816-route-all-new-surfaces-own-wins/dep.sx b/examples/0816-route-all-new-surfaces-own-wins/dep.sx new file mode 100644 index 0000000..d8d3227 --- /dev/null +++ b/examples/0816-route-all-new-surfaces-own-wins/dep.sx @@ -0,0 +1,11 @@ +// A flat-imported module authors its OWN `Box { a }`. The importing file (`main`) +// ALSO authors a `Box { m }` — its own author must win there (own-wins), while this +// module's `Box` stays a DISTINCT nominal type used by `dep_box`. The field sets +// are disjoint, so a cross-binding to the wrong `Box` is a hard compile error. +Box :: struct { a: s32; } + +dep_box :: () -> s32 { + b : Box = ---; + b.a = 9; + return b.a; +} diff --git a/examples/0817-modules-qualified-annotation-single-import-resolve.sx b/examples/0817-modules-qualified-annotation-single-import-resolve.sx new file mode 100644 index 0000000..9c328e6 --- /dev/null +++ b/examples/0817-modules-qualified-annotation-single-import-resolve.sx @@ -0,0 +1,20 @@ +// E6BR-1 — a plain qualified `ns.Type` type ANNOTATION resolves to the namespace +// target module's OWN member type. `a.Box` parses as a single dotted `type_expr`, +// so it reaches the bare nominal-leaf path; before E6BR-1 that path had no +// dot-qualified split and fabricated an empty-struct stub literally named +// "a.Box", so `x.a` failed with `field 'a' not found on type 'a.Box'` — even with +// this SINGLE namespace import and NO same-name flat author. +// +// Pass-after: `resolveNominalLeaf` dot-splits the name and routes it through the +// namespace-author selector (`qualifiedNamedTypeTid`), binding `a`'s real +// `Box { a }`, so `x.a` resolves, exit 0. + +#import "modules/std.sx"; +a :: #import "0817-modules-qualified-annotation-single-import-resolve/dep.sx"; + +main :: () -> s32 { + x : a.Box = ---; + x.a = 4; + print("x.a={}\n", x.a); + 0 +} diff --git a/examples/0817-modules-qualified-annotation-single-import-resolve/dep.sx b/examples/0817-modules-qualified-annotation-single-import-resolve/dep.sx new file mode 100644 index 0000000..796fe26 --- /dev/null +++ b/examples/0817-modules-qualified-annotation-single-import-resolve/dep.sx @@ -0,0 +1,3 @@ +// The namespace target module authors its OWN `Box { a }`. A qualified `a.Box` +// type annotation in the importer must bind THIS type, not a stub named "a.Box". +Box :: struct { a: s32; } diff --git a/examples/0818-modules-qualified-annotation-own-wins.sx b/examples/0818-modules-qualified-annotation-own-wins.sx new file mode 100644 index 0000000..fb9c332 --- /dev/null +++ b/examples/0818-modules-qualified-annotation-own-wins.sx @@ -0,0 +1,21 @@ +// G1 (struct) — a qualified `a.Box` annotation selects the NAMESPACE target's OWN +// `Box`, distinct from `main`'s same-name bare author. `main` authors its OWN +// `Box { m }` AND namespace-imports `a` (which authors `Box { a }`). A bare `Box` +// binds main's own author (own-wins); the qualified `a.Box` binds `a`'s DISTINCT +// nominal even though main has a same-name bare author. The field sets are +// disjoint, so a cross-binding (`q.m` / `own.a`) is a hard compile error — the +// example compiling and running proves the two `Box`es are distinct nominals. + +#import "modules/std.sx"; +a :: #import "0818-modules-qualified-annotation-own-wins/dep.sx"; + +Box :: struct { m: s32; } + +main :: () -> s32 { + own : Box = ---; + own.m = 5; + q : a.Box = ---; + q.a = 9; + print("own.m={} q.a={}\n", own.m, q.a); + 0 +} diff --git a/examples/0818-modules-qualified-annotation-own-wins/dep.sx b/examples/0818-modules-qualified-annotation-own-wins/dep.sx new file mode 100644 index 0000000..15fb3a2 --- /dev/null +++ b/examples/0818-modules-qualified-annotation-own-wins/dep.sx @@ -0,0 +1,3 @@ +// Namespace target authoring its OWN `Box { a }`. The importer (`main`) also +// authors a same-name bare `Box { m }`; the qualified `a.Box` must bind THIS one. +Box :: struct { a: s32; } diff --git a/examples/0819-modules-qualified-annotation-error-set-own-wins.sx b/examples/0819-modules-qualified-annotation-error-set-own-wins.sx new file mode 100644 index 0000000..a3e320a --- /dev/null +++ b/examples/0819-modules-qualified-annotation-error-set-own-wins.sx @@ -0,0 +1,19 @@ +// G1 (non-struct kind = error-set) — a qualified `a.IoErr` annotation selects the +// namespace target's OWN error-set per-decl nominal, distinct from `main`'s +// same-name `IoErr`. `main` authors `IoErr { Disk }` and namespace-imports `a` +// (`IoErr { Net }`). The bare `IoErr` binds main's own (so `error.Disk` is valid); +// the qualified `a.IoErr` binds `a`'s (so `error.Net` is valid). The tag sets are +// disjoint, so a cross-binding (`a.IoErr = error.Disk`) is a hard membership error +// — distinct error-set nominals reached through the qualified annotation surface. + +#import "modules/std.sx"; +a :: #import "0819-modules-qualified-annotation-error-set-own-wins/dep.sx"; + +IoErr :: error { Disk } + +main :: () -> s32 { + e : IoErr = error.Disk; + q : a.IoErr = error.Net; + print("own={} q={}\n", e, q); + 0 +} diff --git a/examples/0819-modules-qualified-annotation-error-set-own-wins/dep.sx b/examples/0819-modules-qualified-annotation-error-set-own-wins/dep.sx new file mode 100644 index 0000000..948afcd --- /dev/null +++ b/examples/0819-modules-qualified-annotation-error-set-own-wins/dep.sx @@ -0,0 +1,4 @@ +// Namespace target authoring its OWN error-set `IoErr { Net }`. The importer +// (`main`) also authors a same-name `IoErr { Disk }`; the qualified `a.IoErr` must +// bind THIS one (whose `Net` tag main's `IoErr` lacks). +IoErr :: error { Net } diff --git a/examples/0820-protocols-same-name-method-own-wins.sx b/examples/0820-protocols-same-name-method-own-wins.sx new file mode 100644 index 0000000..4d869e5 --- /dev/null +++ b/examples/0820-protocols-same-name-method-own-wins.sx @@ -0,0 +1,37 @@ +// E6BR-2 / G8 (own-wins) — a protocol method-signature CONCRETE named return type +// resolves SOURCE-AWARE, pinned to the protocol's defining module. `main` +// flat-imports `dep.sx` (`Box { a }`) AND authors its OWN `Box { m }`; the +// protocol `Provider` returns `Box` and the impl builds main's `Box`. Dispatching +// `p.get()` must type the result as MAIN's `Box`, so `b.m` resolves. +// +// Fail-before (pre-E6BR-2): the method signature went through the no-author +// `type_bridge.resolveTemplateSignatureType` wrapper (global last-wins), so the +// return typed as dep's `Box { a }` and `b.m` was `field 'm' not found on type +// 'Box'`. Pass-after: source-aware → main's `Box`, exit 0. + +#import "modules/std.sx"; +#import "0820-protocols-same-name-method-own-wins/dep.sx"; + +Box :: struct { m: s32; } + +Provider :: protocol { + get :: () -> Box; +} + +Holder :: struct { val: s32 = 7; } + +impl Provider for Holder { + get :: (self: *Holder) -> Box { + b : Box = ---; + b.m = self.val; + b + } +} + +main :: () -> s32 { + h : Holder = .{}; + p : Provider = xx @h; + b := p.get(); + print("m={} dep={}\n", b.m, dep_box()); + 0 +} diff --git a/examples/0820-protocols-same-name-method-own-wins/dep.sx b/examples/0820-protocols-same-name-method-own-wins/dep.sx new file mode 100644 index 0000000..789dbfe --- /dev/null +++ b/examples/0820-protocols-same-name-method-own-wins/dep.sx @@ -0,0 +1,10 @@ +// A flat-imported module authors its OWN `Box { a }`, a DISTINCT nominal from +// main's same-name `Box { m }`. The protocol method-signature return must NOT bind +// this one — the disjoint field sets make a wrong binding a hard compile error. +Box :: struct { a: s32; } + +dep_box :: () -> s32 { + b : Box = ---; + b.a = 9; + return b.a; +} diff --git a/examples/0821-protocols-same-name-method-ambiguous.sx b/examples/0821-protocols-same-name-method-ambiguous.sx new file mode 100644 index 0000000..a336eb3 --- /dev/null +++ b/examples/0821-protocols-same-name-method-ambiguous.sx @@ -0,0 +1,18 @@ +// G8 (ambiguous half) — because a protocol method-signature concrete return type +// is now resolved SOURCE-AWARE, a genuinely ambiguous same-name type poisons +// LOUDLY at the protocol declaration instead of silently picking a global +// `findByName` last-wins author. `main` flat-imports two `Box` authors and +// declares none itself, so the `Provider.get` return `Box` is an unresolvable +// collision and the build exits 1. + +#import "modules/std.sx"; +#import "0821-protocols-same-name-method-ambiguous/a.sx"; +#import "0821-protocols-same-name-method-ambiguous/b.sx"; + +Provider :: protocol { + get :: () -> Box; +} + +main :: () -> s32 { + 0 +} diff --git a/examples/0821-protocols-same-name-method-ambiguous/a.sx b/examples/0821-protocols-same-name-method-ambiguous/a.sx new file mode 100644 index 0000000..1f94711 --- /dev/null +++ b/examples/0821-protocols-same-name-method-ambiguous/a.sx @@ -0,0 +1,3 @@ +// One of two flat-imported same-name `Box` authors — the bare `Box` in the +// protocol return is a genuine collision the source cannot disambiguate. +Box :: struct { a: s32; } diff --git a/examples/0821-protocols-same-name-method-ambiguous/b.sx b/examples/0821-protocols-same-name-method-ambiguous/b.sx new file mode 100644 index 0000000..76d2e55 --- /dev/null +++ b/examples/0821-protocols-same-name-method-ambiguous/b.sx @@ -0,0 +1,3 @@ +// The second flat-imported same-name `Box` author. Two distinct flat authors and +// no own author → the protocol return `Box` is ambiguous. +Box :: struct { b: s32; } diff --git a/examples/0822-route-all-own-wins-surfaces.sx b/examples/0822-route-all-own-wins-surfaces.sx new file mode 100644 index 0000000..136c8f2 --- /dev/null +++ b/examples/0822-route-all-own-wins-surfaces.sx @@ -0,0 +1,39 @@ +// G3 — own-wins HALVES for the route-all surfaces 0815 covered only on the +// ambiguous half. `main` authors its OWN `Box { m }` and flat-imports `dep.sx` +// (`Box { a }`); each surface below must bind main's OWN `Box`, observed by a +// `.m` access (disjoint field sets → a wrong-author binding is a hard compile +// error). Complements 0816 (which covered only the union body-builder child): +// - pointer wrapper-alias element `BoxPtr :: *Box` +// - tuple element `(Box, s32)` +// - enum body-builder child `WrapE :: enum { V: Box }` +// - inline-anonymous union child `x : union { b: Box }` + +#import "modules/std.sx"; +#import "0822-route-all-own-wins-surfaces/dep.sx"; + +Box :: struct { m: s32; } +BoxPtr :: *Box; +WrapE :: enum { V: Box; } + +main :: () -> s32 { + own : Box = ---; + own.m = 10; + + // *Named wrapper-alias element own-wins + bp : BoxPtr = @own; + + // tuple element own-wins + t : (Box, s32) = ---; + t.0.m = 12; + + // enum body-builder child own-wins (payload must be main's `Box`) + we : WrapE = .V(own); + ev := we.V.m; + + // inline-anonymous union child own-wins + x : union { b: Box; n: s32 } = ---; + x.b.m = 13; + + print("bp={} t={} ev={} x={} dep={}\n", bp.m, t.0.m, ev, x.b.m, dep_box()); + 0 +} diff --git a/examples/0822-route-all-own-wins-surfaces/dep.sx b/examples/0822-route-all-own-wins-surfaces/dep.sx new file mode 100644 index 0000000..9c72d84 --- /dev/null +++ b/examples/0822-route-all-own-wins-surfaces/dep.sx @@ -0,0 +1,10 @@ +// A flat-imported module authoring its OWN `Box { a }`, a DISTINCT nominal from +// main's `Box { m }`. The four route-all surfaces in `main` must each bind main's +// own `Box`; the disjoint field sets make a wrong binding a hard compile error. +Box :: struct { a: s32; } + +dep_box :: () -> s32 { + b : Box = ---; + b.a = 99; + return b.a; +} diff --git a/examples/0823-route-all-own-wins-subform-wrappers.sx b/examples/0823-route-all-own-wins-subform-wrappers.sx new file mode 100644 index 0000000..e96b992 --- /dev/null +++ b/examples/0823-route-all-own-wins-subform-wrappers.sx @@ -0,0 +1,32 @@ +// G4 — own-wins halves for the §D row-2 SUB-FORM wrappers (`?Named`, `[]Named`, +// `[N]Named`). Each wraps a named element type whose resolution recurses through +// the SAME source-aware `resolveCompound` element path that `*Named` uses (0822), +// so they share one routing mechanism rather than a per-form path. `main` authors +// its OWN `Box { m }` and flat-imports `dep.sx` (`Box { a }`); each wrapped +// element must bind main's own `Box`, observed by a `.m` access (disjoint field +// sets → a wrong-author binding is a hard compile error). + +#import "modules/std.sx"; +#import "0823-route-all-own-wins-subform-wrappers/dep.sx"; + +Box :: struct { m: s32; } + +main :: () -> s32 { + seed : Box = ---; + seed.m = 1; + + // ?Named element own-wins + opt : ?Box = seed; + o := opt!; + + // [N]Named element own-wins + arr : [2]Box = ---; + arr[0].m = 2; + arr[1].m = 3; + + // []Named element own-wins + sl : []Box = arr[0..2]; + + print("opt={} arr={} sl={} dep={}\n", o.m, arr[0].m, sl[1].m, dep_box()); + 0 +} diff --git a/examples/0823-route-all-own-wins-subform-wrappers/dep.sx b/examples/0823-route-all-own-wins-subform-wrappers/dep.sx new file mode 100644 index 0000000..aa16e78 --- /dev/null +++ b/examples/0823-route-all-own-wins-subform-wrappers/dep.sx @@ -0,0 +1,10 @@ +// A flat-imported module authoring its OWN `Box { a }`, a DISTINCT nominal from +// main's `Box { m }`. The `?Box` / `[N]Box` / `[]Box` element wrappers in `main` +// must each bind main's own `Box`; disjoint field sets make a wrong binding fail. +Box :: struct { a: s32; } + +dep_box :: () -> s32 { + b : Box = ---; + b.a = 99; + return b.a; +} diff --git a/examples/0824-protocols-same-name-method-wrapped-own-wins.sx b/examples/0824-protocols-same-name-method-wrapped-own-wins.sx new file mode 100644 index 0000000..8fca1c5 --- /dev/null +++ b/examples/0824-protocols-same-name-method-wrapped-own-wins.sx @@ -0,0 +1,78 @@ +// E6BR-4 (own-wins) — a protocol method-signature names a same-name `Box` under +// every WRAPPER / COMPOUND form, and each must resolve SOURCE-AWARE (pinned to the +// protocol's defining module = main), selecting main's OWN `Box { m }` rather than +// the global last-wins `Box { a }` from the flat-imported `dep.sx`. +// +// The reconciled choke-point (resolveRegistrationSigTypeInSource → the recursive +// source-aware engine) recurses every structural shape and resolves the leaf +// author own-wins. The E6BR-4 RED CELL is the WRAPPED RETURN `() -> *Box`: pre-fix +// the wrapped sig fell to the no-author `type_bridge.resolveTemplateSignatureType` +// (global last-wins) and typed the result as `dep`'s `Box { a }`, so `bp.m` was +// `field 'm' not found on type 'Box'`. Discriminating returns: `*Box`, `?Box`, +// `(Box,Box)`, `[2]Box` (each observed by a `.m` access). Routing-only params: +// `*Box`, `?Box`, `[]Box`, `[2]Box`, `(Box,Box)`, nested `*?[]Box` — all resolved +// through the same `resolveCompound` recursion at protocol-decl registration. + +#import "modules/std.sx"; +#import "0824-protocols-same-name-method-wrapped-own-wins/dep.sx"; + +Box :: struct { m: s32; } +Holder :: struct { b: Box = ---; } + +Provider :: protocol { + // discriminating wrapped/compound RETURNS + getp :: () -> *Box; + geto :: () -> ?Box; + gett :: () -> (Box, Box); + geta :: () -> [2]Box; + // routing-only wrapped/compound PARAMS + sump :: (p: *Box) -> s32; + sumo :: (o: ?Box) -> s32; + sums :: (s: []Box) -> s32; + suma :: (a: [2]Box) -> s32; + sumt :: (t: (Box, Box)) -> s32; + sumn :: (n: *?[]Box) -> s32; +} + +impl Provider for Holder { + getp :: (self: *Holder) -> *Box { @self.b } + geto :: (self: *Holder) -> ?Box { self.b } + gett :: (self: *Holder) -> (Box, Box) { (self.b, self.b) } + geta :: (self: *Holder) -> [2]Box { r : [2]Box = ---; r[0] = self.b; r[1] = self.b; r } + sump :: (self: *Holder, p: *Box) -> s32 { p.m } + sumo :: (self: *Holder, o: ?Box) -> s32 { o!.m } + sums :: (self: *Holder, s: []Box) -> s32 { s[0].m } + suma :: (self: *Holder, a: [2]Box) -> s32 { a[0].m } + sumt :: (self: *Holder, t: (Box, Box)) -> s32 { t.0.m } + sumn :: (self: *Holder, n: *?[]Box) -> s32 { if n == null { 0 } else { 6 } } +} + +main :: () -> s32 { + h : Holder = ---; + h.b.m = 7; + p : Provider = xx @h; + + // discriminating returns — `.m` only resolves if each binds main's `Box` + bp := p.getp(); + bo := p.geto(); + bt := p.gett(); + ba := p.geta(); + + // routing-only params, constructed in main and passed through the protocol + one : Box = ---; one.m = 1; + arr : [2]Box = ---; arr[0].m = 2; arr[1].m = 3; + sl : []Box = arr[0..2]; + osl : ?[]Box = sl; + tup : (Box, Box) = (one, one); + + sp := p.sump(@one); + so := p.sumo(one); + ss := p.sums(sl); + sa := p.suma(arr); + st := p.sumt(tup); + sn := p.sumn(@osl); + + print("p={} o={} t={} a={} | sp={} so={} ss={} sa={} st={} sn={} dep={}\n", + bp.m, bo!.m, bt.0.m, ba[0].m, sp, so, ss, sa, st, sn, dep_box()); + 0 +} diff --git a/examples/0824-protocols-same-name-method-wrapped-own-wins/dep.sx b/examples/0824-protocols-same-name-method-wrapped-own-wins/dep.sx new file mode 100644 index 0000000..37fb81d --- /dev/null +++ b/examples/0824-protocols-same-name-method-wrapped-own-wins/dep.sx @@ -0,0 +1,11 @@ +// A flat-imported module authors its OWN `Box { a }`, a DISTINCT nominal from +// main's same-name `Box { m }`. The protocol method signatures below name `Box` +// under every wrapper/compound form; a wrong (last-wins) author binds this `Box`, +// whose disjoint field set makes a `.m` access a hard compile error. +Box :: struct { a: s32; } + +dep_box :: () -> s32 { + b : Box = ---; + b.a = 9; + return b.a; +} diff --git a/examples/0825-protocols-same-name-method-wrapped-ambiguous.sx b/examples/0825-protocols-same-name-method-wrapped-ambiguous.sx new file mode 100644 index 0000000..b8ded8a --- /dev/null +++ b/examples/0825-protocols-same-name-method-wrapped-ambiguous.sx @@ -0,0 +1,23 @@ +// E6BR-4 (ambiguous) — because a protocol method-signature WRAPPED return type is +// now resolved SOURCE-AWARE (the reconciled choke-point recurses `*Box` and +// resolves its leaf via `resolveNominalLeaf`), a genuinely ambiguous same-name +// element poisons LOUDLY at the protocol declaration instead of silently picking a +// global `findByName` last-wins author. `main` flat-imports two `Box` authors and +// declares none itself, so the `Provider.getp` return `*Box` is an unresolvable +// collision and the build exits 1. +// +// Fail-before (pre-E6BR-4): the wrapped `*Box` fell to the no-author +// `type_bridge.resolveTemplateSignatureType` wrapper (global last-wins, no +// diagnostic), so the build did NOT report the collision. + +#import "modules/std.sx"; +#import "0825-protocols-same-name-method-wrapped-ambiguous/a.sx"; +#import "0825-protocols-same-name-method-wrapped-ambiguous/b.sx"; + +Provider :: protocol { + getp :: () -> *Box; +} + +main :: () -> s32 { + 0 +} diff --git a/examples/0825-protocols-same-name-method-wrapped-ambiguous/a.sx b/examples/0825-protocols-same-name-method-wrapped-ambiguous/a.sx new file mode 100644 index 0000000..14907d2 --- /dev/null +++ b/examples/0825-protocols-same-name-method-wrapped-ambiguous/a.sx @@ -0,0 +1,9 @@ +// One of two flat-imported `Box` authors. With no own author in main, the +// protocol method-signature `() -> *Box` is a genuine collision. +Box :: struct { a: s32; } + +a_box :: () -> s32 { + b : Box = ---; + b.a = 1; + return b.a; +} diff --git a/examples/0825-protocols-same-name-method-wrapped-ambiguous/b.sx b/examples/0825-protocols-same-name-method-wrapped-ambiguous/b.sx new file mode 100644 index 0000000..839c4ab --- /dev/null +++ b/examples/0825-protocols-same-name-method-wrapped-ambiguous/b.sx @@ -0,0 +1,8 @@ +// The second flat-imported `Box` author — a DISTINCT nominal from a.sx's `Box`. +Box :: struct { b: s32; } + +b_box :: () -> s32 { + x : Box = ---; + x.b = 2; + return x.b; +} diff --git a/examples/0826-protocols-param-impl-source-wrapped-own-wins.sx b/examples/0826-protocols-param-impl-source-wrapped-own-wins.sx new file mode 100644 index 0000000..7708623 --- /dev/null +++ b/examples/0826-protocols-param-impl-source-wrapped-own-wins.sx @@ -0,0 +1,32 @@ +// E6BR-4 (param-impl SOURCE, own-wins) — a parameterised-impl SOURCE type under a +// wrapper (`impl Into(Marker) for *Box`) registers SOURCE-AWARE, pinned to the +// impl's defining module (main), and the `xx` lookup at the use site mangles to the +// SAME author, so the conversion is found and runs. `main` authors its OWN +// `Box { m }` and flat-imports `dep.sx` (`Box { a }`); the `*Box` source binds +// main's `Box`, so `convert`'s `self.m` resolves and `xx @b` selects this impl. +// +// This locks the route-all registration path for the param-impl-source surface: +// the source `*Box` flows through `resolveRegistrationSigTypeInSource` → +// `resolveCompound` → `resolveNominalLeaf` (own-wins), never the no-author leaf. +// The param_impl_map key is name-based (`Into\0Marker\0*Box`), so registration and +// `tryUserConversion` agree by construction. + +#import "modules/std.sx"; +#import "0826-protocols-param-impl-source-wrapped-own-wins/dep.sx"; + +Box :: struct { m: s32; } +Marker :: struct { v: s32; } + +impl Into(Marker) for *Box { + convert :: (self: *Box) -> Marker { + .{ v = self.m } + } +} + +main :: () -> s32 { + b : Box = ---; + b.m = 7; + mk : Marker = xx @b; + print("v={} dep={}\n", mk.v, dep_box()); + 0 +} diff --git a/examples/0826-protocols-param-impl-source-wrapped-own-wins/dep.sx b/examples/0826-protocols-param-impl-source-wrapped-own-wins/dep.sx new file mode 100644 index 0000000..596bb0a --- /dev/null +++ b/examples/0826-protocols-param-impl-source-wrapped-own-wins/dep.sx @@ -0,0 +1,11 @@ +// A flat-imported module authors its OWN `Box { a }`, a DISTINCT nominal from +// main's same-name `Box { m }`. The param-impl SOURCE `*Box` must register against +// main's `Box`, and the `xx` lookup at the use site must mangle to the SAME author, +// so the conversion is found and `self.m` resolves. +Box :: struct { a: s32; } + +dep_box :: () -> s32 { + b : Box = ---; + b.a = 9; + return b.a; +} diff --git a/examples/0827-protocols-param-impl-source-wrapped-ambiguous.sx b/examples/0827-protocols-param-impl-source-wrapped-ambiguous.sx new file mode 100644 index 0000000..9816273 --- /dev/null +++ b/examples/0827-protocols-param-impl-source-wrapped-ambiguous.sx @@ -0,0 +1,27 @@ +// E6BR-4 (param-impl SOURCE, ambiguous) — because a parameterised-impl wrapped +// SOURCE type is now registered SOURCE-AWARE (the source `*Box` flows through +// `resolveRegistrationSigTypeInSource` → `resolveCompound` → `resolveNominalLeaf`), +// a genuinely ambiguous same-name element poisons LOUDLY at registration instead of +// silently selecting a global `findByName` last-wins author. `main` flat-imports two +// `Box` authors and declares none itself, so `impl Into(Marker) for *Box` cannot +// pick a `Box` and the build exits 1. +// +// Fail-before (pre-E6BR-4): the source `*Box` fell to the no-author +// `type_bridge.resolveTemplateSignatureType` wrapper (global last-wins, no +// diagnostic), so the collision was registered silently. + +#import "modules/std.sx"; +#import "0827-protocols-param-impl-source-wrapped-ambiguous/a.sx"; +#import "0827-protocols-param-impl-source-wrapped-ambiguous/b.sx"; + +Marker :: struct { v: s32; } + +impl Into(Marker) for *Box { + convert :: (self: *Box) -> Marker { + .{ v = 0 } + } +} + +main :: () -> s32 { + 0 +} diff --git a/examples/0827-protocols-param-impl-source-wrapped-ambiguous/a.sx b/examples/0827-protocols-param-impl-source-wrapped-ambiguous/a.sx new file mode 100644 index 0000000..7bf44bc --- /dev/null +++ b/examples/0827-protocols-param-impl-source-wrapped-ambiguous/a.sx @@ -0,0 +1,9 @@ +// One of two flat-imported `Box` authors; with no own author the param-impl +// SOURCE `*Box` is a genuine collision. +Box :: struct { a: s32; } + +a_box :: () -> s32 { + b : Box = ---; + b.a = 1; + return b.a; +} diff --git a/examples/0827-protocols-param-impl-source-wrapped-ambiguous/b.sx b/examples/0827-protocols-param-impl-source-wrapped-ambiguous/b.sx new file mode 100644 index 0000000..839c4ab --- /dev/null +++ b/examples/0827-protocols-param-impl-source-wrapped-ambiguous/b.sx @@ -0,0 +1,8 @@ +// The second flat-imported `Box` author — a DISTINCT nominal from a.sx's `Box`. +Box :: struct { b: s32; } + +b_box :: () -> s32 { + x : Box = ---; + x.b = 2; + return x.b; +} diff --git a/examples/0828-protocols-param-impl-arg-wrapped-own-wins.sx b/examples/0828-protocols-param-impl-arg-wrapped-own-wins.sx new file mode 100644 index 0000000..e3c064d --- /dev/null +++ b/examples/0828-protocols-param-impl-arg-wrapped-own-wins.sx @@ -0,0 +1,31 @@ +// E6BR-4 (param-impl ARG, own-wins) — a parameterised-impl PROTOCOL TYPE-ARG under +// a wrapper (`impl Tagged(*Box) for Holder`) is resolved through the same +// source-aware registration helper (`resolveRegistrationSigTypeInSource` with the +// `.param_impl_arg` purpose), pinned to the impl's defining module (main). The arg +// `*Box` flows through `resolveCompound` → `resolveNominalLeaf` and selects main's +// OWN `Box { m }` rather than the flat-imported `dep.sx` `Box { a }`, so the impl +// registers and `Holder.tag` is callable. (The `param_impl_map` key is name-based, +// so own-wins registration and any later lookup agree by construction; this cell +// locks that the wrapped arg routes through the engine, not the no-author leaf.) + +#import "modules/std.sx"; +#import "0828-protocols-param-impl-arg-wrapped-own-wins/dep.sx"; + +Box :: struct { m: s32; } +Holder :: struct { n: s32; } + +Tagged :: protocol(T: Type) { + tag :: () -> s32; +} + +impl Tagged(*Box) for Holder { + tag :: (self: *Holder) -> s32 { + self.n + } +} + +main :: () -> s32 { + h : Holder = .{ n = 7 }; + print("tag={} dep={}\n", h.tag(), dep_box()); + 0 +} diff --git a/examples/0828-protocols-param-impl-arg-wrapped-own-wins/dep.sx b/examples/0828-protocols-param-impl-arg-wrapped-own-wins/dep.sx new file mode 100644 index 0000000..bcdc78a --- /dev/null +++ b/examples/0828-protocols-param-impl-arg-wrapped-own-wins/dep.sx @@ -0,0 +1,11 @@ +// A flat-imported module authors its OWN `Box { a }`, a DISTINCT nominal from +// main's same-name `Box { m }`. The parameterised-impl protocol type-ARG `*Box` +// registers SOURCE-AWARE against main's `Box` (own-wins), never the global +// last-wins author. +Box :: struct { a: s32; } + +dep_box :: () -> s32 { + b : Box = ---; + b.a = 9; + return b.a; +} diff --git a/examples/0829-packs-param-impl-mixed-pack-source-ambiguous.sx b/examples/0829-packs-param-impl-mixed-pack-source-ambiguous.sx new file mode 100644 index 0000000..a250bc3 --- /dev/null +++ b/examples/0829-packs-param-impl-mixed-pack-source-ambiguous.sx @@ -0,0 +1,36 @@ +// E6BR-4 (MIXED pack source — the trap cell) — a pack-closure param-impl source +// `Closure(*Box, ..$args) -> $R` mixes a CONCRETE fixed prefix (`*Box`) with pack +// metadata (`..$args`, `$R`). The reconciled choke-point decides template-vs-author +// AT THE LEAF: `*Box` is resolved SOURCE-AWARE through `resolveCompound` while +// `..$args`/`$R` stay pack metadata via `PackResolver` — NOT a top-level +// "contains-unbound → no-author wrapper" router, which would send `*Box` down the +// global last-wins path (the trap both engines flagged). With two flat `Box` +// authors and none own, the concrete `*Box` prefix is a genuine collision and the +// build exits 1 — proving the prefix IS routed source-aware (it caught the +// ambiguity) while the pack parts did not spuriously error. +// +// Fail-before (pre-E6BR-4): the wrapped/pack source fell to the no-author +// `type_bridge.resolveTemplateSignatureType` wrapper (global last-wins, no +// diagnostic), so the `*Box` collision registered silently. Protects the pure-pack +// `Closure(..$args) -> $R` bridge in `library/modules/std/objc_block.sx` (0504 / +// 1302 / 1304 stay byte-identical), whose single author keeps the prefix-free path. + +#import "modules/std.sx"; +#import "0829-packs-param-impl-mixed-pack-source-ambiguous/a.sx"; +#import "0829-packs-param-impl-mixed-pack-source-ambiguous/b.sx"; + +Block :: struct { tag: s32; } + +Sink :: protocol(T: Type) { + convert :: () -> T; +} + +impl Sink(Block) for Closure(*Box, ..$args) -> $R { + convert :: (self: Closure(*Box, ..$args) -> $R) -> Block { + .{ tag = 0 } + } +} + +main :: () -> s32 { + 0 +} diff --git a/examples/0829-packs-param-impl-mixed-pack-source-ambiguous/a.sx b/examples/0829-packs-param-impl-mixed-pack-source-ambiguous/a.sx new file mode 100644 index 0000000..aac5930 --- /dev/null +++ b/examples/0829-packs-param-impl-mixed-pack-source-ambiguous/a.sx @@ -0,0 +1,9 @@ +// One of two flat-imported `Box` authors; with no own author the FIXED PREFIX +// `*Box` of the pack-closure source is a genuine collision. +Box :: struct { a: s32; } + +a_box :: () -> s32 { + b : Box = ---; + b.a = 1; + return b.a; +} diff --git a/examples/0829-packs-param-impl-mixed-pack-source-ambiguous/b.sx b/examples/0829-packs-param-impl-mixed-pack-source-ambiguous/b.sx new file mode 100644 index 0000000..839c4ab --- /dev/null +++ b/examples/0829-packs-param-impl-mixed-pack-source-ambiguous/b.sx @@ -0,0 +1,8 @@ +// The second flat-imported `Box` author — a DISTINCT nominal from a.sx's `Box`. +Box :: struct { b: s32; } + +b_box :: () -> s32 { + x : Box = ---; + x.b = 2; + return x.b; +} diff --git a/examples/expected/0823-route-all-own-wins-subform-wrappers.exit b/examples/expected/0823-route-all-own-wins-subform-wrappers.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/examples/expected/0823-route-all-own-wins-subform-wrappers.exit @@ -0,0 +1 @@ +0 diff --git a/examples/expected/0823-route-all-own-wins-subform-wrappers.stderr b/examples/expected/0823-route-all-own-wins-subform-wrappers.stderr new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/examples/expected/0823-route-all-own-wins-subform-wrappers.stderr @@ -0,0 +1 @@ + diff --git a/examples/expected/0823-route-all-own-wins-subform-wrappers.stdout b/examples/expected/0823-route-all-own-wins-subform-wrappers.stdout new file mode 100644 index 0000000..5da64c7 --- /dev/null +++ b/examples/expected/0823-route-all-own-wins-subform-wrappers.stdout @@ -0,0 +1 @@ +opt=1 arr=2 sl=3 dep=99 diff --git a/examples/expected/0828-protocols-param-impl-arg-wrapped-own-wins.exit b/examples/expected/0828-protocols-param-impl-arg-wrapped-own-wins.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/examples/expected/0828-protocols-param-impl-arg-wrapped-own-wins.exit @@ -0,0 +1 @@ +0 diff --git a/examples/expected/0828-protocols-param-impl-arg-wrapped-own-wins.stderr b/examples/expected/0828-protocols-param-impl-arg-wrapped-own-wins.stderr new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/examples/expected/0828-protocols-param-impl-arg-wrapped-own-wins.stderr @@ -0,0 +1 @@ + diff --git a/examples/expected/0828-protocols-param-impl-arg-wrapped-own-wins.stdout b/examples/expected/0828-protocols-param-impl-arg-wrapped-own-wins.stdout new file mode 100644 index 0000000..0ff3195 --- /dev/null +++ b/examples/expected/0828-protocols-param-impl-arg-wrapped-own-wins.stdout @@ -0,0 +1 @@ +tag=7 dep=9 diff --git a/tests/resolver-target/README.md b/tests/resolver-target/README.md new file mode 100644 index 0000000..793d98a --- /dev/null +++ b/tests/resolver-target/README.md @@ -0,0 +1,49 @@ +# Resolver-target corpus (Fork C) + +The second of the two Fork C resolver-acceptance corpora. See the full contract in +`../../docs/fork-c/S0.2-e6b-disposition-and-two-corpus-partition.md`. + +- **What it is:** harvested E6b semantics goldens (+ the re-filed E6BR-5 regression) + that encode the **TARGET** behavior of the Fork C resolver for cases where the + **old name-selector is known-wrong on `wt-stdlib-base`** (E6b unmerged) — it + silently resolves a global last-wins author / under-diagnoses / picks the wrong + author. On this base the old selector is **not a valid oracle**, so these are NOT + baseline-green. +- **Why separate:** the S2 assert-only mirror proves `resolver == old-selector` over + the **baseline-green corpus ONLY**. Asserting that over these cases would force the + new resolver to reproduce the old bug. So they live here, inactive, with NO active + `examples/expected/` marker — `tests/run_examples.sh` does not run them. +- **Never silently dropped:** every case is enumerated in `manifest.md`, its TARGET + golden is recorded in `expected/`, and `run_resolver_target.sh` asserts each case + currently FAILS to match its target (xfail). If any case unexpectedly MATCHES on + the base, the runner flags it `LEAKED` — it is actually baseline-green and must be + re-classified (moved to `examples/expected/`), never left here. +- **Flip at S3.9:** the Fork C resolver makes these pass; each golden moves to + `examples/expected/.{exit,stdout,stderr}` and this harness goes empty. + +## Layout + +``` +manifest.md enumerated list of all 18 cases (class / base-now / target / note) +expected/.exit TARGET exit (08xx: exact E6b bytes; e6br5: spec) +expected/.stdout TARGET stdout (08xx only — exact E6b bytes) +expected/.stderr TARGET stderr (08xx only — exact E6b bytes) +expected/e6br5-*.target.md E6BR-5 spec target (exact bytes finalized at S3.9) +cases/e6br5-*.sx + dir/ E6BR-5 authored reproducer (self-contained) +run_resolver_target.sh xfail runner (NOT part of the baseline gate) +``` + +The 08xx **source trees** live under `examples/` (harvested as authored, so their +`#import` paths resolve exactly as their baseline-green siblings do) but carry **no** +`examples/expected/` marker, so they are inert to `run_examples.sh`. Only their +goldens live here. The E6BR-5 reproducer lives entirely under `cases/` (it is +self-contained — `modules/std.sx` resolves via the `library/` search path). + +## Run + +``` +zig build # build the compiler first +bash tests/resolver-target/run_resolver_target.sh +``` + +Expected today (S0): all 18 cases print `xfail`, `0 leaked`, exit 0. diff --git a/tests/resolver-target/cases/e6br5-nested-pack-source-ambiguous.sx b/tests/resolver-target/cases/e6br5-nested-pack-source-ambiguous.sx new file mode 100644 index 0000000..3360b06 --- /dev/null +++ b/tests/resolver-target/cases/e6br5-nested-pack-source-ambiguous.sx @@ -0,0 +1,48 @@ +// E6BR-5 regression — the open nested-pattern ambiguity hole that PAUSED E6b, +// re-filed under Fork C as a RESOLVER-TARGET regression (NOT an E6b attempt-6). +// +// Shape: a param-impl SOURCE pattern whose concrete `*Box` leaf is NESTED inside +// an inner parameterized form that itself carries unbound pack parts +// (`Closure(*Box, ..$inner) -> $IR`). On flow/stdlib/E6b the bug lived in +// `walkConcreteSigArgs` (lower.zig:14686): it SKIPS any direct arg that has an +// unbound part instead of RECURSING into it, so the nested concrete `*Box` leaf +// is never ambiguity-checked — `impl ... for Closure(Closure(*Box, ..)->.., ..)->..` +// compiled rc=0 while the DIRECT `Closure(*Box, ..)` form (0829) errored. It was +// the 3rd consecutive major on the parameterized-pattern surface +// (bare E6BR-2 -> wrapped E6BR-4 -> nested E6BR-5). +// +// CURRENT BASE (wt-stdlib-base, E6b unmerged): both the direct AND the nested +// concrete leaf silently resolve via the pre-E6BR-4 no-author wrapper path +// (`type_bridge.resolveTemplateSignatureType`, global last-wins) — this program +// exits 0 with NO diagnostic. Run it to see the fail-before. +// +// TARGET (Fork C, flips active+green at S3.9): the whole-AST resolver walks EVERY +// reference position — including nested parameterized-pattern leaves — so the +// nested `Box` is ambiguity-checked like any other reference and the build emits +// the loud "type 'Box' is ambiguous" diagnostic and exits 1. The exact golden +// bytes are produced by the resolver at S3.9 (no oracle produces them today); +// see ../expected/e6br5-nested-pack-source-ambiguous.target.md for the spec. +// +// WHY SUBSUMED (one line): a resolver that resolves every reference position has +// no notion of "skip a nested arg with an unbound sibling" — the nested `*Box` +// leaf is resolved by construction, so the hole cannot exist. + +#import "modules/std.sx"; +#import "e6br5-nested-pack-source-ambiguous/a.sx"; +#import "e6br5-nested-pack-source-ambiguous/b.sx"; + +Block :: struct { tag: s32; } + +Sink :: protocol(T: Type) { + convert :: () -> T; +} + +impl Sink(Block) for Closure(Closure(*Box, ..$inner) -> $IR, ..$args) -> $R { + convert :: (self: Closure(Closure(*Box, ..$inner) -> $IR, ..$args) -> $R) -> Block { + .{ tag = 0 } + } +} + +main :: () -> s32 { + 0 +} diff --git a/tests/resolver-target/cases/e6br5-nested-pack-source-ambiguous/a.sx b/tests/resolver-target/cases/e6br5-nested-pack-source-ambiguous/a.sx new file mode 100644 index 0000000..ae773df --- /dev/null +++ b/tests/resolver-target/cases/e6br5-nested-pack-source-ambiguous/a.sx @@ -0,0 +1,4 @@ +// One of two flat-imported `Box` authors. With no own author, the concrete +// `*Box` leaf — even when NESTED inside a parameterized pattern — is a genuine +// collision the importing source cannot disambiguate. +Box :: struct { a: s32; } diff --git a/tests/resolver-target/cases/e6br5-nested-pack-source-ambiguous/b.sx b/tests/resolver-target/cases/e6br5-nested-pack-source-ambiguous/b.sx new file mode 100644 index 0000000..121be91 --- /dev/null +++ b/tests/resolver-target/cases/e6br5-nested-pack-source-ambiguous/b.sx @@ -0,0 +1,3 @@ +// The second flat-imported `Box` author — a DISTINCT nominal from a.sx's `Box`, +// so the nested `*Box` leaf is a real ambiguity the source cannot resolve. +Box :: struct { b: s32; } diff --git a/tests/resolver-target/expected/0811-modules-same-name-error-set-ambiguous.exit b/tests/resolver-target/expected/0811-modules-same-name-error-set-ambiguous.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/resolver-target/expected/0811-modules-same-name-error-set-ambiguous.exit @@ -0,0 +1 @@ +1 diff --git a/tests/resolver-target/expected/0811-modules-same-name-error-set-ambiguous.stderr b/tests/resolver-target/expected/0811-modules-same-name-error-set-ambiguous.stderr new file mode 100644 index 0000000..975ee20 --- /dev/null +++ b/tests/resolver-target/expected/0811-modules-same-name-error-set-ambiguous.stderr @@ -0,0 +1,29 @@ +error: type 'IoErr' is ambiguous: it is declared in multiple flat-imported modules; qualify the reference or remove the duplicate import + --> examples/0811-modules-same-name-error-set-ambiguous.sx:34:18 + | +34 | fail_io :: () -> !IoErr { + | ^^^^^^ + +error: type 'IoErr' is ambiguous: it is declared in multiple flat-imported modules; qualify the reference or remove the duplicate import + --> examples/0811-modules-same-name-error-set-ambiguous.sx:39:19 + | +39 | sz := size_of(IoErr); + | ^^^^^ + +error: type 'IoErr' is ambiguous: it is declared in multiple flat-imported modules; qualify the reference or remove the duplicate import + --> examples/0811-modules-same-name-error-set-ambiguous.sx:40:9 + | +40 | e : IoErr = error.Disk; + | ^^^^^ + +error: type 'IoErr' is ambiguous: it is declared in multiple flat-imported modules; qualify the reference or remove the duplicate import + --> examples/0811-modules-same-name-error-set-ambiguous.sx:41:16 + | +41 | t : Type = IoErr; + | ^^^^^ + +error: type 'IoErr' is ambiguous: it is declared in multiple flat-imported modules; qualify the reference or remove the duplicate import + --> examples/0811-modules-same-name-error-set-ambiguous.sx:28:14 + | +28 | case IoErr: 1; + | ^^^^^ diff --git a/tests/resolver-target/expected/0811-modules-same-name-error-set-ambiguous.stdout b/tests/resolver-target/expected/0811-modules-same-name-error-set-ambiguous.stdout new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/resolver-target/expected/0811-modules-same-name-error-set-ambiguous.stdout @@ -0,0 +1 @@ + diff --git a/tests/resolver-target/expected/0812-modules-same-name-error-set-own-wins.exit b/tests/resolver-target/expected/0812-modules-same-name-error-set-own-wins.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/resolver-target/expected/0812-modules-same-name-error-set-own-wins.exit @@ -0,0 +1 @@ +0 diff --git a/tests/resolver-target/expected/0812-modules-same-name-error-set-own-wins.stderr b/tests/resolver-target/expected/0812-modules-same-name-error-set-own-wins.stderr new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/resolver-target/expected/0812-modules-same-name-error-set-own-wins.stderr @@ -0,0 +1 @@ + diff --git a/tests/resolver-target/expected/0812-modules-same-name-error-set-own-wins.stdout b/tests/resolver-target/expected/0812-modules-same-name-error-set-own-wins.stdout new file mode 100644 index 0000000..ea0346a --- /dev/null +++ b/tests/resolver-target/expected/0812-modules-same-name-error-set-own-wins.stdout @@ -0,0 +1 @@ +own=Disk dep=Net diff --git a/tests/resolver-target/expected/0813-modules-same-name-error-set-lambda-own-wins.exit b/tests/resolver-target/expected/0813-modules-same-name-error-set-lambda-own-wins.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/resolver-target/expected/0813-modules-same-name-error-set-lambda-own-wins.exit @@ -0,0 +1 @@ +0 diff --git a/tests/resolver-target/expected/0813-modules-same-name-error-set-lambda-own-wins.stderr b/tests/resolver-target/expected/0813-modules-same-name-error-set-lambda-own-wins.stderr new file mode 100644 index 0000000..e69de29 diff --git a/tests/resolver-target/expected/0813-modules-same-name-error-set-lambda-own-wins.stdout b/tests/resolver-target/expected/0813-modules-same-name-error-set-lambda-own-wins.stdout new file mode 100644 index 0000000..c53a677 --- /dev/null +++ b/tests/resolver-target/expected/0813-modules-same-name-error-set-lambda-own-wins.stdout @@ -0,0 +1,2 @@ +own=Disk +dep=Net diff --git a/tests/resolver-target/expected/0814-modules-same-name-error-set-lambda-ambiguous.exit b/tests/resolver-target/expected/0814-modules-same-name-error-set-lambda-ambiguous.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/resolver-target/expected/0814-modules-same-name-error-set-lambda-ambiguous.exit @@ -0,0 +1 @@ +1 diff --git a/tests/resolver-target/expected/0814-modules-same-name-error-set-lambda-ambiguous.stderr b/tests/resolver-target/expected/0814-modules-same-name-error-set-lambda-ambiguous.stderr new file mode 100644 index 0000000..07c5f6b --- /dev/null +++ b/tests/resolver-target/expected/0814-modules-same-name-error-set-lambda-ambiguous.stderr @@ -0,0 +1,5 @@ +error: type 'IoErr' is ambiguous: it is declared in multiple flat-imported modules; qualify the reference or remove the duplicate import + --> examples/0814-modules-same-name-error-set-lambda-ambiguous.sx:25:30 + | +25 | fail_io := closure(() -> !IoErr { return; }); + | ^^^^^^ diff --git a/tests/resolver-target/expected/0814-modules-same-name-error-set-lambda-ambiguous.stdout b/tests/resolver-target/expected/0814-modules-same-name-error-set-lambda-ambiguous.stdout new file mode 100644 index 0000000..e69de29 diff --git a/tests/resolver-target/expected/0815-route-all-new-surfaces-ambiguous.exit b/tests/resolver-target/expected/0815-route-all-new-surfaces-ambiguous.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/resolver-target/expected/0815-route-all-new-surfaces-ambiguous.exit @@ -0,0 +1 @@ +1 diff --git a/tests/resolver-target/expected/0815-route-all-new-surfaces-ambiguous.stderr b/tests/resolver-target/expected/0815-route-all-new-surfaces-ambiguous.stderr new file mode 100644 index 0000000..0e12591 --- /dev/null +++ b/tests/resolver-target/expected/0815-route-all-new-surfaces-ambiguous.stderr @@ -0,0 +1,29 @@ +error: type 'Box' is ambiguous: it is declared in multiple flat-imported modules; qualify the reference or remove the duplicate import + --> examples/0815-route-all-new-surfaces-ambiguous.sx:25:12 + | +25 | BoxPtr :: *Box; + | ^^^ + +error: type 'Box' is ambiguous: it is declared in multiple flat-imported modules; qualify the reference or remove the duplicate import + --> examples/0815-route-all-new-surfaces-ambiguous.sx:27:21 + | +27 | WrapU :: union { b: Box; n: s32; } + | ^^^ + +error: type 'Box' is ambiguous: it is declared in multiple flat-imported modules; qualify the reference or remove the duplicate import + --> examples/0815-route-all-new-surfaces-ambiguous.sx:29:20 + | +29 | WrapE :: enum { V: Box; } + | ^^^ + +error: type 'Box' is ambiguous: it is declared in multiple flat-imported modules; qualify the reference or remove the duplicate import + --> examples/0815-route-all-new-surfaces-ambiguous.sx:32:20 + | +32 | sz := size_of((Box, s32)); + | ^^^ + +error: type 'Box' is ambiguous: it is declared in multiple flat-imported modules; qualify the reference or remove the duplicate import + --> examples/0815-route-all-new-surfaces-ambiguous.sx:33:20 + | +33 | x : union { b: Box; n: s32 } = ---; + | ^^^ diff --git a/tests/resolver-target/expected/0815-route-all-new-surfaces-ambiguous.stdout b/tests/resolver-target/expected/0815-route-all-new-surfaces-ambiguous.stdout new file mode 100644 index 0000000..e69de29 diff --git a/tests/resolver-target/expected/0816-route-all-new-surfaces-own-wins.exit b/tests/resolver-target/expected/0816-route-all-new-surfaces-own-wins.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/resolver-target/expected/0816-route-all-new-surfaces-own-wins.exit @@ -0,0 +1 @@ +0 diff --git a/tests/resolver-target/expected/0816-route-all-new-surfaces-own-wins.stderr b/tests/resolver-target/expected/0816-route-all-new-surfaces-own-wins.stderr new file mode 100644 index 0000000..e69de29 diff --git a/tests/resolver-target/expected/0816-route-all-new-surfaces-own-wins.stdout b/tests/resolver-target/expected/0816-route-all-new-surfaces-own-wins.stdout new file mode 100644 index 0000000..bf61927 --- /dev/null +++ b/tests/resolver-target/expected/0816-route-all-new-surfaces-own-wins.stdout @@ -0,0 +1 @@ +own=5 dep=9 diff --git a/tests/resolver-target/expected/0817-modules-qualified-annotation-single-import-resolve.exit b/tests/resolver-target/expected/0817-modules-qualified-annotation-single-import-resolve.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/resolver-target/expected/0817-modules-qualified-annotation-single-import-resolve.exit @@ -0,0 +1 @@ +0 diff --git a/tests/resolver-target/expected/0817-modules-qualified-annotation-single-import-resolve.stderr b/tests/resolver-target/expected/0817-modules-qualified-annotation-single-import-resolve.stderr new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/resolver-target/expected/0817-modules-qualified-annotation-single-import-resolve.stderr @@ -0,0 +1 @@ + diff --git a/tests/resolver-target/expected/0817-modules-qualified-annotation-single-import-resolve.stdout b/tests/resolver-target/expected/0817-modules-qualified-annotation-single-import-resolve.stdout new file mode 100644 index 0000000..d40db42 --- /dev/null +++ b/tests/resolver-target/expected/0817-modules-qualified-annotation-single-import-resolve.stdout @@ -0,0 +1 @@ +x.a=4 diff --git a/tests/resolver-target/expected/0818-modules-qualified-annotation-own-wins.exit b/tests/resolver-target/expected/0818-modules-qualified-annotation-own-wins.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/resolver-target/expected/0818-modules-qualified-annotation-own-wins.exit @@ -0,0 +1 @@ +0 diff --git a/tests/resolver-target/expected/0818-modules-qualified-annotation-own-wins.stderr b/tests/resolver-target/expected/0818-modules-qualified-annotation-own-wins.stderr new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/resolver-target/expected/0818-modules-qualified-annotation-own-wins.stderr @@ -0,0 +1 @@ + diff --git a/tests/resolver-target/expected/0818-modules-qualified-annotation-own-wins.stdout b/tests/resolver-target/expected/0818-modules-qualified-annotation-own-wins.stdout new file mode 100644 index 0000000..fd3e8b4 --- /dev/null +++ b/tests/resolver-target/expected/0818-modules-qualified-annotation-own-wins.stdout @@ -0,0 +1 @@ +own.m=5 q.a=9 diff --git a/tests/resolver-target/expected/0819-modules-qualified-annotation-error-set-own-wins.exit b/tests/resolver-target/expected/0819-modules-qualified-annotation-error-set-own-wins.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/resolver-target/expected/0819-modules-qualified-annotation-error-set-own-wins.exit @@ -0,0 +1 @@ +0 diff --git a/tests/resolver-target/expected/0819-modules-qualified-annotation-error-set-own-wins.stderr b/tests/resolver-target/expected/0819-modules-qualified-annotation-error-set-own-wins.stderr new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/resolver-target/expected/0819-modules-qualified-annotation-error-set-own-wins.stderr @@ -0,0 +1 @@ + diff --git a/tests/resolver-target/expected/0819-modules-qualified-annotation-error-set-own-wins.stdout b/tests/resolver-target/expected/0819-modules-qualified-annotation-error-set-own-wins.stdout new file mode 100644 index 0000000..df4af28 --- /dev/null +++ b/tests/resolver-target/expected/0819-modules-qualified-annotation-error-set-own-wins.stdout @@ -0,0 +1 @@ +own=Disk q=Net diff --git a/tests/resolver-target/expected/0820-protocols-same-name-method-own-wins.exit b/tests/resolver-target/expected/0820-protocols-same-name-method-own-wins.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/resolver-target/expected/0820-protocols-same-name-method-own-wins.exit @@ -0,0 +1 @@ +0 diff --git a/tests/resolver-target/expected/0820-protocols-same-name-method-own-wins.stderr b/tests/resolver-target/expected/0820-protocols-same-name-method-own-wins.stderr new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/resolver-target/expected/0820-protocols-same-name-method-own-wins.stderr @@ -0,0 +1 @@ + diff --git a/tests/resolver-target/expected/0820-protocols-same-name-method-own-wins.stdout b/tests/resolver-target/expected/0820-protocols-same-name-method-own-wins.stdout new file mode 100644 index 0000000..175e119 --- /dev/null +++ b/tests/resolver-target/expected/0820-protocols-same-name-method-own-wins.stdout @@ -0,0 +1 @@ +m=7 dep=9 diff --git a/tests/resolver-target/expected/0821-protocols-same-name-method-ambiguous.exit b/tests/resolver-target/expected/0821-protocols-same-name-method-ambiguous.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/resolver-target/expected/0821-protocols-same-name-method-ambiguous.exit @@ -0,0 +1 @@ +1 diff --git a/tests/resolver-target/expected/0821-protocols-same-name-method-ambiguous.stderr b/tests/resolver-target/expected/0821-protocols-same-name-method-ambiguous.stderr new file mode 100644 index 0000000..aadd967 --- /dev/null +++ b/tests/resolver-target/expected/0821-protocols-same-name-method-ambiguous.stderr @@ -0,0 +1,5 @@ +error: type 'Box' is ambiguous: it is declared in multiple flat-imported modules; qualify the reference or remove the duplicate import + --> examples/0821-protocols-same-name-method-ambiguous.sx:13:18 + | +13 | get :: () -> Box; + | ^^^ diff --git a/tests/resolver-target/expected/0821-protocols-same-name-method-ambiguous.stdout b/tests/resolver-target/expected/0821-protocols-same-name-method-ambiguous.stdout new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/resolver-target/expected/0821-protocols-same-name-method-ambiguous.stdout @@ -0,0 +1 @@ + diff --git a/tests/resolver-target/expected/0822-route-all-own-wins-surfaces.exit b/tests/resolver-target/expected/0822-route-all-own-wins-surfaces.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/resolver-target/expected/0822-route-all-own-wins-surfaces.exit @@ -0,0 +1 @@ +0 diff --git a/tests/resolver-target/expected/0822-route-all-own-wins-surfaces.stderr b/tests/resolver-target/expected/0822-route-all-own-wins-surfaces.stderr new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/resolver-target/expected/0822-route-all-own-wins-surfaces.stderr @@ -0,0 +1 @@ + diff --git a/tests/resolver-target/expected/0822-route-all-own-wins-surfaces.stdout b/tests/resolver-target/expected/0822-route-all-own-wins-surfaces.stdout new file mode 100644 index 0000000..7cb8090 --- /dev/null +++ b/tests/resolver-target/expected/0822-route-all-own-wins-surfaces.stdout @@ -0,0 +1 @@ +bp=10 t=12 ev=10 x=13 dep=99 diff --git a/tests/resolver-target/expected/0824-protocols-same-name-method-wrapped-own-wins.exit b/tests/resolver-target/expected/0824-protocols-same-name-method-wrapped-own-wins.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/resolver-target/expected/0824-protocols-same-name-method-wrapped-own-wins.exit @@ -0,0 +1 @@ +0 diff --git a/tests/resolver-target/expected/0824-protocols-same-name-method-wrapped-own-wins.stderr b/tests/resolver-target/expected/0824-protocols-same-name-method-wrapped-own-wins.stderr new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/resolver-target/expected/0824-protocols-same-name-method-wrapped-own-wins.stderr @@ -0,0 +1 @@ + diff --git a/tests/resolver-target/expected/0824-protocols-same-name-method-wrapped-own-wins.stdout b/tests/resolver-target/expected/0824-protocols-same-name-method-wrapped-own-wins.stdout new file mode 100644 index 0000000..7d38d45 --- /dev/null +++ b/tests/resolver-target/expected/0824-protocols-same-name-method-wrapped-own-wins.stdout @@ -0,0 +1 @@ +p=7 o=7 t=7 a=7 | sp=1 so=1 ss=2 sa=2 st=1 sn=6 dep=9 diff --git a/tests/resolver-target/expected/0825-protocols-same-name-method-wrapped-ambiguous.exit b/tests/resolver-target/expected/0825-protocols-same-name-method-wrapped-ambiguous.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/resolver-target/expected/0825-protocols-same-name-method-wrapped-ambiguous.exit @@ -0,0 +1 @@ +1 diff --git a/tests/resolver-target/expected/0825-protocols-same-name-method-wrapped-ambiguous.stderr b/tests/resolver-target/expected/0825-protocols-same-name-method-wrapped-ambiguous.stderr new file mode 100644 index 0000000..eb7544c --- /dev/null +++ b/tests/resolver-target/expected/0825-protocols-same-name-method-wrapped-ambiguous.stderr @@ -0,0 +1,5 @@ +error: type 'Box' is ambiguous: it is declared in multiple flat-imported modules; qualify the reference or remove the duplicate import + --> examples/0825-protocols-same-name-method-wrapped-ambiguous.sx:18:20 + | +18 | getp :: () -> *Box; + | ^^^ diff --git a/tests/resolver-target/expected/0825-protocols-same-name-method-wrapped-ambiguous.stdout b/tests/resolver-target/expected/0825-protocols-same-name-method-wrapped-ambiguous.stdout new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/resolver-target/expected/0825-protocols-same-name-method-wrapped-ambiguous.stdout @@ -0,0 +1 @@ + diff --git a/tests/resolver-target/expected/0826-protocols-param-impl-source-wrapped-own-wins.exit b/tests/resolver-target/expected/0826-protocols-param-impl-source-wrapped-own-wins.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/resolver-target/expected/0826-protocols-param-impl-source-wrapped-own-wins.exit @@ -0,0 +1 @@ +0 diff --git a/tests/resolver-target/expected/0826-protocols-param-impl-source-wrapped-own-wins.stderr b/tests/resolver-target/expected/0826-protocols-param-impl-source-wrapped-own-wins.stderr new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/resolver-target/expected/0826-protocols-param-impl-source-wrapped-own-wins.stderr @@ -0,0 +1 @@ + diff --git a/tests/resolver-target/expected/0826-protocols-param-impl-source-wrapped-own-wins.stdout b/tests/resolver-target/expected/0826-protocols-param-impl-source-wrapped-own-wins.stdout new file mode 100644 index 0000000..8aa8a5e --- /dev/null +++ b/tests/resolver-target/expected/0826-protocols-param-impl-source-wrapped-own-wins.stdout @@ -0,0 +1 @@ +v=7 dep=9 diff --git a/tests/resolver-target/expected/0827-protocols-param-impl-source-wrapped-ambiguous.exit b/tests/resolver-target/expected/0827-protocols-param-impl-source-wrapped-ambiguous.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/resolver-target/expected/0827-protocols-param-impl-source-wrapped-ambiguous.exit @@ -0,0 +1 @@ +1 diff --git a/tests/resolver-target/expected/0827-protocols-param-impl-source-wrapped-ambiguous.stderr b/tests/resolver-target/expected/0827-protocols-param-impl-source-wrapped-ambiguous.stderr new file mode 100644 index 0000000..abb9e6d --- /dev/null +++ b/tests/resolver-target/expected/0827-protocols-param-impl-source-wrapped-ambiguous.stderr @@ -0,0 +1,5 @@ +error: type 'Box' is ambiguous: it is declared in multiple flat-imported modules; qualify the reference or remove the duplicate import + --> examples/0827-protocols-param-impl-source-wrapped-ambiguous.sx:19:24 + | +19 | impl Into(Marker) for *Box { + | ^^^ diff --git a/tests/resolver-target/expected/0827-protocols-param-impl-source-wrapped-ambiguous.stdout b/tests/resolver-target/expected/0827-protocols-param-impl-source-wrapped-ambiguous.stdout new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/resolver-target/expected/0827-protocols-param-impl-source-wrapped-ambiguous.stdout @@ -0,0 +1 @@ + diff --git a/tests/resolver-target/expected/0829-packs-param-impl-mixed-pack-source-ambiguous.exit b/tests/resolver-target/expected/0829-packs-param-impl-mixed-pack-source-ambiguous.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/resolver-target/expected/0829-packs-param-impl-mixed-pack-source-ambiguous.exit @@ -0,0 +1 @@ +1 diff --git a/tests/resolver-target/expected/0829-packs-param-impl-mixed-pack-source-ambiguous.stderr b/tests/resolver-target/expected/0829-packs-param-impl-mixed-pack-source-ambiguous.stderr new file mode 100644 index 0000000..a29b86e --- /dev/null +++ b/tests/resolver-target/expected/0829-packs-param-impl-mixed-pack-source-ambiguous.stderr @@ -0,0 +1,5 @@ +error: type 'Box' is ambiguous: it is declared in multiple flat-imported modules; qualify the reference or remove the duplicate import + --> examples/0829-packs-param-impl-mixed-pack-source-ambiguous.sx:28:31 + | +28 | impl Sink(Block) for Closure(*Box, ..$args) -> $R { + | ^^^ diff --git a/tests/resolver-target/expected/0829-packs-param-impl-mixed-pack-source-ambiguous.stdout b/tests/resolver-target/expected/0829-packs-param-impl-mixed-pack-source-ambiguous.stdout new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/resolver-target/expected/0829-packs-param-impl-mixed-pack-source-ambiguous.stdout @@ -0,0 +1 @@ + diff --git a/tests/resolver-target/expected/e6br5-nested-pack-source-ambiguous.exit b/tests/resolver-target/expected/e6br5-nested-pack-source-ambiguous.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/resolver-target/expected/e6br5-nested-pack-source-ambiguous.exit @@ -0,0 +1 @@ +1 diff --git a/tests/resolver-target/expected/e6br5-nested-pack-source-ambiguous.target.md b/tests/resolver-target/expected/e6br5-nested-pack-source-ambiguous.target.md new file mode 100644 index 0000000..4a6e06a --- /dev/null +++ b/tests/resolver-target/expected/e6br5-nested-pack-source-ambiguous.target.md @@ -0,0 +1,22 @@ +# E6BR-5 target spec (exact golden finalized at S3.9) + +Unlike the harvested `08xx` resolver-target cases — whose exact target bytes were +produced by the E6b branch and are frozen in `*.exit/*.stdout/*.stderr` here — +**E6BR-5 has no oracle that produced exact bytes today** (it is the open hole that +paused E6b; no implementation ever emitted its diagnosed form). Its target is +therefore recorded as a **specification**, not frozen bytes: + +- **exit:** `1` (see `e6br5-nested-pack-source-ambiguous.exit`). +- **stderr:** at least one `error: type 'Box' is ambiguous: it is declared in + multiple flat-imported modules; qualify the reference or remove the duplicate + import`, pointing at the nested `*Box` leaf inside + `Closure(Closure(*Box, ..$inner) -> $IR, ..$args) -> $R`. +- **stdout:** empty (the build fails before `main` runs). + +The byte-exact `stdout`/`stderr` goldens are generated by the Fork C resolver when +this case **flips to active + green at S3.9** (`run-confirm-r3`: "E6BR-5 +nested-pattern case diagnosed"). At that point this `.target.md` is replaced by +real `e6br5-nested-pack-source-ambiguous.stdout` / `.stderr` files and the case +moves to an active marker, exactly like the `08xx` flips. + +This case is the re-filed E6BR-5 regression — **explicitly NOT an E6b attempt-6.** diff --git a/tests/resolver-target/manifest.md b/tests/resolver-target/manifest.md new file mode 100644 index 0000000..763aa63 --- /dev/null +++ b/tests/resolver-target/manifest.md @@ -0,0 +1,70 @@ +# Resolver-target corpus — enumerated manifest + +**Status:** INACTIVE / xfail from S0 through S3.8. **Flips to active + green at S3.9.** +**Not part of the baseline gate** (`run_examples.sh` does not run these — no active +`examples/expected/` marker). The harness runner `run_resolver_target.sh` asserts +every case below currently FAILS to match its TARGET, so the set is never silently +dropped between S0 and S3.9. + +These cases encode **known-wrong old behavior** on `wt-stdlib-base` (E6b unmerged): +the old name-selector silently resolves a global last-wins author (exit 0) where the +TARGET is a loud ambiguity (exit 1), OR fails to resolve an own-author where the +TARGET is success (exit 0), OR resolves the wrong author (right exit, wrong bytes). +On this base the old selector is **not a valid oracle** for them — so they are NOT +baseline-green and the S2 mirror must NEVER assert `resolver == old-selector` over +this corpus (see `../../docs/fork-c/S0.2-e6b-disposition-and-two-corpus-partition.md`). + +The `08xx` TARGET goldens here are the **exact bytes** the E6b branch +(`flow/stdlib/E6b @ af737b0`) produced; copied verbatim, never the transitional src. +`e6br5-…` has a spec target only (`*.target.md`) — its exact bytes are produced by +the Fork C resolver at S3.9. + +## Failure classes + +- **SILENT-RESOLVE (ambiguous):** base exits 0 (silently picks last-wins) where + TARGET is exit 1 (loud ambiguity). The resolver must error. +- **UNDER-DIAGNOSE (ambiguous):** base exits 1 but emits FEWER ambiguity + diagnostics than the TARGET (catches one site, silently resolves the rest). +- **WRONG-AUTHOR (own-wins):** base exits 0 but resolves the wrong author → + garbage runtime bytes vs the TARGET stdout. +- **OWN-WINS-FAILS (own-wins):** base exits 1 (fails to resolve the own-author) + where TARGET is exit 0 (own-author wins and the program runs). + +## Manifest (18 cases) + +| # | case (source tree under `examples/.sx`) | surface | class | base-now | target | note | +|---|---|---|---|---|---|---| +| 1 | `0811-modules-same-name-error-set-ambiguous` | bare error-set ref (size_of / annotation / type-as-value / match-arm / `!E` channel) | SILENT-RESOLVE | exit 0, silent | exit 1, **5** ambiguity diags | **old selector is wrong here on the E6b-unmerged base** — pre-E6b the `type_bridge.resolveInlineErrorSet` `findByName` short-circuit interned one global last-wins `IoErr` and exited 0 | +| 2 | `0812-modules-same-name-error-set-own-wins` | own error-set author wins | OWN-WINS-FAILS | exit 1 | exit 0, `…` | old path fails to resolve the own-author error-set | +| 3 | `0813-modules-same-name-error-set-lambda-own-wins` | own error-set in lambda return channel | OWN-WINS-FAILS | exit 1 | exit 0 | lambda `-> !E` own-author not resolved by old path | +| 4 | `0814-modules-same-name-error-set-lambda-ambiguous` | ambiguous error-set in lambda return channel | SILENT-RESOLVE | exit 0, silent | exit 1, 1 diag | old path silently resolves the `!E` channel | +| 5 | `0815-route-all-new-surfaces-ambiguous` | `*Box` / `union{Box}` / `enum{Box}` / inline-union ambiguous | UNDER-DIAGNOSE | exit 1, **<5** diags | exit 1, **5** diags | old path catches one site, silently resolves the rest | +| 6 | `0816-route-all-new-surfaces-own-wins` | own author across the new surfaces | OWN-WINS-FAILS | exit 1 | exit 0 | new surfaces' own-author not resolved by old path | +| 7 | `0817-modules-qualified-annotation-single-import-resolve` | qualified annotation, single import | OWN-WINS-FAILS | exit 1 | exit 0 | qualified annotation not resolved by old path | +| 8 | `0818-modules-qualified-annotation-own-wins` | qualified annotation, own wins | OWN-WINS-FAILS | exit 1 | exit 0 | — | +| 9 | `0819-modules-qualified-annotation-error-set-own-wins` | qualified error-set annotation, own wins | OWN-WINS-FAILS | exit 1 | exit 0 | — | +| 10 | `0820-protocols-same-name-method-own-wins` | own protocol-method author wins | OWN-WINS-FAILS | exit 1 | exit 0 | protocol dispatch own-author not resolved by old path | +| 11 | `0821-protocols-same-name-method-ambiguous` | ambiguous protocol-method | SILENT-RESOLVE | exit 0, silent | exit 1, 1 diag | old path silently resolves the protocol head | +| 12 | `0822-route-all-own-wins-surfaces` | own author across route-all surfaces | OWN-WINS-FAILS | exit 1 | exit 0 | — | +| 13 | `0824-protocols-same-name-method-wrapped-own-wins` | wrapped protocol-method, own wins | OWN-WINS-FAILS | exit 1 | exit 0 | — | +| 14 | `0825-protocols-same-name-method-wrapped-ambiguous` | wrapped protocol-method, ambiguous | SILENT-RESOLVE | exit 0, silent | exit 1, 1 diag | — | +| 15 | `0826-protocols-param-impl-source-wrapped-own-wins` | wrapped param-impl source, own wins | WRONG-AUTHOR | exit 0, `v=` | exit 0, `v=7 dep=9` | base resolves the wrong author → garbage field value | +| 16 | `0827-protocols-param-impl-source-wrapped-ambiguous` | wrapped param-impl source, ambiguous | SILENT-RESOLVE | exit 0, silent | exit 1, 1 diag | — | +| 17 | `0829-packs-param-impl-mixed-pack-source-ambiguous` | mixed pack-closure param-impl, concrete `*Box` prefix ambiguous | SILENT-RESOLVE | exit 0, silent | exit 1, 1 diag | **old selector is wrong here on the E6b-unmerged base** — pre-E6BR-4 the `*Box` collision fell to the no-author `resolveTemplateSignatureType` wrapper (global last-wins) and registered silently | +| 18 | `e6br5-nested-pack-source-ambiguous` (tree under `tests/resolver-target/cases/`) | NESTED concrete `*Box` leaf inside `Closure(Closure(*Box,..)->.., ..)->..` | SILENT-RESOLVE | exit 0, silent | exit 1, ≥1 diag (spec, S3.9) | **re-filed E6BR-5** — the open nested-pattern hole that paused E6b (`walkConcreteSigArgs` lower.zig:14686 skipped nested args); subsumed by the whole-AST resolver, **NOT an E6b attempt-6** | + +## Provenance + +- Cases 1–17: source trees harvested from `flow/stdlib/E6b @ af737b0` into + `examples/.sx` (+ sibling module dir), goldens copied verbatim from + `flow/stdlib/E6b:examples/expected/.*` into `expected/.*` here. The + transitional E6b src was NOT harvested. +- Case 18 (E6BR-5): authored reproducer (no E6b tree ever existed); lives entirely + under `cases/` with a spec target in `expected/…target.md`. + +## Flip at S3.9 + +Each row maps to an active `examples/expected/.{exit,stdout,stderr}` marker +when it flips (the `08xx` trees are already in `examples/`; only the goldens move +from `expected/` here to `examples/expected/`). After S3.9 this harness is empty and +every entry above is an active, green baseline test validated against its TARGET. diff --git a/tests/resolver-target/run_resolver_target.sh b/tests/resolver-target/run_resolver_target.sh new file mode 100755 index 0000000..567774e --- /dev/null +++ b/tests/resolver-target/run_resolver_target.sh @@ -0,0 +1,89 @@ +#!/bin/bash +# Resolver-target xfail harness (Fork C S0.2). +# +# NOT part of the baseline gate. The baseline gate is `zig build && zig build test +# && bash tests/run_examples.sh` over the BASELINE-GREEN corpus only; this harness +# is a separate, listed diagnostic so the resolver-target corpus is never silently +# dropped between S0 and S3.9. +# +# Contract (S0 -> S3.8): every case below currently FAILS to match its TARGET +# golden on wt-stdlib-base (the old selector is known-wrong for it). This script +# runs each case and asserts the MISMATCH. It exits 0 when ALL cases still xfail +# (as expected today), and exits 1 if any case unexpectedly MATCHES its target — +# which means that case is actually baseline-green and must be re-classified +# (moved to examples/expected/ with an active marker), not silently left here. +# +# At S3.9 the Fork C resolver makes these pass; the flip is then performed by +# moving each golden to examples/expected/ and this harness goes empty. +# +# Usage: bash tests/resolver-target/run_resolver_target.sh + +set -uo pipefail +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)" +SX="$ROOT_DIR/zig-out/bin/sx" +EXP_DIR="$SCRIPT_DIR/expected" +TIMEOUT=10 + +normalize() { + sed -E \ + -e 's/0x[0-9a-f]{4,}/0xADDR/g' \ + -e 's#(/[^[:space:]]*)?/(examples|issues)/#\2/#g' +} + +if [[ ! -x "$SX" ]]; then + echo "error: $SX not built — run 'zig build' first" >&2 + exit 2 +fi + +TMP_ERR="$(mktemp)" +trap 'rm -f "$TMP_ERR"' EXIT + +XFAIL=0 # currently-failing as expected +LEAKED=0 # unexpectedly matches target -> must be reclassified baseline-green +MISSING=0 + +for exit_file in "$EXP_DIR"/*.exit; do + [[ -e "$exit_file" ]] || continue + name=$(basename "$exit_file" .exit) + + # Source tree: harvested 08xx live under examples/; e6br5 under cases/. + if [[ -f "$ROOT_DIR/examples/${name}.sx" ]]; then + sx_file="$ROOT_DIR/examples/${name}.sx" + elif [[ -f "$SCRIPT_DIR/cases/${name}.sx" ]]; then + sx_file="$SCRIPT_DIR/cases/${name}.sx" + else + printf ' %-55s MISSING-SOURCE\n' "$name" + MISSING=$((MISSING + 1)) + continue + fi + + actual_out=$(timeout "$TIMEOUT" "$SX" run "$sx_file" 2>"$TMP_ERR" | normalize) + actual_exit=${PIPESTATUS[0]} + actual_err=$(normalize < "$TMP_ERR") + + target_exit=$(cat "$exit_file") + # spec-only targets (e6br5) have no .stdout/.stderr — compare exit only. + if [[ -f "$EXP_DIR/${name}.stdout" ]]; then + target_out=$(normalize < "$EXP_DIR/${name}.stdout") + target_err=$(normalize < "$EXP_DIR/${name}.stderr") + if [[ "$actual_exit" == "$target_exit" && "$actual_out" == "$target_out" && "$actual_err" == "$target_err" ]]; then + matches=1 + else + matches=0 + fi + else + [[ "$actual_exit" == "$target_exit" ]] && matches=1 || matches=0 + fi + + if [[ $matches -eq 1 ]]; then + printf ' %-55s LEAKED (matches target on base -> reclassify baseline-green)\n' "$name" + LEAKED=$((LEAKED + 1)) + else + printf ' %-55s xfail (base exit=%s, target exit=%s)\n' "$name" "$actual_exit" "$target_exit" + XFAIL=$((XFAIL + 1)) + fi +done + +echo "$XFAIL xfail (expected), $LEAKED leaked, $MISSING missing-source" +[[ $LEAKED -eq 0 && $MISSING -eq 0 ]] From ca8bc85120365ebdb6f9f94258b92acca965a45d Mon Sep 17 00:00:00 2001 From: agra Date: Tue, 9 Jun 2026 10:47:42 +0300 Subject: [PATCH 2/2] docs(fork-c/S0): correct two doc-accuracy lines (base-equivalence wording + grounded FFI count) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit attempt-2 review fixes (docs-only; contract mechanics confirmed sound): - README + S0.2 grep-clean: 'S0 HEAD == base' / 'S0 == base' were inaccurate (HEAD carries the docs/examples/tests diff). Reword to: production/compiler behavior is base-equivalent — zero src/ changes, single-author output byte-identical to base by construction — HEAD is a distinct commit, not base. - S0.3 ledger: drop the stale '116-class corpus' FFI wording for the grounded live count (96 entry trees / 95 active markers), matching the S0.1 count note. No partition / manifest / examples / harness change. Gate green: zig build + zig build test (LSP sweep 574, no crash) + run_examples (540/0); m3te ios-sim build via main binary exit 0. --- docs/fork-c/README.md | 5 ++++- docs/fork-c/S0.2-e6b-disposition-and-two-corpus-partition.md | 2 +- docs/fork-c/S0.3-reuse-delete-ledger.md | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/fork-c/README.md b/docs/fork-c/README.md index ff403a3..a46ea41 100644 --- a/docs/fork-c/README.md +++ b/docs/fork-c/README.md @@ -21,7 +21,10 @@ tree would not be committed; the S0 contract must be committed. `docs/` is track - **Base:** `wt-stdlib-base @ 1f755284d98c6e8ebba953045c06e35d8cbe6278` (A–E6a merged). - **E6b:** `flow/stdlib/E6b @ af737b0` — PAUSED, **unmerged**, all transitional, destined for S3/S6 deletion. Its semantics goldens are harvested; its src is never merged. -- **This branch:** `flow/stdlib/S0` (branched from the base; S0 HEAD == base). +- **This branch:** `flow/stdlib/S0` (branched from the base). **Production/compiler + behavior is base-equivalent** — zero `src/` changes, single-author output + byte-identical to base by construction — but S0 HEAD is a distinct commit carrying + the docs/examples/tests diff (it does **not** equal base). ## Contents diff --git a/docs/fork-c/S0.2-e6b-disposition-and-two-corpus-partition.md b/docs/fork-c/S0.2-e6b-disposition-and-two-corpus-partition.md index 665e6c7..64e4bfb 100644 --- a/docs/fork-c/S0.2-e6b-disposition-and-two-corpus-partition.md +++ b/docs/fork-c/S0.2-e6b-disposition-and-two-corpus-partition.md @@ -26,7 +26,7 @@ the leaf it polices no longer exists (reconciled §5). A–E6a stays merged pure byte-identity rule / `RawDeclRef` facts → the `DeclId` seed) — see `S0.3-reuse-delete-ledger.md`. -**Grep-clean (verified on this branch, S0 == base):** +**Grep-clean (verified on this branch — `src/` is base-equivalent, zero src changes):** | check | base / S0 | E6b | |---|---|---| diff --git a/docs/fork-c/S0.3-reuse-delete-ledger.md b/docs/fork-c/S0.3-reuse-delete-ledger.md index babae16..1294bc3 100644 --- a/docs/fork-c/S0.3-reuse-delete-ledger.md +++ b/docs/fork-c/S0.3-reuse-delete-ledger.md @@ -22,7 +22,7 @@ is never merged (see `S0.2-…`). | **E-series selection rules** — own-wins / not-visible / ambiguity / direct-flat (the E1–E6a behaviors) | **resolver behavior + regression tests** (the baseline-green corpus is the mirror oracle) | S2 behavior; regressions locked S0 | | **CP rule** — body-author == layout-author | **keyed by `InstantiationId{template_decl, resolved_args}`** in the fact store | S4 | | **E6BR routed-signature cases** (the E6BR-1…4 behavioral cells) | **resolver-signature regressions** — the resolver walks every signature reference position; cases live in the resolver-target corpus, flip at S3.9 | S3.9 | -| **FFI `foreign_class_map` consumers + 116-class corpus** | parallel `DeclId`s land at S1 (map still the consumer); foreign classes keyed by `DeclId` at S4; runtime names stay **payload strings on facts** | S1 → S4 | +| **FFI `foreign_class_map` consumers + FFI corpus (96 entry trees / 95 active markers)** | parallel `DeclId`s land at S1 (map still the consumer); foreign classes keyed by `DeclId` at S4; runtime names stay **payload strings on facts** | S1 → S4 | ## B. DELETED / TRANSITIONAL — removed in S3/S6