Tutorials on Spec# Carl Leonardsson 2/11-2011
So far in the course: We have been looking at Hoare Logic. Specifying contracts: {Pre}Program{Post} Manually computing proof-obligations Manually proving proof obligations Showing partial/total correctness Now: Enter Spec# Integration with actual compilable program code Automation of computation of proof-obligations Automation of proof
So far in the course: We have been looking at Hoare Logic. Specifying contracts: {Pre}Program{Post} Manually computing proof-obligations Manually proving proof obligations Showing partial/total correctness Now: Enter Spec# Integration with actual compilable program code Automation of computation of proof-obligations Automation of proof
1 Outline 2 3 4 RiSE4fun Visual Studio Commandline
What is Spec#? Language for software development with formal verification Extension of C# Developed by Microsoft Research Components Compiler (ssc) Verifier (boogie) Visual Studio plugin
What is Spec#? Language for software development with formal verification Extension of C# Developed by Microsoft Research Components Compiler (ssc) Verifier (boogie) Visual Studio plugin
What does Spec# provide? (Pre- and post conditions for functions.) Verified asserts Verified bounds checks for array indexing Verified freedom of null-derefencing
What does Spec# provide? (Pre- and post conditions for functions.) Verified asserts Verified bounds checks for array indexing Verified freedom of null-derefencing
What does Spec# provide? (Pre- and post conditions for functions.) Verified asserts Verified bounds checks for array indexing Verified freedom of null-derefencing
What does Spec# provide? (Pre- and post conditions for functions.) Verified asserts Verified bounds checks for array indexing Verified freedom of null-derefencing
What does Spec# provide? (Pre- and post conditions for functions.) Verified asserts Verified bounds checks for array indexing Verified freedom of null-derefencing
What does Spec# provide? Also... Not covered in these tutorials (and not in the labs): Class invariants for object oriented programming Support for ownership hierarchies
Specifying pre- and post conditions {Pre}Program{Post} Spec# example static int increase(int x, int delta) { return x + delta; }
Specifying pre- and post conditions {Pre}Program{Post} Spec# example static int increase(int x, int delta) ensures result > x; { return x + delta; }
Specifying pre- and post conditions {Pre}Program{Post} Spec# example static int increase(int x, int delta) requires delta > 0; ensures result > x; { return x + delta; }
requires e; Specifies pre condition e is a C# boolean expression over variables in scope May also contain quantifiers (more later) ensures e; Specifies post condition e is a C# boolean expression over variables in scope Use special variable result for return value
Functions with side-effects Example static void dbl(int[] a) { a[0] = a[0]*2; }
Functions with side-effects Example static void dbl(int[] a) ensures a[0] == 2*old(a[0]); { a[0] = a[0]*2; }
Functions with side-effects Example static void dbl(int[] a) requires a.length > 0; modifies a[*]; ensures a[0] == 2*old(a[0]); { a[0] = a[0]*2; }
old(v) Refers to the value of variable v at the start of the function call. Can be used only in ensures clause. modifies v; Functions may not modify values which are not either class members or specified in the modifies clause. Framing Spec# will produce proof obligations for array bounds checking
References in Spec# can be declared Null or Non-null. string! s; Non-null string string? s; String which may be a null reference string![]? a; a is either null or a string array All elements in the array are non-null. Dereferencing a possibly null reference requires a proof that it is in fact not null. Assigning a possibly null reference r to a non-null reference requires proof that r null.
References in Spec# can be declared Null or Non-null. string! s; Non-null string string? s; String which may be a null reference string![]? a; a is either null or a string array All elements in the array are non-null. Dereferencing a possibly null reference requires a proof that it is in fact not null. Assigning a possibly null reference r to a non-null reference requires proof that r null.
References in Spec# can be declared Null or Non-null. string! s; Non-null string string? s; String which may be a null reference string![]? a; a is either null or a string array All elements in the array are non-null. Dereferencing a possibly null reference requires a proof that it is in fact not null. Assigning a possibly null reference r to a non-null reference requires proof that r null.
References in Spec# can be declared Null or Non-null. string! s; Non-null string string? s; String which may be a null reference string![]? a; a is either null or a string array All elements in the array are non-null. Dereferencing a possibly null reference requires a proof that it is in fact not null. Assigning a possibly null reference r to a non-null reference requires proof that r null.
Example static void dbl(int[]? a) requires a.length > 0; modifies a[*]; ensures a[0] == 2*old(a[0]); { a[0] = a[0]*2; }
Example static void dbl(int[]? a) /* Note order of require clauses */ requires a!= null; requires a.length > 0; modifies a[*]; ensures a[0] == 2*old(a[0]); { a[0] = a[0]*2; }
Example assert e - assert a boolean expression Verified at compile time! Usage: Making sure that the code does what it should Use during development to understand your code Give hints to the automatic verifier static int foo(int x, int y) requires x >= 2 && y >= x-1; { assert y*2 >= x; return y*2; }
Example assert e - assert a boolean expression Verified at compile time! Usage: Making sure that the code does what it should Use during development to understand your code Give hints to the automatic verifier static int foo(int x, int y) requires x >= 2 && y >= x-1; { assert y*2 >= x; return y*2; }
Example assert e - assert a boolean expression Verified at compile time! Usage: Making sure that the code does what it should Use during development to understand your code Give hints to the automatic verifier static int foo(int x, int y) requires x >= 2 && y >= x-1; { assert y*2 >= x; return y*2; }
Example assert e - assert a boolean expression Verified at compile time! Usage: Making sure that the code does what it should Use during development to understand your code Give hints to the automatic verifier static int foo(int x, int y) requires x >= 2 && y >= x-1; { assert y*2 >= x; return y*2; }
Example assert e - assert a boolean expression Verified at compile time! Usage: Making sure that the code does what it should Use during development to understand your code Give hints to the automatic verifier static int foo(int x, int y) requires x >= 2 && y >= x-1; { assert y*2 >= x; return y*2; }
assume e - assume a boolean expression Assumed true by the verifier - Not verified Usage: Introducing lemmas which can not be automatically proven Figuring out what is necessary to get a proof to go through For the labs... Assume statements are useful, but dangerous since they allow verification of completely erroneous programs. In the labs you may use assume statements (sparingly, as necessary) for the last exercise. But not for the other exercises.
assume e - assume a boolean expression Assumed true by the verifier - Not verified Usage: Introducing lemmas which can not be automatically proven Figuring out what is necessary to get a proof to go through For the labs... Assume statements are useful, but dangerous since they allow verification of completely erroneous programs. In the labs you may use assume statements (sparingly, as necessary) for the last exercise. But not for the other exercises.
assume e - assume a boolean expression Assumed true by the verifier - Not verified Usage: Introducing lemmas which can not be automatically proven Figuring out what is necessary to get a proof to go through For the labs... Assume statements are useful, but dangerous since they allow verification of completely erroneous programs. In the labs you may use assume statements (sparingly, as necessary) for the last exercise. But not for the other exercises.
assume e - assume a boolean expression Assumed true by the verifier - Not verified Usage: Introducing lemmas which can not be automatically proven Figuring out what is necessary to get a proof to go through For the labs... Assume statements are useful, but dangerous since they allow verification of completely erroneous programs. In the labs you may use assume statements (sparingly, as necessary) for the last exercise. But not for the other exercises.
In specifications we can use quantifiers forall{int i in (a:b); bexpr} i.a i < b bexpr exists{int i in (a:b); bexpr} i.a i < b bexpr sum{int i in (a:b); iexpr} a i<b iexpr product{int i in (a:b); iexpr} a i<b iexpr min{int i in (a:b); iexpr} min{iexpr a i < b} max{int i in (a:b); iexpr} max{iexpr a i < b} count{int i in (a:b); bexpr} {i a i < b bexpr}
In loops, we may specify invariants Invariants need to be proven to hold initially... and proven to be maintained by the loop body. Simple invariants and framing can be automatically inferred But don t count on it...
In loops, we may specify invariants Invariants need to be proven to hold initially... and proven to be maintained by the loop body. Simple invariants and framing can be automatically inferred But don t count on it...
In loops, we may specify invariants Invariants need to be proven to hold initially... and proven to be maintained by the loop body. Simple invariants and framing can be automatically inferred But don t count on it...
Example static int asum(int[] a) ensures result == sum{int i in (0:a.Length); a[i]}; { int i = 0; int s = 0; while(i < a.length) { s += a[i]; i++; } return s; }
Example static int asum(int[] a) ensures result == sum{int i in (0:a.Length); a[i]}; { int i = 0; int s = 0; while(i < a.length) invariant s == sum{int j in (0:i); a[j]}; { s += a[i]; i++; } return s; }
Example static int asum(int[] a) ensures result == sum{int i in (0:a.Length); a[i]}; { int i = 0; int s = 0; while(i < a.length) invariant 0 <= i && i <= a.length; invariant s == sum{int j in (0:i); a[j]}; { s += a[i]; i++; } return s; }
Proof of Total Correctness is not natively supported by Spec# We want variants! How to hack a proof of total correctness: 1 Declare a loop-local program variable variant 2 Initialise variant = expr; at the start of the loop body 3 At the end of loop body assert assert variant >= 0; and 4 assert expr < variant; for the same expr as above.
Proof of Total Correctness is not natively supported by Spec# We want variants! How to hack a proof of total correctness: 1 Declare a loop-local program variable variant 2 Initialise variant = expr; at the start of the loop body 3 At the end of loop body assert assert variant >= 0; and 4 assert expr < variant; for the same expr as above.
Proof of Total Correctness is not natively supported by Spec# We want variants! How to hack a proof of total correctness: 1 Declare a loop-local program variable variant 2 Initialise variant = expr; at the start of the loop body 3 At the end of loop body assert assert variant >= 0; and 4 assert expr < variant; for the same expr as above.
Example while(i < a.length) invariant 0 <= i && i <= a.length; invariant s == sum{int j in (0:i); a[j]}; { int variant = a.length - i; s += a[i]; i++; assert variant >= 0; assert a.length - i < variant; }
Can we use any boolean expression in specifications? No. Needs to be side-effect free. To be able to use your own functions in specifications they need to be declared [Pure]. Requirements for [Pure]: Example Side-effect free Well-defined (terminating) [Pure] int avg(int a, int b) ensures result == (a+b)/2; { return (a + b) / 2; }
Can we use any boolean expression in specifications? No. Needs to be side-effect free. To be able to use your own functions in specifications they need to be declared [Pure]. Requirements for [Pure]: Example Side-effect free Well-defined (terminating) [Pure] int avg(int a, int b) ensures result == (a+b)/2; { return (a + b) / 2; }
Can we use any boolean expression in specifications? No. Needs to be side-effect free. To be able to use your own functions in specifications they need to be declared [Pure]. Requirements for [Pure]: Example Side-effect free Well-defined (terminating) [Pure] int avg(int a, int b) ensures result == (a+b)/2; { return (a + b) / 2; }
Can we use any boolean expression in specifications? No. Needs to be side-effect free. To be able to use your own functions in specifications they need to be declared [Pure]. Requirements for [Pure]: Example Side-effect free Well-defined (terminating) [Pure] int avg(int a, int b) ensures result == (a+b)/2; { return (a + b) / 2; }
Can we use any boolean expression in specifications? No. Needs to be side-effect free. To be able to use your own functions in specifications they need to be declared [Pure]. Requirements for [Pure]: Example Side-effect free Well-defined (terminating) [Pure] int avg(int a, int b) ensures result == (a+b)/2; { return (a + b) / 2; }
Summary of commands requires ensures modifies old result assert assume invariant count{int i in (a:b); bexpr} sum{int i in (a:b); iexpr} product{int i in (a:b); iexpr} min{int i in (a:b); iexpr} max{int i in (a:b); iexpr} exists{int i in (a:b); bexpr} forall{int i in (a:b); bexpr} Pure
RiSE4fun Visual Studio Commandline RiSE4fun An easy way of trying out Spec# is the web interface at http://rise4fun.com/specsharp.
RiSE4fun Visual Studio Commandline Create a Spec# project by File New Project Spec# Projects Console Application Important: In the project properties Configuration Properties, make sure to enable RunProgramVerifier and Treat Warnings as Errors. The Spec# plugin for Visual Studio is a bit unstable, so if unsure about the results double-check using the commandline method described next.
RiSE4fun Visual Studio Commandline Need to set Path environment variable. (See lab instructions.) Compile by Verify by > ssc /t:library /debug file.ssc > boogie file.dll