Repository navigation
Infer: keep Arrow type arguments, and make the type relations read the signature - #12295
Merged
Merged
Conversation
…ents stay callable child_type_node read a child's inferred field as the type it denotes, but an Arrow stores its return type there, so the element of List<fn() -> Int> became Int and a lambda parameter / match binding drawn from it refused as "function 'r' not found in scope" (broke extdeps.apple.app_attest first_assertion_refusal under --target dag and rust). Witness: test.claim.infer_callable_type_argument_witness_test (3 REDs + unbound-callee control). Residue receipt added to recurring_failure_mode arity_proxy_for_callability_misroutes_a_found_local. Stage0 regen first_generation_equal=true. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…-typed elements Merge-review hold on 4108026: keeping the Arrow in child_type_node exposed relations that never read a signature (it lives in params/inferred, not children). - infer_types: callable_signatures_agree (arity, parameter types, codomain, parameterised by the component relation); node_type_equals_core and node_type_compatible route Arrows through it; type_resolution_verdict inspects params and codomain; node_type_shape renders Callable(params->codomain). - infer: declared_type_conformance_diags gains a one-peel element arm asking the existing callable_signature_mismatch, preserving the List<fn() -> Int> vs List<fn() -> String> refusal main had only by erasure, and newly refusing parameter/arity mismatches. - witness: declared-return cells (3 refusals + same-signature control) and common-field cells through the Rust emitter (mixed signatures no longer render m.callbacks()). Stage0 regen: 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
Sep 26, 2026
…es.dag keeps main's any, moved fns stay on std.algebra Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot
pushed a commit
that referenced
this pull request
Sep 26, 2026
… receipt credits #12295 for the child_type_node repair Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.
Problem
A call through a binding whose type comes from a function type written as a type argument (
List<fn() -> R>) was refused withfunction 'r' not found in scope. This broke gunbc-private CI throughextdeps.apple.app_attestfirst_assertion_refusal, which folds overList<fn() -> AssertionRefusal?>.Reproduction (steps 1 and 4 of the brief)
Built the seed from main e01e3d4 and compiled a public fixture with
gunbc compile --source-root dag --source-root src/v2 --entry <fixture> --target dag|rust. Both targets refused, identically, on 6 forms:fold(rs, …)and methodrs |> fold(…)overList<fn() -> Int?>mapand freemapoverList<fn() -> Int>List<fn(Int) -> Int>match rs |> first { Present { value: r } => r() … }The refusal comes from shared inference, not from the dag emit path, and it has nothing to do with lambdas: a match-arm binding refuses the same way. Public CI stays green only because no required lane compiles
app_attest.Root cause, per §6b
Walking back from the call seam: the seam's callability test (
type_node_is_callable, already repaired for the nullary case) was never given an Arrow.v1.compiler.infer_typeschild_type_nodereads a type argument'sinferredfield as the type the child denotes. But an Arrow stores its codomain ininferred(make_callable_type,parse_callable_type_expr), so the element ofList<fn() -> Int>came out asInt. That is the earliest boundary that can't be justified. The fix is there: an Arrow child denotes itself.I also built a guard one link later (teaching
type_node_is_establishedthat a nullary arrow counts as established). Every specimen compiled without it once the arrow survived, so I withdrew it rather than ship an unneeded guard.Evidence (all run locally with the rebuilt seed)
test.claim.infer_callable_type_argument_witness_test: 3 discriminating cells (fold over nullary thunks, map over unary callables, match arm overfirst). Before the fix, the seed refused each of these shapes. Plus a control: an unbound callee inside the same lambda still refuses, with that callee as the subject. 4/4 PASS.test.claim.infer_nullary_local_callable_witness_test: 6/6 PASS.self_host_emitted_call_target_realization_witness_testcells over an arrow type argument (w_arrow_type_argument_at_a_record_field_renders_the_callable_carrier, and bothw_sort_by_*cells): 3/3 PASS.gunbc compile --entry dag/extdeps/apple/app_attest.dag --target dag: 0 blocking.claim_executor --required-regen:first_generation_equal=true, planned=executed=161, so the stage0 mirror edit matches the generated output. (The first attempt caught a v2-parse ambiguity:!= Arrow { … }inside anifread as a record literal. It is now parenthesised.)Sweep
The only production site with a function type inside a type argument is
extdeps.apple.app_attest. The other hits are a witness probe string (above, green) and a comment in05_emit_rust.dag.Failure-mode row
This is the same class, so I didn't file a new one. I added a residue receipt and evidence refs to
gunbc.recurring_failure_modearity_proxy_for_callability_misroutes_a_found_local.Not claimed
I didn't compile the private composed root. The new witness drives the seed through
gunbc.compile_census_probe(inference, shared by all targets). The--target dagreceipt is theapp_attestcompile above.Rework after the merge-review hold on 4108026
The hold was right: once
child_type_nodekeeps the arrow, the relations downstream meet function types they never read. An arrow's signature lives inparamsandinferred, notchildren.node_type_equals_coreused to compare two arrows by the nameCallable, soList<fn() -> Int>andList<fn() -> String>compared equal. Now it compares arity, parameter types and codomain through one helper,v1.compiler.infer_typescallable_signatures_agree, which takes the component relation as a parameter. The equality arm that already compared params (unreachable for arrows) now goes through the same helper, so there is still only one signature relation.node_type_compatiblenow applies that relation with compatibility as the component, where it used to fall through to name compatibility.type_resolution_verdictgets an arrow arm that checks params and the codomain. It runs first because the checks below it readinferredas the node's own type, which on an arrow is the codomain.node_type_shaperendersCallable(params->codomain).List<fn() -> Int>whereList<fn() -> String>is declared was refused on main only because the pair read asList<Int>againstList<String>. I measured this with a binary built from main. With the arrow kept, neither side counts as ground and the pair was going unjudged.declared_type_conformance_diagsnow has a one-level element arm,callable_element_signature_mismatch, which hands both element arrows to the existingcallable_signature_mismatch, so no second conformance relation is added. Parameter and arity mismatches also refuse now; main accepted both.New cells in
test.claim.infer_callable_type_argument_witness_test(floor-admitted), all run locally: 10/10 PASSTypeMismatch. For example:expected 'Container(List,Callable(->Primitive(String)))', got 'Container(List,Callable(->Primitive(Int)))'. The control: the same signature is accepted with 0 blocking.enum_field_type_consistent), observed through the Rust emitter. On a coproduct whose variants carryList<fn() -> Int>andList<fn() -> String>, the previous head renderedm.callbacks |> countasm.callbacks(), a call to an accessor that is never generated. I diffed the output of the 4108026 binary against this head. That call is now gone, matching main. The control: one shared signature still rendersimpl SameCallbacksands.callbacks().Re-verified on the new seed
claim_executor --required-regen:first_generation_equal=true(the v1 self-compile runs clean under the new relations).app_attest,std.algebraandstd.materialization_laddercompile under--target dagwith 0 blocking.infer_nullary_local_callable_witness_test: 6/6. The arrow cells ofself_host_emitted_call_target_realization_witness_test: 3/3.Not covered by a gated cell: the resolution-verdict arm. No floor witness can import v1 compiler modules. The regen fixed point exercises it over the whole v1 corpus.
🤖 Generated with Claude Code