# Formal techniques for getting software right: some old ideas and some new tools Applied Formal Methods Research Group 2012-11-02 David Lightfoot:

## Presentation on theme: "Formal techniques for getting software right: some old ideas and some new tools Applied Formal Methods Research Group 2012-11-02 David Lightfoot:"— Presentation transcript:

Formal techniques for getting software right: some old ideas and some new tools Applied Formal Methods Research Group 2012-11-02 David Lightfoot: dlightfoot@brookes.ac.uk Department of Computing and Communication Technologies Oxford Brookes University England

Abstract Getting software right is difficult and the more so if we dont have a clear idea of what it is supposed to do before we start writing it. There are some simple ideas that were devised nearly fifty years ago that help in specifying formally what software is to do, that is, by the use of simple discrete mathematics. These ideas are gaining acceptance in programming languages and modern languages, such as Eiffel and Spec# (spec sharp) now contain features to make use of these ideas.

Motivating example: hotel What does available mean? Check rooms available at affordable hotel: 18 rooms available Book (expensive) flight. Try to book rooms: Sorry, no rooms available Problem! Check rooms available at affordable hotel, again: 18 rooms available What is the explanation?

Motivating example: hotel Hotel closed for Christmas break! So, no rooms booked, hence 18 rooms available But hotel closed, so Sorry, no rooms available What does available mean?

We need specifications! What does available mean? Heres another one: (from an exam paper): Write a Pascal program to return a pointer to the element in the linked list ls that has key value x: function Find(ls: Pointer; x: integer): Pointer; What if there isnt one? Can we assume there is? Which one, if there are several? Answer: return NIL. Why?

Whats the requirement? Write a program to input a three integer numbers, each of them is between 1 and 1000, and determine whether the first number is the multiple of the other two numbers. Print a message to tell if the first number is the multiple of the other two numbers. [22 marks] 8 of the 22 marks were for checking that the three integer numbers were in range 1 to 1000 and writing a message. (But it says each of them is …). (and no marks for checking that a well formed integer was input)

Campaign for clear specifications In order to write correct programs we need clear specifications: A program is syntactically correct with respect to its language definition It is logically/semantically correct with respect to its specification We can check syntactic correctness with a (correct) compiler. We can check logical correctness by: testing use of suitable notation and of provers

State-based specification Both at the system level and at program level we can specify by a state-based approach: Observe state of system before an operation Observe state of system after an operation This puts emphasis on what is achieved, not how it is achieved. Specification notations, such as: Z, VDM, B, Event B …

Not a new idea: Professor Sir Tony Hoare Formerly Head of Programming Research Group (PRG), Oxford University, England Senior Researcher, Microsoft Research, Cambridge, England Hoare, C.A.R.: An axiomatic basis for computer programming. Communications of the ACM 12 (1969) 576-580 [WH66] N. Wirth and C. A. R. Hoare. A contribution to the development of Algol. Comm. ACM, 9(6):413-432, June 1966 Also famous as deviser of Quicksort.

Hoare triple { pre } prog { post } Starting in a before-state where the Boolean expression pre holds, running the program prog results in an after-state where the Boolean expression post holds. pre is precondition post is postcondition

