0%

Intelligence Reconnaissance

Queries run 2026-04-23 (Phase 2 research):

  • scripts/intel-query.sh --human file-symbols "registry/traits" --repo ori — inventory TraitRegistry surface. Result: lookup_method + lookup_method_checked are the single entry points for trait-method lookup; impls_by_type: FxHashMap<Idx, Vec<ImplEntry>> is the keying structure.
  • scripts/intel-query.sh --human callers "lookup_method_checked" --repo oriinfer/expr/calls/impl_lookup.rs:35 is the sole caller from inference paths.
  • scripts/intel-query.sh --human callers "lookup_method" --repo oriinfer/expr/operators.rs:708,759 + registry/methods/mod.rs:36 (thin wrapper lookup_trait_method).
  • scripts/intel-query.sh --human similar "bound_chain_dispatch" --repo rust,swift --limit 5 — Rust’s assemble_inherent_candidates_from_param at probe.rs:958 scans self.param_env.caller_bounds() for ClauseKind::Trait predicates.
  • scripts/intel-query.sh --human file-symbols "FunctionSig" --repo oriFunctionSig.type_params: Vec<Name>, FunctionSig.type_param_bounds: Vec<Vec<Name>>, FunctionSig.scheme_var_ids: Vec<u32> — parallel arrays. Bounds are stored HERE, not in TraitRegistry.

Results summary (≤500 chars) [ori]: registry/traits/mod.rs is 765 lines — split prerequisite. method_call.rs is 524 lines — also split. lookup_method_checked keys on receiver Idx; RigidVar Idx is never registered as an impl’s self_type, so dispatch misses. Bounds live on FunctionSig.type_param_bounds parallel to type_params / scheme_var_ids. §10.1’s bound-chain walk consumes FunctionSig + TraitRegistry jointly — retrieve current function’s sig via engine.signatures(), scan type_param_bounds[idx_of_rigid_var] for trait names, call TraitRegistry::get_trait_by_name for each, check that trait’s methods for method_name. Capability handlers at infer_with_capability bind provider type in env without trait-shape validation — §10.2 extends to surface registered trait methods of the capability.


Section 10: Dispatch-on-Rigid-Receiver Gaps

Goal: Close method-dispatch-on-rigid-receiver gaps that block tests/spec/declarations/traits.ori (§10.1) and tests/spec/capabilities/propagation.ori (§10.2). Prerequisite §10.0 splits over-500-line files to satisfy impl-hygiene.md §File Organization before §10.1/§10.2 edit them.

Depends on: §03 (bodies-pass + validator), §04 (codegen assertions), §09 (especially §09.2 def-impl Self — same impl_self_type infrastructure; landing §09.2 first ensures §10.1 RigidVar dispatch doesn’t interact with un-scoped Self fabrication).

Sequencing rule: §10.0 MUST complete before §10.1 or §10.2 touch their target files. §10.1 before §10.2 (Codex 2.6B: capability dispatch wires through the same lookup policy as bound-chain dispatch; §10.1 establishes the policy).


10.0 Prerequisite file splits

Goal: Reduce compiler_repo/compiler/ori_types/src/registry/traits/mod.rs from 765 lines to ≤500 lines AND compiler_repo/compiler/ori_types/src/infer/expr/calls/method_call.rs from 524 to ≤400 lines. Extract cohesive submodules; keep mod.rs as a dispatch hub. Mechanical split — zero semantic change.

10.0.1 Discovery

  • Run wc -l compiler_repo/compiler/ori_types/src/registry/traits/mod.rs — verify 765 lines baseline.
  • Run wc -l compiler_repo/compiler/ori_types/src/infer/expr/calls/method_call.rs — verify 524 lines baseline.
  • Re-read both files end-to-end to identify cohesive extraction boundaries:
    • registry/traits/mod.rs: public TraitRegistry struct + inherent methods; separate lookup_method* paths (lookup) from register_impl* + register_trait* paths (registration). Candidate split: registry/traits/lookup.rs (lookup paths) + registry/traits/registration.rs (registration paths) + registry/traits/mod.rs (struct definition + dispatch hub).
    • method_call.rs: infer_method_call + infer_method_call_named are the public entry points; resolve_receiver_and_builtin, check_positional_args, unify_higher_order_constraints, unify_closure_param_with_iterator_elem, check_range_float_iteration, check_infinite_iterator_consumed, find_infinite_source are private helpers. Candidate split: method_call/receiver_dispatch.rs (receiver/builtin resolution + closure-param unification) + method_call/mod.rs (public entry points).

10.0.2 Design

