Skip to content

Filter a module's own coproduct-variant names out of the emitter's import candidates - #9461

Merged
briansrls merged 3 commits into
mainfrom
session/deep-raven-866
Aug 27, 2026
Merged

briansrls merged 3 commits into
mainfrom
session/deep-raven-866

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Aug 27, 2026 •

Copy link
Copy Markdown
Contributor

The defect

v1.05_emit_rust reference_derived_use_lines filters import candidates against the names the module declares. Its own note names the hazard exactly: the bare-name registry is last-write-wins across the closure, so a homonym elsewhere steals the row and a self-colliding pub use is synthesized (the E0255 class).

The filter was written over items — one authored name per TOP-LEVEL item. A coproduct's variants are CHILDREN of the item, so they never entered it. The guard was at the wrong grain, not missing.

A bare self-reference to a module's own variant therefore fell through to map_get(registry, name), resolved to whichever module last wrote that global name, passed export proof, and got a use-line for a symbol the module already binds itself (via use self::Enum::*;, or natively for a grounded alias).

The fix

local_coproduct_variant_names reads the variant set from each coproduct item's own children, and the result joins the already-filter.

Deliberately not via emit_info.variant_to_enum or type_summaries: both are keyed by bare name and carry the same closure-global last-write-wins, so answering a module-local question through them would reintroduce the defect one layer down.

Measurement — prediction stated first, then measured

Predicted delta: only REMOVALS of pub use lines whose imported name is a variant of a coproduct declared in the emitting module. Nothing else.

Two arms, each built from its own stage0 mirror (distinct sha256), over the emitted closure of src/v2/compiler/00_compile.dag — 175 files:

  • Membership delta: exactly one file. v2_std_node.rs loses
    pub use crate::std_constructors::{Cardinality};
    use crate::std_constructors::Cardinality::*;
    
    Cardinality is a variant of v2.std.node's own Connective; the homonym that stole the registry row is std.constructors' type Cardinality = Required | Optional. The measured set is the predicted set.
  • v1 seed: zero bytes. With the mirror installed, claim_executor --required-regen reports first_generation_equal=true planned=136 executed=136.

READ THIS BEFORE TRYING TO REPRODUCE ON A SMALL ENTRY — you will get zero, and the fix is not inert

The class is closure-size-dependent. Measured: --entry dag/std/algebra.dag (7 files) and --entry dag/std/node.dag (15 files) both show a zero delta on both arms.

That is the mechanism, not a weakness. The escape requires the homonym to be present in the same closure to win the last-write-wins registry row; an entry-scoped closure usually excludes it, and then the pre-existing info.module_name != this_module_name check already skips the name. So a per-entry emission can be clean while a wide one fabricates — and any population of this class measured at entry grain is complete over the wrong subject while looking complete.

Control failed, and that is a separate finding

Run-to-run over the same binary, BOTH the unmodified and the modified compiler emit v2_lens_enforcement_vocab.rs and v2_std_cross_tree_resolution.rs with import blocks in varying order (identical membership) — three runs of one binary differ. This instability is present on main at 38a127b, is not caused by this change, and is why the arm comparison above is stated at membership grain for those two files. It is an unordered-iteration defect (an unordered collection where an ordered one belongs); it is routed to its own owner and is not chased here. Note the scope: it makes empirical byte-comparison controls unreadable on those two files; it says nothing about structural byte-identity arguments.

Re-measured after merging main

