100%

Section 01: IndexSet Trait + updated Method + ARC COW

Goal

  • See frontmatter. Proposal Phase 1.
  • The updated method is the desugar target for index assignment (list[i] = x → list = list.updated(key: i, value: x)); it ships before the section-03 desugar consumes it.

Implementation Sketch

  • IndexSet<Key, Value> is a prelude trait (companion to read-only Index).
  • The updated method is publicly callable (functional style: list.updated(key: 0, value: x)) independent of the assignment sugar.
  • The contract is value-semantic: updated returns a NEW collection, observably distinct from the receiver.
  • Built-in impls are compiler-provided intrinsics; for all three types ([T] / [T, max N] / {K:V}) the existing ARC copy-on-write mutates the backing buffer in place at refcount==1 (provable uniqueness) and allocates-then-rebinds otherwise — observably identical to the always-copy path (Swift Array/Dictionary COW under ARC).
  • Map in-place COW is live in the runtime: ori_rt/src/map/cow.rs cow_insert_existing / cow_insert_new carry a unique-fast-path (ori_rc_is_unique) that overwrites in place at refcount==1 — maps are NOT always-allocate.
  • str deliberately omits IndexSet (codepoint replacement may change byte length).

01.1 IndexSet Trait + updated Method + ARC COW Implementation

  • TDD-first: failing test matrix BEFORE implementation — updated on [int] / [str] / {str:int} / [int, max N] × {in-bounds, out-of-bounds, refcount==1 in-place, refcount>1 copy} × {interpreter, LLVM}. OOB semantics per type: [T].updated panics, [T, max N].updated panics (key >= length), {K:V}.updated inserts. Negative pin: str.updated direct-resolution failure (str implements Index, not IndexSet).
    • [T, max N].updated OOB cell (parallels [T]): positive pin — a [int, max 3]-annotated literal .updated(key: 1, value: 9) yields [1, 9, 3] in both backends; negative pin — assert_panics(() -> fixed.updated(key: 5, value: 0)) for key >= length (in-bounds-of-capacity but out-of-bounds-of-length still panics, matching [T] index semantics, NOT the fixed-list try_push overflow path). Construction uses the [int, max 3] annotation form: .to_fixed<$N>() is unimplemented (BUG-02-043).
      • Ori Tests: tests/spec/traits/index_set/fixed_oob.ori
  • Define IndexSet<Key, Value> trait in compiler_repo/library/std/prelude.ori with @updated (self, key: Key, value: Value) -> Self.
    • Ori Tests: tests/spec/traits/index_set/basic.ori
  • Register IndexSet trait definition + built-in derived/impl signatures in ori_types/src/check/registration/ (mirror how Index is registered). Mirrored outcome: Index carries NO check/registration/ entry — both traits register via prelude parse + registry-driven resolution (registry_bridge), so the registration surface is the ori_registry MethodDef (Some("IndexSet")) consumed by resolve_builtin_method; no parallel check/registration/ table exists to extend.
    • Rust Tests: ori_types/src/infer/expr/methods/tests.rs (resolution-path pins; check/registration/tests.rs has no Index-trait analog to mirror)
  • Register updated as a built-in method on [T] + {K: V} in ori_eval method dispatch + ori_registry (per .claude/rules/registry.md — methods on builtin types live in the registry). Map registration carries the K: Eq + Hashable bound (mirrors Index / map-key constraints). [T, max N] is NOT a separate registration surface: it erases to TypeTag::List at registration/resolution, so the [T] (List-tag) registration in ori_registry/src/defs/list/mod.rs (registered under tag: TypeTag::List) covers fixed-lists — registering updated on [T, max N] separately would be redundant.
    • Rust Tests: ori_types/src/infer/expr/methods/tests.rs (companion to resolve_builtin_method at ori_types/src/infer/expr/methods/mod.rs; the resolver lives in ori_types, NOT ori_eval/src/method_dispatch/) — registry resolution of updated succeeds for each type: [int].updatedIndexSet<int, int>, {str: int}.updatedIndexSet<str, int>, [int, max 4].updatedIndexSet<int, int> resolves via the List tag (assert resolve_builtin_method returns the registered impl + correct key/value types per type).
    • Rust Tests: non-hashable map-key negative pin lands as the spec compile-fail tests/spec/traits/index_set/non_hashable_key.ori (E2031 at map-type formation via check_map_key_hashable, which is private to infer/expr/type_resolution.rs — no Rust-level E2031 pin surface exists; the bound is enforced before method resolution is reached); positive pin: hashable K resolves per updated_method.ori + infer/expr/methods/tests.rs.
    • Ori Tests: tests/spec/traits/index_set/updated_method.ori
  • Implement updated ARC copy-on-write in ori_patterns / ori_eval value model: for [T] / [T, max N] AND {K:V} refcount==1 → mutate backing buffer in place (list: ori_rt/src/list/cow.rs; map: ori_rt/src/map/cow.rs unique-fast-path), refcount>1 → clone-then-mutate + rebind. List/fixed-list out-of-bounds panics; map inserts-or-replaces.
    • Rust Tests: ori_patterns/src/value/tests.rs — COW behavior matrix (positive: in-place fires at rc==1 for list/fixed-list AND map; negative: aliased list/map forces copy, alias unchanged)
    • Ori Tests: tests/spec/traits/index_set/cow_behavior.ori
  • Register updated AIMS builtin ownership contract in ori_arc so AIMS can prove the value-flow rather than emit RC ops on the inserted value. Add updated’s param/return ownership + borrow contract to BuiltinOwnershipSets / seed_builtin_contracts (ori_arc/src/aims/builtins/mod.rs) and the borrow-builtin surface: self is consumed-and-returned (the return aliases or replaces the receiver’s storage), value is moved into the collection (ownership transferred — its RC must NOT be dropped after insert). Without the interprocedural contract AIMS may emit an erroneous RC dec on the inserted value (leak at rc==1 in-place; double-free at rc>1 copy).
    • 3-param contract constructor required: updated is 3-param (self, key, value); the existing cow_receiver_only_contract (ori_arc/src/aims/builtins/mod.rs) marks ONLY the receiver and cannot express the value-param ownership-transfer — seed_builtin_contracts MUST NOT default updated into it (param-length mismatch). Add a new 3-param parameterized contract constructor (or specialized memory contract) that models self consumed-returned + value moved-in + key borrowed.
    • Uniform COW contract across all three types: [T] / [T, max N] AND {K:V} share one value-flow shape — in-place-at-rc==1 / clone-at-rc>1, the return aliases-or-replaces receiver storage (no list-vs-map asymmetry; the runtime map unique-fast-path at ori_rt/src/map/cow.rs:106 is the in-place path). The seeded contract models the single COW value-flow (self consumed-returned, value moved-in) so AIMS proves all three without parallel emission paths.
    • Rust Tests: ori_arc/src/aims/builtins/tests.rs — contract seeded for updated on [T] / [T, max N] / {K:V}; value-param ownership-transfer asserted (no RC dec on the moved value).
    • Ori Tests: tests/spec/traits/index_set/arc_insert.ori — insert an ARC-managed element ([str] / {str: [int]}) under updated; ORI_CHECK_LEAKS=1 reports zero leaks debug + release, interpreter == AOT.
  • LLVM updated codegen for the three built-in types; verify interpreter == AOT (dual-execution parity); ORI_CHECK_LEAKS=1 zero leaks debug + release. The codegen + AIMS contract + runtime are CORRECT. BUG-04-142 (parallel session’s 59f645300 burden-only-default RC regression: __index double-inc str-element leak + premature receiver dec / alias corruption) resolved 2026-06-10; AOT parity restored.
    • Re: index_set_updated AOT 13/13 debug + 13/13 release (incl. test_updated_list_str_elements_no_leak, test_updated_list_str_shared_copy_no_double_free, test_updated_list_shared_preserves_alias, test_updated_map_arc_value_moved_no_leak — the exact cases BUG-04-142 had broken).
  • Verify: matrix green debug + release, both backends. Interpreter index_set spec corpus green (0 failures in tests/spec/traits/index_set/); LLVM AOT 13/13 debug + 13/13 release. BUG-04-142 resolved — AOT parity restored, section unblocked.

