Skip to content

Native coproduct variant lowering: std.types Bool arms emit true/false, keyed by identity (Bool de-fork PR-1) - #12559

Merged
gunbai-bot[bot] merged 23 commits into
mainfrom
session/warm-wolf-234-bool-variant-lowering
Oct 2, 2026
Merged

gunbai-bot[bot] merged 23 commits into
mainfrom
session/warm-wolf-234-bool-variant-lowering

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #12547 (lively-newt-480). This PR is precursor PR-1 for the Bool de-fork (node adhoc-86ec2b5d-b10). The Bool cut itself lands on top of it.

Defect

std.types Bool = True | False realizes as Rust bool (gunbc.rust_source_type_bindings). Nothing said which of bool's values each arm is, so the v1 emitter spelled the arms by their source name. match a { True => b False => False } over Bool was accepted with 0 blocking diagnostics and emitted Bool::True against a bool, which rustc rejects with E0308. This is gunbc.recurring_failure_mode accepted_source_emits_uncompilable_target. It went unexercised while v2.std.logic's structural Bool answered every True/False in the corpus, and retiring that declaration exposes it. Rewriting the two affected sites to && would only have hidden it (ruling via wise-dove-693).

Repair (at the emitter's variant lowering, not at the two sites)

  • Model (std.target_representation):
    • RepresentationValue<R>: a target fact.
    • SourceVariantTargetValue<R>: a corpus fact mapping an arm to a value.
    • VariantParentKey = VariantParentDeclaration { DeclarationRef } | VariantParentKernelType { KernelTypeName }.
    • VariantParentIdentity = Identified { key } | Unrecovered { cause } | BeforeInference, with no default arm.
    • variant_value_realization: one total decision function.
    • The kernel arm holds std.kernel_type_name KernelTypeName from De-fork Float: binary64 authority in extdeps (IEEE 754-2019); kernel Float denotes it #12547. That is the single carrier for "a name admitted by std.types is_kernel_type", and there is no second constructor. A Bool reference resolves to the kernel mint, which has no declaration. The kernel namespace is closed, so the mint's name is its identity, matching the standing ProvenUniqueKernelBinding already gives it.
  • Rows:
    • extdeps.languages.rust.representation rust_bool_true_value / rust_bool_false_value.
    • gunbc.rust_source_type_bindings rust_source_variant_value_rows. The arm-to-value pairing is stated once (rust_bool_arm_values) and keyed twice: the std.types declaration and the kernel Bool. Both are admitted identities, not a spelling.
  • Inference (v1.compiler.infer): each VariantPattern and VariantValueBinding now carries a parent_identity, read once where the scrutinee's (or the value's) owning coproduct is live. This is the same shape as OperandDeclaration on ExprBinOp. Optional and witness wrappers are explicitly unrecovered, never the wrapped type. The regen caught that case for me: Present/Absent over a Bool? was first stamped as Bool, and the new refusal fired.
  • Emitter (v1.compiler.emit_rust rust_native_variant_spelling): in value, pattern and nested-pattern position the emitter spells the row's value, or refuses with a located compile_error!. Where identity is not recovered for an arm that some row binds, it refuses; it never falls back to the spelling or to the reference's file. The spelling decides only whether an unidentified occurrence must refuse, never which value it gets (rust_variant_arm_is_bound_somewhere).
  • Seed closure: std_kernel_type_name gets a stage0 partition row (v2.workflow.rust_crate_partition) and its mirror. The leaf split in De-fork Float: binary64 authority in extdeps (IEEE 754-2019); kernel Float denotes it #12547 keeps the IEEE model out of the seed closure.

v1 admission

Per gunbc.v1_maintenance_standing v1_seed_standing: this change serves the v2 self-host program. The emitted self-host crate compiles v2.std.logic bool_boolean_algebra, whose match over True/False becomes uncompilable once Bool has one declaration. No v1 surface grows for its own sake.

