Skip to content

t27c: the use splice reads a name inside a pulled declaration in that declaration's module (Closes #10556) - #11012

Merged
gHashTag merged 5 commits into
masterfrom
t27c/splice-same-name-10556
Oct 19, 2026
Merged

gHashTag merged 5 commits into
masterfrom
t27c/splice-same-name-10556

Conversation

@gHashTag

Copy link
Copy Markdown
Owner

Closes #10556. Unblocks the same / type_text part of epic #10879 ("t27b for t27o").

What was wrong

use_resolve.rs keys every imported declaration by bare name. When two imported modules declared one name differently it refused both (// UNRESOLVED <name> ... ambiguous) and pulled neither. That is right for a name the importer wrote and wrong for a name found inside a pulled declaration: admit in service.t27 calls name_ok, and the only name_ok its author can mean is the one in service.t27. t27c gen still exited 0 and the generated file called a function it never declared.

A second defect sat on the same path and is what actually stopped tri t27o bootstrap/src/compiler.rs --check: a path inside a pulled declaration (type_text::same in compound_binop, pulled from operator_text) kept its type_text::. The importer's own paths were always flattened (#5574); pulled ones were not, so the flat file declared same and called type_text.same. This one needs no ambiguity at all.

The rule, and where it lives

specs/compiler/use_splice.t27 (new; no spec for use resolution existed: git grep "UNRESOLVED|ambiguous" specs finds comments only). t27c gen-rust writes bootstrap/gen/rust/compiler/use_splice.rs (byte-equal to a regeneration), which use_resolve.rs mounts by #[path], as compiler.rs mounts operator_text. Five rules, each with tests:

  • R1 scope: a name inside a pulled declaration of module O is read in O's scope: O's own declaration, else the one its direct imports make (scope_index).
  • R2 only on ambiguity: consulted only for a name the whole closure cannot answer (one declaration, agreeing declarations, an alias). A name with one answer keeps it.
  • R3 flat names: the output is one namespace, so scoped copies are named <module path>__<name> (flat_name) and the uses in that module's texts are rewritten (rewrite). A qualified reference the importer writes (beta::name_ok) is read in beta.
  • R4: a bare name the importer writes, declared differently by two imported modules, is still UNRESOLVED.
  • R5: in a pulled text, stem::name loses stem:: when the stem is a module that text's module imports and the splice pulled name. A path to a name not pulled (gf16::GF16) is left alone.

Also moved into the spec, line for line: all_agree/normalised/without_comment (now same_decl) and alias_target (now alias_class), so the hand Rust shrinks.

t27b needs no change: cli/t27b/src/front.rs calls the same use_resolve::resolve (mounted by #[path]). Measured: t27b test on the issue's importer prints PASS importer: serves with the branch; the base t27b says unsupported construct ExprCall(undeclared fn) ... call to name_ok. specs/compiler/core/t27core.t27 keeps refusing a name two modules declare (E_USE, #7939); that is conservative, not a mismatch, and it is untouched.

Hand-written lines (own-language gate)

file added deleted
bootstrap/src/use_resolve.rs 38 188
bootstrap/tests/unresolved_use.rs 38 0
total counted 76 188 (net -112)

The gate compiled from the merge base's gen/c/policy/own_language.c and fed git diff --numstat --no-renames BASE...HEAD: rc=0 (per file <= 40, total <= 80, net <= 0). FROZEN_HASH is untouched: compiler.rs is not edited. bootstrap/gen/ is gitignored, so the generated file needs git add -f.

Generated output that changed

gen, gen-rust, gen-c, gen-verilog over every spec with a use line (756, the new spec aside), merge-base t27c against this branch's: 20 differ, 736 are byte-identical. gen-verilog is identical for all. UNRESOLVED lines in the corpus go from 20 to 9; the 9 left are names the importer writes itself (R4) or two modules' real disagreement. None of the 20 compiled in Zig before and none stopped compiling; the error count never rises (it falls in 6) and the first error changes in 5.

Had an UNRESOLVED line (11), before -> after:

spec before after
demos/jones_topology_filter constants.abs(jones - PHI), UNRESOLVED abs math_constants__abs(jones - PHI), no UNRESOLVED
demos/jones_topology_decision_gate constants.abs(...) math_constants__abs(...); UNRESOLVED abs stays (importer writes it)
fpga/bridge [_]TernaryWord{ operand_a }, UNRESOLVED TernaryWord, pack_trit [_]fpga_mac__TernaryWord{ ... }, fpga_mac__pack_trit, none left
fpga/testbench/mac_tb [_]TernaryWord{ ... }, 2 UNRESOLVED fpga_mac__TernaryWord; 1 left (importer writes TernaryWord)
igla/coder/_tmp_pipeline_import eval.check_sacred_compliance(verilog), 5 UNRESOLVED igla_coder_eval__check_sacred_compliance(verilog); 1 left (BeamCandidate)
physics/pellis-formulas sacred_physics.TRINITY, UNRESOLVED TRINITY math_sacred_physics__TRINITY, none left
physics/sacred_verification sacred_physics.TRINITY math_sacred_physics__TRINITY; UNRESOLVED TRINITY stays (importer writes it)
physics/zamolodchikov_4d_conjecture constants.PHI PHI; UNRESOLVED abs stays
test_framework/verilog_bench_harness gf16_decode_to_f32(a) undeclared, 2 UNRESOLVED helper declared, none left
cloud/railway_deploy []Trit (pulled alias) []vsa_vsa_core__Trit; UNRESOLVED Trit stays
neural/forward_pass []Trit in a pulled fn vsa_vsa_core__Trit; UNRESOLVED Trit stays

Had no UNRESOLVED line but named an undeclared path (R5, 9): compiler/pipeline (parser.Node -> Node, 45 uses), numeric/gf4, gf8, gf12, gf20, gf24, gf32, gf64 (sacred_physics.PHI_INV -> PHI_INV), ml/transformer/mha_block (C and Rust only: undeclared gf16_GF16 -> the declared GF16).

Unchanged and still refused: base_ring_32 (TRINITY), claw/control_pins (COUNT).

Seals

Only the new spec is sealed (.trinity/seals/compiler_UseSplice.json, 23/23, seal --verify all MATCH). No existing seal needs re-saving: a seal hashes the unspliced source, so none of the 20 changes moves a gen hash. Measured: t27c seal --verify over all 756 importing specs prints the same bytes with the merge-base binary and this branch's (0 differ); 19 of the 20 changed specs have seals and all MATCH. tools/check_seal_coverage.py itself was not run in full (about 2900 t27c seal calls).

Tests

  • specs/compiler/use_splice.t27: 23 tests; t27b test 23 passed, t27c test-report 23/23, 0 vacuous. They pin each rule, the old agree/alias cases, and negatives (member, comment, string, field, parameter, variant, a path to a name not pulled).
  • bootstrap/tests/unresolved_use.rs, through the shipped binary, with the issue's shape (two modules each declare ok, a pulled fn calls its own): both helpers spliced under r_a__ok / r_b__ok, no UNRESOLVED; a qualified b::ok takes r_b__ok; negative control: a bare ok the importer writes is still refused (UNRESOLVED ok: declared in r/a.t27 and r/b.t27); a path in a pulled declaration loses its module. cargo test -p t27c --test unresolved_use: 7 passed (4 new). --bin t27c use_resolve: 31 passed (the existing two_real_definitions_stay_ambiguous and a_listed_item_two_modules_declare_stays_unresolved among them).
  • Issue repro on the merge base: UNRESOLVED name_ok and zig test says use of undeclared identifier 'name_ok'. On the branch: both repro_alpha__name_ok and repro_beta__name_ok are declared, zig test passes, and the control file is byte-identical. The real modules (use t27mcp::service::admit; use t27mcp::tool_schema;): base UNRESOLVED name_ok and the same zig error; branch: empty stderr, zig test passes.

tri t27o on the motivating files (branch t27c/t27b/tri, run from a t27 checkout)

  • bootstrap/src/compiler.rs --check: merge base BLOCKED does not compile: use of undeclared identifier 'type_text' (rc=2; t27b also said undeclared fn ... type_text::same). Branch: t27b stops only on @max (a t27b gap, not this change), t27c judges: 87 tests, 87 pass, 4 vacuous, rc=0.
  • bootstrap/src/main.rs --check: the UNRESOLVED same (sha256 vs type_text) and type_text errors are gone. Still BLOCKED (rc=2) on t27a_ASM_NO_MNEMONIC undeclared (the converter writes the Rust path t27a::ASM_NO_MNEMONIC) and t27b trim_end_matches: separate defects, not touched.

Traps and what was not run

  • identifiers reads ; prose lines, so a comment in the importer that names the ambiguous name makes it a name the importer wrote. The issue's importer.t27 has one in its header: on the branch its stderr still prints UNRESOLVED name_ok although the output is right; with that line dropped stderr is empty. Not changed here: it would change the output of specs that resolve today.
  • The splice also rewrites module::name inside string literals of the importer, so a test string in a spec that imports type_text cannot contain type_text::same.
  • dupe_scan.py is red on this branch with 12 copied bodies (at_text, code_at, ..., weight) and on the merge base with the identical 12; this change adds no group. check_devhome_paths.py: OK.
  • Not run: the whole cargo test -p t27c suite and core_selfhost; deterministic_output (1 passed) and dotted_module_name (9 passed) ran on an earlier commit of this branch, not on the final head; the wasm-explorer crate (it mounts use_resolve.rs the way compiler.rs is mounted) was not built; tools/check_seal_coverage.py in full; the full 2398-spec tree (specs without a use line return from resolve untouched).
  • Not done: replacing home_of with service::admit in claw_serves (PR t27f: frontend.t27 -- T27F is t27's frontend, every visual surface in one law, served by TRI CLAW (Closes #10537) #10551's follow-up).

🤖 Generated with Claude Code

claude and others added 5 commits October 19, 2026 15:27
… declaration's module; the rule is specs/compiler/use_splice.t27 (Closes #10556)

Two imported modules that each declare one helper name made the splice drop
both, so a pulled fn called a helper the generated file never declared. A name
found inside a pulled declaration now resolves against the module whose text
names it (its own declaration, else the one its direct imports make) when the
whole closure cannot answer it; both copies are spliced under
<module path>__<name> and the uses are renamed. A qualified reference the
importer writes is read in the module it names. A bare name the importer writes
stays UNRESOLVED.

The rule, the old all_agree/normalised/without_comment and alias_target are
specs/compiler/use_splice.t27, lowered by t27c gen-rust to
bootstrap/gen/rust/compiler/use_splice.rs and mounted by #[path]; the hand
Rust in use_resolve.rs shrinks.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…e pass in use_splice.t27 renames scoped helpers and strips imported paths (Closes #10556)

compound_binop, pulled from operator_text into a converted compiler.rs, kept
`type_text::same`; the flat file declared no `type_text`, so `tri t27o --check`
stopped on it (BLOCKED, undeclared identifier). The importer's own paths were
always flattened; pulled ones were not. use_splice.rewrite now strips
`stem::` in a pulled text when the stem is a module that text's module imports,
and applies the scoped renames in the same pass. rename_ident is folded into it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…es; seal use_splice.t27 (Closes #10556)

rewrite takes the names the splice pulled and strips `stem::name` in a pulled
text only for those, so a path to a type the backends spell themselves
(gf16::GF16) is left alone. The seal of specs/compiler/use_splice.t27 is saved
with the final spec (23 tests).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Closes #10556)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-10-19 11:23:39 UTC

Summary

Status Count
Total Open PRs 50
PRs with Failing Checks 47
PRs with All Checks Green 3
READY 2
FAILING 47
PENDING 0
NO CHECKS YET 0

These columns do not partition: 2 + 47 + 0 + 0 = 49, and there are 50 open PRs. A PR is being counted twice or not at all.

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=2d301cdb4a58 != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

t27c gen: two imported modules that each declare a helper of the same name make the splicer drop both, so a pulled function loses its own helper

2 participants