The Calculus of Computation: Decision Procedures with 5. Program Correctness: Mechanics Applications to Verification by Aaron Bradley Zohar Manna Springer 2007 5- 1 5- 2 Program A: LinearSearch with function specification Function LinearSearch searches subarray of array a of integers for specified value e . @pre 0 ≤ ℓ ∧ u < | a | Function specifications @post rv ↔ ∃ i . ℓ ≤ i ≤ u ∧ a [ i ] = e ◮ Function postcondition (@ post ) bool LinearSearch( int [] a , int ℓ, int u , int e ) { It returns true iff a contains the value e in the range [ ℓ, u ] for @ ⊤ ◮ Function precondition (@ pre ) ( int i := ℓ ; i ≤ u ; i := i + 1) { It behaves correctly only if 0 ≤ ℓ and u < | a | if ( a [ i ] = e ) return true ; } for loop: initially set i to be ℓ , return false ; execute the body and increment i by 1 } as long as i ≤ n @ - program annotation 5- 3 5- 4
Program B: BinarySearch with function specification The recursive function BinarySearch searches subarray of sorted array a of integers for specified value e . @pre 0 ≤ ℓ ∧ u < | a | ∧ sorted( a , ℓ, u ) sorted: weakly increasing order, i.e. @post rv ↔ ∃ i . ℓ ≤ i ≤ u ∧ a [ i ] = e sorted( a , ℓ, u ) ⇔ ∀ i , j . ℓ ≤ i ≤ j ≤ u → a [ i ] ≤ a [ j ] bool BinarySearch( int [] a , int ℓ, int u , int e ) { if ( ℓ > u ) return false ; Defined in the combined theory of integers and arrays, T Z ∪ A else { int m := ( ℓ + u ) div 2; Function specifications if ( a [ m ] = e ) return true ; ◮ Function postcondition (@ post ) else if ( a [ m ] < e ) return BinarySearch( a , m + 1 , u , e ); It returns true iff a contains the value e in the range [ ℓ, u ] else return BinarySearch( a , ℓ, m − 1 , e ); } ◮ Function precondition (@ pre ) } It behaves correctly only if 0 ≤ ℓ and u < | a | 5- 5 5- 6 Program C: BubbleSort with function specification Function BubbleSort sorts integer array a a: unsorted sorted @pre ⊤ @post sorted( rv , 0 , | rv | − 1) largest int [] BubbleSort( int [] a 0 ) { int [] a := a 0 ; by “bubbling” the largest element of the left unsorted region of a for @ ⊤ toward the sorted region on the right. ( int i := | a | − 1; i > 0; i := i − 1) { for @ ⊤ Each iteration of the outer loop expands the sorted region by one ( int j := 0; j < i ; j := j + 1) { cell. if ( a [ j ] > a [ j + 1]) { int t := a [ j ]; a [ j ] := a [ j + 1]; a [ j + 1] := t ; } } } return a ; } 5- 7 5- 8
Sample execution of BubbleSort Program Annotation ◮ Function Specifications 2 3 4 1 2 5 6 function postcondition (@ post ) j i function precondition (@ pre ) 2 3 4 1 2 5 6 j i ◮ Runtime Assertions e.g., @ 0 ≤ j < | a | ∧ 0 ≤ j + 1 < | a | 2 3 4 1 2 5 6 a [ j ] := a [ j + 1] j i ◮ Loop Invariants 2 3 1 4 2 5 6 e.g., @ L : ℓ ≤ i ∧ ∀ j . ℓ ≤ j < i → a [ j ] � = e j i 2 3 1 2 4 5 6 j , i 2 3 1 2 4 5 6 j i 5- 9 5- 10 Program A: LinearSearch with runtime assertions Program B: BinarySearch with runtime assertions @pre ⊤ @pre ⊤ @post ⊤ @post ⊤ bool BinarySearch( int [] a , int ℓ, int u , int e ) { bool LinearSearch( int [] a , int ℓ, int u , int e ) { if ( ℓ > u ) return false ; for @ ⊤ else { ( int i := ℓ ; i ≤ u ; i := i + 1) { @ 2 � = 0; @ 0 ≤ i < | a | ; int m := ( ℓ + u ) div 2; if ( a [ i ] = e ) return true ; @ 0 ≤ m < | a | ; } if ( a [ m ] = e ) return true ; return false ; else { } @ 0 ≤ m < | a | ; if ( a [ m ] < e ) return BinarySearch( a , m + 1 , u , e ); else return BinarySearch( a , ℓ, m − 1 , e ); } } } 5- 11 5- 12
Program C: BubbleSort with runtime assertions Loop Invariants while @pre ⊤ @ F @post ⊤ � cond � { � body � } int [] BubbleSort( int [] a 0 ) { int [] a := a 0 ; ◮ apply � body � as long as � cond � holds for @ ⊤ ◮ assertion F holds at the beginning of every iteration ( int i := | a | − 1; i > 0; i := i − 1) { for @ ⊤ evaluated before � cond � is checked ( int j := 0; j < i ; j := j + 1) { @ 0 ≤ j < | a | ∧ 0 ≤ j + 1 < | a | ; for if ( a [ j ] > a [ j + 1]) { @ F int t := a [ j ]; ( � init � ; � cond � ; � incr � ) { � body � } a [ j ] := a [ j + 1]; a [ j + 1] := t ; ⇒ } } � init � ; } while return a ; @ F } � cond � { � body � � incr � } 5- 13 5- 14 Proving Partial Correctness Program A: LinearSearch with loop invariants A function is partially correct if when the function’s precondition is satisfied on entry, @pre 0 ≤ ℓ ∧ u < | a | its postcondition is satisfied when the function halts. @post rv ↔ ∃ i . ℓ ≤ i ≤ u ∧ a [ i ] = e bool LinearSearch( int [] a , int ℓ, int u , int e ) { for ◮ A function + annotation is reduced to finite set of @ L : ℓ ≤ i ∧ ( ∀ j . ℓ ≤ j < i → a [ j ] � = e ) verification conditions (VCs), FOL formulae ( int i := ℓ ; i ≤ u ; i := i + 1) { ◮ If all VCs are valid, then the function obeys its specification if ( a [ i ] = e ) return true ; (partially correct) } return false ; } 5- 15 5- 16
Program A: LinearSearch Basic Paths: Loops To handle loops, we break the function into basic paths Basic Paths of LinearSearch (1) @ ← precondition or loop invariant @pre 0 ≤ ℓ ∧ u < | a | i := ℓ ; sequence of instructions @ L : ℓ ≤ i ∧ ∀ j . ℓ ≤ j < i → a [ j ] � = e (with no loop invariants) (2) @ ← loop invariant, assertion, or postcondition @ L : ℓ ≤ i ∧ ∀ j . ℓ ≤ j < i → a [ j ] � = e assume i ≤ u ; assume a [ i ] = e ; rv := true ; @post rv ↔ ∃ j . ℓ ≤ j ≤ u ∧ a [ j ] = e 5- 17 5- 18 (3) Visualization of basic paths of LinearSearch @ L : ℓ ≤ i ∧ ∀ j . ℓ ≤ j < i → a [ j ] � = e assume i ≤ u ; @pre assume a [ i ] � = e ; (1) i := i + 1; (3) L @ L : ℓ ≤ i ∧ ∀ j . ℓ ≤ j < i → a [ j ] � = e (2) , (4) (4) @post @ L : ℓ ≤ i ∧ ∀ j . ℓ ≤ j < i → a [ j ] � = e assume i > u ; rv := false ; @post rv ↔ ∃ j . ℓ ≤ j ≤ u ∧ a [ j ] = e 5- 19 5- 20
Program C: BubbleSort with loop invariants for 1 ≤ i < | a | ∧ 0 ≤ j ≤ i @pre ⊤ ∧ partitioned( a , 0 , i , i + 1 , | a | − 1) @post sorted( rv , 0 , | rv | − 1) @ L 2 : ∧ partitioned( a , 0 , j − 1 , j , j ) int [] BubbleSort( int [] a 0 ) { ∧ sorted( a , i , | a | − 1) int [] a := a 0 ; ( int j := 0; j < i ; j := j + 1) { for if ( a [ j ] > a [ j + 1]) { − 1 ≤ i < | a | int t := a [ j ]; @ L 1 : ∧ partitioned( a , 0 , i , i + 1 , | a | − 1) a [ j ] := a [ j + 1]; ∧ sorted( a , i , | a | − 1) a [ j + 1] := t ; ( int i := | a | − 1; i > 0; i := i − 1) { } } } return a ; } 5- 21 5- 22 Partition (2) @ L 1 : − 1 ≤ i < | a | ∧ partitioned( a , 0 , i , i + 1 , | a | − 1) ∧ sorted( a , i , | a | − 1) partitioned( a , ℓ 1 , u 1 , ℓ 2 , u 2 ) assume i > 0; ⇔ ∀ i , j . ℓ 1 ≤ i ≤ u 1 < ℓ 2 ≤ j ≤ u 2 → a [ i ] ≤ a [ j ] j := 0; in T Z ∪ T A . � 1 ≤ i < | a | ∧ 0 ≤ j ≤ i ∧ partitioned( a , 0 , i , i + 1 , | a | − 1) � @ L 2 : That is, each element of a in the range [ ℓ 1 , u 1 ] is ≤ each element ∧ partitioned( a , 0 , j − 1 , j , j ) ∧ sorted( a , i , | a | − 1) in the range [ ℓ 2 , u 2 ]. (3) Basic Paths of BubbleSort � 1 ≤ i < | a | ∧ 0 ≤ j ≤ i ∧ partitioned( a , 0 , i , i + 1 , | a | − 1) � @ L 2 : (1) ∧ partitioned( a , 0 , j − 1 , j , j ) ∧ sorted( a , i , | a | − 1) @pre ⊤ ; assume j < i ; assume a [ j ] > a [ j + 1]; a := a 0 ; t := a [ j ]; i := | a | − 1; a [ j ] := a [ j + 1]; @ L 1 : − 1 ≤ i < | a | ∧ partitioned( a , 0 , i , i + 1 , | a | − 1) a [ j + 1] := t ; ∧ sorted( a , i , | a | − 1) j := j + 1; � 1 ≤ i < | a | ∧ 0 ≤ j ≤ i ∧ partitioned( a , 0 , i , i + 1 , | a | − 1) � @ L 2 : ∧ partitioned( a , 0 , j − 1 , j , j ) ∧ sorted( a , i , | a | − 1) 5- 23 5- 24
Recommend
More recommend