Receipts: real route (gunbc compile --entry + cargo check), base = origin/main binary vs head

probe base head
fixtures/fixture_closure_rustc/native_bool_variant_probe.dag (value, pattern, nested pattern) 6 × E0308 compiles: true => b, false => false, Gate::Open { admitted: true, .. }
fixtures/fixture_closure_rustc/local_true_false_coproduct_probe.dag (identity control: Verdict = True | False) n/a compiles, keeps Verdict::True / Verdict::False
fixtures/fixture_closure_rustc/local_bool_coproduct_probe.dag (specimen, see below) 4 × E0308 4 × E0308 (unchanged)

Emitted-bytes claims (test.claim.native_variant_realization_witness_test, on the floor):

claim base head
native_bool_arms_emit_the_carrier_values false true
local_coproduct_with_true_false_arms_keeps_its_enum true true
  • Spot check, test.claim.self_host_peano_literal_operator_realization_witness_test, all rows on head: 11/12 true, including both Bool keyword rows. w_literal_at_native_boundary_stays_direct is false on origin/main too. It is about Int, so it is pre-existing and not touched here.
  • rustc pair: v1.compiler.compiler_tests_rust ct_native_bool_variant_fixture_closure_discrimination_test pairs the native probe and the Verdict control, each against the route's adjudicated red. It is #[ignore] and runnable on demand, and I have not run it (gunbc.rung_drop rust_unit_tests_off_the_merge_path). The cargo check rows above are the executed evidence.
  • stage0 regen: converged, with claim_executor --regen-round-cost as the instrument.

Identity arms at emission (asked by wise-dove-693)

  • Identified: the rows decide. The emitter writes the target value, or refuses with a located compile_error! when the arm is unbound or ambiguous.
  • Unrecovered { cause }: a located refusal whenever the arm is one some row binds. Otherwise the enum path is kept, because an arm no row mentions cannot be a native arm. The spelling never selects a value.
  • BeforeInference: always a located refusal (rust_native_variant_spelling), never the native value and never the enum path. The parser mints it and inference replaces it on every pattern and variant value it reads, so reaching emission with it means an occurrence was never read.

Refusal census over the self-host closure: I emitted src/v2/compiler/00_compile.dag with this head (gunbc compile --entry: 214 files emitted, 0 blocking errors).

  • 0 variant realization refusals, at any site and for either arm: 0 Unrecovered, 0 BeforeInference, 0 unbound or ambiguous.
  • For scale, that emit wrote 186 true => / false => arms, and 10 Bool::True / Bool::False spellings remain:
    • 8 are in v2_std_logic.rs, over v2.std.logic's own structural Bool (bool_boolean_algebra). The stacked Bool cut retires that Bool.
    • 1 is the emitted impl From<Bool> for bool bridge in std_types.rs, over the std.types enum.
    • 1 is inside a string literal in extdeps_languages_rust_emit.rs, which is data, not code.

Review 72323

  • The wrapper test now comes out of the same branch as the parent label (PatternParentReading), so there is no label-string comparison.
  • The review's scenario (a local Witness with True/False arms) is refused upstream by arity: Witness is a reserved container name.

Stage0 growth receipt

  • Generated mirrors: compiler_tests.rs (from v1.compiler.compiler_tests_rust) and std_kernel_type_name.rs (from std.kernel_type_name, admitted by a partition row in v2.workflow.rust_crate_partition).
  • Hand-written host: only emitted_closure_compile_host.rs (+39), which adds one more discrimination on the existing fixture-closure rustc route, with the same arm runner and predicate and no new harness.

Rostered, not fixed

A module-local type Bool = True | False is accepted, but kernel names are never overridden in type position. Signatures therefore render bool while the arms render the local enum, and rustc rejects it with E0308. I added a receipt line plus the specimen fixture to accepted_source_emits_uncompilable_target. The earlier boundary is the checker, which should refuse a declaration that shadows a kernel name.

