Lectures onType Theory
Chapter 46
Chapter 46Optional

Ownership, Borrowing, and Affine Resource Protocols

Prerequisites. Direct starred prerequisites: none. No later core chapter depends on this route.

Let b be the sole handle of an allocated buffer. Affine typing accepts the transfer sendb because the continuation does not use b. It rejects let n=lengthb in sendb, because the call to length consumes the handle before send receives it. Copying b would make the second call typable, but would also create two handles with deallocation authority. The missing operation must expose less authority for a bounded interval and then restore the sole owner.

A finite ownership protocol

The first calculus isolates that interval. It is small enough that every transition and every metatheoretic case can be written on the page. Its progress argument identifies the exact invariant needed by the larger language comparison.

Definition 46.1 — Ownership states

Fix a set of handle types A, closed values v:A, and a countable set of lifetime names α,β. A static handle state is q::=O(A)DSB(A)Xα¯(A). Here O(A) means sole ownership, D means unavailable, B is a nonempty finite set of active shared lifetimes, and α¯ is a nonempty stack of active exclusive lifetimes. A protocol environment Ω maps finitely many handles to states. A lifetime stack L records lexical nesting.

The word owner denotes the handle in state O(A); it is the only handle permitted to transfer or destroy its payload. A shared borrow is read authority indexed by one member of B. An exclusive borrow is read–write authority indexed by the top of α¯. These names describe static authority, not the number of machine pointers. The picture breaks if raw pointer equality is taken to be ownership: several shared views may contain the same address while none may deallocate it.

Commands change protocol environments. We write L;ΩcL;Ω. In move h as k, the handle k is fresh. In every begin command, the lifetime is fresh for L; in every end command, it is the top of L.

Definition 46.2 — Protocol rules

The following rules generate the transition judgment. Unmentioned handles are unchanged.

kdom(Ω)
L;Ω,h:O(A)move h as kL;Ω,h:D,k:O(A)
Move
L;Ω,h:O(A)drop hL;Ω,h:D
Drop
αL
L;Ω,h:O(A)beginSαhLα;Ω,h:S{α}(A)
Share-begin
αL
L;Ω,h:SB(A)beginSαhLα;Ω,h:SB{α}(A)
Share-nest
αBB{α}
Lα;Ω,h:SB(A)endSαhL;Ω,h:SB{α}(A)
Share-end
B={α}
Lα;Ω,h:SB(A)endSαhL;Ω,h:O(A)
Share-return
αL
L;Ω,h:O(A)beginXαhLα;Ω,h:Xα(A)
Ex-begin
βL
L;Ω,h:Xα¯(A)reborrowXβhLβ;Ω,h:Xα¯β(A)
Ex-reborrow
α¯ϵ
Lβ;Ω,h:Xα¯β(A)endXβhL;Ω,h:Xα¯(A)
Ex-pop
Lα;Ω,h:Xα(A)endXαhL;Ω,h:O(A)
Ex-return

The access and sequencing rules complete the definition:

q{O(A),SB(A),Xα¯(A)}
L;Ω,h:qreadhL;Ω,h:q
Read
q{O(A),Xα¯(A)}v:A
L;Ω,h:qwritehvL;Ω,h:q
Write
L;Ωc1L1;Ω1L1;Ω1c2L2;Ω2
L;Ωc1;c2L2;Ω2
Seq

Neither access rule applies to D, and Write does not apply to a shared state. In an exclusive state, read and write denote access through the view at the top of the lifetime stack; an outer view remains suspended until Ex-pop removes the inner lifetime.

The set and the stack in definition 46.1 perform different work. Shared scopes may be nested while the same read authority remains available. Exclusive reborrowing suspends the outer exclusive view; only the top lifetime may act.

For shared access, the rejected affine program becomes beginSαb; n:=readb; endSαb; move b as b. Its state calculation is b:O(A)Sharebeginb:S{α}(A)Readb:S{α}(A)Sharereturnb:O(A)Move(b:D, b:O(A)). The third step, not the read, returns transfer authority.

Exercise 46.1 — A second shared scope

