370 likes | 484 Vues
This work by Peter O'Hearn, John Reynolds, and Hongseok Yang explores the ideal assertion language for local reasoning in programming, particularly with pointers and concurrent computations. It discusses the challenges of pointer manipulation and proposes separation logic as a solution. The paper highlights Hoare proof rules for verifying partial correctness and presents the ideal assertion language's properties: being natural, succinct, expressive, and efficient for entailment checking. The discussion extends to inductive definitions and concurrent programming, illustrating how separation logic provides a robust framework for reasoning about shared states and memory.
E N D
Local Reasoning Peter O’Hearn John Reynolds Hongseok Yang
Outline • The ideal assertion language • Difficulties with pointers • Solutions • The separation logic • Concurrent Separation Logic
Hoare Proof Rules for Partial Correctness {A} skip {A} {B[a/X]} X:=a {B} (Assignment Rule) {P} c0 {C} {C} c1 {Q} {P} c0;c1{Q} (Composition Rule) {Pb} c0 {Q} {P b} c1 {Q} {P} if b then c0 else c1{Q} (Conditional Rule) (Loop Rule) {Ib} c {I} {I} while b do c{Ib} P P’ {P’} c {Q’} Q’ Q {P} c {Q} (Consequence Rule)
The Ideal Assertion Language • Natural • Succinct • Expressive • Closed under WP for every command • Efficient algorithm for entailment checking
IMP++ • Abstract syntaxcom::= X := a | X:= cons(a1, a2) | X := [a] |[a1] := a2 | dispose(a) |skip | com1 ; com2 |if b then com1else com2 |while b do com|
Informal Semantics IMP++ Store: [x:3, y:40, z:17] Heap: empty • Allocation x := cons(y, z) • Heap lookup y := [x+1] • Mutation [x + 1] := 3 • Deallocation dispose(x+1) Store: [x:37, y:40, z:17] Heap: 37:40, 38:17 Store: [x:37, y:17, z:17] Heap: 37:40, 38:17 Store: [x:37, y:17, z:17] Heap: 37:40, 38:3 Store: [x:37, y:17, z:17] Heap: 37:40
First assertion language Aexpv a:= n | X | [a] | i | a0 + a1 | a0 - a1 | a0 a1 Assn A:= true | false | a0 = a1 | a0 a1 | A0 A1 | A0 A1 | A | A0 A1| i. A | i. A
Destructive Pointer Reversal y: = nil ; while x nil do ( t := y y := x x := [x +1] [y+1] := t )
Assignment Axioms {?} [x] := z {[x] =y} {?} [t] := z {[x] =y} {?} [a1] := a2 {p}
Assignment Rule with Mutations • [Morris 1982] Characterize aliasing patterns with assertions • [McCharthy ?] Define a logic for stores • heap:locval • First order assertions over heap • {p} [a1] := a2 {p[a2 /heap(a1)]} • [Burstall 1972] Local reasoning • Split the heap into disjoints parts • Contents can be shared {x=37 z=x heap(37)=40} [x] := 77 {x=37 z=x heap(37)=77}
Separation Logic x y * y x
Separation Logic x y y x
Separation Logic y x y x
Separation Logic x y * y x y x
42 10 x=10 y=42 10 42 Separation Logic x y y x
42 10 x=10 y=42 10 42 Separation Logic y x y x
42 10 x=10 y=42 10 42 Separation Logic x y * y x y x
Assertions in Separation Logic e _ l: e l ee0, e1, …, en-1 e e0* e+1 e1* … * e+n-1 en-1 ef e f * true
p * q p Weakening Unsound Axioms p p * p Contraction p= x1 p= x1 q= y 2
{true} {true} {x_ * p} [x] := 7 dispose(e) dispose(e) {?} {?} {p} In-Place Reasoning {x_ * p} [x] := 7 {x7 * p} if {p} c {q} holds then p describes the resources that c needs
s, h e = f s, h e f s, h emp s, h p * q es = fs {es} = dom(h) and h(e) = fs h=[] exist h1, h2: dom(h1)dom(h2)= s, h1 p s, h2 q h = h1 # h2 Semantics of separation logics
s, h false s, h p q s, h x. p never if s, h p then s, h q exists v: s[v/x], h p Semantics of separation logics(cont)
Three “Small” Axioms {e_} [e] := b {e b} {emp} x := cons(y, z) {x y, z} {e_} dispose(e) {emp}
{p} c {q} {p * r} c {q * r} Mod(c) free(r)={} The Frame Rule Mod(x := _) = {x} Mod([e]:=f) = Mod(dispose(e)) =
{p} c {q} {p * r} c {q * r} Mod(c) free(r)={} A simple application of the frame rule {(e_ )* p } dispose(e) {p}
Inductive Definitions • Define assertions inductively • Allows natural specifications list [r] x (r= x= nil emp) ( a, s, y: r=a.s xa, y * list [s] y) list(x, y) (x=y emp) ( t: x_, t * list(t, y)) tree(r) (r=nil emp) (l, r: r_, l, r * tree(l) * tree(r))
The Reverse Example y: = nil ; while x nil do ( t := y y := x x := [x +1] [y+1] := t ) , . list [] y * list [] x rev(0)= rev().
The Delete Example {list(c, nil)} bool elem_delete(delval, c) prev=nil elem = c while (elem nil) ( if ([elem] = delval) then ( if (prev = nil) then c = [elem+1] else [prev+1] = [elem+1]; dispose(elem); return TRUE) prev=elem; elem = [elem+1] prev=nil /\ list(c,nil) prev != nil /\ (list (c,prev) * (prev -,elem) * list (elem, nil)) list(x, y) (x=y emp) t: x_, t * list(t, y) {list(c, nil)}
Extensions • For WP we need another operator • “Fresh” implication p -* q • We can extend a heap in which p is true with an additional disjoint heap such that q is true in the combined heap
Disjoint Concurrency {p1} c1 {q1} {p2} c2 {q2 } {p1* p2} c1 || c2 {q1* q2 }
Disjoint Concurrency {p1} c1 {q1} {p2} c2 {q2 } {p1* p2} c1 || c2 {q1* q2 } {10_} [10] := 5 || [10] := 7 {?} Cannot prove racy programs
Disjoint Concurrency {p1} c1 {q1} {p2} c2 {q2 } {p1* p2} c1 || c2 {q1* q2 } Preconditions can pick race free programs when they exist {x 3 * y 3} {y3} [y] :=7 {[y]= 7} {x3} [x] :=4 {[x]= 4} {x 4 * y 7}
Example: Parallel Dispose Tree procedure dispTree(p) { local l, r if (p !=nil) then { l = [p+1]; r = [p+2]; dispose(p) || dispTree(l) || dispTree(r) } }
Parallel Dispose Tree - Proof Sketch {tree(p)} dispTree(p) {emp} {p_, l, r} dispose(p) {emp} {tree(l)} dispTree(l) {emp} {tree(r)} dispTree(r) {emp} p _, l, r * tree(l) * tree(r) dispose(p) || dispTree(l) || dispTree(r) emp * emp * emp
Extensions • Hoare Conditional Critical Regions • with r when B do C • Fine-Grained Concurrency • Combine with Rely/Guarantee
Summary • Separation logic provides a solution for the heap • Limited aliasing • Dynamic ownership • Concise specifications • Elegant proofs for several examples