0%

§12 Partial-Move Validator Overcorrection

Chain predecessor: §07 (close-out) — INV-19 linear-chain edge (ordering-only; see 00-overview.md HISTORY 2026-06-07).

§12.0 Discovered Work Context

FieldValue
Source commite186e2754 feat(typeck): conditional-partial-move validator + pre/post-contract validation + tagged_ptr drop walk
Sibling commitf5a37d327 test(typeck): partial_move validator sibling tests
Discovery date2026-05-16 (via ./test-all.sh baseline run; 114 failures total, ~60 traced to this regression)
Routing case (per routing.md §3)(b) Independently-workable in plan’s bounded context
Insertion orderinginserts_after: 11; sibling to §02 Validator Module (not amendment-of — §02 covers PC-2 enforcement post-body, §12 covers a NEW validator added at a separate seam)
Failures absorbed~60 (10 AOT E2043 + 47 interpreter destructure/match + 3 compile-fail contract-test expectations)

§12.1 Mission

Narrow the conditional-partial-move validator’s projection-conditionality detection so it correctly distinguishes:

  • Conditional partial moves (validator MUST flag) — if c then move w.name else { /* w.name remains valid */ }; use(w) — branches diverge on whether a field projection was moved out, leaving the parent struct in an inconsistent partial-move state at the join point.
  • Unconditional total bindings (validator MUST NOT flag) — let { name, age } = w / let (a, b, c) = t / let [$h, ..$rest] = xs — every branch (or the single binding site) moves the same set of fields/elements; no inconsistency at any join point.
  • Match-arm independent rebinding (validator MUST NOT flag) — match scrutinee { Some(x) -> ..., None -> ..., _ -> ... } — each arm independently rebinds the scrutinee or its sub-projections; arms do not share partial-move state across the match expression because each arm’s binding scope ends at the arm’s -> body terminus.

§12.2 Root Cause Hypothesis

Per CLAUDE.md §Invariant-Anchoring Before Test-Chasing:

QuestionAnswer
What invariant does this validator enforce?Reject moves out of struct fields where the projection is taken conditionally (branches diverge on whether the projection happened) — guards against the canonical w.name-on-one-branch failure mode where w ends up partially-moved on one path but not another, leaving downstream code observing inconsistent partial-move state.
Which downstream subsystem consumes this invariant?AIMS lattice + ARC realization — partial-move state on a heap-allocated struct affects RC accounting (which fields’ RC ops fire on which branches) and drop ordering.
Tests fail → fix code-under-test or the invariant?Almost always code-under-test. The validator predicate is too broad: it currently flags any pattern where field projection appears (destructure literals, match arms), regardless of whether the projection is genuinely conditional (taken on some branches, not on others) vs unconditional (taken always, in every branch / arm).
Where is the predicate?compiler/ori_types/src/check/validators/partial_move/ (full path to be confirmed during §12.3 discovery — directory landed in e186e2754; section may include mod.rs, classify.rs, or similar). The predicate’s projection-walk logic does not currently exclude (a) let-binding destructure literals (no branch divergence — every bind takes every field), (b) match-arm rebinding scopes (each arm’s bindings live within the arm’s body terminus; no cross-arm partial-move state).

§12.3 Implementation Sketch

