CS 641 meeting -*- Outline -*- * procedures ** specification and invocation of procedures (2.3) main idea of procedures is abstraction by specification the specification is contract between clients and implementation; shows intent *** format specification is a Hoare triple plus list of external variables. ------------------- PROCEDURE SPEC FORMAT examples: proc maxTo2nd(x: int, var y: int) { all m \in int : m = x max y : pre true, post y = m } ------------------- Q: what do you think this should mean? ------------------- proc maxToM(x: int) { ext y, M!: int all m \in int : m = x max y : pre true, post M = m } schematic form: proc h(x:item; var y: item) { ext u, v!: item; all i \in item : C : pre P, post Q } ------------------- In the schematic form (used in the theory work), only one value, one var parameter, one external value, one external written. In practice more are permitted, in theory, with data structures no more are needed. Call the list "u, v" by the name "Ext" The "all" clause introduces specification constants, C is a condition on them Omitted C means true The value parameters can only appear in the precondition, have to use spec constants if want in postcondition Why? so assertions only refer to one state, so can use in annotations, and so can't change the value parameters. *** correctness rule ---------------------- CORRECTNESS RULE FOR PROCS An implementation of a procedure h is *correct* if the specification is well-formed: (b) no value parameters in the postcondition, Q, and the body, body.h satisfies: (a) all externals used in body.h are listed as ext, those threatened are also marked, !, (c) for all values of the spec constants that satisfy, C, {P} body.h {Q} ----------------------- Threatened externals are those: on lhs of :=, passed as var parameters to other procs, listed as threatened by procedures this one calls This is sound (but proof later), but not necessary Examples: proc copy0 (var p: item) { ext i; all v \in item :: pre i = v, post p = v } ; begin p := i end Q: why is this correct? *** external invocation rule invocations of the form: h(E,t), where E an expression (well-defined), and t a variable. avoidance of aliasing: require t \not\in Ext (list of externals) (Need to have different versions of the procedure to deal with effect for alias.) Notation: Ext is list of externals, Ext! is sublist of threatened externals frame axiom: Let R be a predicate that expresses whatever is of interest about the state Then R is invariant across the call, provided it does not mention var-parameters or threatened variables notation: Var.R is set of free variables in R so formally, the only requirement on R is that: Var.R \intersect ({t} \union ext!) = {} ----------------------- INVOCATION RULE specification: proc h(x:item; var y: item) { ext u, v!: item; all i \in item : C : pre P, post Q } invocation rule: if Var.R \intersect ({t} \union v!) = {}, then x,y y {P /\ R} h(E,t) {Q /\ R} E,t t ----------------------- This just means to replace the formals by the actuals if more than one var-parameter, also require that all var-parameters differ this gets complex when have array elements as actuals... Remark: note that the var parameters are always dereferenced, so can't talk directly about aliasing, except by var-names ** correctness of recursive procedures *** example ---------------------- EXAMPLE CORRECTNESS ARGUMENT FOR RECURSIVE PROCEDURES spec: proc divi(y:integer) { ext x!,q! : integer; all X,Y \in integer : X >= 0 /\ Y > 0; pre P : x = X /\ y = Y, post Q : X = q * Y + x /\ 0 <= x < Y } ---------------------- Q: informally, what is this doing? Let's develop this like a loop, our invariant will be the pre-condition, As with while loops, we'll need a bound function, here called a... ---------------------- variant function: vf = x - y ---------------------- we choose this because we can easily establish Q if x < y by q := 0 otherwise, we aim towards that by doing a division on 2*y, what will that establish? X = q*2*Y + x /\ 0 <= x < 2*Y we then can multiply q by 2 to get X = q*Y + x /\ 0 <= x < 2*Y, then to establish 0 <= x < Y, we need an if and a subtraction... --------------------- impl: proc divi(y:integer) {x = X /\ y = Y /\ x-y <= m} if x < y then {x = X /\ y = Y /\ x-y <= m /\ x < y} (* arithmetic, calculus *) {x = X >= 0 /\ x < y = Y} (* arithmetic *) {X = 0*Y + x /\ 0 <= x < Y} q := 0 {Q} else {x = X /\ y = Y /\ x-y <= m /\ ~(x < y)} (* arithmetic, Y > 0 is part of spec *) {x=X /\ y = Y > 0 /\ 0 <= x-y <= m} (* arithmetic, calculus *) {x=X /\ 2*y = 2*Y /\ x-2*y < m /\ m >= 0 /\ (R: y = Y)} divi(2 * y); (* induction hypothesis *) {X = q*2*Y + x /\ 0 <= x < 2*Y /\ y=Y} q := q*2; {X = q*Y+x /\ 0 <= x < 2*Y /\ y = Y} if x < y then {X = q*Y+x /\ 0 <= x < 2*Y /\ y = Y /\ x < y} (* calculus *) {Q} skip {Q} else {X = q*Y+x /\ y <= x < 2*Y /\ y=Y} x := x - y; {X = (q+1)*Y + x /\ 0 <= x < Y} q := q + 1 {Q} fi {Q} fi {Q} ----------------------- Q: does this help with that one homework problem? Note that we used as our induction hypothesis, invocation rule based on the spec of divi, using something like the specification of divi, with the extra proviso that vf < m and m >= 0 in the precond. *** correctness rule ---------------------------- CORRECTNESS RULE FOR RECURSIVE PROCS A recursive implementation of procedure h is *correct* if the specification is well-formed: (b) no value parameters in the postcondition, Q, and the body, body.h satisfies: (a) all externals used in body.h are listed as ext, those threatened are also marked, !, (d) there is an integer-valued function, vf (of i, x, y, and vars in Ext) such that for all integers m, and for all i such that C (from h's spec), the induction hypothesis below gives: {P /\ vf <= m} body.h {Q} (induction hypothesis): each recursive call h(E,t) satisfies i for all j such that C j and for all predicates R such that Var.R \intersect ({t} \union ext!) = {} the following triple is valid i,x,y {(P /\ vf = 0 /\ R} j,E,t h(E,t) i,y {Q /\ R} j,t ---------------------------- Q: how does this rule relate to the proof of the example? To see idea of soundness, use induction on vf. (base case) vf is negative. use m = -1, this makes the induction hypothesis valid (as precond is false) (inductive case) if 0 <= vf <= m, recursive calls use vf < m don't forget m>=0 in precondition of induction: it gives base case. *** proof, abstract version of procedures (2.5) how to prove correctness of procedure rule Q: did we already do that? not carefully Such a proof would be complicated, dealing with renamings... so motivation is (1) to do a real proof, and (2) to have a more abstract/simpler rule. for this, use the idea of procedures as deterministic choice (as in section 1.8) and model the renamings into function application **** abstract specification of procedure so predicates become functions yielding predicates. ------------------------ ABSTRACTION OF PROC SPEC {p.a.b} h.a {q.a.b} where a is actuals arguments b is externals -------------------- Q: can this be simplified more? -------------------- code pair (a,b) as single thing: {p.i} h.i {q.i} --------------------- **** abstract implementation of procedure Q: how to abstract the procedure body? each proc has a body, which is a command abstractly, function from name to body. --------------------------- ABSTRACTION OF PROC DECL C = command-expression H = proc-names (a set) example: (y : y > 0 : divi.y) body: H -> C example: body.(divi.y) --------------------------- **** semantics of procedure bodies --------------------------- POSTULATED SEMANTICS OF PROC BODIES wp.h = wp.(body.h) wlp.h = wlp.(body.h) --------------------------- Q: Does this define semantics of h? if h is non-recursive, what does it mean? if h is recursive, looks circular... suppose body.h = h, then wp.h = wp.h, not clear that tells us anything... Thm (recursion): Let H = (i \in I :: h.i) be a family of procedure names. Let (i \in I :: p.i) and (i \in I :: q.i) be families of predicates. Suppose vf: I -> (X -> Integers) be such that for all integers m, (all i \in I :: {p.i /\ vf.i < m /\ m >= 0 } h.i {q.i}) {ind hyp.} ==> (all \in I :: {p.i /\ vf.i <= m} body.(h.i) {q.i}). Then {p.i} h.i {q.i} for all i \in I. Will prove this theorem with respect to the postulates that still leaves justifying the postulates. Pf: (all i \in I :: {p.i /\ vf.i < m /\ m >= 0 } h.i {q.i}) {ind hyp.} ==> (all \in I :: {p.i /\ vf.i <= m} body.(h.i) {q.i}). equiv {wp.h = wp.body.h (14) in the text} (all i \in I :: {p.i /\ vf.i < m /\ m >= 0 } h.i {q.i}) {ind hyp.} ==> (all \in I :: {p.i /\ vf.i <= m} h.i {q.i}) Lemma: for all I \in I, (all m >= -1 :: {p.i /\ vf.i <= m} h.i {q.i}) Pf: (by induction on ints at least -1) (basis) suppose m = -1. m >= 0 is false, so triples in antecedent are true, thus (all i \in I :: {p.i /\ vf.i <= -1} h.i {q.i}) (inductive case) suppose m > -1. ind hyp for this proof is. (all i \in I :: {p.i /\ vf.i <= m-1} h.i {q.i}) need to show (all i \in I :: {p.i /\ vf.i <= m} h.i {q.i}) true equiv {inductive hyp} (all i \in I :: {p.i /\ vf.i <= m-1} h.i {q.i}) ==> {main hypothesis of whole theorem, m > -1} (all \in I :: {p.i /\ vf.i <= m} h.i {q.i}) QED for lemma. Since in every state x there is a m with vf.i.x <= m, so by the covering rule {p.i} h.i {q.i} QED for theorem