Section 03: Type-Directed Desugar + Validation (DOMINANT)
Goal
See frontmatter. Proposal Phase 3 (desugar) + Phase 4 (validation). This is where BUG-04-129 is actually fixed.
Phase-placement invariant — ABSOLUTE
The desugar runs in ori_types (type checker), NOT ori_canon. Per typeck.md §EX-17 + canon.md §2 rows 3-4 (currently marked Target-only). Rationale (load-bearing): AIMS must see the post-desugar pure-reassignment IR; a canon-phase desugar would let CanExpr::Assign{target: Index/Field} reach AIMS, which emits RC ops on the mutation expression instead of the COW updated reassignment — violating canon.md §7.1 Invariant 5 (unified model). A canon-phase or ARC-phase desugar is BANNED.
Implementation Sketch
After type inference resolves the AssignTarget’s root + per-step receiver types, walk the chain right-to-left (assignment point → root binding): each index step wraps the inner value in receiver.updated(key: k, value: inner); each field step wraps it in { ...receiver, field: inner }. Intermediate reads use index per level. The whole target becomes root = <wrapped> (pure reassignment). Compound assignment composes: the parse-time compound expand produces target = target op rhs, then this desugar rewrites the index/field target. Side-effecting index/key expressions are hoisted to per-step let temporaries so they evaluate once.
03.1 Type-Directed Desugar + IndexSet Resolution + Validation + Diagnostics
- TDD-first: failing matrix BEFORE implementation — target shape (
x[i]/x.f/x.f[i]/x[i].f/x.f.g[i].h) × root binding (mutable /$-immutable / parameter / loop var) × operator (=/+=/-=/*=//=/%=/**=) × index expr (literal / variable / side-effectingf()) × backend (interpreter / LLVM). Each cell positive + negative pin. Side-effect cell (load-bearing):arr[f()] += 1with adbg!-countedf()— reject any desugar that double-evaluates. - Index-step desugar:
recv[k] = v→recv = recv.updated(key: k, value: v)inori_types(resolveIndexSet<Key,Value>onrecv’s type). Hoistkto a temporary when side-effecting.- Ori Tests:
tests/spec/expressions/index_assignment_simple.ori,tests/spec/expressions/map_assignment.ori
- Ori Tests:
- Field-step desugar:
recv.f = v→recv = { ...recv, f: v }(resolverecv’s struct type; field must exist). No trait needed.- Ori Tests:
tests/spec/expressions/field_assignment.ori
- Ori Tests:
- Nested + mixed chains: right-to-left wrapping per proposal §Mixed Chains; intermediate reads via
index. Covergrid[x][y][z]=v,state.items[i]=x,list[i].name=x,game.levels[i].enemies[j].hp=0.- Ori Tests:
tests/spec/expressions/nested_index_assignment.ori,tests/spec/expressions/mixed_chain_assignment.ori
- Ori Tests:
- Compound assignment: verify two-step desugar (
list[i] += 1→list[i] = list[i] + 1→list = list.updated(...)) for all compound ops × all target forms; single evaluation of the target/index expressions.- Ori Tests:
tests/spec/expressions/compound_index_assignment.ori
- Ori Tests:
- Validation + diagnostics (proposal §Type Checking Rules + §Error Cases): root-binding mutability (reject
$/param/loop-var); field membership;Index(non-final) +IndexSet(final) resolution with agreeing Value types; RHS assignability; key-type match. Allocate E-codes inori_diagnostic(structured construction, neverformat!strings, perdiagnostic.md); each error gets a#compile_failpin.- WHERE:
ori_types/src/check/(assignment-target checking; impl-method/desugar entry — verify exact module against the shipped tree, NOT a guessed path) +ori_diagnostic/src/error_code/mod.rs. - Ori Tests:
tests/compile-fail/index_assignment_errors.ori(immutable, parameter, loop-var, str-no-IndexSet, no-field, value-mismatch, key-mismatch)
- WHERE:
- Dual-execution parity + BUG-04-129 close: interpreter == LLVM for every desugared form;
ori build repro.ori+ori run repro.oriboth correct (E4003 + E6080 gone). Remove the now-unreachable E4003 (ori_arc) + E6080 (ori_eval) guards OR convert to internal-invariant assertions (no valid program reaches them).ORI_CHECK_LEAKS=1zero leaks. - Verify: full matrix green debug + release, both backends;
./test-all.sh.
Intelligence Reconnaissance
2026-06-01 — feature-mode scaffold; proposal is the research artifact. Desugar contract: read .claude/rules/typeck.md §EX-17 + canon.md §2 rows 3-4 directly (not graph-indexed).
scripts/intel-query.sh file-symbols "ori_canon/src/lower/expr" --repo ori— whereExprKind::Assign{target,value}lowers as-is today (the E4003 path)[ori:ori_canon/src/lower/expr.rs].scripts/intel-query.sh callers "index_assignment_not_supported" --repo ori— the E6080 eval guard to retire[ori:ori_eval/src/interpreter/can_eval/control_flow.rs].- Verify the assignment-checking module against the shipped tree
[ori:ori_types/src/check/bodies/]—ori_types/src/infer/methods/does NOT exist; impl-method body checking lives undercheck/bodies/(provenance: roadmap §15D supersession recorded inindex.md).
Spec References
- Proposal §Desugaring (all cases), §Type Checking Rules, §Error Cases, §Implementation Note (type-directed).
typeck.md §EX-17(the desugar contract:list[i]=v → list.updated(...),state.f=v → {...state, f:v}, Spec §13.6.2 index assignment).canon.md §2 rows 3-4(Target-only → shipped here, type-checker phase).- BUG-04-129 (the symptom this closes).
Tests
tests/spec/expressions/{index_assignment_simple,map_assignment,field_assignment,nested_index_assignment,mixed_chain_assignment,compound_index_assignment}.ori + tests/compile-fail/index_assignment_errors.ori + Rust unit tests in ori_types.