★☆☆ Starting from b:O(A), derive a trace that opens lifetime α, opens β inside it, reads through β, closes β, reads through α, and returns b:O(A). Name the static state after every command.

An exclusive reborrow uses a stack: b:O(A)Exbeginb:Xα(A)Exnestb:Xαβ(A)Writeb:Xαβ(A)Expopb:Xα(A)Exreturnb:O(A). The middle action may write through β. The outer view for α is suspended at that point because β is the top lifetime.

Exercise 46.2 — Exclusive reborrow boundary

★☆☆ Give the rule derivation for the preceding trace. Then locate the missing premise in each of these invalid commands: opening a shared borrow while Xα(A) is active, and ending α while the state is Xαβ(A).

Dynamics and safety

A runtime resource map R sends a handle to a payload together with one of the same four modes. A moved payload is installed under the fresh destination handle before the source becomes unavailable; dropping destroys the payload. Borrow-begin and borrow-end change only the mode. Reads return the payload and writes replace it. We write (L,R;c)own(L,R;skip) for one command transition.

Definition 46.3 — Agreement

The judgment L;RownΩ holds when the maps have the same domain, every O(A), SB(A), and Xα¯(A) entry contains an A-payload with the same mode in R, and every D entry is unavailable in R. Moreover, each lifetime in B and α¯ occurs in L, and the order of α¯ is the induced stack order.

Lemma 46.4 — One-command preservation

Suppose L;RownΩ and L;ΩcL;Ω. Then there is a unique runtime resource map R such that (L,R;c)own(L,R;skip)andL;RownΩ.

Proof of Lemma 46.4 — One-command preservation

Proof. Rule induction on the static transition. In Move, agreement gives one A-payload at h; freshness of k makes the map update functional. The runtime rule moves that payload to k, marks h unavailable, and therefore establishes agreement with the conclusion. In Share-begin and Share-nest, the runtime rule adds the fresh lifetime to exactly the set named by the static conclusion. The two shared end rules remove the top lifetime; the singleton test decides whether the resulting mode is shared or owned. The exclusive rules push or pop exactly the top lifetime, so the stack clause of agreement is preserved. Read and write retain the mode, and write is defined precisely for the two modes admitted by its typing rule. Drop removes the unique payload. These cases exhaust the generating rules, and each runtime rule determines its result map. ◻

Theorem 46.5 — Protocol progress and preservation

Let c=c1;;cn have a transition derivation from (ϵ,Ω0) to (ϵ,Ωn). If ϵ;R0ownΩ0, then either n=0, or the first command takes one runtime step. Every finite prefix has a unique runtime execution whose final resource map agrees with the corresponding static environment.

Proof of Theorem 46.5 — Protocol progress and preservation

Proof. For n>0, invert the sequencing derivation to obtain the transition for c1. Apply lemma 46.4; this gives the first step and agreement with the intermediate environment. Induction on the length of c repeats the lemma for the remaining commands. The empty sequence is the base case. Uniqueness at each prefix follows from uniqueness in the lemma. ◻

Corollary 46.6 — No use after move and no dangling final borrow

In an execution covered by theorem 46.5, no read, write, drop, or second move can name the source of an earlier move. If the final lifetime stack is empty, every available handle is owned; no shared or exclusive borrow remains.

Proof of Corollary 46.6 — No use after move and no dangling final borrow

Proof. After Move, the source has state D, for which none of the listed rules has a premise. For the second claim, agreement requires every lifetime index in a shared set or exclusive stack to occur in the lifetime stack. An empty stack therefore rules out both borrowed states. ◻

The empty-stack hypothesis is substantive. Stopping after beginSαh leaves a well-typed prefix with an active borrow. It is not a completed scope and does not satisfy the corollary’s final-state premise.

Exercise 46.3 — A moved handle

★☆☆ Derive move h as k from h:O(A). Prove by inversion that no protocol rule types readh, writehv, or droph in the resulting environment. Explain why readk remains typable.

Token-passing elaboration