The branch was merged onto 3bbd53c05a (which includes #9439's four-armed disposition refactor of this same function; the conflict in 05_emit_rust.dag was resolved by keeping main's function and re-applying this change on top, and the stage0 mirror was regenerated, never hand-resolved). The two-arm delta was re-run against that base:

  • v2_std_node.rs loses the same two lines — unchanged result.
  • v2_std_cross_tree_resolution.rs shows an order-only change with identical membership. That is one of the two files named in the control finding above, so it is instrument noise, not a delta.
  • claim_executor --required-regen → first_generation_equal=true planned=138 executed=138, and --required-regen-fixed-point → fixed_point_equal=true referenced_at=3bbd53c05a.

First measurement was taken against 38a127bd60; the re-measurement above is against 3bbd53c05a.

Brian Searls and others added 2 commits August 27, 2026 14:52
…port candidates

reference_derived_use_lines already filters candidates against the names the
module declares, and its own note names the hazard: the bare-name registry is
last-write-wins across the closure, so a homonym in another module steals the
row and a self-colliding `pub use` is synthesized. The filter was written over
`items` -- one authored name per TOP-LEVEL item -- and a coproduct's variants
are CHILDREN of the item, so they never entered it. The guard was at the wrong
grain, not missing.

A bare self-reference to a local variant therefore fell through to
`map_get(registry, name)`, resolved to whichever module last wrote that global
name, passed export proof, and got a use-line for a symbol the module already
binds itself.

The variant set is read from each coproduct item's OWN children, never from
`emit_info.variant_to_enum` or `type_summaries`: both are keyed by bare name and
carry the same closure-global last-write-wins, so answering a module-local
question through them would reintroduce the defect one layer down.

MEASURED, two arms, each built from its own stage0 mirror, over the emitted
closure of `src/v2/compiler/00_compile.dag` (175 files):

  membership delta: exactly one file, `v2_std_node.rs`, which loses
    pub use crate::std_constructors::{Cardinality};
    use crate::std_constructors::Cardinality::*;
  `Cardinality` is a variant of v2.std.node's own `Connective`; the homonym is
  `std.constructors`' `type Cardinality = Required | Optional`. Nothing else in
  the 175 files changes membership -- the predicted set exactly.

  regen is a fixed point with the mirror installed
  (first_generation_equal=true, 136/136), so the v1 seed closure moves zero
  bytes.

  CONTROL, and it is a finding rather than a pass: run-to-run over the same
  binary, BOTH the unmodified and the modified compiler emit
  `v2_lens_enforcement_vocab.rs` and `v2_std_cross_tree_resolution.rs` with
  import blocks in varying ORDER (identical membership). That instability is
  present on main, is not caused by this change, and is why the arm comparison
  is stated at membership grain for those two files.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
# Conflicts:
#	src/v1/05_emit_rust.dag
#	src/v1/stage0/src/v1_compiler_emit_rust.rs
@gunbai-bot

gunbai-bot Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

CI red here is main's red, not this PR's. Same failure signature, both phases, all six errors identical:

main run 33095906314 (4e5486487) and this PR's run 33097895476 (913d073) both report:

required-ci: lane=witnesses phases_run=3 failed=2
required-ci: FAILED PHASE declarations (1 finding(s))
required-ci: FAILED PHASE floor refused: modules_resolved=4136 modules_excluded=4
  dag/gunbc/fleet_fan_wiring_witness.dag:754  duplicate declaration 'srv3_wiring_with_a_duplicated_header'
  dag/product/fabric/contention.dag:23        'grant_duration_seconds' not found in module 'product.fabric.supply'
  dag/product/fabric/contention.dag:280       non-exhaustive match: missing UnobservedGrantDuration
  dag/product/fabric/contention.dag:421       non-exhaustive match: missing QuoteNotPriceableWithoutDuration
  dag/product/fabric/contention.dag:463,469   function 'grant_duration_seconds' not found in scope

The witnesses lane has been failing on main for the last five commits (4e5486487, 353529a35, b37bb5c30, 26eee4b8e, f820985cc). None of the named files is in this PR's diff, which is src/v1/05_emit_rust.dag and its generated stage0 mirror only. Nothing here is fixable from this branch.

@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review August 27, 2026 18:51
@gunbai-bot

gunbai-bot Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

Two updates now that this is out of draft.

The reordering class is independently confirmed. The control finding in the body — that both the unmodified and the modified compiler emit v2_lens_enforcement_vocab.rs and v2_std_cross_tree_resolution.rs with varying import-block order — has been measured independently over six runs on one binary and one corpus: two distinct outputs per file, sorted lines identical, every changed line a pub use, and rustfmt normalizes the difference away. So the order-only change this PR's arm comparison shows on v2_std_cross_tree_resolution.rs is instrument noise, not a delta, and the membership-grain framing in the body stands as measured. The class is owned elsewhere and is not chased here.

The CI red is main's, and both halves are owned. Per the comparison above: the duplicate declaration in dag/gunbc/fleet_fan_wiring_witness.dag was fixed by #9497, and the dag/product/fabric/contention.dag break came in with #9397 and is being repaired by #9488. Note the floor phase is currently dark on main rather than merely failing — it refuses at strict preparation, so no claims execute and no ledger is written; a green on this branch is not currently producible from main's state. Nothing in this PR's diff touches either file.

@briansrls
briansrls merged commit 58d10ac into main Aug 27, 2026
3 checks passed
@briansrls
briansrls deleted the session/deep-raven-866 branch August 27, 2026 22:48
@briansrls
briansrls restored the session/deep-raven-866 branch August 27, 2026 22:48
@gunbai-bot

gunbai-bot Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

Executed-specimen gap from node adhoc-0a3a31ae-4ee: I ran this PR head (96000c2) through the 175-file Rust emission of src/v2/compiler/00_compile.dag (BuildBuddy invocation). The compile completed with 0 blocking errors and 175 emitted files, but the node’s exact std.algebra / Optional / empty_map specimen survives in emitted src/std_algebra.rs:

pub use crate::v2_std_collection::{empty_map};
pub use crate::v2_std_optional::{Optional};

So the direct local-coproduct-child filter fixes the PR’s measured Cardinality case but does not reach this assigned specimen. I am not opening a competing implementation PR; this case should join this PR’s construction or receive an explicit disposition here. The same emitted tree had zero Option<()> / Optional<()> matches, consistent with the unit-collapse face having been repaired independently before this PR.

briansrls pushed a commit that referenced this pull request Aug 28, 2026
Same resolution as the previous merge: the single conflict was a generated
mirror, zero .dag conflicts, so stage0 was reduced to exactly main's and
the merged .dag emitted the union.

Six mirrors move again -- emit_core_support, emit_go and emit_python
alongside the three that git flags -- because adding CompilerDiagnostic
variants changes emitted match arms in every emitter matching on it.

Picks up #9461, which filters a module's own coproduct-variant names out of
the emitter's import candidates. That is this branch's candidate set, so
the export-proof population is re-measured on this tree rather than carried
across; the prediction published before measuring is that the 87 is
unchanged (all dotted cross-module, where #9461 removes bare local
self-references) and that registry-absent shrinks.
gunbai-bot Bot pushed a commit that referenced this pull request Aug 28, 2026
Recomputed at 5a62da7 (RUNNER_HEAD confirmed on the runner), replacing the
be89e23-based bytes: a mirror computed at a stale base is stale whatever CI
later reports, so the base window was closed rather than waited out.

Two-generation procedure, pristine tree at that head:
  gen-0  first_generation_equal=false, drift in exactly these four files
  install candidate, REBUILD claim_executor
  gen-1  first_generation_equal=true

The rebuild between passes is the point: a single pass verifies an emission
against a binary that predates it.

The four files are byte-identical to the be89e23-based output, which
answers a question we had declined to spend a corpus emit on: #9535 added seven
test fn declarations under dag/test/claim, enlarging the module INDEX while
leaving the regen POPULATION (the import-only union under src/v1) untouched,
and the emitted qualification did not move. #9543 independently regenerated the
same four files at the same head and got the same bytes. Two independent
negatives on index-sensitivity at this grain.

Attribution, stated because an earlier draft of this body had it wrong: the
drift is NOT #9436. It is a composition of #9486, which introduced
module_filename_collision_diagnostics, and #9461, which changed import-candidate
selection so calls to it need qualifying -- merged three minutes apart from an
identical base aea5e0d. Neither alone produces the stale bytes and there was
no textual conflict for any gate to see, which is why delete-first's census
could not surface it.

Discriminator for the next red, with its left endpoint at the base these bytes
were computed at:
  git log --oneline 5a62da7..origin/main -- 'src/v1/**/*.dag'
Empty means the population has not moved and a regen red is not base staleness.
briansrls pushed a commit that referenced this pull request Aug 28, 2026
…qualification is closure-dependent, not hand-carried wrong (#9551)

`required-regen` has refused every main push for six hours:

  required-regen: first_generation_equal=false planned=138 executed=138 declared_divergent=1 [main.rs]
  required-ci: regen FAIL generated surface drift: v1_compiler_emit_core_support.rs,
    v1_compiler_emit_go.rs, v1_compiler_emit_python.rs, v1_compiler_emit_rust.rs

THIS IS NOT A MISSED REGENERATION, and the framing matters because the obvious
reading teaches the wrong lesson. #9486 hand-carried these mirrors alongside its
`.dag` edits and its own PR run was GREEN on regen -- run 33109524057 at head
c84f377, `first_generation_equal=true`. The bytes it committed were exactly
what the emitter produced ON ITS BASE. Nothing about them changed between that
run and the merge.

WHAT THE DRIFT ACTUALLY IS. The emitted call spelling is CLOSURE-DEPENDENT.
Every one of the 28 changed lines here is one class: a bare call name where the
emitter now renders a fully-qualified `crate::<module>::<fn>` path.

  -        let filename_collisions = module_filename_collision_diagnostics(typed.clone());
  +        let filename_collisions =
  +            crate::v1_compiler_emit_core_support::module_filename_collision_diagnostics(
  +                typed.clone(),
  +            );

The bare form compiles -- the file's own `pub use crate::v1_compiler_emit_core_support::{...}`
re-export brings the name into scope -- so the committed mirror was correct Rust
with correct semantics. It simply is not what the emitter renders once the
surrounding closure changed. Two changes, each green on its own base, red in
combination: a semantic merge conflict, which no per-PR gate can see BY
CONSTRUCTION, since each PR's regen runs against a closure the other has not
joined yet.

ONE MEASURED DETAIL THAT NARROWS THE MECHANISM. The `pub use` header in
`v1_compiler_emit_go.rs` is UNCHANGED between committed and candidate -- the
import list still carries `module_filename_collision_diagnostics` -- and the
call renders qualified anyway. So the emitter's qualification decision is not
simply "is this name in the import list", and any future explanation of this
class has to account for that. #9461 (58d10ac, emitter import candidates) is
the natural other half of the pair and three of the emit_rust sites are its
code, but this PR does not assert that as established.

CHANGED-LINE CENSUS, complete, nothing elided:

  v1_compiler_emit_core_support.rs   4  authored_name_at, make_error_node -> crate::v1_std_core::
  v1_compiler_emit_go.rs             5  module_filename_collision_diagnostics
  v1_compiler_emit_python.rs         5  same
  v1_compiler_emit_rust.rs          14  same, plus is_type_def_item / is_coproduct_type / authored_name_at

NO AUTHORITY EDIT. The `.dag` is correct and untouched. Only the emitted mirror
bytes moved, and they were installed VERBATIM from `target/stage0-regen-candidate/src`
-- no hand-editing of emitted output, which is the move that created this
situation and must not be the move that resolves it.

EVIDENCE BY EXECUTION, with a non-stale instrument. `claim_executor` was built
locally from main tip b6003a4 (verified equal
to `origin/main`, not an ancestor), finishing 01:08 UTC -- so it postdates every
commit it measures. Three separate lanes reported findings from stale binaries
on this defect tonight; this one cannot be.

  BEFORE: first_generation_equal=false, FAIL naming those four files
  AFTER:  first_generation_equal=true  planned=138 executed=138

Also verified: `cargo fmt --all --check` clean (no collision with the fmt gate,
which consumes the next formatter pass of this same artifact), and
`cargo build --release -p v1-compiler` clean under `RUSTFLAGS=-D warnings`.

Regen deleted no orphan content and reached nothing under `dag/extdeps`:
`changed_paths` named exactly these four files and every other emitted file in
the 138 already matched.

DURABILITY LIMIT, STATED SO IT IS NOT LATER READ AS AN INEFFECTIVE FIX. This
mirror is correct for the closure at b6003a4. If a PR landing ahead of this
one changes that closure again, the mirror re-drifts and the gate refuses again
through no fault of this change. That is a property of the class -- emitted
bytes are a function of the whole closure, and the merge queue does not
serialize on it -- not a defect in this repair.


Claude-Session: https://claude.ai/code/session_01KQiX51cUPte6qm5zQfrbfr

Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

1 participant