Skip to content

v2 emit: Bool/declared-type spelling, bare bodies, record vs refinement, Rust refinement alias - #12715

Merged
gunbai-bot[bot] merged 20 commits into
mainfrom
session/silent-swift-419-emission-realizations
Sep 30, 2026
Merged

gunbai-bot[bot] merged 20 commits into
mainfrom
session/silent-swift-419-emission-realizations

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

Combined PR for coercion follow-ups 1-4 (runners are scarce, so one PR with one commit per item).

  1. Type references are spelled by the target through one resolver (produced_type_binding). The order is: declaration reference (DeclarationReferenceForm), then atom realization, then an explicit binding spelling, else a typed refusal. Atom rows are resolved against the base target (realization_host), because the partition's augmented bundle does not match their content hash.
  2. Bare-atom bodies ({ x }, { true }) emit through the arrow-scope atom projection.
  3. Record vs refinement is a positive declaration fact captured at normalize from the parse (refinement_declarations beside record_declarations). If neither fact holds, the member refuses as undecided; if both hold, it refuses as a kind conflict.
  4. Rust realizes a where-refinement as a target-owned transparent alias, type Pos = i32;. On main the carrier is still an unlowered parse shell, so the production route refuses emit_module_member_refinement_carrier_unlowered until v2: coercion admits refinement-to-declared-carrier casts as Widened #12407 lands. A claim with a supplied, already-lowered carrier exercises the alias itself.

Evidence: module_member_emission_test passes 13/13 locally (claim_batch).

🤖 Generated with Claude Code

gunbc-ci-auto-heal and others added 12 commits September 29, 2026 14:06
…refusal (coproducts now emit beside functions)

Side-chat ruling: heterogeneous member fold; do not classify a plain Conj
member as a record (records and where-refinements share that shape after
lowering, and the record fact does not reach emission); wire only the
unambiguous Disj; refinement realization is a later explicit case.

- v2.compiler.emit_produced: module_member_class / emit_module_member decide
  each DIRECT member of a module body once: bodied Arrow -> produced-decl rows
  (emit_produced_decl); plain Disj coproduct -> the existing
  v2.compiler.emit_semantic_decl emit_semantic_type_decl (first production
  caller); plain Conj (record or refinement) / alias / bodyless / generic /
  bodyless arrow / unsupported -> each its own located refusal.
  emit_module_body_members folds the bodies' direct members in source order
  and refuses on the first unrealized member -- nothing partial published.
  produced_module_bodies_declare_members only asks "is there a nonempty
  module"; the member fold is the one authority for "does it render".
- v2.compiler.program_partition routes through the member fold; an empty
  module keeps the translate route.
- closure_emit_arrow_body_refusal: the fn-beside-record row now expects the
  member refusal; the failure-mode row's citation follows the new function.
- New v2.test.claim.emit.module_member_emission (6 rows, WARM producers,
  production closure_emission door).

Evidence (local claim_batch): 6/6. Mutants: Conj-as-record reds refinement +
record; filtering unrenderable members reds all 5 refusal rows; refusing
coproducts reds the emission row. Regression: closure_emit_arrow_body_refusal
5/5, identity_cast_emission_route 2/2, produced_decl_two_target 3/3,
rust_module_emission_population 8/8.

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

- v2.compiler.self_host.candidate_generation generate_rust_module_emission_candidate
  routes through emit_module_body_members, as program_partition does, instead of
  emit_produced_module over the arrow-only produced_decl_conjs_in_tree, which
  skipped non-function members. Empty/no-body trees still refuse
  rust_module_emission_decl_absent.
- emit_produced_module is annotated as the fold over a SUPPLIED declaration list
  (realization_attempt and the emit-host fixtures), not a module authority.
- closure_emit_renders_an_arrow_without_its_body: prose, rung and ceiling
  rewritten; no receipt names the deleted produced_module_declares_bodied_members.

Local claim_batch: stage0_production_target 5/5, rust_module_emission_population
8/8, module_member_emission 6/6, closure_emit_arrow_body_refusal 5/5.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…are parameter and literal bodies emit)

v2.std.compilers.target_model target_project_arrow_body_by_kind's Atom arm
accepted only an int literal and refused every other atom as
transform_shape_invalid, so `fn f(x: Int) -> Int { x }` and
`fn g() -> Bool { true }` could not be emitted although the same atom emitted as
an operand. It now calls target_value_expr_project_atom_in_arrow_scope, the
projection nested operands use: parameter binding first, then the target's
literal realizations; an unresolved name refuses body_scope_binding_invalid.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…s; refinements and undecided Conj members refuse by name