The protocol states can be made explicit as affine values. Let Caph(q) be an affine token for handle h in state q, and let Life(L) be an affine token for the current lifetime stack. If the ordered domain of Ω is h1,,hm, define R(L;Ω)=Life(L)Caph1(Ω(h1))Caphm(Ω(hm)). The target has one proof-indexed affine primitive for each non-sequencing rule of definition 46.2. Representative signatures are move^h,k:R(L;Ω,h:O(A))R(L;Ω,h:D,k:O(A)),beginS^α,h:R(L;Ω,h:O(A))R(Lα;Ω,h:S{α}(A)),reborrowX^β,h:R(L;Ω,h:Xα¯(A))R(Lβ;Ω,h:Xα¯β(A)). Each primitive exists only when the corresponding source premises hold. Thus there is no move constant without a fresh destination and no end constant for a lifetime other than the stack top. Read and write constants preserve the state token; drop, the two shared end rules, and the two exclusive end rules have exactly the source conclusions. Their target reduction contracts c^r to the canonical representation of the conclusion. The proof-directed translation E maps a primitive source derivation to its indexed constant and maps Seq to affine composition.

Proposition 46.7 — Protocol-step simulation

If L;ΩcL;Ω, then the token representation R(L;Ω), followed by E(c), reduces to R(L;Ω). A primitive derivation takes one target step; a sequence takes the concatenation of its component steps.

Proof of Proposition 46.7 — Protocol-step simulation

Proof. Induct on the static derivation. A non-sequencing case is the defining target reduction of its proof-indexed primitive; its domain and codomain are the representations of that rule’s premise and conclusion. In Seq, apply the two induction hypotheses and concatenate their reductions. The intermediate representation is identical because the first conclusion is the second premise. These are all generating cases. ◻

This is an explicit elaboration of the finite protocol derivation into affine state tokens. It does not erase the protocol indices and is not a compiler-correctness theorem for Affe, Rust, or Pure Borrow.

The Affe foundation

Affe is an impure, strict ML-like language with unrestricted, affine, and linear kinds and both shared and exclusive borrowing. Its internal language uses explicit regions. The book freezes the part of that signature needed to explain the opening program; it does not replace Affe’s full constrained polymorphism by the finite protocol calculus.

Definition 46.8 — Selected Affe bindings and split

An Affe environment contains ordinary bindings x:σ, suspended bindings [x:σ]bn, and active borrow bindings &bx:&kσ, where b{S,X}. We write CΓ=Γ1Γ2 for its ordered split under kind constraint C. An unrestricted ordinary or shared-borrow binding may occur on both sides. Any binding may occur on exactly one side. The load-bearing rule is XCΓ,x:σ=(Γ1,[x:σ]bn)(Γ2,x:σ)Susp. Thus the left subexpression receives only suspended access, while the later right subexpression retains ownership. Splitting is ordered because Affe’s evaluation is left to right.

The region and borrow rules are

[x:τx]bnΓCxΓregΓCΓe:τCkind(τ)Ln1
CΓ{|e|}x,bn:τ
Region
&bx:&kσΓCx,τx=inst(Γ,σ)CCxdrop(Γx)
CΓ&bx:&kτx
Borrow

The auxiliary relation CxΓregΓ replaces the selected suspended binding by a borrow binding whose kind is confined to level n. The result-kind premise excludes that level, so neither the borrow nor a closure containing it can leave the region.

The opening application is typed by splitting the environment so that its left side contains [b:Buffer]S1 and its right side contains b:Buffer. Rule Region activates the suspended binding only around length(&Sb). After that region returns an integer, the right subexpression sends the owner.

Exercise 46.4 — The Affe region premise

★★☆ Attempt to give the region body the result &Sb. Show which kind inequality would be required and why it contradicts the result-kind premise. Repeat the argument for a closure that captures the borrow.

Affe states soundness for a fuel-bounded definitional evaluator rather than a small-step progress–preservation pair. The distinction matters.

Theorem 46.9 — Affe type soundness, imported