PhaseDescriptionFiles touched
DiscoveryRead compiler/ori_types/src/check/validators/partial_move/ end-to-end; map the projection-walk + branch-divergence detection. Identify where destructure literals and match-arm bindings flow through the validator. Compare against the validator’s stated invariant (§12.2 question 1) to identify the precise over-broad branch.compiler/ori_types/src/check/validators/partial_move/**/*.rs
TDD matrix authoringAuthor the §12 test matrix BEFORE touching validator code per CLAUDE.md §TDD for Bugs + tests.md §Matrix Testing Rule. Positive cells (destructure + match-arm rebinding patterns currently failing) + negative cells (genuine conditional partial moves the validator MUST continue to reject). Co-locate matrix in compiler/ori_types/src/check/validators/partial_move/tests.rs (sibling to the validator module per tests.rs SSOT convention).compiler/ori_types/src/check/validators/partial_move/tests.rs
Validator narrowingModify the projection-conditionality predicate so destructure literals and match-arm rebinding scopes are excluded from the “conditional partial move” classification. Implementation shape TBD during §12.3 discovery — likely an early-return on (a) AST node is Pattern::Tuple/Pattern::Struct/Pattern::List in let-binding position, (b) AST node is a MatchArm body’s binding scope. NOT a syntactic skip-list — predicate refinement that captures the semantic distinction.compiler/ori_types/src/check/validators/partial_move/<module>.rs
Compile-fail expectation refreshUpdate expectation strings in 3 tests/compile-fail/*_contract_*.ori files to match the actual E2044/E2046/E2047 wording the validator now emits. Verify by reading the validator’s emission site + comparing the produced wording vs the test expectation.tests/compile-fail/post_contract_void_return.ori, tests/compile-fail/pre_contract_not_bool.ori, tests/compile-fail/pre_contract_scope_violation.ori
Verificationtimeout 150 ./test-all.sh clean; dual-exec parity clean; /tpr-review + /impl-hygiene-review clean.

§12.4 Implementation Items

  • §12.3.0 Discovery: read compiler/ori_types/src/check/validators/partial_move/ end-to-end; document the projection-walk + branch-divergence detection algorithm; identify the precise predicate location that classifies destructure/match-arm as “conditional partial move” when it should not.
  • §12.3.1 TDD matrix author (positive pins): write failing tests in compiler/ori_types/src/check/validators/partial_move/tests.rs covering tuple/struct/list destructure + match-arm rebinding patterns. Tests assert validator does NOT flag these. RED state before §12.3.3 lands.
  • §12.3.2 TDD matrix author (negative pins): write tests in same file covering genuine conditional partial moves (canonical w.name-on-one-branch repro + 2-3 variants). Tests assert validator DOES flag these. GREEN state immediately (validator already flags correctly here).
  • §12.3.3 Validator narrowing: modify the projection-conditionality predicate. §12.3.1 tests flip from RED to GREEN; §12.3.2 tests remain GREEN.
  • §12.3.4 Compile-fail expectation refresh: update tests/compile-fail/post_contract_void_return.ori expectation string to match actual E2046 wording emitted by pre/post-contract validator.
  • §12.3.5 Compile-fail expectation refresh: update tests/compile-fail/pre_contract_not_bool.ori expectation string to match actual E2044 wording.
  • §12.3.6 Compile-fail expectation refresh: update tests/compile-fail/pre_contract_scope_violation.ori expectation string to match actual E2047 wording.
  • §12.3.7 Cross-coverage verification: timeout 150 cargo t -p ori_llvm --test aot test_borrow_separate_heap_types test_h12 test_h3 passes; AOT E2043 cluster clears.
  • §12.3.8 Spec sweep: timeout 150 cargo st tests/spec/patterns/binding_patterns.ori tests/spec/patterns/syntax.ori passes; interpreter cluster clears.
  • §12.3.9 Compile-fail sweep: timeout 150 cargo st tests/compile-fail/post_contract_void_return.ori tests/compile-fail/pre_contract_not_bool.ori tests/compile-fail/pre_contract_scope_violation.ori passes.
  • §12.3.10 Regression guard: timeout 150 cargo stf tests/spec/types/empty_literals shows identical pre/post counts (validator narrowing did not perturb empty-literal defaulting).
  • §12.3.11 Dual-execution parity: timeout 150 diagnostics/dual-exec-verify.sh --json | jq '.per_test[] | select(.parity_status != "match")' returns empty.
  • §12.3.12 Debug + release parity: timeout 150 ./test-all.sh clean in both profiles per CLAUDE.md §Fix Completeness.
  • §12.3.R /tpr-review on §12 diff returns clean across codex + gemini + opencode; no actionable findings.
  • §12.3.N /impl-hygiene-review on §12 diff returns clean after TPR.
  • §12.3.Close flip status: in-progress → complete in this section’s frontmatter via flip_from_in_review_clean() per state-discipline.md §4.

§12.5 Banned Approaches

Per CLAUDE.md §INVERTED-TDD + §The One Rule + §Never Reason Out of TPR Findings:

  • Adding #[skip] markers to destructure/match-arm tests — banned. Tests are the deliverable that caught the validator regression; the validator is the deliverable that needs narrowing.
  • Weakening test expectations to match the over-broad validator — banned. Test expectations are correct; validator predicate is wrong.
  • Gating destructure/match-arm patterns syntactically (e.g., if ast_node.kind == Pattern::Tuple { return Ok }) — banned shape per §INVERTED-TDD. The fix is predicate refinement that captures the semantic distinction (unconditional total binding vs conditional projection), NOT a syntactic skip-list.
  • Reverting commit e186e2754 wholesale — banned. The validator’s Pre/Post-Contract + tagged_ptr drop walk deliverables are correct and load-bearing; only the conditional-partial-move predicate is over-broad. Surgical narrowing, not whole-commit revert.
  • “Pre-existing” framing — banned per routing.md §2. The regression is from a recent commit, but routing.md §NEVER Investigate "Pre-Existing?" says timeline is irrelevant; only question is “is it fixed?”

Intelligence Reconnaissance

Date: 2026-05-16. Graph: HEAD be3a7ba (CPG stale per status; symbols/relationships current).

  • [ori:compiler/ori_types/src/check/validators/] scripts/intel-query.sh file-symbols compiler/ori_types/src/check/validators/ --repo ori → 30 symbols (ValidatorContext, validate_body_types, site_from_expr_kind, narrow_to_lambda_param_span, …) — §02’s existing validator module surface.
  • [ori:compiler/ori_types/src/check/validators/partial_move/] scripts/intel-query.sh file-symbols compiler/ori_types/src/check/validators/partial_move/ --repo ori → 1 symbol (partial_move_validator_handles_invalid_body_root_without_panicking in tests.rs:14); production-code symbols from e186e2754 not yet indexed (embedding staleness — partial_move/ landed post last enrich_embeddings.py run). §12.3.0 discovery must read the directory contents directly (Read permitted at execution-time per plan-read-discipline.md §3.1).
  • [ori] scripts/intel-query.sh symbol-plans partial_move_validate --repo ori → 0 hits (no cross-plan references; this is the first plan section to claim ownership of the partial_move validator narrowing).
  • scripts/intel-query.sh status → ori repo HEAD be3a7ba, 31,374 symbols indexed; last_synced: 2026-05-16T19:21:28Z (post-e186e2754); cpg_stale: true + embedding_stale: true warnings noted. CPG queries (callers, callees) on the new partial_move/ symbols will return empty until intel-query.sh refresh --code runs against the post-e186e2754 tree.
  • Symbol references confirmed via direct file read at §12.3.0 will populate MENTIONS_CODE for /review-plan Step 4 blind-spot synthesis on subsequent re-runs.

§12.6 References

  • e186e2754 — source commit (typeck validator + pre/post contract + tagged_ptr drop walk)
  • f5a37d327 — sibling tests commit
  • compiler/ori_types/src/check/validators/partial_move/ — validator implementation (path to be confirmed during §12.3.0 discovery)
  • tests/spec/patterns/binding_patterns.ori — interpreter destructure failures
  • tests/spec/patterns/syntax.ori — interpreter match failures
  • tests/compile-fail/post_contract_void_return.oriE2046 expectation refresh
  • tests/compile-fail/pre_contract_not_bool.oriE2044 expectation refresh
  • tests/compile-fail/pre_contract_scope_violation.oriE2047 expectation refresh
  • section-02-validator-module.md — sibling validator module (PC-2 enforcement at body-finalize seam; NOT amended by §12)
  • CLAUDE.md §INVERTED-TDD Is BANNED
  • CLAUDE.md §Invariant-Anchoring Before Test-Chasing
  • CLAUDE.md §Fix Completeness
  • .claude/rules/tests.md §Matrix Testing Rule
  • .claude/rules/impl-hygiene.md §Cross-Phase Invariant Contracts

HISTORY

  • 2026-05-16 — Section created via /create-plan --inline inline-insertion: discovered work routed per routing.md §3 case (b) — conditional-partial-move validator overcorrection in commit e186e2754 regressed ~60 tests across AOT + interpreter + compile-fail surfaces. Section absorbs the regression into typeck-inference-completeness umbrella (validator landed adjacent to §02 Validator Module’s existing work; shares typeck inference completeness bounded context). Sibling section to §02 (not amendment-of — §02 covers PC-2 enforcement post-body, §12 covers a separate validator at a different seam).