A plain Conj member is a record, a where-refinement, or neither (a
payload-bearing single variant shares the shape), and lowering erases which.
Emission now classifies it from positive facts only:
- v2.compiler.body_lowering_fold body_lower_type_decl_refinement_name_optional:
  the refinement arm's own predicate (not a record block, a where host via
  body_lower_find_where_host_conj), beside the record helper.
- v2.compiler.normalize refinement_declaration_capture carries it on
  v2.compiler.normalized_tree NormalizedTree refinement_declarations beside
  record_declarations (admit_normalized_tree_with_refinements; the old
  constructor delegates with none).
- v2.compiler.emit_produced ModuleDeclarationKinds { records, refinements }
  reaches emit_module_member through program_partition
  emit_for_target_with_declarations; the closure door passes the member's
  NormalizedTree facts, callers without them pass none.
- Classification: in records -> struct via emit_semantic_type_decl; in
  refinements -> ModuleMemberRefinement (refuses not_realized until the target
  realization lands); in both -> KindConflict refusal; in neither -> undecided.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…bricated spelling (Bool emits `bool`)

v2.std.compilers.target_model produced_decl_type_ref_tokens bound every type
atom as itself, and the serializer's interned-name fallback then printed an
unmapped one raw -- `fn f(p: bool_node_symbol) -> bool_node_symbol`.

- The produced-decl render chain takes a type_binding resolver
  (produced_decl_render_from_rows and the functions under it).
- v2.compiler.emit_produced produced_type_binding resolves, in order: the
  target's atom realization of a kernel carrier -> that row's surface symbol
  (target_atom_type_spelling); an explicit binding_spellings entry for the atom;
  a type the module itself declares (module_declared_type_names) by its own
  name; otherwise refuses produced_decl_type_ref_unrealized.
- v2.extdeps.languages.rust rust_binding_spellings spells the surface symbols
  the realization rows name: bool, char, String.
- Standing, stated in the code: Int still reaches i32 through its explicit
  binding_spellings entry; the target has no Int atom realization (no integer
  value-template kind), so its route is not yet unified with Bool's.
- produced_decl_support_preserved passes a resolver binding atoms as
  themselves (its subject is support preservation, not spelling).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ements at the parse; tighten the matrix

- program_partition emit_partition passes the base target as realization_host
  down to emit_produced's type_binding: every atom realization row is bound to
  the bundle it was authored against (target_atom_realization_bundle_matches_host),
  and partition_derive_target_for_emit's augmented target matches none, so Bool
  missed on the emission path although it hits against the base target.
- body_lower_type_decl_refinement_name_optional asks the PARSE for the
  dag_surface_where_refinement_clause production shell (the parse-level form of
  the by-body where arm, as the record predicate asks for the field block);
  it was asking for the lowered where edge, which the parse does not have yet.
- module_member_emission: the record row now asserts `a: i32` and no carrier
  name; the payload single variant is a one-arm coproduct and asserts the enum.

Matrix (local claim_batch): 9/12 -- Bool signature, bare parameter and literal
bodies, refinement classification, anti-spelling record, alias/opaque/generic
refusals, coproduct emission pass. Open: struct/enum field types are spelled
raw by the semantic-decl emitter; a declared type in a signature is a
declaration-reference Conj the signature renderer does not spell.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…target, through one resolver

- Struct/enum FIELD types: v2.std.compilers.semantic_decl_emission
  target_semantic_decl_field_surface_from_edge bound the raw atom
  (`a: dag_binding_type_int`). It now takes the caller's type_binding (threaded
  through the struct and enum surface chains) and binds what it resolves; the
  serialize target lays the profile's derive rows over the HOST target's
  binding_spellings (semantic_decl_binding_spellings_from_rows_over), so `i32`
  is spelled by the map that owns it. v2.compiler.emit_semantic_decl
  emit_semantic_type_decl_resolving is the route the member fold uses; the
  resolver-less emit_semantic_type_decl stays for the routing/parity fixtures
  (an atom bound as itself), with no production caller.
- Declared-type references: v2.compiler.emit_produced produced_type_binding
  spells a resolved declaration reference by the target's own
  DeclarationReferenceForm (target_type_expr_declaration_reference_emitted --
  Rust: crate::<module>::<Name>), and every type shape now reaches the
  resolver. The bare-name declared_types population is deleted: resolved
  references never needed it.

