variant.append (crates/runtime/src/variant_ops.rs): #380
No reviewers
Labels
No labels
bug
dependencies
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
refactor
rust
technical-debt
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
navicore/patch-seq!380
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "arc-migration-done"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
list.set (crates/runtime/src/list_ops.rs):
Results: 944 unit tests, 443 integration tests, 0 clippy warnings, fmt clean.
That completes all four candidates from the migration doc: list.push, map.set, map.remove, variant.append, and list.set.
Overall this is a well-structured optimization. The safety reasoning is sound and the fast/slow paths preserve identical stack semantics.
variant_ops.rs - variant.append fast path
No stack-depth guard before peek_heap_mut_second. The slow path also lacks one, so this is consistent, but a malformed program reaching this with fewer than 2 stack values would go out of bounds silently. A debug-mode assert_depth could help.
Return value: fast path returns stack after one pop, Variant ends up at new sp-1. Matches the slow path. Correct.
list_ops.rs - list.set fast path
Double Arc::get_mut is correct. Both Arc (heap_value_mut) and Arc (Arc::get_mut(variant_arc)) must show sole ownership before mutating. This is right.
Live data: &mut VariantData across pop calls. The safety claim - pop only touches sp-1/sp-2 while data lives in the Arc at sp-3 - is correct. Worth keeping the comment current if pop internals change.
Out-of-bounds fast path: after two pops, sp = old sp-2. Variant untouched at old sp-3. push(Bool(false)) at old sp-2. New sp = old sp-1. Result: ( -- Variant Bool(false) ), matches slow path. Correct.
Nits
Comment removals are fine, remaining code is self-evident. list.set doc says Stack must have Value on top, Int below, and Variant below that, but the fast path now inlines a peek at the Int without popping it. A brief doc note would align the docs with the implementation.
Test coverage
Worth confirming coverage of: (1) list.set with a shared Arc (refcount > 1 for Arc or Arc) to exercise the slow COW clone path; (2) variant.append on a shared Variant for the same slow path; (3) list.set out-of-bounds in the fast path (tagged-int index, valid Variant, index >= len).
Summary
Core logic is correct, safety invariants are well-stated, optimization is meaningful. Main asks: verify OOB and shared-Arc test cases are covered, and optionally update the list.set doc to mention the inline index peek.
Code Review
(Note: A prior Claude review was posted 2026-03-28; this is a follow-up pass.)
Overall
The optimizations are correct and the pattern is consistent with the already-merged
list.push,map.set,map.removefast paths. Safety reasoning is well-documented. A few observations below.variant_ops.rs—variant.appendfast pathCorrect.
peek_heap_mut_secondborrows theVariantatsp-2,popreadssp-1only (adjusts sp by 1), so thedatareference into the Arc heap remains valid across the pop. Stack result: Variant stays atsp-1, value appended in place. Matches slow-path semantics.The sole-owner check still happens in the slow path's match arm, so shared-Arc Variants still clone correctly.
list_ops.rs—list.setfast pathCorrect overall, with one structural observation worth calling out:
Out-of-bounds early return is gated inside
Arc::get_mutIf the list Arc is shared and the index is out-of-bounds, the fast path falls through to the slow path unnecessarily — the slow path then pops all three values, clones the Arc (needlessly), and returns
false. The clone is wasted work. This is not a bug, but it's a mild missed optimization and a potential source of confusion: the OOB fast path only fires for sole-owned variants. A comment acknowledging this or restructuring to hoist the OOB check outside theArc::get_mutguard would help.databorrow live across pops in the OOB branchIn the OOB branch,
data: &mut VariantDatais technically still in scope when the twopopcalls happen. The safety holds —poponly decrementsspand reads the top slot; it never touches Arc heap memory — but it creates a pattern where a mutable borrow is live across pointer-manipulating operations. Consider droppingdatabefore the pops (it isn't used in the OOB branch) or adding a comment noting this explicitly:This is a style/clarity point, not a correctness issue in
unsafecode.Test coverage
The PR reports 944 unit + 443 integration tests pass. Two cases worth confirming are explicitly covered:
list.setwith a sharedArc<VariantData>(refcount > 1) to exercise the slow-path COW clone — ensures the fast path doesn't accidentally mutate shared state.list.setwith a shared Arc and an out-of-bounds index — currently hits the slow path; verify it returns(list, false)without panicking or cloning unnecessarily.If these are already covered by existing integration tests, the PR is good to merge.
Minor
// Return the list and false,// COW: if we're the sole owner,// Shared — clone and replace,// Out of bounds - return original list and false) are fine to drop — the code is self-evident without them.list.setdoc comment still says "returns a new list" in the first sentence of the new summary. The updated summary block is good, but the one-liner at the top ("Set an element in a list by index with COW optimization") no longer says "returns new list", which is correct. No action needed.