Fix shape (mechanical):

  1. Create new files with extracted content:
    • registry/traits/lookup.rsimpl TraitRegistry { pub fn lookup_method(...) { ... } pub fn lookup_method_checked(...) { ... } pub fn impls_for_type(...) { ... } } + helper private functions.
    • registry/traits/registration.rsimpl TraitRegistry { pub fn register_impl(...) { ... } pub fn register_trait(...) { ... } } + helper private functions (coherence checks, specificity, conflicting-defaults etc.).
    • registry/traits/mod.rs — struct definition + mod lookup; mod registration; + re-exports.
    • method_call/receiver_dispatch.rsresolve_receiver_and_builtin, unify_higher_order_constraints, unify_closure_param_with_iterator_elem, check_range_float_iteration, check_infinite_iterator_consumed, find_infinite_source. Keep them pub(crate) so method_call/mod.rs can call them.
    • method_call/mod.rsinfer_method_call + infer_method_call_named + check_positional_args.
  2. Update use statements in dependents (none should need changes if re-exports are preserved).

No semantic change: the split is git mv-like at function granularity. Existing behavior preserved; all existing tests pass unchanged.

10.0.3 Implementation

  • Create registry/traits/lookup.rs with extracted content.
  • Create registry/traits/registration.rs with extracted content.
  • Trim registry/traits/mod.rs to struct + module declarations + re-exports. Verify ≤300 lines.
  • Create method_call/receiver_dispatch.rs with extracted content.
  • Trim method_call/mod.rs (or rename it to method_call.rs, TBD by existing module convention). Verify ≤400 lines.
  • Run cargo build — compiles clean.
  • Run cargo test -p ori_types — all existing tests pass unchanged.
  • Run cargo clippy -p ori_types — clean.
  • Commit the split as ONE atomic commit (per compiler.md §Multi-commit sequences).
  • Post-split line counts: each file ≤500 lines.

10.0.4 Close §10.0

  • All §10.0.3 checkboxes marked [x].
  • cargo t + cargo clippy + cargo fmt all green post-split.
  • /tpr-review on §10.0 diff → clean (mechanical split should be low-finding).
  • /impl-hygiene-review on §10.0 diff → BLOAT findings for the two files cleared.
  • Frontmatter 10.0 status: complete.

10.1 Generic-param method dispatch via bound-chain walk

Goal: Extend TraitRegistry::lookup_method_checked to consult FunctionSig.type_param_bounds when receiver is Tag::RigidVar (generic type parameter). The current behavior keys impls_by_type by concrete Idx, so RigidVar receivers always miss. @f<T: Clone>(val: T) -> str = val.clone() fails E2003 method-not-found despite the declared bound.

Guards (both reviewers 2.6B): visited-set keyed on (rigid_var_idx, trait_idx) to prevent infinite loops on cyclic bounds A: B, B: C, C: A; recursion depth limit 256 per impl-hygiene.md §Panic & Assertion.

Priority (Rust + Swift consensus): bound-chain candidates are inherent-priority — they win over extension-method lookup when receiver is a rigid var. This matches Rust’s WhereClauseCandidate in probe.rs:958.

10.1.1 Discovery + root cause verification

  • Re-read registry/traits/lookup.rs (after §10.0 split) — confirm lookup_method_checked returns NotFound when impls_by_type.get(rigid_var_idx) is empty.
  • Re-read infer/expr/calls/method_call.rs (post-§10.0) + impl_lookup.rs — confirm dispatch path: infer_method_callresolve_receiver_and_builtin (for Tag::RigidVar, falls through to Continue { resolved }) → lookup_impl_methodTraitRegistry::lookup_method_checked(rigid_var_idx, method_name)NotFound.
  • Re-read output/mod.rs:373 (FunctionSig struct) — confirm type_params, type_param_bounds, scheme_var_ids are parallel Vec<…> with the same length.
  • Confirm engine access: InferEngine::signatures() returns Option<&FxHashMap<Name, FunctionSig>>. A current_function_sig() accessor may or may not exist — verify.
  • Write TDD matrix BEFORE implementation:
    • Positive test_method_dispatch_on_rigid_var_with_single_bound@f<T: Clone>(val: T) -> T = val.clone() compiles clean; val.clone() resolves to Clone::clone(val).
    • Positive test_method_dispatch_on_rigid_var_with_multiple_bounds_different_methods@f<T: Clone + Debug>(val: T) with val.clone() and val.debug() both resolve.
    • Negative test_method_dispatch_on_rigid_var_without_bound_reports_missing@f<T>(val: T) -> T = val.clone() reports E2003 (or equivalent) with suggestion “add T: Clone bound”.
    • Negative test_method_dispatch_on_rigid_var_ambiguous_bounds_reports_ambiguity@f<T: Foo + Bar>(val: T) where both Foo and Bar have method() reports E2023 ambiguous method.
    • Cyclic guard test_method_dispatch_cyclic_bounds_does_not_infinite_loop — construct cyclic-bound scenario (if possible within Ori’s surface syntax; else test via direct TraitRegistry fixture) and verify visited-set prevents loop. Test MUST have a bounded timeout (timeout: 5000 ms or similar in the harness).
    • Depth guard test_method_dispatch_deep_supertrait_chain_respects_depth_limit — construct supertrait chain of ≥256 levels (via fixture), verify error instead of stack overflow.