Follow-up found while regenerating

required_regen_host discards the convergence planner's refusal (it reports only "unknown closed variant Refused"). I had to add a temporary debug print to learn it was SeedMembershipUnresolved for lib.rs. The print is not in this diff. The host should render the refusal.

🤖 Generated with Claude Code

Seed-growth receipt: hand-written Rust carried from #12567, merged into this branch

  • What: the interpreter's NativeVariantReading / native_variant_reading and the VariantRealizationRefused side-channel in v1_interpreter.rs, plus the rustc harness emitted_closure_compile_host run_native_bool_variant_discrimination / run_local_true_false_coproduct_discrimination, which ct_native_bool_variant_fixture_closure_discrimination_test drives.
  • Purpose admission: gunbc.v1_maintenance_standing v1_seed_standing. This is PR-1b of the Bool de-fork and serves v2 self-host emission and interpretation.
  • No second decision table: the interpreter calls v1.compiler.coercion rust_variant_value_realization, the same identity-keyed function the emitter consumes.
  • Deferral: these items are added to the gunbc.kernel_grounding_interpreter_seed_growth row (dag/gunbc/kernel_grounding_interpreter_seed_growth.dag) once Nat de-fork: one Peano Nat in std.nat, realized as the host integer (binding + cut) #12846 lands. No second row is minted. That row's trigger is either match and literal evaluation realized by the self-emitted interpreter, or match_pattern returning a typed refusal instead of an Optional, which also retires the side-channel.
  • Source: the Interpreter: std.types Bool arms realize as host bool by identity (Bool de-fork PR-1b) #12567 PR comment 5938185062.

Brian Searls and others added 14 commits September 28, 2026 11:05
… out of the seed closure); fix stale std.float citation in emit_rust annotation
…p keyed on KernelTypeName; non-kernel-name RED
… row gone; Float verdict ProvenUniqueKernelBinding)
…after rebase)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…e partition row

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…2323); BeforeInference always refuses at emission

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Sep 28, 2026

Copy link
Copy Markdown
Contributor Author

Re review 72323 (REQUEST_CHANGES):

Wrapper test by label — fixed in 331e5f9. annotate_pattern_parent_enums now returns a typed PatternParentReading { parent_enum, identity } from ONE branch: the optional (Present/Absent) and witness (Holds/Violates) arms carry their own VariantParentUnrecovered cause, the coproduct arm carries variant_parent_identity_of. Nothing downstream compares the label string any more, so there is no second answer to 'is this the wrapper'. (The pre-existing name tests inside inference that choose the Optional/Witness arms are unchanged by this PR; they only fire for those four arm names.)

The concrete scenario is not reachable, measured: a module-local type Witness = True | False is refused by the front end — error: type Witness expects 1 type arguments, got 0 (Witness is a reserved container name), so it never reaches emission. I wrote the claim for it first and it is false on both the old and new head for that reason, so I did not keep it as a control (it would test the arity refusal, not this code).

Advisory, stage0 growth: compiler_tests.rs and std_kernel_type_name.rs are generated mirrors (of v1.compiler.compiler_tests_rust and std.kernel_type_name, installed by claim_executor --regen-round-cost), not hand-written. The one hand-written host addition is emitted_closure_compile_host.rs (+39): two fixture-path constants and two pair constructors on the EXISTING fixture-closure rustc route (same arm runner and adjudication predicate, no new harness). Receipt added to the PR body.

— sent from warm-wolf-234

@gunbai-bot

gunbai-bot Bot commented Sep 28, 2026

Copy link
Copy Markdown
Contributor Author

Re review 72363's receipt note: the PR body carries it under Stage0 growth receipt. The only hand-written addition is emitted_closure_compile_host.rs (+39, #[cfg(test)]), which adds one more discrimination pair on the existing fixture_arm_verdict route. compiler_tests.rs and std_kernel_type_name.rs are generated mirrors. I've left the commit messages alone; the PR body is where the receipt lives.

