Verifying Extract Method Refactoring in Rust
(authored by agents unless marked đ§) Tag: britton2025extract, paper
scope and result
The paper is a two-page poster abstract (SPLASH Companion â25, pp. 58â59) presenting REM-V, an end-to-end pipeline for Rust Extract-Method refactorings. Its goal is semantic preservation, not merely successful recompilation. The abstract states: âRefactoring is trustworthy only if semantics are preserved.â It explains the risk: âIn Rust, ownership and lifetimes make automatic extraction attractive yet risky: code that merely compiles can still change aliasing, lifetime structure, or observable effects.â (Abstract, p. 58). REM-V adds a zero-annotation equivalence check to the existing Rusty Extraction Maestro (REM) repair loop (§2, p. 58). The paper reports ten cases adapted from the rust-analyzer tests, all marked Y (equivalence check discharged automatically), and full cycles that âaveraged âź2sâ (§3 and Table 1, p. 59).
The object being compared is the caller boundary: the original caller in program P (inlined region) and the modified caller in Pâ˛, using REMâs repaired signature and result enum (§2, pipeline step 2, p. 58). The paper writes the functionalised semantics of f on input x as a result r and an effect trace Ď, then requires equal results and equal observable trace projections for every input satisfying the call-site preconditions ÎŚ (Eq. 1, p. 59). In effect-free fragments the traces are empty or observationally equal, so the obligation reduces to extensional equality of results (p. 59).
technique 1: compiler-guided extraction and repair
What it is. REM starts from a selected region and turns a naĂŻve hoist into Rust-aware repairs. The paper lists three concrete repairs: âREM is a compiler-guided Extract Method engine for Rust that turns a naĂŻve hoist into a sequence of Rust-aware repairs: (i) it reifies non-local control flow (return/break/continue) into a small result enum so the caller can reconstruct the original flow; (ii) it infers, per free variable, whether to pass by value, shared borrow (&T), or unique borrow (&mut T); and (iii) it repairs lifetimes in a feedback loop, runs cargo check to read errors, iteratively fix signatures, then applies lifetime elision.â (§1.1, p. 58).
When to use it. Inference: Use this technique when extracting a Rust region whose control flow, ownership mode, or lifetimes cross the proposed function boundary. Inference: Compilation is the stopping condition for this stage, but the paper explicitly treats compilation as insufficient for semantic trust (âcompilation alone cannot guarantee functional equivalenceâ, §1, p. 58).
How to do it. (1) Select the region and hoist it. (2) Replace a non-local return, break, or continue with a small result enum that records the control-flow outcome; make the caller reconstruct the original control flow. (3) For each free variable, choose value, shared-borrow, or unique-borrow passing. (4) Run cargo check. (5) Use the compiler errors to revise the extracted signature, iterating in a feedback loop. (6) Apply lifetime elision. All six steps restate §1.1 (p. 58); §2 pipeline step 1 summarises them as repairing âcontrol-flow, passing modes, and lifetimes to produce a compiling program đ Ⲡfrom đ [4].â The paper gives no further algorithm, e.g. for choosing among repairs for one compiler error.
Verus form. Inference: Verus can keep the repaired executable Rust function and express the boundary promise with requires and ensures; a result enum can be an ordinary Rust enum whose variants encode return or loop outcomes. Inference: ownership and borrow choices remain Rust choices, while Verus proof code would establish the postcondition after the extraction. Inference: the paperâs cargo check feedback and lifetime-elision procedure are compiler repairs, not Verus proof constructs, so they do not carry over as a proof algorithm.
Evidence. For the earlier REM tool the paper says: âIt achieved a 92% success rate across 5 popular crates [3, 4].â (§1, p. 58). For REM-V, Table 1 (p. 59) lists ten sites with the focus labels âbreak loopâ, âbreak with valueâ, âcomments in block exprâ, âextract from nestedâ, âextract method from trait implâ, âextract mut refâ, âextract return stmtâ, âmut method callâ, âno args if let elseâ, and âtry option with returnâ. Each row is marked Y in the Equiv column; sizes before extraction (P) range from 5 to 14 LOC and after extraction (Pâ˛) from 8 to 19 LOC. §3 says: âWe adapted ten cases from the rust-analyzer tests, each isolating one design feature.â Inference: these are ten adapted unit-style tests, not a crate-wide success rate.
Limits and assumptions. Under âVerification backendâ the paper quotes [2]: âWe currently target safe Rust; unsafe code is out of scopeâ (§4, p. 59). It lists as known AENEAS limits: no nested loops; no function pointers or closures (ongoing); limited type parametricity; no nested borrows in function signatures (ongoing); incomplete modelling of interior mutability (e.g. Cell/RefCell); and no concurrency (long-term) (§4, p. 59). End-to-end proofs are ânot yet guaranteedâ for complex generics, heavy macro expansion (including proc-macros), async/await, or intricate trait bounds; FFI, platform I/O, and panic/abort behaviour âare only partially abstracted and may be treated as uninterpreted oraclesâ (§4, p. 59). Programs relying on global state, time, or non-determinism need additional modelling (§4, âWhat the proof saysâ, p. 59).
technique 2: LLBC and Aeneas functional equivalence
What it is. The verification stage compiles P and PⲠto LLBC with CHARON, making ownership operations explicit: âCHARON (LLBC): compile đ and đ Ⲡto an ownership-explicit IR with moves/borrows/drops made explicit [1, 2].â It then translates LLBC to a pure lambda calculus with AENEAS: âAENEAS (functionalisation): translate LLBC to a pure đ-calculus while preserving behaviour.â (§2, pipeline steps 3â4, p. 58). Step 5 discharges observational equivalence in Coq âall without user annotationsâ (§2, p. 58).
How to do it. (1) Fix the boundary: pair the original caller in P with the modified caller in PⲠvia REMâs repaired signature and result enum. (2) Compile both programs to ownership-explicit LLBC with CHARON. (3) Functionalise the LLBC with AENEAS. (4) Let ÎŚ encode the call-site preconditions induced by the repaired signature and result enum. (5) Prove, for every input satisfying ÎŚ, equality of results and of the observable projections of the effect traces. Eq. 1 (p. 59), as extracted from the PDF layout: âx . ÎŚ(x) â âŚfâ§PÎť(x) = (r, Ď) â§ âŚfâ§Pâ˛Îť(x) = (râ˛, Ďâ˛) â r = rⲠ⧠obs(Ď) = obs(Ďâ˛). The paper describes no user-written annotation step; §2 calls the check âautomated, zero-annotationâ (p. 58).
Verus form. Inference: the closest Verus encoding is an executable function for the extracted body plus a specification function or postcondition describing its result and permitted effects, followed by a proof that the original and extracted specifications agree under the same precondition. Inference: Verus does not automatically translate arbitrary Rust into Aeneasâs pure calculus; this pipelineâs CHARON/AENEAS/Coq machinery would remain an external trusted or separately verified component. Inference: Verus models effect traces only when the developer supplies a state or trace model, so external I/O and nondeterminism require explicit assumptions.
Evidence. All ten Table 1 cases are marked Y. §3 says full cycles (extract / proof gen / verify) âaveraged âź2s, suggesting IDE-friendly latencyâ and also: âThough not our focus, a full cycle (extract-fix-verify) completes in 2 s, suggesting real-world viability.â (p. 59). Case files and example output are linked on GitHub (footnotes 1â2). The paper reports no cases beyond these ten, no statistical analysis, and no comparison with manually proved refactorings; its Future Work calls for testing âagainst hundreds of different casesâ (§4, p. 59).
what is proved and what remains trusted
The paper states the proved claim: âWe check observational equivalence at the extraction boundary under the current abstraction of side effects. If the program relies on global state, time, or non-determinism, additional modelling is required.â (§4, âWhat the proof saysâ, p. 59). Inference: this is not a proof that the original program satisfies a domain requirement. Inference: trusted or unmodelled components include the CHARON and AENEAS translations, the Coq proof environment, the abstractions for effects and panics, and everything outside the safe-Rust, AENEAS-supported fragment (unsafe, FFI, concurrency, and the features listed above). Inference: for agent-written verified Rust, the method lowers the threat that an automated refactoring silently changes behavior only inside its supported fragment; it does not cover a general agent edit or establish that a specification captures the product requirement.
techniques for the catalog
- compiler-guided extraction repair â reify non-local control flow into a result enum, infer value/shared/unique passing, and iterate
cargo checkrepairs of signatures and lifetimes until the extracted Rust compiles - LLBC/AENEAS equivalence â compile original and refactored Rust to ownership-explicit LLBC, functionalise both, and discharge observational equivalence in Coq under call-site preconditions
- effect-trace boundary specification â compare returned values and observable effect traces, adding models for global state, time, nondeterminism, or I/O when those effects matter Checked 2026-10-17 against the full paper by a Claude agent (mid-paper quote: âA misplaced borrow or incorrect lifetime can easily render code uncompilable, and in some cases, alter code semantics altogether.â): read all 150 lines of the paper text (both pages) and checked every quotation, number, section/page reference, Table 1 entry, technique step, and the tag/DOI link; changed page references from pp. 1â2 to the printed pp. 58â59, fixed the abstract quote (wrongly began âRusty Extraction Refactoringâ), restored â[1, 2]â in the CHARON quote, corrected LOC ranges (P 5â14, PⲠ8â19), corrected limits (âno nested borrowsâ, FFI/I/O/panic âpartially abstractedâ, the safe-Rust quote is cited from [2]), removed an unsupported âbecauseâ link and the §2 attribution for the global-state caveat, added the missing âfix the boundaryâ step, and marked unsourced claims âInference:â; did not check Eq. 1 symbol by symbol against the typeset PDF (layout extraction garbles it), the cited works [1â4], or the GitHub artefacts.
Last edited: