check: report a top-level name declared twice - #3439
Merged
Conversation
Closes #3438 Every backend rejects a redeclaration and `t27c check` exited 0. Measured against the real compilers rather than assumed: struct A + struct A cc "redefinition of 'S'" rustc error[E0428] zig "duplicate ..." iverilog "already been declared" struct A + enum A cc "tag type that does not match" rustc error[E0428] struct A + fn A rust and C ACCEPT it -- types and values are separate namespaces; zig rejects it, one namespace per container So there are TWO findings, because one message would be false for half the cases. A same-namespace collision gets "every backend rejects a redeclaration"; a type and a function sharing a name gets "zig rejects this; rust and C do not". The corpus carries 25 of the first and 0 of the second, and that zero is printed only because a test proves the second message is reachable. The issue this closes was filed by me with the wrong diagnosis: I said the name arrived through `use`. It does not. `specs/ml/optimizer/adamw.t27` declares `AdamWConfig` itself at lines 28 and 483, with different bodies -- one ends `use_phi_betas`, the other `phi_variant`. My grep looked for `struct AdamWConfig` while the spec uses the Zig spelling `pub const Name = struct { ... }`, and finding zero I concluded it was imported. `use_resolve.rs` emits nothing twice. The compiler is right and the spec is wrong, the fourth time this campaign after #3420, #3422 and #3428. Population, from the compiler's own AST: 3 specs, 25 names -- agreeing exactly with an independent count taken from the generated Rust two passes ago. The detector walks the AST because a line-based matcher written while scoping this reported 8 specs and 35 names, and five of the eight were its own false positives: `impl`-block methods in files written in Rust rather than t27, which do not parse and emit 0 bytes. Scope is not visible to a regex. Corpus effect, old binary against new, spec by spec: 651 specs, 573 exit 0 and 78 exit 1 on both sides, and NOT ONE spec's exit code moved. The finding is a warning; promoting it is a separate, test-visible decision. Wired into Spec Guards as a per-spec ratchet rather than a total -- a ceiling on the sum is satisfied by zero and says nothing about a spec that grew while another shrank. Cost 7s, measured. The gate's self-check has three halves (the planted duplicate must be found, the clean spec must stay quiet, the cross-namespace case must take the weaker message), and the gate itself was controlled three ways: a new spec turns it red, a baselined spec that grows turns it red, and a shrink is reported as progress and passes. Four mutants, four dead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
gHashTag
enabled auto-merge (squash)
September 8, 2026 00:29
Contributor
|
📓 NotebookLM Notebook linked to this PR
This notebook contains session context, decisions, and artifacts for this work. |
Contributor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3438
Every backend rejects a redeclaration and
t27c checkexited 0.The issue I filed was wrong, and I wrote it
I diagnosed this as a type arriving twice through
use. It does not.specs/ml/optimizer/adamw.t27declaresAdamWConfigitself, at lines 28and 483, with different bodies — one ends
use_phi_betas : bool, the otherphi_variant : PhiVariant. My grep looked forstruct AdamWConfigwhile thespec uses the Zig spelling
pub const Name = struct { … }; finding zeromatches I concluded it must be imported.
use_resolve.rsemits nothing twice.The compiler is right and the spec is wrong — the fourth time this
campaign, after #3420, #3422 and #3428.
Two findings, because one message would be false
Measured against the real compilers, not assumed:
struct A+struct Aredefinition of 'S'E0428duplicate …already been declaredstruct A+enum Atag type that does not matchE0428struct A+fn Aduplicate …Types and values are separate namespaces in Rust and C; a Zig container has
one. So a same-namespace collision gets "every backend rejects a
redeclaration" and the cross-namespace case gets its own weaker, true message.
The corpus carries 25 of the first and 0 of the second. That zero is
printed only because a test proves the second message is reachable — a zero
from a message nothing could emit is worth nothing.
Population
From the compiler's own AST: 3 specs, 25 names, agreeing exactly with an
independent count taken from the generated Rust two passes ago.
specs/igla/coder/benchmark.t27specs/ml/optimizer/adamw.t27specs/file/operations.t27The detector walks the AST because a line-based matcher I wrote while scoping
this said 8 specs, 35 names. Five of the eight were its own false
positives:
impl-block methods in files written in Rust rather than t27(
Vec<PinToken>,&self,Option<&PinToken>), which do not parse and emit0 bytes. Scope is not visible to a regex.
Corpus effect
Old binary against new, spec by spec:
It is a warning. Promoting it to an error is a separate decision and the test
pins the current one.
The guard runs
Not another check that only runs when someone types it. Wired into
Spec Guardsas a per-spec ratchet, not a total — a ceiling on the sum issatisfied by zero and says nothing about a spec that grew while another shrank.
Cost 7s, measured.
Self-check, three halves: the planted duplicate must be found, the clean spec
must stay quiet, and the cross-namespace case must take the weaker message.
The gate itself was controlled three ways:
NEW: specs/_ctl_dup.t27 …exit 1GREW: …adamw.t27 10 -> 11exit 1progress: …adamw.t27 12 -> 11exit 0Mutation
enumdropped from the populationFull suite: 2598 passed, 0 failed.