Skip to content

Fix reference lifetime error in diff inner loop#3

Merged
hellerve merged 2 commits into
masterfrom
claude/fix-borrow-lifetime
May 19, 2026
Merged

Fix reference lifetime error in diff inner loop#3
hellerve merged 2 commits into
masterfrom
claude/fix-borrow-lifetime

Conversation

@carpentry-agent
Copy link
Copy Markdown

The inner loop of Diff.diff fails to compile due to a borrow checker error:

The reference '(ref iold)' (depending on the variable
'(Int.inc (if (/=__int iold 0) ...))') isn't alive

The root cause: Map.put _overlap &iold &(Int.inc ...) passes &iold as the key and an expression that also uses iold as the value — both are arguments to the same function call, so the borrow checker sees &iold as depending on the value computation and rejects it.

Fix: precompute the overlap value into a let binding (new-val) before calling Map.put, so &iold and &new-val are independent references with no lifetime conflict.

Note: CI has continue-on-error: true on the test step, which masked this failure. The GitHub App token lacks workflows permission to fix that here — it should be removed separately so future breakage isn't hidden.

Closes #2


Opened by the carpentry-org heartbeat agent (Claude). Veit has not reviewed this yet.

Precompute the overlap value into a let binding (new-val) before
passing it to Map.put, so the &iold key reference and the value
computation are no longer arguments to the same function call. This
resolves the borrow checker conflict where &iold depended on the
Int.inc expression that also consumed iold.

Closes #2
@carpentry-agent
Copy link
Copy Markdown
Author

Review (reviewer agent)

Build & Tests

Build: pass (code compiles and runs on CI)
Tests: pass (3/3 — equal lists, non-equal lists, string diffing)
CI: failcarp-fmt --check reports ./diff.carp: would be reformatted. The Format check step fails on both ubuntu and macOS. Build and tests pass.

Findings

The borrow fix is correct and semantics-preserving. The old code passed &iold as the key and an expression containing iold as the value to Map.put in the same call — the borrow checker rightfully rejected the overlapping lifetimes. Hoisting the value computation into a let binding (new-val) breaks the dependency: at the Map.put call site, &iold and &new-val are independent references in the same let scope.

The computation is identical: Int.inc, /=, Map.get, Int.dec are all pure, iold is a value type (Int), so the hoist is semantically equivalent. Edge cases verified:

  • iold == 0: guarded by /= iold 0, so Int.dec is never called with 0.
  • Missing keys in overlap map: Map.get returns 0 (zero value for Int), correct for the LCS algorithm.

Good catch that master is broken too — the continue-on-error: true on the test step was silently masking the compilation failure.

The only CI blocker is formatting. Run carp-fmt diff.carp and commit the result.

No CHANGELOG exists in this repo.

Verdict: revise

The fix is correct but CI fails on formatting. Run carp-fmt on diff.carp and push the reformatted output, then this is ready to merge.

@carpentry-agent
Copy link
Copy Markdown
Author

Review (reviewer agent)

Build & Tests

Build: pass (CI green on both ubuntu and macOS)
Tests: pass (3/3)
Format: pass (carp-fmt --check now succeeds)

Prior feedback

The previous review confirmed the borrow-lifetime fix was correct and semantics-preserving. The sole remaining issue was that carp-fmt --check failed. This push addresses that — the formatting now passes CI.

Findings

None. The code change is the same hoisted new-val binding reviewed previously, now with formatter-compliant layout. Verified:

  • new-val computation is identical to the original inline expression (Int.inc of a conditional Map.get/identity)
  • &iold and &new-val are independent references at the Map.put call site — no lifetime conflict
  • iold == 0 guard prevents Int.dec on zero
  • Missing keys in overlap map return 0 (Int zero value), correct for the LCS algorithm

No CHANGELOG in this repo.

Verdict: merge

Formatting issue resolved, CI is green, fix is correct. Ready to merge.

@hellerve hellerve merged commit 8e9364a into master May 19, 2026
2 checks passed
@hellerve hellerve deleted the claude/fix-borrow-lifetime branch May 19, 2026 05:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

diff.carp: reference lifetime error in Map.put inside inner loop

1 participant