10.1.2 Design

Fix shape:

  1. In TraitRegistry (or lookup.rs post-§10.0), add a helper find_method_via_bound_chain(rigid_var_idx, method_name, signatures, current_fn_sig) -> Option<MethodLookup>:
    • Retrieve current_fn_sig.scheme_var_ids.iter().position(|&id| id == pool.data(rigid_var_idx)) to find the bound index.
    • For each trait name in current_fn_sig.type_param_bounds[bound_idx]:
      • Call self.get_trait_by_name(trait_name)Option<&TraitEntry>.
      • Check trait_entry.collected_methods for method_name (covers supertrait-inherited methods).
      • If found: record as BoundCandidate(trait_idx, method_item).
    • Deduplicate candidates. If 0: return None. If 1: return Some(MethodLookup::Trait { trait_idx, impl_idx: /* synthetic */, method }). If 2+: return with Ambiguous { candidates }.
    • Guard with visited-set keyed on (rigid_var_idx, trait_idx) passed through the recursion. Max recursion depth 256.
  2. In lookup_method_checked, when receiver_idx’s tag is Tag::RigidVar, call find_method_via_bound_chain BEFORE returning NotFound. Priority: bound-chain candidates are inherent-priority (Rust + Swift consensus) — they win over extension-method lookup in the resolved ordering.
  3. In infer/expr/calls/method_call.rs (or receiver_dispatch.rs post-§10.0), when resolve_receiver_and_builtin encounters Tag::RigidVar receiver, fall through to lookup_impl_method (unchanged); the new bound-chain walk runs inside lookup_method_checked and returns the right method.

Operator dispatch overlap: §10.1’s fix inside lookup_method_checked automatically extends to resolve_binary_op_via_trait at operators.rs:708 and resolve_unary_op_via_trait at operators.rs:759, both of which call lookup_method. No separate operator-dispatch subsection needed. Add one matrix cell to verify val + other where val: T, T: Add works.

10.1.3 Implementation

  • Write failing tests FIRST.
  • Add find_method_via_bound_chain helper in registry/traits/lookup.rs.
  • Wire it into lookup_method_checked for Tag::RigidVar receivers.
  • Add visited-set + depth-256 guards.
  • Run unit tests — all pass including cyclic-bounds + deep-chain guards.
  • Run timeout 150 cargo stf tests/spec/declarations/traits.ori — green (previously 3 E2005 sites).
  • Verify operator dispatch overlap: test @f<T: Add>(val: T, other: T) -> T = val + other works.
  • Matrix: receiver × bound × method:
    • Receivers: Tag::RigidVar from @f<T>, Tag::RigidVar from impl<T: Bound> Type<T>, Tag::SelfType in trait default method.
    • Bounds: single bound, multi-bound (A + B), supertrait chain (A: B: C), bound on trait method’s generic param.
    • Methods: trait-required method, trait-default method, supertrait-inherited method, operator (+, -, ==, <).
  • Verify cargo st tests/ green, debug + release parity.

10.1.4 Close §10.1

  • All §10.1.3 checkboxes marked [x].
  • /tpr-review on §10.1 diff → clean.
  • /impl-hygiene-review on §10.1 diff → clean.
  • /improve-tooling retrospective (cyclic-guard pattern may generalize to other compiler recursion paths).
  • /sync-claude retrospective — update typeck.md §TR-4 bound-satisfaction discussion with bound-chain walk specifics.
  • Frontmatter 10.1 status: complete.

10.2 Capability-method dispatch — trait-method surface for bound handler value

Goal: Extend infer_with_capability at constructors.rs:99-122 to surface the capability trait’s methods for dispatch on the bound handler value. When body calls Http.get(url: "..."), the Http identifier resolves to the handler’s type, and method lookup must find get via the capability trait’s method set (not only via the handler type’s own impls).

Composition with §10.1: §10.1’s bound-chain walk generalizes — §10.2 uses the same TraitRegistry::lookup_method_checked extension plus a capability-specific surface that registers the capability trait’s methods as candidates when the receiver is a capability-bound name.

