Fix a countably infinite variable set 𝑉; each program and derivation uses only finitely many variables. Stores 𝜎:𝑉→𝖵𝖺𝗅 are total, and all syntax is well scoped over 𝑉. Heaps ℎ are finite partial maps from non-null locations to values. Write ℎ1#ℎ2 for disjoint domains and ℎ1⊎ℎ2 for their disjoint union. Assertions are 𝑃,𝑄::=𝗍𝗋𝗎𝖾∣𝖿𝖺𝗅𝗌𝖾∣𝐸=𝐹∣𝐸≠𝐹∣𝖾𝗆𝗉∣𝐸↦𝐹∣𝑃∧𝑄∣𝑃∨𝑄∣𝑃∗𝑄∣𝑃−∗𝑄∣∃𝑥.𝑃. Their load-bearing heap clauses are 𝜎,ℎ⊧𝖾𝗆𝗉⟺ℎ=∅,𝜎,ℎ⊧𝐸↦𝐹⟺ℎ={ℓ↦𝑣},ℓ=[[𝐸]]𝜎isanon-nulllocation,𝑣=[[𝐹]]𝜎,𝜎,ℎ⊧𝑃∗𝑄⟺∃ℎ1,ℎ2.ℎ=ℎ1⊎ℎ2∧𝜎,ℎ1⊧𝑃∧𝜎,ℎ2⊧𝑄. Separating implication is the extension condition 𝜎,ℎ⊧𝑃−∗𝑄⟺∀𝑟.(ℎ#𝑟∧𝜎,𝑟⊧𝑃)⟹𝜎,ℎ⊎𝑟⊧𝑄. Pure equality and the Boolean connectives have their ordinary clauses, and ∃𝑥.𝑃 ranges over values by store update. Separating conjunction has unit 𝖾𝗆𝗉 and is commutative and associative up to mutual entailment. If 𝑆 is pure, then (𝑃∗𝑄)∧𝑆, (𝑃∧𝑆)∗𝑄, and 𝑃∗(𝑄∧𝑆) are mutually entailing.
Expressions are variables or literal values. Commands are 𝑐::=𝗌𝗄𝗂𝗉∣𝑥:=𝐸∣𝑥:=[𝐸]∣[𝐸]:=𝐹∣𝑥:=𝖺𝗅𝗅𝗈𝖼(𝐸)∣𝖿𝗋𝖾𝖾(𝐸)∣𝑐1;𝑐2∣𝗂𝖿𝐸=𝐹𝗍𝗁𝖾𝗇𝑐1𝖾𝗅𝗌𝖾𝑐2. The complete big-step semantics is
⟨𝗌𝗄𝗂𝗉,𝜎,ℎ⟩⇓⟨𝜎,ℎ⟩
E-Skip
𝑣=[[𝐸]]𝜎
⟨𝑥:=𝐸,𝜎,ℎ⟩⇓⟨𝜎[𝑥↦𝑣],ℎ⟩
E-Assign
ℓ=[[𝐸]]𝜎ℎ(ℓ)=𝑣
⟨𝑥:=[𝐸],𝜎,ℎ⟩⇓⟨𝜎[𝑥↦𝑣],ℎ⟩
E-Load
ℓ=[[𝐸]]𝜎ℓ∈dom(ℎ)𝑣=[[𝐹]]𝜎
⟨[𝐸]:=𝐹,𝜎,ℎ⟩⇓⟨𝜎,ℎ[ℓ↦𝑣]⟩
E-Store
𝑣=[[𝐸]]𝜎ℓ∉dom(ℎ)ℓ≠𝗇𝗎𝗅𝗅
⟨𝑥:=𝖺𝗅𝗅𝗈𝖼(𝐸),𝜎,ℎ⟩⇓⟨𝜎[𝑥↦ℓ],ℎ⊎{ℓ↦𝑣}⟩
E-Alloc
ℓ=[[𝐸]]𝜎ℓ∈dom(ℎ)
⟨𝖿𝗋𝖾𝖾(𝐸),𝜎,ℎ⟩⇓⟨𝜎,ℎ↾(dom(ℎ)∖{ℓ})⟩
E-Free
⟨𝑐1,𝜎,ℎ⟩⇓⟨𝜎1,ℎ1⟩⟨𝑐2,𝜎1,ℎ1⟩⇓⟨𝜎2,ℎ2⟩
⟨𝑐1;𝑐2,𝜎,ℎ⟩⇓⟨𝜎2,ℎ2⟩
E-Seq
[[𝐸]]𝜎=[[𝐹]]𝜎⟨𝑐1,𝜎,ℎ⟩⇓⟨𝜎′,ℎ′⟩
⟨𝗂𝖿𝐸=𝐹𝗍𝗁𝖾𝗇𝑐1𝖾𝗅𝗌𝖾𝑐2,𝜎,ℎ⟩⇓⟨𝜎′,ℎ′⟩
E-IfT
[[𝐸]]𝜎≠[[𝐹]]𝜎⟨𝑐2,𝜎,ℎ⟩⇓⟨𝜎′,ℎ′⟩
⟨𝗂𝖿𝐸=𝐹𝗍𝗁𝖾𝗇𝑐1𝖾𝗅𝗌𝖾𝑐2,𝜎,ℎ⟩⇓⟨𝜎′,ℎ′⟩
E-IfF
Safety is structural: skip, assignment, and allocation are safe; load, store, and free require the evaluated address in the heap domain; a sequence requires the first command safe and the second safe after every first result; a conditional requires its selected branch safe. The set mod(𝑐) contains all store variables assigned by 𝑐. The set vars(𝑐) contains every variable occurrence in 𝑐, including assignment, load, and allocation targets: vars(𝗌𝗄𝗂𝗉)=∅,vars(𝑥:=𝐸)={𝑥}∪fv(𝐸),vars(𝑥:=[𝐸])={𝑥}∪fv(𝐸),vars(𝑥:=𝖺𝗅𝗅𝗈𝖼(𝐸))={𝑥}∪fv(𝐸),vars([𝐸]:=𝐹)=fv(𝐸,𝐹),vars(𝖿𝗋𝖾𝖾(𝐸))=fv(𝐸),vars(𝑐1;𝑐2)=vars(𝑐1)∪vars(𝑐2),vars(𝗂𝖿𝐸=𝐹𝗍𝗁𝖾𝗇𝑐1𝖾𝗅𝗌𝖾𝑐2)=fv(𝐸,𝐹)∪vars(𝑐1)∪vars(𝑐2).
A semantic triple {𝑃}𝑐{𝑄} requires safety and 𝑄 for every result from every state satisfying 𝑃. The proof judgment is generated by
⊢{𝑃}𝗌𝗄𝗂𝗉{𝑃}
H-Skip
⊢{𝑃[𝐸/𝑥]}𝑥:=𝐸{𝑃}
H-Assign
⊢{𝑃}𝑐1{𝑅}⊢{𝑅}𝑐2{𝑄}
⊢{𝑃}𝑐1;𝑐2{𝑄}
H-Seq
𝑃′⊧𝑃⊢{𝑃}𝑐{𝑄}𝑄⊧𝑄′
⊢{𝑃′}𝑐{𝑄′}
H-Conseq
⊢{𝑃}𝑐{𝑄}𝑎∉vars(𝑐)∪fv(𝑄)
⊢{∃𝑎.𝑃}𝑐{𝑄}
H-Exists
𝑥∉fv(𝐸,𝐹)
⊢{𝐸↦𝐹}𝑥:=[𝐸]{𝐸↦𝐹∧𝑥=𝐹}
H-Load
⊢{𝐸↦−}[𝐸]:=𝐹{𝐸↦𝐹}
H-Store
𝑥∉fv(𝐹)
⊢{𝖾𝗆𝗉}𝑥:=𝖺𝗅𝗅𝗈𝖼(𝐹){𝑥↦𝐹}
H-Alloc
⊢{𝐸↦−}𝖿𝗋𝖾𝖾(𝐸){𝖾𝗆𝗉}
H-Free
⊢{𝑃∧𝐸=𝐹}𝑐1{𝑄}⊢{𝑃∧𝐸≠𝐹}𝑐2{𝑄}
⊢{𝑃}𝗂𝖿𝐸=𝐹𝗍𝗁𝖾𝗇𝑐1𝖾𝗅𝗌𝖾𝑐2{𝑄}
H-If
⊢{𝑃}𝑐{𝑄}mod(𝑐)∩fv(𝑅)=∅
⊢{𝑃∗𝑅}𝑐{𝑄∗𝑅}
H-Frame
The exact-chain family used by the running program is defined for expressions: 𝖼𝗁𝖺𝗂𝗇0(𝐸)=𝖾𝗆𝗉∧𝐸=𝗇𝗎𝗅𝗅,𝖼𝗁𝖺𝗂𝗇𝑛+1(𝐸)=∃𝑎.𝐸↦𝑎∗𝖼𝗁𝖺𝗂𝗇𝑛(𝑎), where 𝑎 is alpha-renamed away from the free variables and surrounding binders at every unfolding.
Assertion substitution is capture avoiding (lemma 44.14), and the fresh-variable coincidence lemma lemma 44.9 is the semantic premise behind H-Exists.