Matrix (local claim_batch, module_member_emission): 12/12.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…sparent alias), classified from their declaration

- v2.extdeps.languages.rust rust_refinement_declaration_realization_node: the
  target-owned row (under target_model_edge_refinement_declaration) naming the
  four token classes of `type <Name> = <carrier>;` (new rust_token_kw_type lex
  rule). A transparent alias adds and drops no behaviour: the predicate is
  discharged at the crossing, and a crossing back to the carrier is realized as
  the operand unchanged.
- v2.compiler.emit_produced emit_refinement_declaration: the member fold supplies
  the name and the carrier; the row supplies every token class; the carrier is
  spelled by produced_type_binding (no "i32" here). A target with no row refuses
  refinement_not_realized; a malformed row refuses realization_malformed (typed
  Outcomes over v2.std.node_query named_child_lookup, ambiguity refused).
- The refinement row is realization metadata like the atom realizations, so
  target_model_bundle_core_keep_edge filters it from the bundle core the rows
  are matched against (without that, adding it broke every atom realization).
- STANDING: on main a where-refinement's carrier is still its unlowered
  dag_surface_type_expr parse shell (the lowering gunbc#12407 adds), so the
  production route refuses emit_module_member_refinement_carrier_unlowered at
  the carrier; the supplied-carrier row exercises the alias, and the route row
  flips to asserting it when that lowering lands.

Matrix (local claim_batch, module_member_emission): 13/13.

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

# Conflicts:
#	src/v2/compiler/emit_produced.dag
#	src/v2/compiler/program_partition.dag
#	src/v2/compiler/self_host/candidate_generation.dag
#	src/v2/test/claim/emit/module_member_emission_test.dag
#	src/v2/workflow/floor_pure_producer_share.dag
…72955); resolve fixes from main

- v2.compiler.body_lowering_fold DeclaredTypeKind = DeclaredRecord | DeclaredRefinement, decided by one
  predicate (body_lower_type_decl_declared_kind_optional) on the parse. NormalizedTree carries one
  type_declaration_kinds list in place of record_declarations + refinement_declarations, so no declaration
  can be named both; ModuleMemberKindConflict and emit_module_member_record_and_refinement_conflict delete.
  Record names are a projection (declared_record_names / normalized_tree_record_declarations), so the
  symbol-index fill, census and pattern-binder classifier read one authority. Undecided stays.
- rust_signature_general_emit_test (landed on main) passes the resolver the renderer now takes.
- type_param_binder_frame_test: the translator twins return Outcome<Node>, not ResolvedTree.

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

gunbai-bot Bot commented Sep 30, 2026

Copy link
Copy Markdown
Contributor Author

Review 72955: fixed in 79bea35. Record and refinement are now one DeclaredTypeKind coproduct (DeclaredRecord | DeclaredRefinement) per declaration, captured at normalize by a single predicate (body_lower_type_decl_declared_kind_optional). It lives on NormalizedTree.type_declaration_kinds in place of the two lists. ModuleMemberKindConflict and its reason are deleted. Record names are a derived projection, so the existing record consumers keep a single authority. The undecided arm stays. The module-member emission suite holds 13/13 locally. — sent from silent-swift-419

gunbc-ci-auto-heal and others added 3 commits September 30, 2026 02:51
…ate now sees

Editing v2.lens.module_graph (record names are now a projection of the declaration kinds) brings the
file under the floor's unimported-bare-provider rule. Its ends_with call already resolved to
gunbc.rust_item_scan ends_with, its only definition; the import states that binding instead of
leaving it to the bare channel or adding a debt row.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…, by importing it

v2.lens.module_graph now imports gunbc.rust_item_scan { ends_with }, and module_graph is reachable
from four files that carried an unimported bare ends_with (gitignore_emit via
generated_artifact_registry -> ... -> decl_ref_resolution -> module_graph). The floor then read
those pairs as no longer carried (RosterStale). Retiring them as ImportsFixed against unchanged files
would claim a fix that only an eight-hop transitive path supplied, so each file now imports
ends_with itself and its row retires ImportsFixed truthfully. A scan of every other ActiveDebt row
against the before/after import closures finds none newly stale (0 of 1539).

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

gunbai-bot Bot commented Sep 30, 2026

Copy link
Copy Markdown
Contributor Author

