v2: coercion admits refinement-to-declared-carrier casts as Widened - #12407
Conversation
A cast from a refinement to its DECLARED carrier (x as Int from x: Pos,
type Pos = Int where positive) is admitted as Widened: the declaration's
carrier edge composed with the existing exact-structural find_witness.
No preservation rule is added (ruling: neat-boar-16).
- v2.compiler.infer refinement_declaration: reference -> declaration
{path, carrier, where_clause} by the reference's own path down the
containment spine; one reader for this cast and the literal-into-
refinement producer (shape agreed with quick-crab-850 / deep-bee-18).
- v2.std.coercion coercion_cast_crossing takes source_declared_carrier and
stays tree-free; a non-carrier target refuses with the original operand
mismatch. One step only: Pos2 = Pos where .. widens to Pos, not Int.
- body lowering lowers a where-refined head as a type (it rode as the raw
parse sequence nothing resolved), so the carrier is resolved and an
undeclared carrier now refuses unbound.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Conflict resolution with #12381, worked out in advance (a trial merge of
After the merge, re-run both PRs' where-refinement claims: |
…d as a declared frontier (review 71764) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Review 71764 (REQUEST_CHANGES, DESIGN §3c): verified; fixed in 415fdb7.
|
…he over-budget assembly rows Floor refused three new witnesses over the 72300 eval-step new-witness budget (no claim failed). The refusal logic lives in v2.std.coercion, so 15b/15c now supply the reference, declared carrier and target there (2652 / 2015 steps); row 15 stays the inhabitance claim on the production route. The undeclared-carrier assembly row is dropped: row 15 is the lowering change's discriminating red (an unlowered head cannot widen). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
CI on 415fdb7:
|
…; cut every consumer root-first Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…heck clean) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…2); spine walk deleted (neat-boar-16 ruling) The declaration is symbol_index_lookup(tree.symbol_index, reference path), threaded into infer's gather to the cast and let-annotation arms. A lookup miss refuses (bcn_a_refinement_lookup_miss_refuses). A refinement of a refinement refuses: the index holds Pos2 as authored, so its carrier is the unresolved atom Pos (measured); 15b pins that and names the trigger. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
neat-boar-16's change request (infer-private reference→declaration walk) is addressed at 5fa0197, stacked on gunbc#12432 (base retargeted):
|
…over-reached) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…k (review 71857) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…parameter_order, reference_closure) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…erge brought in Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…nal (review 71891) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…h frozen as a declared frontier (neat-boar-16 ruling) Every infer function that took tree: Node (plus the separate symbol_index) now takes resolved: ResolvedTree and reads resolved.root / resolved.symbol_index. infer_parameter_scope_search stays FROZEN (no new callers or arms): a frame-bound parameter reference reaches infer as a bare canonical_atom with no path, so the index cannot key it; the trigger is on the carrier comment. infer_branch_operand_resolved_type no longer passes its operand as a fake tree: it states the literal-else-facts result that call always produced. Row 16 (positive(x: Int) beside f(x: Pos)) is the ruling's required control. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
neat-boar-16's ResolvedTree ruling is implemented at 6d32120:
|
…red input); retype main's new resolved-tree test sites Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…e resolved Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ts use the named no-declarations constructor Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…its structure; resolved threaded through it; tree-less operand type mirrors #12379's literal arm Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
briansrls
left a comment
There was a problem hiding this comment.
Verdict: APPROVE 90b0cf2
My earlier infer-private declaration-walk blocker at 59e14d6 is resolved in this stacked change: refinement_declaration consumes declaration_reference_path_optional and the actual ResolvedTree.symbol_index via symbol_index_lookup. The old spine walk is absent. Missing/ambiguous lookup or missing declaration fields supplies no carrier and preserves the original located exact-crossing refusal.
The admitted widening composes the declaration's carrier with the existing exact-structural find_witness, marks Widened, and adds no preservation predicate. Sibling/non-carrier targets remain refused; the unresolved carrier of a refinement-of-refinement remains an explicitly declared frontier. The production cast control pairs with the supplied coercion controls. The retained parameter-scoping search and the future where_clause consumer each have explicit capability triggers.
This clears the source blocker for this PR's delta, not the stack's landing requirements. It is based on #12432 at 0fb011f, on which I have separately requested the two incorrectly retyped translation helpers be repaired. There are no check-runs attached to this exact child SHA in the API response I inspected. Integrate the repaired/qualified parent, preserve both carrier lowering and #12381's predicate binding when resolving their overlap, and obtain the required checks/new-head rebind before landing. I reviewed source and recorded evidence, not an independent local execution.
…); typecheck clean over 116 files Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ResolvedTree and walk .root Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…solvedTree Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…vedTree.resolved_declarations (neat-boar-16 ruling) The head reached resolve as an unlowered dag_surface_qualified_name shell, which resolve preserves unchanged as module metadata, so a declaration's carrier was never resolved. It is now lowered through the one type-expression lowering; resolve binds it; an undeclared carrier refuses unbound. ResolvedTree gains resolved_declarations, the same module fold over the resolved root, alongside symbol_index (the index resolution consulted). The other declaration-body type positions are a declared frontier (gunbc.recurring_failure_mode declaration_body_type_shell_preserved_unresolved). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…solved_declarations; Pos2 widens one step on the production route Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Reworked per neat-boar-16's resolved-declarations ruling, now stacked on gunbc#12629 (#12432 has landed). Head 3c18481.
|
…o later stage reads it; #12407 reads resolved_declarations) (review 72652) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…; infer's admission is row 15's subject (over the new-witness budget) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…12407) uses bool_node's identity Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…er grounding reads resolved.resolved_declarations) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Conflicts: - 04_infer.dag: main threads resolved: ResolvedTree through infer's gather (#12407). The payload-family dispatch and its helper take that parameter instead of tree: Node. - v1_compiler_emit_rust.rs: regenerated, not text-merged. Starting from main's mirror, claim_executor --required-regen reached first_generation_equal=true on the third pass. std_types.rs differs from main only by #12798's pub type Unit = (). Also, following review 73303 on #12809 (finding 2): the gather binds the payload family once, with one arm for InferNotALiteralPayload and one for every family, instead of rebuilding each variant. All kernel-String, Symbol and #12540 claims hold on a compiler rebuilt from this tree. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…r seams git reported clean Main's #12407 widened several infer readers from a bare `tree: Node` to the whole `resolved: ResolvedTree`, renamed the match entry, and froze the parameter-scope walk. Each conflict decided by what the merged declarations actually are, not by side: - import list: UNION (this lane's symbol_index_declared_type_params_at, main's SymbolIndex). - the binding reader: THIS LANE'S infer_binding_type_in_scope, because it is the superset -- it resolves ArmBound, which needs the index, where the tree-only infer_parameter_type_in_scope answers Absent. Main froze that reader as a declared frontier and this lane's arm is what supersedes it. - the match gather row: THIS LANE'S infer_match, because it is the DISPATCHER -- it routes to infer_match_bool or infer_match_coproduct. Taking main's direct call to infer_match_bool would have silently lost every coproduct match. - infer_transform_freemonoid_introduction and infer_transform_derived_optional: MAIN'S widened signatures. - the annotation: BOTH notes kept. Main's records the freeze and its next-rung trigger; this lane's records that a match arm is a binding scope that shadows. Neither restates the other. AND FOUR CALL SITES GIT REPORTED AS CLEANLY MERGED DID NOT COMPILE, which is the same class as the previous merge and the reason every one of these is compiled rather than read: the two infer_match_bool calls inside infer_match, and two infer_branch_operand_resolved_type_in_tree calls, still passed the retired `tree` parameter. Threading those surfaced two more helpers of this lane's (infer_coproduct_arm_body_types, infer_match_coproduct_rows) that carried `tree: Node` and now carry the whole ResolvedTree. I had read those last two sites and concluded they were legitimate `Node` consumers. They were not. The compile is what said so. reference evidence 11/11. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Admits
x as Intfromx: Pos(type Pos = Int where positive) as a Widened crossing inv2.std.coercion, decided from the type declaration. Work item adhoc-936e83cd-865 (parent deep-bee-18).Model (ruled by neat-boar-16 before building)
The crossing is two facts composed, not a new rule: the declaration's carrier edge (every
Posis anIntby declaration, so no predicate or value set is involved), followed by the existing exact-structuralfind_witnessfrom that carrier to the target. NoPreservationPredicaterule is added.refinement_widening_predicateanswers value-set containment between two domains, and this answers nothing but structural equality, whichfind_witnessalready owns, so there is no second widening algebra.std.coercion dag_cast_rulesis not consulted.v2.std.coercion coercion_cast_crossing(source, source_type, source_declared_carrier, target): exact on the source type → Identity/Exact, unchanged. On refusal, when a declared carrier is present, it tries exact from the carrier: success isWidened(witness rulehomomorphism_rule_refinement_declared_carrier), and failure returns the original operand mismatch. Coercion stays tree-free.v2.compiler.infer refinement_declaration(index, reference) -> Optional<RefinementDeclaration { carrier, where_clause }>:symbol_index_lookup(tree.symbol_index, reference path), the same authority resolution used, carried onResolvedTreeby gunbc#12432 (this PR is stacked on it) and threaded into infer's gather to the cast and let-annotation arms. The infer-private spine walk is deleted (neat-boar-16's ruling). A lookup miss returns Absent, which refuses (bcn_a_refinement_lookup_miss_refuses). One reader, two consumers: this cast projects.carrier; deep-bee-18's literal-into-refinement arm (work item adhoc-032c89dc-138) projects.where_clauseinto quick-crab-850'swhere_predicate_bindings, and must refuse on Absent too.type Pos2 = Pos where ..as authored, so its carrier is the unresolved atomPos(measured on the production route), and that never equals a resolved target:x as Posandx as Intfromx: Pos2both refuse. Next trigger: the carrier carried resolved on the index.Root cause found on the way (chain re-derivation)
The first implementation failed: the declared carrier was the raw parse sequence, and its
Intatom was never resolved.body_lower_type_variantcarried a where-refined head unlowered, so no stage lowered or resolved the carrier. The earliest unjustified link was therefore lowering, not coercion. The head now goes through the one type-expression lowering that signatures and cast targets use (body_lower_type_expr_lowered_optional); an unreadable head refusesbody_lowering_reason_type_annotation_not_carried.Consequence: a where-alias over an undeclared carrier now refuses unbound, where it used to assemble silently.
declaration_graft_assemblefixtures carried an unimportedStringexactly that way and now carryInt; the new red isdeclaration_graft_where_alias_over_an_undeclared_carrier_refuses. Corpus heads are all plain names (NonEmptyStr 237, String 25, Int 18, …), all readable by that lowering; whether every one resolves is for the CI floor to show.Evidence (claim_batch, remote, counts matched)
All pass: body_cast_node 22/22, declaration_graft_assemble 17/17, compilation_unit_witness 15/15, match_arm_binder_frame 15/15, declaration_structure_preserved 4/4, variant_field_lowering 34/34, reference_conservation 16/16, where_refinement_clause_parse 5/5, d1_declaration_grammar_parse 8/8, type_param_binder_frame 37/37.
bcn_cast_out_of_a_refinement_to_its_declared_carrier_widens: infer admits the cast, and the quality isWidened(route asserted, not only verdict). It was RED on main.…_to_a_non_carrier_refuses(supplied values at coercion's interface):x as Boolrefuses;Pos2 → PosandPos2 → Intrefuse, with the carrier supplied in the shape the index holds (bare atom).bcn_a_refinement_lookup_miss_refuses: no declared carrier reaches coercion, sox as Intfrom a refinement keeps the refusal.bcn_cast_between_sibling_refinements_refuses:Pos → Neg(bothInt where ..) refuses.The rfm row
as_cast_has_no_lowered_formis updated: the carrier-widening trigger is retired, and the next trigger (deciding the predicate on the operand) is unchanged.Residue, stated in the rfm row: the lowered cast's operator names
CoercionCrossingby the exact rule, which is fixed at lowering. Both rules areOperandUnchanged, so no emitted program differs.🤖 Generated with Claude Code