Let CΓe:τ be derivable. Let the value environment agree with Γ, let the store agree with its store typing, and let the permission set be well formed, contain every reachable address, separate the active, mutable-borrow, shared-borrow, and suspended roots, and contain each incoming live resource exactly once where affinity requires it. If evaluation with fuel i returns rather than timing out, it returns a value of type τ, an extended well-typed store, and a well-formed permission set. Moreover, the result preserves the frame, preserves shared reachable cells, does not free a cell reachable through an active borrow, records one incoming pointer for each active linear resource and frees an incoming linear resource by return, and creates no permission except for a freshly allocated location.

Proof of Theorem 46.9 — Affe type soundness, imported

Proof. This is Radanne–Saffrich–Thiemann, Theorem 5.1, with the complete statement and selected induction cases in Appendix G, Theorem G.6 [RST20]. The proof is by induction on the fuel. The region case exchanges the owner’s permission for the selected borrowed permission while evaluating the body, then reverses that exchange. The containment lemma uses the result-kind bound to show that no address reachable from the result carries the region’s level. The borrow case obtains an address only from the selected borrow binding and checks that address in the permission set. The application, match, resource-creation, and resource-destruction cases maintain the ten result invariants stated in the theorem. Those cases are the imported proof mechanism; the shorter finite-protocol proof above is not substituted for them. ◻

The theorem implies the precise no-use-after-free property recorded by its permission clauses: evaluation can access an address only while its permission is present, and the result clauses keep borrowed reachable cells live. The Region result-kind premise gives the no-dangling property. The source does not state a separate small-step progress theorem or preservation theorem, so neither is attributed to Affe here. A timeout is the evaluator’s fuel result, not a stuck well-typed term. Affe’s inference soundness, completeness, and principality are separate Theorems 4.1, 4.4 and Appendix E; they are not needed for the borrowing argument.

Uniqueness is not temporary authority

Let replacei(a,v) denote the pure array that equals a except at index i. If a has no aliases, a compiler may implement this value by overwriting a[i]. If b=a remains observable, the same machine choice changes b[i] and fails to implement the pure operation. The required fact is absence of aliases, not permission to ignore a variable.

Definition 46.10 — Simplified uniqueness core

In the monomorphic call-by-need calculus of de Vries–Plasmeijer–Abrahamson, types carry Boolean uniqueness attributes. Context splitting duplicates a nonunique binding and places a unique binding on exactly one side. A function type records both the uniqueness of its argument/result and whether its closure may contain unique values. Evaluation is the Maraist-style call-by-need small-step system with value, commute, association, and closure rules.

Theorem 46.11 — Simplified uniqueness metatheory, imported

For the monomorphic calculus of definition 46.10:

  1. a closed well-typed term is an answer or takes a step;

  2. one step preserves its type and free-variable environment; and

  3. if an abstraction has a nonunique function type, every binding needed by its closure has a nonunique type and its environment can be split into two copies.

Proof of Theorem 46.11 — Simplified uniqueness metatheory, imported

Proof. Items 1 and 2 are Theorems 1 and 2 of the paper. Item 3 is the shared_function lemma in §7.2 of the technical report [dVPA08]. Weak progress first gives an answer, a reduct, or an evaluation context containing a free variable; empty typing excludes the third case. Preservation uses substitution. In the duplicating substitution case, the shared-function lemma makes the closure environment duplicable. The technical report gives the complete locally-nameless, Coq-oriented proof text, but the historical Coq sources were not recovered; this book therefore records a proof report, not a replayed mechanization. ◻

No mutable store occurs in this theorem. It therefore does not prove that the destructive array implementation is correct, and it does not give a lifetime, a lender, or reclamation. Affinity says at most one use of a handle; uniqueness says that a value has no aliases at the relevant program point; ownership grants a distinguished authority; borrowing suspends part of that authority temporarily. Clean’s full mutable language contains further rules and is not the calculus of theorem 46.11.

Futhark makes the compiler-facing distinction concrete. Its expression a with [i]=v is semantically a pure array update. The operation consumes a; neither a nor a variable that aliases it may be used on the continuation path. The compiler may then choose an in-place write. The rejected program let b=a in let a=a with [i]=v in (a,b[i]) keeps an alias observable after consuming a. This is the uniqueness and alias-analysis contract documented in §3 of the PLDI 2017 paper and the “In-Place Updates” section of the pinned Futhark book [HSE^+17]. It is not an end-to-end compiler-correctness theorem and transfers no borrowing result.