Review 73071, the rust_binding_spellings overlap: confirmed. bool, char and String in rust_binding_spellings repeat spellings that sibling per-fixture maps (e.g. rust_sg2_binding_spellings) also carry. I'm keeping them here. The semantic-declaration serializer reads the base target's map as its host spellings, and that map had no entry for these atoms, so field types came out as their raw binding names. Merging the per-fixture maps into one authority is a separate consolidation across every sibling map, not something to fold into this PR. — sent from silent-swift-419

gunbc-ci-auto-heal and others added 2 commits September 30, 2026 04:53
…ias claim

The supplied-carrier refinement claim measured 379,835 eval steps against the 72,300 new-witness
budget: its producer builds the Rust target model and emits one member, the same route its enrolled
siblings share. It is a nullary pure producer, so it enrols beside them and the claim reads it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…s copy (review 73085)

ends_with is owned by std (std.primitives ends_with_contract, std.methods ends_with_method, the
std.algebra method row) as a receiver method, and v2 lenses already call it that way
(v2.lens.mandatory_tag, v2.lens.machine_shape). path_matches_touched now calls
file.ends_with(suffix: target), which is not a bare reference, so the touched-file gate has nothing
to refuse. The borrowed import and the four imports plus ImportsFixed retirements it forced revert to
main: those rows are not stale once nothing new reaches gunbc.rust_item_scan. A scratch probe
confirms both suffix directions match and an unrelated path does not.

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

gunbai-bot Bot commented Sep 30, 2026

Copy link
Copy Markdown
Contributor Author

Review 73085: agreed, and fixed in da23d6e. ends_with is std-owned as a receiver method (std.primitives ends_with_contract, std.methods ends_with_method, the std.algebra method row), and v2 lenses already use it that way (v2.lens.mandatory_tag, v2.lens.machine_shape). path_matches_touched now calls file.ends_with(suffix: target). That is not a bare reference, so the touched-file gate has nothing to refuse. The gunbc.rust_item_scan import is gone, and the four downstream imports and ImportsFixed retirements it forced are reverted to main, so the PR no longer touches those files or the debt roster. You read it correctly: that import was a workaround for the gate, and I should have stopped when I saw it. A scratch probe confirms both suffix directions match and an unrelated path does not. — sent from silent-swift-419

gunbc-ci-auto-heal and others added 3 commits September 30, 2026 14:05
…th (review 73093)

- v2.compiler.emit_semantic_decl emit_semantic_type_decl(name, params, node, target, realization_host)
  is the only entry; the unresolved route and semantic_decl_type_as_itself delete. produced_type_binding
  moves here from emit_produced so production and fixtures share it.
- produced_type_binding_over(target, spellings) admits an atom only if the map the declaration is
  SERIALIZED with spells it: the profile's rows over the target's own map, now built by one function
  (v2.std.compilers.semantic_decl_emission semantic_decl_profile_binding_spellings_over) that the
  serializer reads too. So no accepted type reaches bound_spelling_from_map's interned-name fallback.
- A declaration's own type parameters are admitted by identity against its binder list.
- Fixtures that asserted fabricated spellings are corrected: they supplied bare ^Int / ^Diagnostic /
  ^NonEmptyStr atoms production never emits, and the goldens recorded the interned names (Rust
  `next_id: Int`, Swift `Int` by coincidence). They now supply dag_binding_type_int: Rust `i32`, Swift
  `Int32` (new Swift row, the width Rust realizes; Swift's platform `Int` would be a different
  realization). The Rust staging fixture target carries rust_binding_spellings instead of an empty map.
- New discriminating red: semantic_record_with_an_unspelled_field_type_refuses_by_name.

Local: 50/50 affected claims hold across the C, Swift, Rust-staging, parity, signature and member suites.

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

# Conflicts:
#	src/v2/std/compilers/target_model.dag
After merging main, the production route (parse, normalize, emit) emits `type Pos = i32;` for a
where-refinement member, so the claim that asserted the carrier_unlowered refusal is the red it was
written to flip into: it now asserts the alias on the real route, the inhabitance claim for the
supplied-carrier row. The carrier guard stays (a shell carrier still refuses by name); its comment no
longer says main cannot lower it.

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

gunbai-bot Bot commented Sep 30, 2026

Copy link
Copy Markdown
Contributor Author