Example { x >= 0 } IntegerSqrt (z*z <= x and x < (z+1)*(z+1) Precondition means: IntegerSqrt is only applicable to non-negative x Postcondition means z has been set to the integer square-root of x

Specification of a function function IntegerSqrt(x: integer): integer; { pre: x >= 0 post: result*result <= x and x < (result+1)*(result+1) } result is here a special key word denoting the result of a function

Techniques for programming Hoare developed techniques based on axioms (rules) for devising programs that can be shown (proved) to satisfy their specifications.

Assignment axiom (* P[var/exp ] *) var := exp (* P *) If P is true of var after the assignment, then P must have been true of exp before. P[var/exp ] means P, with all (free) occurrences of var replaced by exp.

Composition axiom if we can prove(* P *) S1 (* Q *) and we can prove(* Q *) S2 (* R *) ___________ then it follows that(* P *) S1 ; S2 (* R *) If doing S1 when P is true makes Q true and doing S2 when Q is true makes R true, then doing S1 ; S2 when P is true makes R true.

Selection axiom (* P & guard *) S1 (* Q *) (* P & ¬guard *) S2 (* Q *) ___________ (* P *) if guard then S1 else S2 end (* Q *) If performing S1 when P is true and guard is true makes Q true and performing S2 when P is true and guard is false makes Q true, Then performing if guard then S1 else S2 end when P is true makes Q true.

Repetition axiom (* pre *) initialisation (* invariant *) while guard do (* invariant & guard *) body (* invariant *) end (* invariant & ~guard => post *)

Dijkstra E.W.Dijkstra observed that rather than proving an existing program correct, it is easier to develop a program hand-in- hand with showing it to be correct. Developed weakest-precondition, wp notation For example: wp(var := exp, P) is P[var/exp ] Also famous for P and V and Travelling- Salesman problem, …

Whats the precondition?: a real example Recent student work: Required to implement undo. Keep stack of visited nodes. Undo then means pop from stack. In method undo, do we need to guard with: if(!stack.isEmpty()) node = stack.pop(); ? Or do we have precondition: !stack.isEmpty(), so can just implement undo as: node = stack.pop(); ?

Design by Contract Hoares ideas given a business flavour by Bertrand Meyer, currently Professor of Software Engineering at ETH Zurich. Precondition and postcondition are expressed as a contract between the supplier and the client of a service. Also designer of Eiffel programming language. Eiffel contains facilities for run-time checking of precondition and postcondition…

IntegerSqrt as contract: suppliers view Supplier is writer of IntegerSqrt Suppliers expectation: x >= 0 Suppliers obligation: must deliver result such that result*result <= x and x < (result+1)*(result+1)

IntegerSqrt as contract: clients view Client is user of IntegerSqrt Clients expectation: obtains result such that result*result <= x and x < (result+1)*(result+1) Clients obligation: give x such that x >= 0

integerSqrt in Eiffel integerSqrt (x: INTEGER) : INTEGER -- return the integer square-root of non-negative x require non-negative_x: x >= 0 ensure result is floor of square-root of x: result*result <= x and x < (result+1)*(result+1)

Embodied in Eiffel The ideas of pre- and post-conditions are embodied in the keywords require and ensure in the Eiffel programming language. Eiffel compilers embed code to test these at run time (can be selectively turned on and off by compiler switches). Failure leads to diagnostic information being displayed.

Use of assert We can simulate the effects of require and ensure by use of assert statement, provided by many programming-language implementations: assert(b), where b is a Boolean expression. If b is true, all is well If b is false, program halted and message displayed. Sometimes assert has a second parameter, which is message to be displayed.

Example of use of assert function IntegerSqrt(x: integer): integer; begin assert (x >= 0); calculate result assert (result*result <= x and x < (result+1)*(result+1) ); return result; end;

A new style of programming: offensive! Practical context for the formal, Hoare style. An attitude – the opposite of defensive programming – Meyer calls it offensive programming!. Pre- and post-conditions are used to specify a (business) contract between the supplier of a software component and the components clients. Design by Contract by Example, Richard Mitchell and Jim McKim, Addison Wesley, 2002 Since Design by Contract is a trademark, it is often referred to as 'Programming by Contract'.

New style With Design by Contract we dont check preconditions: Responsibility of client to ensure. Example: function DaysEarlier(y1, m1, d1, y2, m2, d2: integer); integer; { return number of days by which date y1-m1-d1 is earlier than date y2-m2-d2}

This formalised as … function DaysEarlier(y1, m1, d1, y2, m2, d2: integer); integer; { require y1-m1-d1 denotes a date and y2-m2-d2 denotes a date ensure result is number of days by which y1-m1-d1 is earlier than y2-m2-d2 }

Defensive programming Always check parameters. Problem, what if there is nothing to be done in case of error? What is Sqrt(-16.0)? (real square-root, not complex) What do you think of this (real) example? function Sqrt(x: real): real; begin if x < 0 then x := -x: calculate square-root of x …

DaysEarlier: defensive version function DaysEarlier(y1, m1, d1, y2, m2, d2: integer); integer; { return number of days by which y1-m1-d1 is earlier than y2-m2-d2 or -999 if either or both not a valid date} days := DaysEarlier(2012, 11, 02, 1943, 06, 31); if days = -999 then writeln(One or both dates not valid) else writeln(days earlier is, days); Says One or both dates not valid! What is wrong?

Problem days := DaysEarlier(2012, 05, 21, 2010, 02, 07); if days = -999 then writeln(One or both dates not valid) else writeln(days earlier is, days); Says One or both dates not valid! What is wrong? True story: Took three weeks to solve!

Java Modeling Language: JML The ideas of Design by Contract and Eiffel are extended and made available for Java in JML: Java Modeling Language http://www.eecs.ucf.edu/~leavens/JML/ Lots of @ and \. Includes quantifiers (,,, …) Following examples are from: An overview of JML tools and applications Lilian Burdy1, Yoonsik Cheon2, David R. Cok3, Michael D. Ernst4, Joseph R. Kiniry5, Gary T.Leavens6?, K. Rustan M. Leino7, Erik Poll51 Software Tools for Technology Transfer

JML: Special comments JML is simply extending Java uses special comments with lots of @, \ Keywords are requires ensures \ result \ old(x) \ forall …

JML example /*@ requires amount >= 0; @ assignable balance; @ ensures balance == \old(balance) - amount @ && \result == balance; @*/ int debit(int amount) throws PurseException { if (amount <= balance) { balance -= amount; return balance; } else { throw new PurseException("overdrawn by " + amount); }

JML quantifiers /*@ requires 0 < mb && 0 <= b && b <= mb @ && p != null && p.length == 4 @ && (\forall int i; 0 <= i && i < 4; @ 0 <= p[i] && p[i] <= 9); @ assignable MAX_BALANCE, balance, pin; @ ensures MAX_BALANCE == mb && balance == b @ && (\forall int i; 0 <= i && i < 4; p[i] == pin[i]); @*/ Purse(int mb, int b, byte[] p) { MAX_BALANCE = mb; balance = b; pin = (byte[]) p.clone(); Symbol abuse?

Provers Showing that assertions (requires, ensures..) are true when program runs only demonstrates correctness for chosen test cases. (Dijkstra famously observed; testing can show presence of errors, not their absence) Much better is to use software tools called provers, that reason about whether program matches its specification. These exist for JML and for Spec#.

Spec# Spec sharp: based on the C# programming language. http://research.microsoft.com/en-us/projects/specsharp/ Similar to JML, but neater syntax, since a new language based on C#; not just C# with special comments. Spec# is available (free of charge) as a plug-in to Visual Studio.

Spec# keywords requires ensures result old(x) forall exists …

Simple example of Spec# int max(int x, int y) ensures (result == x && x >= y) || (result == y && y >= x); { if (x >= y) return x; else return y; } Note: no requires, because no precondition. Is this correct? What about when x == y?

Easy way to use provers http://rise4fun.com/specsharp/

What if it is wrong? int max(int x, int y) ensures (result == x && x > y) || (result == y && y > x); { if (x >= y) return x; else return y; } What about when x == y?

Result from Spec# Unsatisfied postcondition

Example with a loop: DivMod void DivMod(int x, int y, ref int q, ref int r) requires x >= 0 && y > 0; ensures x == q*y + r && 0 <= r && r < y; { r= x; q= 0; while (r >= y) invariant x == q*y + r && 0 <= r; { r= r - y; q= q + 1; } invariant will be true at this point every time round.

Spec# happy

Quantifiers and ranges in Spec# forall exists Ranges int i in (m..n) means: all i m <= i <= n int i in (m:n) means: all i m <= i < n Second form useful; saves lots of -1s

What do you think of this? static int Min(int[] a) requires a != null && a.Length != 0; ensures a == old(a) && (forall {int i in (0:a.Length); result <= a[i]}; { int min = 0; for (int j = 0; j < a.Length; j++) invariant forall {int i in (0:j); min <= a[i]}; if (a[j] < min) min = a[j]; return min; }

Example with quantifiers int First(int[] a, int x) ensures forall{k in (0: result); a[k] != x} && (result == -1 || 0 <= result && result < a.Length && a[result] == x); { int i; i = 0; while (i != a.Length && a[i] != x) invariant 0 <= i && i <= a.Length && forall{k in (0: i); a[k] != x}; { i++; } if (i == a.Length) return -1; else return i; }

Further useful feature of Spec# What does this do? float AverageLength(string [ ] s) { int sum = 0; for (int i = 0; i < s.Length; i++) sum += s[i].Length; return float(sum)/s.Length; } What is it precondition?

AverageLength Returns average length of the strings in array of strings s. Precondition? s.Length > 0 float AverageLength(string [] s) requires s.Length > 0; { int sum = 0; for (int i = 0; i < s.Length; i++) sum += s[i].Length; return float(sum)/s.Length; } Anything else?

Precondition of AverageLength s.Length > 0 But arrays implemented by pointers in Java, C# … So we need to add s != null

float AverageLength(string [] s) requires s != null && s.Length > 0; { int sum = 0; for (int i = 0; i < s.Length; i++) sum += s[i].Length; return float(sum)/s.Length; }

s[j] != null But strings implemented by pointers in Java, C# … So we need to add s[i] != null for all the indexes of s forall{int j in (0: s.Length); s[j] != null};

s[j] != null float AverageLength(string [] s) requires s != null && forall{int j in (0: s.Length); s[j] != null} && s.Length > 0; { int sum = 0; for (int i = 0; i < s.Length; i++) sum += s[i].Length; return float(sum)/s.Length; }

Non-null pointers Very often we require a pointer not to be null. Easy to forget; Untidy to check or to add as precondition. Special syntax in Spec# ! float AverageLength(string ! [] ! s) requires s.Length > 0; ! means must be non-null type. Enforced by compiler.

Proving termination What we have shown so far is partial correctness.; program satisfies its specification, if it terminates. We prove termination by finding a relationship between the variables on a loop that is initially non- negative and that is strictly reduced by each iteration. No support for this in Spec#, but can make our own by introducing auxiliary variables to hold value of bound.

Example of bound in Spec# void DivMod(int x, int y, ref int q, ref int r) requires x >= 0 && y > 0; ensures x == q*y + r && 0 <= r && r < y; { int bound; r= x; q= 0; while (r >= y) invariant x == q*y + r && 0 <= r && bound = r && bound >= 0; { r= r - y; q= q + 1; assert(r < bound); }

Unresolved so far (by me, at least)… assert does run-time check. How to check termination without resorting to run-time check. How to specify processes that modify a structure (such as sorting). Need to be able to use old (or equivalent) inside the body (old only useable in postcondition).

Summary Techniques are not new – 1960s Not widely known outside universities. Not even known to nuclear-physicist researchers in England. Now taken seriously by Microsoft in Spec# and similar projects and in Eiffel and JML. Advantages: Can get programs right much earlier in development hence much less time in debugging. Reduced danger of erroneous programs.

Current use JKU Linz course: Romanian-speaking Hungarian students. Politechnika Warszawska P00401 Formal Software Engineering U08186 Advanced Object-Oriented Programming

Contact information www.brookes.ac.uk David Lightfoot Email: dlightfoot@brookes.ac.uk Telephone: +44 18 65 48 45 39 School of Technology Oxford Brookes University Wheatley Campus Oxford OX33 1HX United Kingdom

Dziękuję!

Download ppt "Formal techniques for getting software right: some old ideas and some new tools Applied Formal Methods Research Group 2012-11-02 David Lightfoot:"

Similar presentations