Exercise 46.5 — Unique update versus affine use

★★☆ Give two environments in which a with [i]=v is affine-usage correct. In the first, require that a has no aliases and justify an in-place implementation. In the second, retain an alias b but never use it; explain why affine use alone does not establish the no-alias fact needed by the compiler choice.

Pure Borrow: theorem, conjecture, and artifact

Pure Borrow is a 2026 Linear Haskell library and formal calculus. Its status is part of the mathematics: three results are published as theorems, two load-bearing claims are conjectures, and two further results are conditional on one conjecture.

The library interface

A lifetime is atomic, static, or an intersection: α,β::=AlιStaticαβ. Lifetime inclusion αβ has intersection as meet and Static as top. The paper-facing core signatures are runBO:Linearly(α.BOα(Endαa))a,borrow:Linearlya(MutαaLendαa),share:MutαaUr(Shareαa),reclaim:LendαaEndαa,joinMut:Borrowkβ(Mutαa)Borrowkαβa. The final evaluated implementation’s lower layer exposes newLifetime:LinearlySomeNow, endLifetime:NowαUr(EndTokenα), and execBO:BOαaNowα(Nowαa). Here SomeNow existentially packages a fresh atomic lifetime; the persistent end token implements the paper-facing Endα evidence. These are two interface layers, not interchangeable signatures. Mutable borrowers and live lifetime tokens are linear. A shared borrower is movable and may be duplicated. A lender is linear; reclamation also needs persistent evidence that its lifetime ended. Hence ownership returns without collecting each borrower as a runtime value.

For the vector [0,1,2], borrow once, perform the updates 0+=3, 2+=5, and 0=4, share the mutable borrower to copy position 0, end the lifetime, and reclaim. The pure result is (12,[12,1,7]). The lender cannot be reclaimed before the Endα evidence exists; the mutable borrower cannot be used after share consumes it.

The typing trace keeps each linear handoff visible: stagelinear resultborrowvecm0:Mutα(VectorInt):Lendα(VectorInt)modifyAt0(+3)m0m1:Mutα(VectorInt)modifyAt2(+5)m1m2:Mutα(VectorInt)modifyAt0(4)m2m3:Mutα(VectorInt)sharem3Ur(s:Shareα(VectorInt))copyAt0sUr(12)reclaime[12,1,7](e:Endα). The body therefore has type BOα(Endα(IntVectorInt)), which is exactly the input expected by runBO. This is a typing/evaluation trace for the published example, not a replay of its Haskell implementation.

Reborrowing is derived by borrowing a mutable borrower and flattening: MutαaMutαβaLendβ(Mutαa). The inner borrower is confined to αβ. Reclaiming its lender with Endβ returns the outer mutable borrower.

Exercise 46.6 — Reclaiming twice

★★☆ Starting with m:Mutαa, type the derived reborrow above, end β, recover m, then end α and recover the original owner from its outer lender. Identify every linear input consumed at the two reclamation points.

Parallel quicksort splits a mutable vector borrower into disjoint slice borrowers, partitions in BO, and evaluates the recursive calls with parBO. Each base case consumes its slice borrower; after both branches finish, the outer lender from the initial whole-vector borrow is reclaimed. The work-stealing version changes the scheduler, not these public types. This is a case study in implementability, not a theorem about scheduler correctness.

The formal calculus and its two executions

Definition 46.12 — Pure Borrow formal card

The source calculus is call-by-need Linear Haskell with multiplicities 1,ω, first-class polymorphism, references, lifetime tokens, MutαA, ShareαA, LendαA, and BOαA. Its primitive operators include allocation, deallocation, borrow, share, reclaim, reborrowing, BO execution, dereference, and update. Arguments of a primitive may reduce in parallel.

The mutative semantics has configurations M=(H;μ;x): a lazy binding heap H, global memory μ, and result variable x. Representative rules allocate a fresh location, update μ(), and free . We write MmM.