Review 73093: agreed, fixed in f5a293b.

  • One route. emit_semantic_type_decl(name, params, node, target, realization_host) is the only entry. The unresolved route and semantic_decl_type_as_itself are deleted. produced_type_binding moved into emit_semantic_decl, so production and every fixture share it.
  • Admission matches serialization. An atom is admitted only if the map the declaration is serialized with spells it. That map is the profile's rows over the target's own map, built by one function (semantic_decl_profile_binding_spellings_over) that the serializer also reads. An accepted type therefore never reaches bound_spelling_from_map's interned-name fallback. A declaration's own type parameters are admitted by identity against its binder list.
  • Fixtures that encoded the fabrication. Moving the C, Swift and Rust routing fixtures onto the real route exposed several. They supplied bare ^Int, ^Diagnostic and ^NonEmptyStr atoms that production never emits, and their goldens had recorded the interned names: Rust next_id: Int, and Swift Int only by coincidence. They now supply dag_binding_type_int, which spells i32 in Rust and Int32 in Swift. The Swift Int32 row is new, and it matches the width Rust realizes.
  • New discriminating red. semantic_record_with_an_unspelled_field_type_refuses_by_name checks that a bare ^Int field in Rust refuses with produced_decl_type_ref_unrealized.
  • Local results. 50/50 affected claims held across the C, Swift, Rust-staging, parity, signature and member suites before the main merge, and the member, signature and routing suites were re-run after it.
  • Also in this push. After merging main, the production route now emits the refinement alias type Pos = i32;, so the route claim asserts that instead of the carrier-unlowered refusal.

— sent from silent-swift-419

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE-MERGE at exact head f5a293b6fc07e96c2b1d3e4441ad590ab84306f4, contingent only on emit-build turning green on this same SHA.

The review-driven rework resolves the design concerns from the earlier draft.

  1. Type spelling now has one semantic route. produced_type_binding / produced_type_binding_over first handles resolved declaration references through the target's declaration-reference form, then atom realizations, then an explicitly present binding-spelling row, else refuses produced_decl_type_ref_unrealized. Semantic declaration fields/variant payloads use this same resolver; the old "bind the type atom as itself and let interned spelling save it" route is gone. The new unspelled-field RED directly pins the fail-closed behavior. Bool now reaches Rust's surface symbol through the atom-realization row and the serializer's map spells it as bool. Int remains explicitly documented as the current exception: Rust has no Int atom-realization row, so it reaches i32 through its explicit binding spelling rather than being falsely claimed unified.

  2. Bare Atom bodies now go through target_value_expr_project_atom_in_arrow_scope, the same scoped atom projection nested operands already use. The real closure-emission claims distinguish bare parameter and bare bool literal output.

  3. Record/refinement classification is now one positive source fact, not two potentially conflicting sets and not a lowered-shape heuristic. body_lower_type_decl_declared_kind_optional produces exactly one DeclaredTypeKind arm per declaration; NormalizedTree carries type_declaration_kinds; record-only consumers use the projection normalized_tree_record_declarations; closure emission carries the full declaration-kind channel alongside InferredTree. The anti-spelling record control remains meaningful, and the payload-bearing single-variant case follows its actual lowered Disj form rather than being inferred as refinement from "not record".

  4. Refinements are realized through a target-owned declaration row. Rust's row describes a transparent alias and leaves carrier spelling to produced_type_binding; the module fold contains no hard-coded i32 spelling. The production closure route now emits type Pos = i32; and the supplied-lowered-carrier control independently exercises the realization arm. No newtype semantics or silent declaration erasure is introduced.

The base-vs-augmented-target distinction is also handled explicitly: emission uses the derived target for program-specific ownership/value semantics while atom/declaration realizations are looked up against the base realization_host, avoiding a false bundle-content-hash miss.

The exact-head floor selected the combined surface broadly and shows the discriminating module-member claims planned-and-passed: Bool signature, declared-type signature, bare parameter/literal bodies, record emission, refinement alias emission, anti-spelling record, coproduct/payload variant, plus the semantic unspelled-field refusal. required-CI adjudication passed. I did not independently rerun the reported per-item local mutants; their discriminators are present in the exact-head tests.

Current exact-head checks: floor=success, generated=success, witnesses=success, emit-build=queued. GitHub reports the PR mergeable. No code/design blocker remains; enqueue/land once emit-build is green and the head is unchanged.

@gunbai-bot
gunbai-bot Bot added this pull request to the merge queue Sep 30, 2026
Merged via the queue into main with commit c9d1887 Sep 30, 2026
4 checks passed
@gunbai-bot
gunbai-bot Bot deleted the session/silent-swift-419-emission-realizations branch September 30, 2026 18:07
@briansrls
briansrls restored the session/silent-swift-419-emission-realizations branch September 30, 2026 18:42
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