10.2.1 Discovery + root cause verification

  • Re-read constructors.rs:99-122 (infer_with_capability) — confirm it binds capability → provider_ty in env (line 113) and marks capability provided (line 117-118) but does NOT validate provider’s trait conformance.
  • Trace Http.get(url: "...") path:
    • Parser emits MethodCall { receiver: Ident("Http"), method: "get", ... }.
    • infer_method_callinfer_ident(receiver) resolves Httpprovider_ty (the handler’s type).
    • resolve_receiver_and_builtin(provider_ty, "get") → no builtin match.
    • lookup_impl_method(provider_ty, "get")TraitRegistry::lookup_method_checked(provider_ty, "get") → NotFound if handler has no registered impl for get.
  • Confirm whether built-in capability traits (Http, Logger, Env, etc.) are pre-registered as trait entries in TraitRegistry via register_builtin_types or register_traits passes.
  • Write TDD matrix BEFORE implementation:
    • Positive test_capability_method_dispatch_via_handler_implwith Http = RealHttp in { Http.get(url: "foo") } resolves via RealHttp’s registered impl Http.
    • Positive test_capability_method_dispatch_via_capability_trait_surface — handler provides the right method-shape even without a formal impl Http declaration (per the stateful-handler-handler form with Cap = handler(state: S) { method: ... } in ...).
    • Negative test_capability_method_missing_on_handler_reports_error — handler doesn’t provide get → clean error message pointing at the missing trait method, NOT a confusing generic lookup failure.

10.2.2 Design

Fix shape:

  1. At infer_with_capability, when the capability name resolves to a registered trait (via TraitRegistry::get_trait_by_name(capability)), wire the bound identifier in scope to also surface that trait’s method set. This may be done via a new CapabilityBinding { trait_idx, provider_ty } TypeEnv entry type, or via a TraitRegistry::lookup_method_checked extension that checks “is this type currently bound as a capability handler? If yes, try the capability trait’s methods first.”
  2. For method lookup on a capability-bound identifier: try capability-trait method set (from get_trait_by_name(capability_name).collected_methods) with the handler’s provider_ty as Self; fall back to regular dispatch on provider_ty if not found.
  3. Trait-shape validation (optional for §10.2 core, filed-if-not-done): verify the handler’s type implements the required capability trait methods. If missing, report clean error at the with ... = handler site.

10.2.3 Implementation

  • Write failing tests FIRST.
  • Extend infer_with_capability to register capability-trait-method surface.
  • Extend method lookup path to consult capability-trait methods before falling through to NotFound.
  • Run unit tests — all pass.
  • Run timeout 150 cargo stf tests/spec/capabilities/propagation.ori — green (previously 4 [CAPABILITY-METHOD-DISPATCH] sites).
  • Matrix: capability × handler-shape × method:
    • Capabilities: Http, Logger, FileSystem, user-defined capability Foo { ... }.
    • Handler shapes: concrete struct with impl Http, stateful handler with Cap = handler(state: S) { op: (s, ...) -> (s', val) } in ..., default impl.
    • Methods: required methods, default methods, cross-trait method calls.
  • Verify cargo st tests/ green, debug + release parity.

10.2.4 Close §10.2

  • All §10.2.3 checkboxes marked [x].
  • /tpr-review on §10.2 diff → clean.
  • /impl-hygiene-review on §10.2 diff → clean.
  • /improve-tooling retrospective.
  • /sync-claude retrospective — update typeck.md §CP-2/CP-3 with capability-method-dispatch specifics.
  • Frontmatter 10.2 status: complete.

10.R Third Party Review Findings

  • None.

10.N Completion Checklist

  • 10.0 complete — file splits landed; registry/traits/* + method_call/* all ≤500 lines; all existing tests pass unchanged.
  • 10.1 complete — bound-chain dispatch on Tag::RigidVar; cyclic + depth guards; traits.ori green; operator dispatch overlap verified.
  • 10.2 complete — capability-method dispatch; propagation.ori green.
  • §10 aggregate /tpr-review on full §10 diff → clean.
  • §10 aggregate /impl-hygiene-review → clean.
  • /improve-tooling section-close sweep — cyclic-guard pattern generalization.
  • /sync-claude section-close doc synctypeck.md §TR-4 + §CP-2/CP-3 updates.
  • Plan sync — §10 frontmatter status: complete; §00-overview Quick Reference updates; §index.md section 10 status updated.

Exit criteria: All 3 subsections complete with TPR + hygiene clean; §10-touched test files green (traits.ori, propagation.ori); dual-exec parity preserved. §11 may begin.