Intelligence Reconnaissance

2026-06-01 — feature-mode scaffold; approved proposal index-assignment-proposal.md is the research artifact.

  • scripts/intel-query.sh symbols "Index" --repo ori --kind trait — mirror the read-side trait registration [ori:ori_types/src/check/registration/] for IndexSet.
  • scripts/intel-query.sh file-symbols "ori_eval/src/method_dispatch" --repo ori — built-in method routing surface [ori:ori_eval/src/method_dispatch/].
  • scripts/intel-query.sh similar "updated" --repo swift,rust --limit 5 — Array/Dictionary copy-on-write prior art.

Spec References

  • compiler_repo/docs/ori_lang/v2026/spec/13-variables.md §13.6.2 (index assignment) — the AUTHORITATIVE SSOT defining x = x.updated(key: i, value: v) as the index-assignment desugar contract; spec is SSOT over the proposal. The §13.6 NOTE establishes that in-place mutation under refcount==1 is an optimization observably identical to the always-copy value-semantic path.
  • Proposal index-assignment-proposal.md §The IndexSet Trait, §Standard Implementations (research artifact; subordinate to the spec).
  • index-trait-proposal.md (the read-side Index pair).
  • Semantic contract: t.updated(key:k, value:v).index(key:k) == v (not compiler-enforced).

