You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
soundness: use-after-move through a callee-returned borrow is accepted end-to-end (post-CORE-01 hole) #554
Probe (verified twice: original survey + independent critic re-run, 2026-06-11)
This program passes the full pipeline (typecheck → borrow → quantity, "Type checking passed", exit 0):
fn pick(ref x: Int) -> ref Int {
return &x;
}
fn consume(own v: Int) -> Int { return v; }
fn main() -> Int {
let a: Int = 7;
let r = pick(a);
let _gone = consume(a); // a is moved here
let v = *r; // read through r AFTER the move — accepted
return v;
}
Control probe (plain double-consume of an own param) is correctly rejected with "use of moved value" — so move-tracking is live and this is a genuine false negative, not a Copy exemption.
Root cause (three interacting decisions in lib/borrow.ml)
Temporary call-argument borrows are released at call end (borrow.ml:893-896).
ref_source_borrow matches only direct &p forms — a call result (ExprApp) never registers as a borrow source (borrow.ml:553-566).
Returning &(ref-param) from a callee is intentionally allowed (borrow.ml:1675) — contradicting the model comment at ~borrow.ml:847-849 ("AffineScript's value model does not let a callee retain a borrow past return").
The borrow graph therefore has no edge from r to a; the loan returned by pick is invisible. This is precisely the loan-propagation problem ADR-022's Polonius origins solve (tracking: #553).
Requested actions
Add this hole to the known-gaps trailer in lib/borrow.ml (it is currently absent from the "Still deferred" block) — same day as triage, per the doc-truthing discipline.
Refresh the CAPABILITY-MATRIX borrow row in the same PR (per the matrix's own change-control rule). ⚠️ Note the trap: README.adoc:36 / CAPABILITY-MATRIX:213 still say "known soundness gap (CORE-01)" — stale, but accidentally still true because of this hole. Do not refresh those lines to "closed" without this issue documented first, or stale-conservative becomes active overclaim (the DOC-16 ratchet only catches overclaims, so it cannot catch that transition).
Land the probe as a regression artifact. The test tree has no skip/xfail mechanism (no Skip/xfail anywhere in test/), so options: (a) unwired fixture in tests/borrow/ (12 existing unwired .affine files set precedent) with a pointer here, or (b) wire it as an expected-reject E2E Borrow Graph case once the fix lands.
Formal counterpart
#515 (borrow-graph soundness, Lean 4) is the proof obligation this hole would have falsified — a useful concrete counterexample for whoever picks that up. The 416-test green gate did not catch this; that is the operational-only soundness story's known limit (#513).
Filed from the 2026-06-11 whole-picture survey; probe verified by execution.
Activity
security: standardize secret scanning on TruffleHog (+ ADR-023 draft …
docs: truth-refresh after CORE-01 closure + 2026-06-11 survey — matri…
docs: truth-refresh after CORE-01 closure + 2026-06-11 survey — matri…
docs: truth-refresh after CORE-01 closure + 2026-06-11 survey (matrix…
docs: consolidated session overview — migration corpus + string/float…
fix(borrow): close #554 residuals (a) interprocedural + (c) reassign-…
fix(borrow): close #554 use-after-move through a callee-returned borrow
fix(borrow): close #554 residuals (a) interprocedural + (c) reassign-…
fix(borrow): round-3 hardening — ref-count Slice-B reassign + union r…
fix(borrow): close #554 use-after-move through a callee-returned borrow
fix(borrow): close #554 residuals (a) interprocedural + (c) reassign-…
fix(borrow): round-3 hardening — ref-count Slice-B reassign + union r…
fix(codegen): close #556 — async CPS table-miss loud-fail fence (#596)
docs: author the proof-needs inventory (P-/F-/K- obligation series) (#…
feat(formal): F-1 preservation + grow K-1 (let/vars) + state P-2/P-3/…
docs: make soundness status a single, test-anchored source of truth (#…
feat(formal): mechanize the four Wave-0 siblings (P-2, P-3, F-3, F-4) (…
feat(formal): grow P-3 to a real borrow graph (loan edges + move-loca…
docs(proof-needs): refresh stale §1 baseline + §6 sequencing after Wa…