v2 inhabitance: an optional produced at a required declared type is refused, not counted - #12806
Merged
Merged
Conversation
…; 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>
…over-reached) 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>
…red input); retype main's new resolved-tree test sites 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>
Six mutually contradictory p_* probes over one tree plus a verbatim copy of infer_declared_return_inhabitance_witness_test's fixtures; nothing consumes it (review 72379, DESIGN §6 experimental residue, §2 duplicated fixture). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…); typecheck clean over 116 files Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ted symbol and reads .root 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>
…n from their post-split homes 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>
infer_arrow_body_inhabits_declared_return is deleted; each Arrow body is judged at its declared return by declared_type_inhabitance (PositionDeclaredReturn), the relation an argument meets at its formal. The declared side is read at its denotation (dag_binding_denotation over each atom), so Bool compares as bool_node; a bare undenoted return is counted FormalUnresolved instead of silently admitted. #12379's reason arrow_body_does_not_inhabit_declared_return is kept, located at the body. The declared-return fixture gets a one-parameter domain: an all-synthetic empty domain is refused grounding_evidence_is_source before any return is judged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…o later stage reads it; #12407 reads resolved_declarations) (review 72652) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…n/patterns), reader census Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…truct-tag misread row (confirmed by execution) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…terns and nullary values lower through one route The construct tag edge carries construct_tag_marker and targets the authored qualified-name spine (a bare tag is its one-segment case); resolve binds it through the existing doors (qualified door for 2+ segments, bare door for 1) and refuses a non-constructor answer. Every reader migrates in one motion; no Symbol tag remains. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…er is the only new case Narrowing it to the first edge refused field-projection bodies (v2.std.diagnostic diagnostics_fatal_reason) and with them every importer of diagnostic on the native route. The qualified_construct fixture now carries a field-projection body so the shared ingest reds if it narrows again. Also: a construct tag answered by anything but a constructor declaration or a kernel atom refuses; plan records the one-door ruling. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ion/smart-newt-725
…to session/smart-newt-725
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…on symbol fork, not a missing spelling Measured on the real route: resolve binds a declared Bool to bool_node_symbol and dag_binding_denotation denotes dag_binding_type_bool. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…er refused for infer_descent_not_derived) v2.std.cardinality connective_multiplicity mapped Cardinality to RequiresTerminationProof while every other type connective is Bounded; termination_proof_witness_for_node folds that over every node, so any tree carrying T? refused. A type node is never evaluated; repetition enters only through Loop (loop_multiplicity), which is unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
# Conflicts: # src/v2/compiler/04_infer.dag # src/v2/std/inhabitance.dag
…ctly-shaped Loop descent control Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…on/smart-newt-725-optional-converse
…relation Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ure-mode row Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Contributor
Author
|
Native-route census from #12777's side: not obtained. — sent from cool-cat-831 |
added 2 commits
September 30, 2026 16:48
# Conflicts: # src/v2/compiler/04_infer.dag
6 tasks
…rough a forwarding second name (review 73309) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…not inside it (DESIGN 4c) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…5-optional-converse # Conflicts: # dag/gunbc/recurring_failure_mode/optional_admitted_where_a_required_value_is_declared.dag
Closed
4 tasks
added 3 commits
September 30, 2026 18:56
# Conflicts: # src/v2/std/node.dag # src/v2/workflow/floor_pure_producer_share.dag
…5-optional-converse # Conflicts: # src/v2/workflow/floor_pure_producer_share.dag
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.
DRAFT. This PR carries #12777 and is the one landing for both (ruling relayed by quiet-gull-780). Its diff includes cool-cat-831's descent fix from #12777 (
v2.std.cardinalityconnective_multiplicity:Cardinality => Bounded, its claims and its row update); #12777 closes as absorbed. The two cannot land apart: with the descent fix alone,fn f(x: Int?) -> Int { x }is accepted. It is stacked on #12763 (and #12714 under it). Landing order: #12714, then #12763, then this. Do not merge.The two changes in this landing
v2.compiler.inferrefused every tree carrying aT?node withinfer_descent_not_derived. The earliest unjustified boundary wasconnective_multiplicity: every type connective wasBoundedexceptCardinality, which wasRequiresTerminationProof. A type node is never evaluated, andCardinalitysays how many values inhabit a position, not how often evaluation repeats. The fix is that one arm. v2 infer: the Cardinality type connective owes no descent (T? no longer refused) #12777's description has the full chain.What it does
v2.std.inhabitancedeclared_type_inhabitancenow refuses a producedT?at a required declared type, with its own reasoninhabitance_optional_produced_at_required_declared. Before, it answeredUndecidableOptionalCarrierand the program was admitted with an advisory.T?admits absence; a required type admits no absent value. So the pair is decided, whateverTis. The undecidable reason was written for the other direction, the lift (aTat a declaredT?), and because it was read off either side it swallowed this one. The seed closed the same misfiling for v1 (optional_produced_at_required_declared); this is the v2 route.The fix is where the verdict is computed, in the relation. There is no guard in infer.
How far it goes, and where it stops
The relation refuses only when it can know the declared type is required: the declared side is a kernel value type (an atom, not a type variable).
It deliberately stays counted, not refused, when the declared side is:
type Maybe = Int?), and the relation unfolds no alias;Optional<T>;Refusing those on a guess would be a false refusal. The trigger for the declaration-reference case is the relation reading a declared type's resolved declaration (#12726 PR2). The lift and optional-at-optional are unchanged.
Why the two are one landing
Measured on main and on #12566: any program mentioning
Int?was refused for descent before a declared-type judgment was asked. The descent fix removes that, and with it alonefn f(x: Int?) -> Int { x }becomes accepted, because the relation admitted the pair with an advisory. The relation fix is what makes it refuse.Evidence
v2.test.claim.compiler.infer_optional_at_required_witness_test, 6 claims, 6/6 locally.Relation level:
Int?at a declaredIntis refused by the new reason.Real route, from source text through the production front end into
infer:fn oar_f(x: Int?) -> Int { x }refuses.OarR { n: x }withx: Int?at a field declaredIntrefuses.fn oar_f(x: Int) -> Int? { x }is still accepted.Not a route red yet: a direct-call argument. A named call's callee is a declaration reference that infer does not type by its declaration (#12506), so its formals are unresolved whatever is passed. The relation refuses the pair; the route claim is owed when #12506's capability lands.
Descent evidence. From #12777, on supplied trees:
infer_optional_formal_reaches_optional_carrier_frontier_holds(anInt?formal reaches its inhabitance judgment instead of a descent refusal) and the controlinfer_loop_without_descent_fact_still_refuses_for_descent_holds(a Loop with no descent fact still refuses). On the real route the descent red is this module's three route claims: each needs aT?program to get past descent.Revert-red, executed. On a scratch copy of this head with only the descent arm put back to
RequiresTerminationProof:infer_optional_formal_reaches_optional_carrier_frontier_holdsSo the descent fix is what the route claims and #12777's claim depend on, and it does not loosen the Loop obligation.
Neighbouring suites on this head, same binary: application-argument 10/10, #12777's
infer_cardinality_type_owes_no_descent_test1/1, declared-return 12/12, arrow-elimination 7/7, record-field witness 13/13, body-let-annotation 22/22, list-introduction 6/6.Route claim cost, locally (infer minus a resolve-only baseline): about 25k, 32k and 43k eval steps against the 72.3k new-witness budget.
The existing row
optional_admitted_where_a_required_value_is_declaredgains the v2 route's rung and its counted populations.Census on the v2 native route (the measurement for the combined change)
Instrument:
claim_executor --v2-native-route --source-root dag --source-root src/v2, one BuildBuddy dispatch per sha, each verified at its pin, all three exit 0. #12777's own census could not be produced (its runs hit a 45-minute cap), so this is the native measurement for both changes. Three points, so each change's effect is separable:09b5bf8923— v2 infer: a record construct's field values are judged against the fields its record declares #12763's head, neither changebf905db7a7— A plus the descent fix7ea7a06c20— B plus the relation fix (this PR before its later main merge)infer_descent_not_derived..._is_refused_for_unproven_cardinality_descent_holdsbecomes..._reaches_optional_carrier_frontier_holds) and its new Loop control. NoT?program is newly admitted in this population: at A, none of the 65 modules that reach infer was refused for descent (zero verdicts carry that reason), so the fix had nothing here to unblock.dag/std/algebra.dagimport does not lower natively), like most of the universe; they execute on the seed-interpreted floor route, where the results in the Evidence section were read.What this does and does not establish. For the 65
v2.test.*modules that reach v2 infer today, the combined change alters no verdict. It says nothing about the 769 that do not reach infer, and nothing about the product corpus: aT?program there that the descent fix would newly admit, or that the relation fix would then refuse, is not measured, not zero. The effect on realT?programs is established by the source-route claims and the revert-red above, not by this census. The census obligation carries forward to when the v2 front end reaches a wider population.Still owed
🤖 Generated with Claude Code