The denotational semantics has configurations D=(G;x), with no global memory. A borrow receives a fresh identifier ι. Each update appends or replaces the entry for its borrow path in a finite history η. Ending a lifetime moves η to its dead token, and reclamation restores the lender’s value by replaying the final history. We write DdD. Thus the two rule tables differ exactly at state: operationmutativeclausedenotationalclausenewRefvdom(μ), μ[v]RefvupdateRefpvμ[pv]η[pv]endLifetimeconsume live tokenmove η to the dead tokenreclaimread the lender’s locationrestore the lender by η The table records the state-changing clauses; the complete calculus also has the shared call-by-need context and operator rules.

Definition 46.13 — Association and safety

The judgment MAD relates a mutative configuration and a denotational configuration at return type A. Its term rules are compositional; ghost resources record the borrow paths represented by a mutative location and a denotational history. A mutative configuration is safe if MAD for some A,D. A denotational configuration is safe if the same judgment holds for some A,M.

Theorem 46.14 — Published Pure Borrow results

At the signature of definition 46.12:

  1. if DdD1, DdD2, and D1D2, there is D+ with D1dD+ and D2dD+;

  2. if e:A, then the initial mutative and denotational configurations for e satisfy MeADe;

  3. if a final mutative configuration (x=v,H;μ;x) is safe, then μ=.

Proof of Theorem 46.14 — Published Pure Borrow results

Proof. These are the denotational strong-confluence theorem, the theorem taking well-typedness to association, and the safe-final-state leak-freedom theorem in §5 and the metatheory appendix of the extended version [MI26]. For item 1, name the target variables of the two distinct steps. Equal targets would determine equal reducts, contrary to D1D2. Distinct targets commute: reduce the second target after the first and the first after the second. Reclamation is the exceptional-looking case, but restoring one variable from its history does not change the history used by a reduction at another target. Item 2 is rule induction on typing, using closure of association under subtyping. Item 3 unfolds final association: all owned resources are accounted for by the returned value and binding heap, so the global memory component must be empty. The source proofs are paper proofs, not machine-checked derivations. ◻

Definition 46.15 — Open Pure Borrow obligations

The following are conjectures in the extended paper.

  1. Every safe nonnormal mutative configuration and every safe nonnormal denotational configuration can take a step.

  2. For every type A, A is a bisimulation between m and d.

The paper notes that rigorous proofs may require minor changes to the association system.

The dependency boundary is typingprovedinitial associationbisimulation conjecturepreservation of safetybisimulation conjecture+denotational diamond theoremmutative behavior uniqueness. The last two implications are conditional corollaries. The diamond theorem alone says nothing about a mutative execution, and association alone is not left-unique. There is therefore no unconditional complete safety, purity, or behavior-uniqueness theorem to import.

Exercise 46.7 — Status-preserving consequences

★☆☆ Classify each claim as proved, conjectured, conditional, or unsupported: denotational strong confluence; progress of safe mutative configurations; preservation of safety; leak freedom of a safe final mutative configuration; strong confluence of the mutative semantics; and behavior uniqueness of safe mutative configurations.

The version-one scheduler failure

The first public Zenodo record reported a race in the work-stealing API. Its scheduler assumed an invalid timing invariant and could, with low probability, return a partially unsorted array. The repaired source used a global atomic counter to decide completion. That repair changed only Control.Concurrent.DivideConquer.Linear and its submodules; it did not change the core borrowing interface. It also reduced performance. The record contains the original source and images, fixed source and images, and a replacement benchmark plot. The later PLDI and final Zenodo revisions contain the repair.

This history separates four claims. A type-correct client can be written with the API. The original scheduler had a race. The atomic-counter revision fixes the reported completion condition in the evaluated artifact. None of these facts proves the conjectured bisimulation or purity of the unsafe internals.

Exercise 46.8 — Bug boundary

★☆☆ Suppose all worker deques appear empty before a delayed worker publishes two children. Explain why an “all queues empty” completion test may return too early, and why a global counter of outstanding tasks blocks this execution. State one claim about the core API and one metatheoretic conjecture that this repair does not establish.

