Prerequisites. Direct starred prerequisites: none. No later core chapter depends on this route.
Let 𝑝,𝑞:𝑠, where the structure 𝑠 has two mutable integer fields. Value assignment requires 𝑞:=𝑝;𝑞.𝑥:=7⟹𝑝.𝑥isunchanged. Temporary mutable access imposes a different obligation: 𝗌𝗐𝖺𝗉𝖷(&𝑝,&𝑝.𝑥) must be rejected because its two arguments expose overlapping parts of one representation. Copy independence proves the first claim; an access-conflict check must prove the second.
Trees, paths, and pointer stores
The calculus in this chapter is Swiftlet [RSZ^+22]. A structure context Δ maps structure names to qualified field declarations, and a term context Γ maps names to qualified types. Qualifiers are 𝑚∈{𝗅𝖾𝗍,𝗏𝖺𝗋}. The fragment needed below is 𝑝::=𝜏∣𝗂𝗇𝗈𝗎𝗍𝜏,𝜏::=ℤ∣[𝜏]∣𝑠∣𝖠𝗇𝗒∣(𝑝1,…,𝑝𝑘)→𝜏,𝑟::=𝑥∣𝑒.𝑓∣𝑒1[𝑒2],𝑎::=𝑒∣&𝑟. A Swiftlet expression𝑒 is an integer, array or structure literal, a name or path, a function declaration or call, a conditional, a binding, an assignment, a sequence, or a cast. The load-bearing forms are displayed by 𝑒::=𝑐∣𝑥∣[𝑒1,…,𝑒𝑘]∣𝑠(𝑒1,…,𝑒𝑘)∣𝑒(𝑎1,…,𝑎𝑘)∣𝑚𝑥:𝜏=𝑒1𝗂𝗇𝑒2∣𝑟=𝑒∣𝑒1;𝑒2∣𝑒𝖺𝗌𝜏∣⋯. A runtime value in this fragment is 𝑣::=𝑐∣[ℓ1,…,ℓ𝑘]∣[ℓ1,…,ℓ𝑘]𝑠∣𝜆(⃗𝑥:⃗𝑝,𝜂,𝑒)∣𝖻𝗈𝗑(ℓ). For a well-typed stored value, typeofΔ,𝜋(𝑣) denotes its unique source type. A boxed value has source type 𝖠𝗇𝗒; its referenced stored value retains the concrete type used by a checked downcast. A path is an lvalue description, not a storable reference. The array-element qualifier is inherited from the path: an element reached through a 𝗅𝖾𝗍 expression is immutable, while an element reached through a mutable path has qualifier 𝗏𝖺𝗋.
A small-step state contains a pointer store 𝜋 and a nonempty stack 𝜂=𝜂1,…,𝜂𝑛, with 𝜂1 the innermost, hence currently accessible, frame: 𝜋:𝖫𝗈𝖼⇀{𝗅𝖾𝗍,𝗏𝖺𝗋}×𝖵𝖺𝗅,𝜂𝑖:𝖭𝖺𝗆𝖾⇀𝖫𝗈𝖼. Arrays and structures store the locations of their immediate parts.
For a scalar root, acc(𝜋,ℓ)={ℓ}. If the value at ℓ contains child locations ℓ1,…,ℓ𝑘, then acc(𝜋,ℓ)={ℓ}∪𝑘⋃𝑖=1acc(𝜋,ℓ𝑖). Roots are independent when their accessible representations are pairwise disjoint.
Swiftlet excludes recursive structure declarations, so these representations are finite trees. The lvalue judgment and its five load-bearing rules are Δ;Γ⊢𝗉𝖺𝗍𝗁𝑟:𝑚𝜏,Γ(𝑥)=𝑚𝜏Δ;Γ⊢𝗉𝖺𝗍𝗁𝑥:𝑚𝜏T−Path−Name
Δ;Γ⊢𝑒:𝑠Δ(𝑠)(𝑓)=𝑚𝜏
Δ;Γ⊢𝗉𝖺𝗍𝗁𝑒.𝑓:𝗅𝖾𝗍𝜏
T-LetPropRef
Δ;Γ⊢𝗉𝖺𝗍𝗁𝑟:𝗏𝖺𝗋𝑠Δ(𝑠)(𝑓)=𝗏𝖺𝗋𝜏
Δ;Γ⊢𝗉𝖺𝗍𝗁𝑟.𝑓:𝗏𝖺𝗋𝜏
T-VarPropRef
Δ;Γ⊢𝑒1:[𝜏]Δ;Γ⊢𝑒2:ℤ
Δ;Γ⊢𝗉𝖺𝗍𝗁𝑒1[𝑒2]:𝗅𝖾𝗍𝜏
T-LetElemRef
Δ;Γ⊢𝗉𝖺𝗍𝗁𝑟:𝗏𝖺𝗋[𝜏]Δ;Γ⊢𝑒:ℤ
Δ;Γ⊢𝗉𝖺𝗍𝗁𝑟[𝑒]:𝗏𝖺𝗋𝜏
T-VarElemRef
The source keeps immutable expression roots and mutable path roots separate; in particular, an immutable field cannot become a mutable access merely because its enclosing structure is mutable.
Lvalue evaluation, written Δ⊢𝜋;𝜂;𝑟⟶𝗅𝗏𝜋′;𝜂′;𝑟′, starts with
𝜂1(𝑥)=ℓ𝜋(ℓ)=𝑚𝑣
Δ⊢𝜋;𝜂;𝑥⟶𝗅𝗏𝜋;𝜂;ℓ𝑚
PSS-Name
Δ⊢𝜋;𝜂;𝑟⟶𝗅𝗏𝜋′;𝜂′;𝑟′
Δ⊢𝜋;𝜂;𝑟.𝑓⟶𝗅𝗏𝜋′;𝜂′;𝑟′.𝑓
PSS-Struct
𝑚′=min(𝑚,𝑚𝑖)𝜋(ℓ)=𝑚[ℓ1,…,ℓ𝑘]𝑠Δ(𝑠)(𝑓𝑖)=𝑚𝑖𝜏𝑖
Δ⊢𝜋;𝜂;ℓ𝑚.𝑓𝑖⟶𝗅𝗏𝜋;𝜂;ℓ𝑚′𝑖
PSS-Prop
𝜋(ℓ)=𝑚[ℓ1,…,ℓ𝑘]0≤𝑐<𝑘
Δ⊢𝜋;𝜂;ℓ𝑚[𝑐]⟶𝗅𝗏𝜋;𝜂;ℓ𝑚𝑐+1
PSS-Elem
The omitted PSS-Array and PSS-Index congruence rules first reduce the array path and its index. The meet 𝑚′=min(𝑚,𝑚𝑖) in PSS-Prop is the dynamic reason that an immutable field remains immutable below a mutable root.
If the roots of 𝜂1 are independent, evaluation of a well-typed path from 𝑥 selects a subtree of acc(𝜋,𝜂1(𝑥)). Paths beginning at distinct roots select disjoint subtrees.
Proof. Induct on the path. PSS-Name selects the root. A field or element step follows one child of the current tree, so the selected accessible set is a subset of its predecessor’s. Subsets of disjoint root representations remain disjoint. ◻
Swiftlet’s helper copy(𝜋,𝑣)=(𝜋′,𝑣′) recursively allocates fresh locations for compound values, closures, and boxed 𝖠𝗇𝗒 values.
If 𝑣 is well typed in 𝜋 and copy(𝜋,𝑣)=(𝜋′,𝑣′), then 𝑣′ has the type of 𝑣, and every new representation location of 𝑣′ lies outside the accessible set of every old frame root. Successive copies may be chosen mutually fresh.
Proof of Proposition 49.3 — Copy produces an independent value
Proof. Type preservation is the source’s Lemma A.2. For independence, induct on the definition of copy. A compound case chooses fresh child locations and invokes the induction hypothesis once for each child. The finite union in definition 49.1 is disjoint from the old roots and from the unions produced by earlier copies. ◻
The source states type preservation as Lemma A.2 and the corresponding memory preservation fact as Lemma A.3. Mutual freshness of successive copies is the additional invariant proved above.
★☆☆ Let 𝑝 contain two scalars and execute 𝑞:=𝑝;𝑞.𝑥:=7. Draw both accessible sets and identify the disjointness fact a shallow interior-pointer copy would violate.
Racordon et al.’s Definition 4.1 writes &𝑟⪯&𝑟′. Despite the suggestive glyph, its fourth rule makes it an access-relatedness relation, not literal syntactic prefix. It is the least reflexive, transitive relation closed by &𝑟⪯&𝑟′&𝑟⪯&𝑟′.𝑓Src−Field&𝑟⪯&𝑟′&𝑟⪯&𝑟′[𝑒]Src−Index and ¬const(𝑒)∨¬const(𝑒′)&𝑟[𝑒]⪯&𝑟[𝑒′]Src−Unknown−Index Here const(𝑒) holds exactly when 𝑒 is an integer literal, as in the paper’s deliberately conservative definition. Thus &𝑎[0] and &𝑎[1] may be separated, whereas &𝑎[𝑖]⪯&𝑎[𝑗] when either index is unknown.
The published relation does not close related paths on the left. Consequently it relates &𝑎[𝑖] to &𝑎[𝑗] but need not relate &𝑎[𝑖].𝑥 to &𝑎[𝑗].𝑥. If 𝑖 and 𝑗 evaluate to the same index, those nested paths alias. The two-sentence proof of source Lemma A.1 does not resolve this case. We therefore do not use that lemma at its unrestricted published signature.
Write a path as a root followed by a component list 𝑢::=𝜖∣𝑓⋅𝑢∣[𝑐]⋅𝑢∣[?]⋅𝑢. The last form stands for a nonliteral index. Define the total Boolean comparisons 𝗌𝗍𝖺𝖻𝗅𝖾(𝜖)=𝗍𝗋𝗎𝖾,𝗌𝗍𝖺𝖻𝗅𝖾(𝑓⋅𝑢)=𝗌𝗍𝖺𝖻𝗅𝖾(𝑢),𝗌𝗍𝖺𝖻𝗅𝖾([𝑐]⋅𝑢)=𝗌𝗍𝖺𝖻𝗅𝖾(𝑢),𝗌𝗍𝖺𝖻𝗅𝖾([?]⋅𝑢)=𝖿𝖺𝗅𝗌𝖾,𝖺𝗉𝖺𝗋𝗍(𝜖,𝑢)=𝖿𝖺𝗅𝗌𝖾,𝖺𝗉𝖺𝗋𝗍(𝑢,𝜖)=𝖿𝖺𝗅𝗌𝖾,𝖺𝗉𝖺𝗋𝗍(𝑓⋅𝑢,𝑔⋅𝑣)={𝖺𝗉𝖺𝗋𝗍(𝑢,𝑣),𝑓=𝑔,𝗌𝗍𝖺𝖻𝗅𝖾(𝑢)∧𝗌𝗍𝖺𝖻𝗅𝖾(𝑣),𝑓≠𝑔,𝖺𝗉𝖺𝗋𝗍([𝑐]⋅𝑢,[𝑑]⋅𝑣)={𝖺𝗉𝖺𝗋𝗍(𝑢,𝑣),𝑐=𝑑,𝗌𝗍𝖺𝖻𝗅𝖾(𝑢)∧𝗌𝗍𝖺𝖻𝗅𝖾(𝑣),𝑐≠𝑑,𝖺𝗉𝖺𝗋𝗍(𝑢,𝑣)=𝖿𝖺𝗅𝗌𝖾otherwise. For access arguments 𝑎=&𝑥.𝑢 and 𝑎′=&𝑦.𝑣, put 𝑎⋈𝖡𝑎′⟺¬(𝗌𝗍𝖺𝖻𝗅𝖾(𝑢)∧𝗌𝗍𝖺𝖻𝗅𝖾(𝑣)∧(𝑥≠𝑦∨𝖺𝗉𝖺𝗋𝗍(𝑢,𝑣))). Thus every accepted path has literal array indices, and separation has a first pair of distinct tree children. This strengthening prevents evaluation of a later index expression from mutating the store after an earlier access has already selected its location. The relation is symmetric and decidable by structural recursion on the two component lists.
This conservative repair deliberately rejects (&𝑎[𝑖].𝑥,&𝑎[𝑖].𝑦): the unknown array choice is settled before the distinct fields are reached. It also rejects unknown indices below an already distinct root or component. It is stronger than Definition 4.1 and is not attributed to the source.
Suppose 𝜋;𝜂 is well formed and two well-typed access arguments satisfy definition 49.5. Evaluate both paths from this same state; their literal indices take no expression steps. If the evaluations select ℓ𝑖 and ℓ𝑗, then acc(𝜋,ℓ𝑖)∩acc(𝜋,ℓ𝑗)=∅.
Proof of Lemma 49.6 — Static separation implies physical disjointness
Proof. Because both component lists are stable, path evaluation performs only PSS-Name, congruence, PSS-Prop, and PSS-Elem; it leaves the store unchanged. Different roots are handled by lemma 49.2. On a common root, induction over the equal prefix shows that both evaluations have selected the same location after each pair of equal fields or equal literal indices. The first unequal fields or literal indices therefore select distinct children of that one tree. Their subtrees are disjoint, and further stable descent preserves disjointness. Every case without such a witness is classified as a conflict. ◻
For the opening call, &𝑝⋈𝖡&𝑝.𝑥, because the first path ends before the second. By contrast, distinct fields &𝑝.𝑥 and &𝑝.𝑦 are separated at their first unequal component.
★☆☆ Classify the following pairs under definition 49.4; then classify them under the source relation and identify the pair on which the two checks differ: (&𝑝.𝑥,&𝑝.𝑦),(&𝑝,&𝑝.𝑥),(&𝑎[0],&𝑎[1]),(&𝑎[𝑖].𝑥,&𝑎[𝑖].𝑦).
An access argument must denote a mutable path: Δ;Γ⊢𝗉𝖺𝗍𝗁𝑟:𝗏𝖺𝗋𝜏Δ;Γ⊢𝖺𝗋𝗀&𝑟:𝗂𝗇𝗈𝗎𝗍𝜏T−Inout Assignment has the same qualifier boundary: Δ;Γ⊢𝗉𝖺𝗍𝗁𝑟:𝗏𝖺𝗋𝜏Δ;Γ⊢𝑒:𝜏Δ;Γ⊢𝑟=𝑒:𝗎𝗇𝗂𝗍T−Assign For example, if Δ(𝑠)(𝑥)=𝗏𝖺𝗋ℤ and Γ(𝑜)=𝗏𝖺𝗋𝑠, then Δ;Γ⊢𝗉𝖺𝗍𝗁𝑜.𝑥:𝗏𝖺𝗋ℤΔ;Γ⊢𝖺𝗋𝗀&𝑜.𝑥:𝗂𝗇𝗈𝗎𝗍ℤT−Inout
Let a closure have parameters 𝑥𝑖:𝑝𝑖, captured frame 𝜂𝜆, and body 𝑒. The call step copies every ordinary value into a mutually fresh root ℓ𝑖, maps every access parameter directly to the location selected by &𝑟𝑖⟶𝗅𝗏ℓ𝑖, and checks pairwise physical disjointness. Its new innermost frame extends 𝜂𝜆. The generated form 𝑒;𝗉𝗈𝗉𝐿 records exactly the fresh copied roots 𝐿. Put 𝐼𝖼𝗉𝗒={𝑖∣𝑝𝑖=𝜏𝑖},𝐼𝗋𝖾𝖿={𝑖∣𝑝𝑖=𝗂𝗇𝗈𝗎𝗍𝜏𝑖}. The static and dynamic call rules expose the two corresponding premises: Δ;Γ⊢𝑒:(𝑝1,…,𝑝𝑘)→𝜏(Δ;Γ⊢𝖺𝗋𝗀𝑎𝑖:𝑝𝑖)1≤𝑖≤𝑘𝖾𝗑𝖼𝗅𝗎𝗌𝗂𝗏𝖾𝖡(𝑎1,…,𝑎𝑘)Δ;Γ⊢𝑒(𝑎1,…,𝑎𝑘):𝜏T−Call Here 𝖾𝗑𝖼𝗅𝗎𝗌𝗂𝗏𝖾𝖡 is exactly definition 49.5, including stability of every access path. After evaluation has produced a closure 𝜆(⃗𝑥:⃗𝑝,𝜂𝜆,𝑒), ordinary values 𝑣𝑖, and access locations ℓ𝑖, the root call step is {ℓ𝑖∣𝑖∈𝐼𝖼𝗉𝗒}∩dom(𝜋)=∅∀𝑖≠𝑗∈𝐼𝗋𝖾𝖿.acc(𝜋,ℓ𝑖)∩acc(𝜋,ℓ𝑗)=∅𝜋′=𝜋[ℓ𝑖↦𝗅𝖾𝗍𝑣𝑖∣𝑖∈𝐼𝖼𝗉𝗒]𝜂′=𝜂𝜆[𝑥𝑖↦ℓ𝑖∣𝑖∈𝐼𝖼𝗉𝗒∪𝐼𝗋𝖾𝖿]Δ⊢𝜋;𝜂;𝜆(⃗𝑥:⃗𝑝,𝜂𝜆,𝑒)(⃗𝑣)⟶𝜋′;𝜂′,𝜂;𝑒;𝗉𝗈𝗉{ℓ𝑖∣𝑖∈𝐼𝖼𝗉𝗒}ESS−Call In the conservative variant, the stable static premise lets lemma 49.6 establish the dynamic disjointness premise in the single final store: reducing access arguments performs no index-expression steps. On return, 𝜋0;𝜂′,𝜂;𝑣;𝗉𝗈𝗉𝐿⟶drop∗(𝜋0,𝐿);𝜂;𝑣, where drop∗ recursively removes the representations rooted at 𝐿. Borrowed caller locations are absent from 𝐿. Assignment reaches a root step only through a mutable lvalue: 𝜋(ℓ)=𝑚𝑣0𝜋0=drop(𝜋,𝑣0)𝜋′=𝜋0[ℓ↦𝗏𝖺𝗋𝑣]Δ⊢𝜋;𝜂;ℓ𝗏𝖺𝗋=𝑣;𝑒⟶𝜋′;𝜂;𝑒ESS−Assign Successful checked downcast exposes the concrete stored value and copies it: 𝜏≠𝖠𝗇𝗒𝜋(ℓ)=𝗅𝖾𝗍𝑣typeofΔ,𝜋(𝑣)=𝜏copy(𝜋,𝑣)=(𝜋′,𝑣′)Δ⊢𝜋;𝜂;𝖻𝗈𝗑(ℓ)𝖺𝗌𝜏;𝑒⟶𝜋′;𝜂;𝑣′;𝑒ESS−Downcast Two and only two dynamic side-condition failures are designated runtime errors: a downcast 𝖻𝗈𝗑(ℓ)𝖺𝗌𝜏, where 𝜋(ℓ)=𝗅𝖾𝗍𝑣 but typeofΔ,𝜋(𝑣)≠𝜏, and an array access at a literal 𝑐 with 𝑐<0 or 𝑐≥𝑘. They are the failed premises of ESS-Downcast and PSS-Elem, respectively. No other stuck state is included in the error alternative below.
Let 𝖻𝗎𝗆𝗉:(𝗂𝗇𝗈𝗎𝗍ℤ)→𝗎𝗇𝗂𝗍 and 𝖻𝗎𝗆𝗉𝟤:(𝗂𝗇𝗈𝗎𝗍ℤ,𝗂𝗇𝗈𝗎𝗍ℤ)→𝗎𝗇𝗂𝗍. If 𝑜.𝑝.𝑥 and 𝑜.𝑝.𝑦 are mutable fields, then 𝑜⟶𝗅𝗏ℓ𝑜,𝑜.𝑝⟶𝗅𝗏ℓ𝑝,𝑜.𝑝.𝑥⟶𝗅𝗏ℓ𝑥. Hence 𝖻𝗎𝗆𝗉(&𝑜.𝑝.𝑥) binds its parameter to ℓ𝑥. The two binary cases are 𝖻𝗎𝗆𝗉𝟤(&𝑜.𝑝.𝑥,&𝑜.𝑝)reject:onepathends,𝖻𝗎𝗆𝗉𝟤(&𝑜.𝑝.𝑥,&𝑜.𝑝.𝑦)accept:lastfieldsdiffer.
A state 𝜋;𝜂 is well formed when 𝜂 is nonempty and distinct bindings in its currently accessible frame 𝜂1 have disjoint accessible representations. It is well typed, written Δ⊢𝜋;𝜂:Γ, when it is well formed and, for every frame 𝜂𝑗 in 𝜂 and every contextual binding 𝑚𝑥:𝜏, 𝜋(𝜂𝑗(𝑥))=𝑚𝑣 for a value 𝑣 of type 𝜏, whenever 𝜂𝑗(𝑥) is defined.
Proof of Theorem 49.9 — Safety of the conservative Swiftlet variant
Proof. Follow the source inductions for Lemmas 4.1–4.2, with one repaired case. In a call, lemma 49.6 proves the dynamic disjointness premise. By proposition 49.3, the copied roots are fresh from one another and from all borrowed roots. Assignment uses T-Assign, PSS-Prop, and ESS-Assign, so it can update only a location reached with qualifier 𝗏𝖺𝗋. Pop invokes drop∗ only on copied roots. The remaining nine source syntax families—bindings, reads, literals, functions, conditionals, sequences, casts, evaluation contexts, and pop—are the finite case analysis printed in the proofs of Lemmas 4.1–4.2. They do not use the repaired call premise; their induction hypotheses reconstruct the corresponding rule and memory-typing judgment. ◻
Every reachable non-error state of a closed program in the conservative variant is well formed and well typed. No step assigns through an immutable path, and no call exposes overlapping inout representations.
Proof. Induct on the reduction prefix. Preservation maintains memory well-typedness. Inversion of T-Assign gives a mutable path, PSS-Prop propagates the meet of root and field qualifiers, and ESS-Assign requires the final ℓ𝗏𝖺𝗋; together these prove that no immutable cell is overwritten. Inversion of ESS-Call gives pairwise disjointness for every exposed inout representation. ◻
The source states corresponding progress and preservation results as Lemmas 4.1–4.2 and packages them as Theorem 4.1. Because its access relation has the nested-index gap isolated above, this chapter does not claim that the published proof establishes the unrestricted theorem.
Representation correspondence and boundaries
Swiftlet also has a value-tree semantics in which abstract copying is the identity. A readback |𝑣|𝜋Δ expands a pointer-store value.
For an expression in the natural-semantics fragment, if small-step execution from the initial state reaches 𝜋;𝜂;𝑣, then natural evaluation reaches some 𝜇;𝑣0 with |𝑣|𝜋Δ=𝑣0.
Proof of Theorem 49.11 — Representation correspondence, source sketch
Imported proof. The JOT article labels this Theorem 4.2 and gives an induction-on-run-length proof sketch. It does not verify LLVM lowering, stack allocation, copy-on-write, reference counting, or any particular compiler revision. The exact source is Racordon et al.’s Theorem 4.2, with the natural and small-step semantics of its Sections 4.1–4.5 [RSZ^+22]. ◻
Current Swift access enforcement and Hylo yielding projections are comparison boundaries, not metatheory for Swiftlet. A yielding projection temporarily opens a component of a value to a caller without turning that component into an independently owned result. The pinned Swift design archive documents exclusive-access and yielding-access proposals [Swi17]; Hylo’s richer checker is described by Racordon and Abrahams [RA23]. Racordon’s dissertation gives background on assignment semantics, but owns none of the Swiftlet results [Rac19].
Sources.
Sections 4.1–4.5, Definitions 4.1–4.2, Lemmas 4.1–4.2, Theorems 4.1–4.2, and Appendix A of Racordon et al. own the published calculus and proof claims [RSZ^+22]. The conservative conflict relation and its disjointness proof are book-owned repairs.
★★☆ For one ordinary array argument and two separated inout integers, construct the complete callee frame, including a captured frame assumed well formed before the call. State which roots are copied, borrowed, and removed by 𝗉𝗈𝗉, and prove frame independence.
★★☆ Give a well-typed out-of-bounds access. Explain why its stuck state is allowed by theorem 49.9, while an overlapping access call has no typing derivation.
★★★Practical project.swiftlet-access-auditor Implement definition 49.4 in Kappa. The invariant is: acceptance has found a first component pair that denotes distinct tree children; reaching the end of either path or any unknown index before that witness means conflict. Use the chapter’s four pairs as named oracles, add a different-root pair with unknown indices, and compute the decisive comparison for every rejection. Mutate the stability test to accept a nonliteral index; the mutant must check and audit cleanly but fail the different-root negative oracle.