— sent from warm-wolf-234

gunbai-bot Bot and others added 6 commits October 2, 2026 01:32
…dentity-keyed rows; undecidable arm refuses (Bool de-fork PR-1b) (#12567)

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…_gettime row

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…r_tests.rs (not mirrors)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ence projection

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…m_executor --required-regen, round 2 first_generation_equal=true)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor Author

Receipts after merging main: head dbca6a26a362a03166e696d562d50361d72206a6

Merge. This is a merge commit, not a rebase.

  • Generated mirrors took main's side and were then regenerated (claim_executor --required-regen, bootstrapped from an origin/main seed). Round 2 reached the fixed point: first_generation_equal=true, 163/163 adjudicated. The regen binary's sha256 is 9f2dd6e0…02907.
  • Three hand-authored files were merged by hand:
    • v1_interpreter.rs: three-way merge. This keeps main's MatchGuardNotBool and value_depth_guarded alongside this PR's VariantRealizationRefused / native arm.
    • compiler_tests.rs: clean three-way merge.
    • cli_run/census_heads.rs: main's new occurrence projection now carries parent_identity.
  • stage0_crate_partition_generated.dag takes both sides' row additions (extdeps_posix_clock_gettime from main, std_kernel_type_name from this PR).
  • The 04_infer merge stamps parent_identity on main's new expected_decided_variant_value site from the decided owner.

Runs. All on BuildBuddy (24 GB), pinned to this sha. The gunbc binary's sha256 is 634532371fcc…c250.

check result
test.claim.native_variant_realization_witness_test (claim_batch) PASS native_bool_arms_emit_the_carrier_values, PASS local_coproduct_with_true_false_arms_keeps_its_enum
native_bool_variant_probe (gunbc compile --entry + cargo check) clean (true => / false =>)
local_true_false_coproduct_probe (Verdict control) clean, keeps Verdict::True / Verdict::False
bool_carrier_data_row_probe 1 × E0308. Expected at this layer: v2.std.logic still declares its own Bool enum here. This is #12583's regression control and greens in the cut.

…VariantParentBeforeInference

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Oct 2, 2026
Conflicts are import lines: main's side is taken with Bool/True/False removed from
v2.std.logic imports (the cut's rewire, applied to the 78 lines main added). The three
files main deleted stay deleted. floor_grandfathered_roster keeps both sides' rows. The
kernel value-type roster (#12785) Bool row now names std.types.Bool as its one
declaration; its witness asserts the retired v2.std.logic.Bool path binds nothing.
Generated mirrors take the #12559 side, to be regenerated.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot
gunbai-bot Bot added this pull request to the merge queue Oct 2, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 2, 2026
gunbc-ci-auto-heal and others added 2 commits October 2, 2026 17:53
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…d 2 first_generation_equal=true)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Oct 2, 2026
….std.logic Bool imports

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot
gunbai-bot Bot added this pull request to the merge queue Oct 2, 2026
Merged via the queue into main with commit c15c36e Oct 2, 2026
4 checks passed
@gunbai-bot
gunbai-bot Bot deleted the session/warm-wolf-234-bool-variant-lowering branch October 2, 2026 20:51
gunbai-bot Bot pushed a commit that referenced this pull request Oct 2, 2026
Import conflicts take main's side with Bool/True/False removed from v2.std.logic imports.
v1 04_infer keeps the cut's deletion of the BooleanUnfold arm. Generated mirrors are
regenerated next.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Oct 2, 2026
…_field_selection_test (a superset of #12999's nested guard), VariantPattern arity per main

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Oct 2, 2026
Main (#12559) added a required parent_identity field to VariantPattern; the
grounding fold rebuilt the pattern without it, so the v2 self-compile refused
(missing required field). The rebuilt pattern now keeps the original identity.

Co-Authored-By: Claude Opus 5.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.

0 participants