Two exact neighboring boundaries

Oxide v4 uses regions, loans, non-lexical stacks, numeric paths, and an imperative store. Its Lemmas 3.1 and 3.3 establish progress and preservation, and Theorem E.70 gives closed-program type safety at that exact signature [WGPA21]. Oxide’s region/loan invariant is mature evidence for an imperative Rust-shaped calculus. It does not prove Pure Borrow’s association conjectures, and its store theorem is not a theorem about Affe’s fuel-bounded evaluator.

The Borrow Calculus of Wagner–Gierczak–Marshall–Li–Ahmed begins with a linear language and derives immutable borrows, lexical lifetimes, reborrowing, and mutable borrows without adding runtime borrowing operations. Its exact endpoints are the Fundamental Property (Lemma 3.1), weakest-precondition adequacy (Theorem 3.2), and closed-unit termination with empty final memory (Corollary 3.3) [WGM^+25]. Borrow Logic proves those results for BoCa through a semantic model. It is neither an elaboration of Pure Borrow nor a proof of the conjectures in definition 46.15.

Fractional permissions divide access quantitatively; separation logic divides heap assertions; regions delimit validity. A translation from any of these systems would have to map its states, typing rules, and dynamics and then prove preservation or simulation. No such map is assumed here. Full Rust, unsafe code, concurrency libraries, and arbitrary mutation lie outside this chapter’s theorem boundary.

Source boundary.

Affe’s exact full theorem is Theorem 5.1 and Appendix G of [RST20]. The uniqueness results are bounded to the monomorphic calculus and proof report of [dVPA08]. The Futhark comparison is a language/compiler design claim from [HSE^+17], not a compiler proof. The Pure Borrow theorem/conjecture split and the association proof sketches are from the extended v4 source of [MI26]; the final evaluated artifact is recorded separately in subsubappendix E.3.21. The BoCa and Oxide comparisons retain the signatures of [WGM^+25, WGPA21]. No result transfers between these calculi without an explicit translation theorem.

Suggested first pass.

None of these problems is a prerequisite for a later chapter. Begin with exercise 46.9, exercise 46.11, then implement exercise 46.13.

Exercise 46.9 — Protocol reconstruction

★★☆ Give a complete static and runtime trace that starts with two owned buffers, opens two nested shared borrows of the first, moves the second, closes the shared borrows, opens and closes an exclusive borrow of the first, and finally drops it. Verify agreement after every step. Then change only one command so that typing fails for a unique stated reason.

Exercise 46.10 — Hypothesis boundary

★★☆ Construct three counterexamples: remove the fresh-destination premise from Move; remove the top-of-stack premise from Ex-pop; and remove the Affe result-kind premise from Region. For each change, identify the first conclusion of lemma 46.4, corollary 46.6 or the Affe nonescape argument that fails.

Exercise 46.11 — The evidence ledger

★★☆ Reconstruct the Pure Borrow dependency display from the statements of theorem 46.14, definition 46.15. Prove the conditional preservation-of-safety implication from bisimulation. Explain why denotational confluence plus mere existence of an association is insufficient for mutative behavior uniqueness.

Exercise 46.12 — Comparing five invariants

★★★ For one array update, give separate judgments that would express affine use, uniqueness, ownership, an active exclusive borrow, and a separating-logic points-to assertion. Give one pair of programs separated by each invariant. Do not appeal to a theorem outside the systems frozen in this chapter.

Exercise 46.13 — Ownership protocol checker

★★★ Practical project.ownership-protocol-checker Implement the finite protocol of definition 46.2 in Kappa. Maintain the invariant that an owned resource has no active lifetime, shared access has a nonempty set of distinct lifetimes, and exclusive access has a nonempty well-nested stack. The program must accept nested sharing, exclusive reborrowing, and owner reclamation; it must reject use after move, sharing during exclusive access, ending an outer exclusive lifetime first, and ending a shared lifetime absent from that handle’s active set. On the seven named inputs, print the seven case results followed by

All 7 Chapter 46 corpus cases passed.

Search the book

Type to search the local edition.