Programs and Facts

Programs

• We have considered programs composed of the following constructs

• `val`
• `var`
• `S; T`
• `if (C) {S} else {T}`
• `while (C) {S}`
• For all of these constructs we’ve considered how facts can be traced though them

• Facts describe relationships among variables that occur in a program (and constants)

• `val`:

 ``````1 2 3 4 `````` ``````val x: Z = 1 Deduce(|- (x == 1)) val y: Z = x + 1 Deduce(|- (y == x + 1)) ``````
• We can deduce the value of the variable as an equation

• Recall, that the value cannot be modified

• Variables introduced with `val` are immutable

• `var`:

 ``````1 2 3 4 `````` ``````var x: Z = 1 Deduce(|- (x == 1)) x = x + 1 Deduce(|- (x == At(x, 0) + 1)) ``````
• We can deduce the value of the variable as an equation
• When a variable `v` is modified the old values can be accessed using the notation
• `At( v , i )`
• Where `i` refers to the “time” at which the prior assignment took place
 ``````1 2 3 `````` ``````S ; T: var x: Z = 1; x = x + 1 Deduce(|- (x == At(x, 0) + 1)) ``````
• We can write composed programs on one line separated by semicolons
• Usually, we don’t do this to improve readability
 `````` 1 2 3 4 5 6 7 8 9 10 11 12 `````` ``````if ( C ) { S } else { T }: val a: Z = randomInt() var x: Z = 1 Deduce(|- (x == 1)) if (a == 0) { Deduce(|- (a == 0)) x = x + a + 1 } else { Deduce(|- (a != 0)) x = x + (a / a) } Deduce(|- (x == At(x, 0) + 1)) ``````
• We can deduce the condition or it’s negation in either branch
• A fact to be deduced is established by both branches
 `````` 1 2 3 4 5 6 7 8 9 10 11 12 `````` ``````while ( C ) { S }: var x = 10 while (x < 12) { Invariant( Modifies(x), x <= 12 ) Deduce(|- (x < 12)) x = x + 1 } Deduce(|- (x >= 12)) Deduce(|- (x == At(x, 0) + 2)) ``````
• The invariant traces facts from the beginning throughout the execution of the loop
• In the beginning of the loop body, the loop condition holds
• On termination, the negated loop condition holds

Contracts

Function Contracts I

• Side-effect free contract:
 `````` 1 2 3 4 5 6 7 8 9 10 11 12 13 `````` ``````def square(x: Z): Z = { Contract( Requires(x >= 0), Ensures(Res == x * x) ) var res = 0 var k = 0 while (k < x) { res = res + x k = k + 1 } return res } ``````
• Specifies pre- and post-condition
• No modifies clause

Function Contracts II

• Contract with side-effect (parameter):
 ``````1 2 3 4 5 6 7 8 `````` ``````def inc0(x: ZS) { Contract( Requires(x.size == 1), Modifies(x), Ensures(x(0) == In(x)(0) + 1 ) ) x(0) = x(0) + 1 } ``````
• Specifies pre- and post-condition, and modifies clause
• Initial values of variables are referred to using the `In` notation

Function Contracts III

• Contract with side-effect (variable):
 `````` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 `````` ``````var x: Z = 0 def square() { Contract( Requires(x >= 0), Modifies(x), Ensures(x == In(x) * In(x)) ) var res = 0 var k = 0 while (k < x) { res = res + x k = k + 1 } x = res } ``````
• Specifies pre- and post-condition, and modifies clause
• Initial values of variables are referred to using the `In` notation

Function Contracts IV

• Contract between participating entities:
 `````` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 `````` ``````def one(x: Z): Z = { Contract( // Contract between caller (two) and callee (one) Requires(x > 0), // Caller (two) is obliged to guarantee Ensures(Res == 1) // Callee (one) is obliged to guarantee ) return x / x } def two(x: Z): Z = { Contract( Requires(x - 1 > 0), Ensures(Res == 2) ) return one(x - 1) + one(x) } ``````
• A contract fixes obligations on both sides

Specification

• A specification describes the expected behaviour of a program in simple and clear terms
• Usually, this is done in terms of `@pure` functions
• Specifications themselves rely on maths (consider, in particular, `@strictpure`)
• Specifications in Slang are executable but not necessarily efficient
• It should be easy to reason about specifications (and prove properties)
• Clarity and efficiency can often not be had at the same time

Implementation

• An implementation describes the behaviour of a program in efficient terms
• Usually, efficiency comes at the cost of reduced clarity
• Implementations describe how the behaviour is computed
• Specifications describe what is to be computed

Proof

• Commonly used `assert` statements are executable, aborting when their condition is false

• In Slang we have seen commands that support proving the stated facts correct

• `Deduce`
• `Invariant`
• `Contract`
• `Requires`
• `Ensures`
• A proof establishes these facts for all possible executions

• The facts stated in one of these commands are not evaluated at run-time
• They do not lead to program abortion
• We have seen how facts are traced through programs and used to prove new facts

Testing

• Test cases can be generated from specifications

• We can use contracts and do this directly using

• equivalence partitioning and
• boundary value analysis
• We can use specifications with their “mathematical” function bodies to generate test cases

• These cases can be used to test implementations

• Given, the contracts provided, test cases can also be generated from implementations

• If test cases are generated on specification level, they are available when implementation starts

Proof and Testing

• Many reasoning techniques for programs can be used for proof and test case generation

• Considering programs as facts,

• we can ask for states satisfying the post-condition to create a test case
• we can ask whether the program implies the post-condition to prove it correct
• Whereas proof verifies correctness for all possible input values, testing verifies correctness for specific inputs. It is fallible.

• Testing is somewhat like scientific experimentation: We consider the correctness of a program confirmed until we encounter a failing test (experiment).

• We can generate tests from specifications that are easier to reason about, and use them for implementations that are harder to reason about

• The specifications themselves can be proved correct based on the maths by which means they are expressed