Skip to content

fix(typecheck): resolve use before checking, under the backends contract - #3413

Merged
gHashTag merged 1 commit into
masterfrom
fix/typecheck-resolves-imports
Sep 7, 2026
Merged

fix(typecheck): resolve use before checking, under the backends contract#3413
gHashTag merged 1 commit into
masterfrom
fix/typecheck-resolves-imports

Conversation

@gHashTag

@gHashTag gHashTag commented Sep 7, 2026

Copy link
Copy Markdown
Owner

Closes #3412

Every gen-* path splices imported declarations in before compiling. typecheck read the raw source. So a type arriving through an import read as undeclared, and the unknown-type check I shipped one pass ago in #3409 warned about types the spec correctly imports.

This is my own defect, found by measuring my own output rather than by anyone reporting it.

Measured, all 651 specs

before after
unknown type warnings 1283 1045
all warnings 1775 1570
exit-code changes 0

specs/base/ternary_add.t27 alone goes from 10 warnings to 0: it writes use base::types; and every type it names comes from there.

Counted a second way before the repair, by asking whether each warned name appears as a declaration in the resolved output: 41 distinct names across 29 of the 169 flagged files were false, against 228 true. Precision was about 85% — not broken, but noise in a check whose only job is to be believed.

The naive repair is wrong, and the reason was already written down

run_gen carries the contract:

Safety contract … this may only ADD declarations, never break a spec. If the spliced source stops compiling, the original is used and the spec generates exactly what it generated before.

Resolving without that fallback took specs/nn/hslm.t27 from exit 0 to

Error: Expected RParen, got Eof () at line 652:1

while all four backends still compiled it — because they fall back and my first version did not. The splice can produce source the parser rejects; that is a handled condition, not a verdict about the spec.

With the contract mirrored: exactly 1 of 651 specs takes the fallback, and it says so on stderr rather than silently checking something other than what it claims to.

Test

One new, seven green in the file. It runs the shipped binary on specs/base/ternary_add.t27 and asserts zero unknown-type warnings, skipping loudly if the spec is absent.

Mutation-checked: disabling the resolve fails it with the measured number — "it warned 10 times".

Named, not done

Trit is declared by four specs as pub const Trit = enum(i8) and simply not imported by the files that use it — specs/ar/restraint.t27 uses it with no use statement at all. Adding one line there takes its emitted Rust from 30 rustc errors to 19 and brings pub enum Trit into the output. That is a spec-side repair with a measured payoff and deserves its own change rather than riding along here.

compiler.rs is untouched, so FROZEN_HASH is unchanged.

…ntract

Closes #3412

Every `gen-*` path splices imported declarations in before compiling.
`typecheck` read the raw source. So a type arriving through an import read as
undeclared, and the unknown-type check added one pass ago in #3409 warned about
types the spec correctly imports -- my own defect, found by measuring my own
output.

Measured over all 651 specs:

  unknown type warnings   1283 -> 1045
  all warnings            1775 -> 1570
  exit-code changes                  0

specs/base/ternary_add.t27 alone goes from 10 warnings to 0: it writes
`use base::types;` and every type it names comes from there. Counted a second
way before the repair -- asking whether each warned name appears as a
declaration in the RESOLVED output -- 41 distinct names across 29 of the 169
flagged files were false, against 228 true.

The naive repair is wrong, and the reason is already written down. run_gen
carries the contract:

    Safety contract ... this may only ADD declarations, never break a spec. If
    the spliced source stops compiling, the original is used and the spec
    generates exactly what it generated before.

Resolving without that fallback took specs/nn/hslm.t27 from exit 0 to
`Expected RParen, got Eof at line 652:1`, while all four backends still
compiled it -- because they fall back and this did not. The splice can produce
source the parser rejects; that is a handled condition, not a verdict about the
spec. With the contract mirrored, exactly 1 of 651 specs takes the fallback and
says so on stderr rather than silently checking something other than what it
claims to.

Mutation-checked: disabling the resolve fails the new test with the measured
number, "it warned 10 times".

compiler.rs is untouched, so FROZEN_HASH is unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@gHashTag
gHashTag enabled auto-merge (squash) September 7, 2026 20:53
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

📓 NotebookLM Notebook linked to this PR

This notebook contains session context, decisions, and artifacts for this work.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-09-07 20:53:40 UTC

Summary

Status Count
Total Open PRs 16
PRs with Failing Checks 14
PRs with All Checks Green 2
READY 2
FAILING 14
PENDING 0
NO CHECKS YET 0

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=fa7f44546531 != 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).

@gHashTag
gHashTag merged commit 03fe7b2 into master Sep 7, 2026
31 of 34 checks passed
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.

typecheck reads the raw source while every backend resolves use first

1 participant