Tests

tests/spec/traits/index_set/{basic,updated_method,cow_behavior,fixed_oob,arc_insert,non_hashable_key}.ori + Rust unit tests in ori_types / ori_registry / ori_patterns / ori_arc + AOT integration compiler/ori_llvm/tests/aot/index_set_updated.rs per items above.

01.R — Third Party Review Findings

Section-close /tpr-review (codex + agy + opencode + fork-context Opus adjudicator), 2 rounds, exit cap_reached_with_substantive (wall-clock cap).

  • [TPR-01-001-codex][Major] (CURED) Decorative Unicode box-drawing banner comments in compiler/ori_llvm/tests/aot/index_set_updated.rs (6 occurrences, COMMENT_HYGIENE_DRIFT:decorative-banner). Replaced with plain ASCII section comments (working tree; commit deferred behind BUG-04-142 cross-scope gate).
  • [TPR-01-002-codex][Critical] (RESOLVED) INVERTED-TDD:commit-with-red-tests — section claimed complete + 13/13 AOT while 5/13 index_set_updated AOT tests fail. Adjudicated: the updated() feature is correct (interpreter 33/33; AOT was 13/13 at d2237c1df); the regression is the parallel session’s 59f645300 burden-only-default flip (two general ori_arc burden-path RC bugs). Dispositioned: filed BUG-04-142, reverted premature completion, declared blocked_by: [BUG-04-142]. BUG-04-142 resolved 2026-06-10; re — AOT 13/13 debug + release, interpreter index_set green. blocked_by cleared.

HISTORY

  • 2026-06-12 — BUG-04-142 cleared; §01 re-verified + unblocked: BUG-04-142 (parallel-session burden-only-default RC regression) resolved 2026-06-10. Re-verified index_set_updated AOT 13/13 debug + 13/13 release (the str-leak / double-free / alias cases the regression had broken now pass) and the interpreter index_set spec corpus green. Flipped §01.1’s two remaining verification items, cleared blocked_by: [BUG-04-142]. No code changed this session — verify-first re-close after the cross-scope blocker cleared.

Adjudicator-refuted (not actionable, recorded for trail): agy INVERTED-TDD claims on fixed_oob.ori turbofish removal (false positive — .to_fixed<$N>() unimplemented per BUG-02-043, construction rerouted to [int, max 3] annotation with documented rationale); opencode VERIFIED:no-drift positive verification record.

  • 2026-06-11 — Stale review_pipeline: marker cleared by /continue-roadmap orchestrator: marker carried stage: ?, next_step: ?, updated: ?. Per /review-plan SKILL.md §Step 1a stale-marker rule (reviewed: false + marker present → STALE by definition), marker invalid; prior diagnosis preserved here for traceability. Cure rooted in scripts/plan_orchestrator/markers.py:clear_stale_marker_if_unreviewed.