Dafny: Automated Program Verification Guide
Dafny: Automated Program Verification Guide
Preface
These lecture notes introduce the programming and verication language Dafny.
They are primarily based on lectures given by Rustan Leino in 2011 at the 8th
LASER Summer School, as transcribed by Luke Herbert and Jose Quaresma
(who were students at that summer school). Other references to Dafny and
inuences on this tutorial include the Marktoberdorf Summer School lectures
from 2008 [11] and 2011 [10], and the online Dafny tutorial [9].
Dafny is a state-of-the-art implementation of an automated verication system based around the idea of dynamic frames [6,7]. This is an approach to
formal verication of program correctness that attempts to prove correctness
of individual program parts locally, and from there infer the correctness of the
whole program. The dynamic-frames approach makes it possible to reason about
subparts even in the presence of data-abstraction [15]. Dafny is a usable implementation of this approach which has been used to verify several non-trivial
algorithms.
This tutorial is a practical guide to using Dafny to write veriable programs.
While some description of the design of Dafny is given, this is not complete and
serves mostly to overview Dafny use, and to point to more authoritative sources
of information on Dafny internals.
This tutorial provides extensive code examples. Most code examples have web
links to code pre-loaded in an online Dafny environment, which demonstrates
many key Dafny features. You can follow the code examples by visiting the corresponding footnote link, to see what Dafny reports, and to further experiment
with Dafny.
Dafny Background
Fig. 1.
rise4fun.com/Dafny.
http://
full Visual Studio version of the tool, in particular with regard to debugging, it
is able to thoroughly demonstrate the main capabilities of Dafny. Indeed, this
entire tutorial can be completed using the
rise4fun
website.
http://
boogie.codeplex.com. The binaries run on any .NET platform, which includes
To run Dafny using your own computer, download the binaries from
Windows as well as any system with the Mono .NET development framework.
The smoothest way to run Dafny is in Microsoft Visual Studio 2010, which
brings the benets of an integrated development environment where the program
verier runs in the background as the Dafny code is being developed. The installation currently requires downloading and building Dafny from source (also from
rise4fun
method
Swap ( a
: int
: int ) returns
(x
: int
: int )
This method declaration states that the Swap method receives two values a
and b as input and returns two values x and y as output. The intention is to let
the output values be the input values in swapped order, that is to say that x
should have the value of b and y the value of a.
The central idea in Dafny is to express what the method is intended to do,
and then have Dafny verify that this is indeed what the method actually does
(an implementation of the concept of Hoare Logic [5]). Dafny can use an
ensures
method Swap ( a : i n t ,
e n s u r e s x == b &&
: int ) returns
(x
: int
: int )
y == a ;
After the signature of the method has been declared, we can write the body
of the method which implements the swapping and use Dafny to verify that the
method does indeed achieve what is intended. The suggested way to use Dafny
is to rst express what a method is intended to do, and then write the implementation of the method and let Dafny check if it is correct. For the preceding
The swap example from section 3 on the previous page can be used to introduce
the basic constructs of the Dafny language. That example used two of Dafny's
basic constructs, namely
method
and
ensures.
method Swap ( a : i n t ,
e n s u r e s x == b &&
: int ) returns
(x
: int
: int )
y == a ;
{
x
y
:=
:=
b;
a;
The method body is contained within the braces and it consists of a series of
statements, such as assignments, conditionals, loops, and method calls. Assignments in Dafny are performed using
:=
semi-colon.
The types of parameters, result values, and object elds must be declared
explicitly, whereas the types of local variables (and of bound variables of quantied expressions, see section 7 on page 13) are usually inferred and can be
sequence of values of type T. In addition, there are array types of one and more
dimensions, written
type
object
null .
object
How-
ever, it should be noted that Dafny has no inheritance support or other class
subtypes.
The
ensures
dition expresses a property that must hold after every invocation of the method
through all possible return points. Postconditions form part of the method declaration and appear before the body block. In the Swap method, the postcondition
says that the output values should have the inverse order from the input arguments.
To make the example more interesting, instead of using method in- and outparameters, we can change the method to operate on two variables in the scope
enclosing the method. These variables are declared using the keyword
var outside
the scope of the method. To be able to verify this new version of the Swap method,
two important modications are needed.
The rst modication is due to the fact that the postcondition must be
expressed in terms of the state of the variables before and after method execution.
Dafny allows for this by making use of the
old
a variable ( old ( variable ) ) operates as a function which refers to the value of the
variable at the time the method was invoked.
The second modication required is to declare which variables the method
is allowed to change, which is done using the keyword
modies .
Although the
declared variables may look like global variables, they are in fact elds of an
implicit class. For now, just specify the Swap method with
modies this ,
which
gives the method license to modify the elds of the object on which it is invoked, which here means it is allowed to modify the variables x and y. These
modications result in a new swap program:
var
var
x
y
: int
: int
;
;
method Swap ( )
modifies this ;
e n s u r e s x == o l d ( y )
&& y ==
old ( x ) ;
{
x
y
:=
:=
y;
x;
If you attempt to verify this code, Dafny reports that a postcondition might
not hold. This is because the implementation of the method is now wrong. As
an exercise, try to implement it correctly.
One possible solution using a temporary variable will look like this :
var
4
tmp
:=
x;
x
y
:=
:=
y;
tmp ;
The body of the Swap method can be expressed more succinctly by employing
parallel assignment. This avoids the use of a temporary variable and performs
:=
y ,x;
method Main ( )
modifies this
assert
keyword.
{
x
y
:=
:=
5;
10;
Swap ( ) ;
assert
x == 1 0 && y == 5 ;
An assertion statement forces Dafny to verify that the given boolean expression evaluates to true along all possible program paths to that point. You
can think of the program as crashing if the asserted condition does not hold.
Thus, only executions where the asserted condition holds ever get past the
assert
6
statement. You can observe a consequence of this in the following Warn method ,
where the verier complains about the rst
assert
because the second assertion does hold in every execution that gets past the rst
assertion without crashing.
method
{
Warn ( )
var x ;
assert
assert
x <10;
x <100;
It is okay to think of
assert
but note that programs must pass the verier before they are compiled, and the
verier will complain if it cannot prove the absence of such crashes.
You can now verify the complete swap program . An alternative way to
implement the Swap method is:
5
6
7
x
y
x
:=
:=
:=
x + y;
x
x
y;
y;
You can check that the Swap method making use of this alternative method
implementation
about the arithmetic performed. While Dafny is able to reason about many
common mathematical constructs that appear in programs, like linear arithmetic
and boolean algebra, Dafny is not aware of all mathematical truths. For example,
in the following program, which requires a complex mathematical proof [16],
Dafny will not be certain that the postcondition will always hold.
method F e r m a t ( a : i n t , b : i n t
e n s u r e s ( a n s == t r u e ) ;
{
ans
if
:= t r u e
: int ) returns
( ans
: bool )
:= f a l s e
}
}
We can make the code more reusable if we change the swap program to use
an object encapsulating the data instead of using global variables. We will create
a class called Cell in Dafny in the following way:
class
var
Cell
data
: int
data.
We can now
change the signature of the Swap method to make use of Cell objects. This
new version of the method will receive two Cell objects and swap their values.
However, this introduces a new requirement that the method's input parameters
should refer to proper Cell objects, that is, they can not have the
null
value.
requires
sure the preconditions hold at the call site, and it is the responsibility of the
callee (i.e. the method body) to make sure that the postconditions hold upon
return from the method.
Using the Cell class, we now have the following Swap method, which includes
pre- and post- conditions:
method Swap ( x : C e l l , y : C e l l )
r e q u i r e s x != n u l l && y != n u l l ;
e n s u r e s x . d a t a == o l d ( y . d a t a ) &&
y . d a t a ==
old ( x . data ) ;
8
9
x . data
y . data
x . data
:=
:=
:=
x . data + y . data ;
x . data
x . data
y . data ;
y . data ;
To accommodate the new Cell class, we will also update the method that
calls the Swap method:
method
{
var
Main ( )
c ,
c . data
Swap ( c ,
assert
assert
:= new
:=
10;
Cell ,
d . data
new
:=
Cell ;
20;
d);
d . d a t a == 1 0 ;
c . d a t a == 2 0 ;
that the Swap method performs an assignment which may update an object not
in the enclosing context's modies clause. This is due to the fact that, while
methods are allowed to read whatever memory they like, they are required to
declare which parts of memory they modify. The declaration is done with a
modies
annotation, which lists the objects (or sets of objects) whose elds may
be modied. In this case, the method changes the elds of the objects x and y,
so we change our method declaration to look like this:
method Swap ( x : C e l l , y : C e l l )
r e q u i r e s x != n u l l && y != n u l l ;
modifies x , y ;
e n s u r e s x . d a t a == o l d ( y . d a t a ) &&
y . d a t a ==
old ( x . data ) ;
{
x . data
y . data
x . data
:=
:=
:=
x . data + y . data ;
x . data
x . data
y . data ;
y . data ;
the postcondition of our Swap method might not hold. This might be unexpected,
since almost the same code for the Swap method was veried earlier. The only
dierence is that we are now using instances of the Cell class. This is the source of
the problemthe variables dened in the Cell classes are referenced by pointers,
which means the program now behaves dierently. Specically, when x . data and
y . data refer to the same object, i.e., when the references x and y are the same,
the value of this data eld will always be 0 after we execute the Swap method.
To see if our reasoning is correct, we can use an assumption. An assumption
is a boolean expression, denoted by the
10
11
assume
assume
0 when asked to swap the same variable. This option requires changing the
13 or by adding a precondition
14
that requires x and y to refer to dierent objects . Another option is to change
15
the implementation to allow for swapping without restrictions .
Loop Invariants
So far, the code examples we have used have consisted of a nite number of
control paths. When recursion or loops are involved, the number of control paths
may be innite. To reason about such control paths, it is necessary to provide
annotations at various program points along the way. For recursive calls, you
supply pre- and post- conditions, as we have already seen. For loops, you supply
a loop invariant.
A loop invariant is a boolean expression that holds at the start of every
iteration. The verier checks that the loop invariant holds at the point where
control ow reaches the loop and checks that it holds again at the end of every
loop iteration. Thereby, it can assume the loop invariant to hold at the very
top of each iteration (meaning at the point where the loop guard is about to be
evaluated), which is how the verier reasons about the code in the loop body
and after the loop. In fact, the loop invariant is the only property the verier
remembers about the variables being modied in the loop from one iteration to
another, so it is important to declare a loop invariant that says enough about
these variables. This is similar to the way calls are handled, where pre- and
post- conditions are the only properties that the verier takes across the call
boundaries.
12
13
14
15
Interactive
Interactive
Interactive
Interactive
code
code
code
code
sample:
sample:
sample:
sample:
http://rise4fun.com/Dafny/B78X
http://rise4fun.com/Dafny/YWYs
http://rise4fun.com/Dafny/HIBe
http://rise4fun.com/Dafny/6OL
function
{
if
Fib (n
: nat ) : nat
then
n < 2
else
F i b ( n 2) + F i b ( n 1)
We can use this function in the loop invariant of an iterative version of the
Fibonacci method, and by making use of this invariant, Dafny can reason about
the loop in this method. This method will have the following signature:
method C o m p u t e F i b ( n : nat ) r e t u r n s
e n s u r e s x == F i b ( n ) ;
(x
: nat )
The method receives a natural number (n) as input, and returns a natural
number (x) which is the n'th Fibonacci number. The second line is the postcondition of the method and it tells us that the returned value is indeed the n'th
number of the zero indexed Fibonacci sequence.
In the body of the method, we wish to build the required Fibonacci number
iteratively. This can be done by using parallel assignment to both compute the
next Fibonacci number and perform the needed housekeeping of the position in
the sequence, in eect updating two numbers of the sequence at once. We will
use x and y to keep track of those two consecutive numbers, with y corresponding
to the newly computed number and x corresponding to number computed in the
previous iteration. That is to say, after i iterations of the loop, x is the i 'th
number of the Fibonacci sequence and y is the i +1'th.
method C o m p u t e F i b ( n : nat ) r e t u r n s
e n s u r e s x == F i b ( n ) ;
{
var
x
:=
:=
(x
: nat )
0;
0;
v a r y :=
while ( i
1;
< n)
{
x ,
i
:=
:=
i
y ,
x+y ;
1;
}
}
16 .
16
As you might expect, Dafny cannot be certain that the postcondition holds.
That is because there is no loop invariant that describes the values of x, y, and
i through the iterations. Let us supply a loop invariant. We begin by describing
the possible values of the loop index i . We know that i starts at 0, and it will
increase until it is equal to n at which point the program will exit the loop. So
this is the rst invariant:
invariant
0 <=
i <= n ;
We also need to describe x and y in the loop invariant, but for instructional
purposes, let's explore what happens if we mention just x and not y. Remember,
we intend the code to maintain x as the i 'th value of the Fibonacci sequence,
where i is the number of iterations performed. So, we write the following loop
invariant:
invariant
x == F i b ( i ) ;
Dafny can now tell that the postcondition will hold: Starting from the very
top of a loop iteration, the invariants about i and x hold. If the loop guard
( i < n) happens not to hold, that is, if n <= i, then the rst loop invariant
lets the verier conclude i == n and the second loop invariant lets the verier
conclude x == Fib(n), which is the desired postcondition.
However, Dafny will now tell us that the second loop invariant might not
be preserved by the loop. This is because the next value of x depends on the
previous value of y, about which our loop invariants do not yet say anything. So,
we need to provide some information about the value of y in the loop. Remember
our intention about y, which is to maintain it as the i +1'th Fibonacci number.
Adding the corresponding loop invariant and running Dafny will now report that
the program is veried successfully
function
{
if
Fib (n
n < 2
17 .
: nat ) : nat
then
else
F i b ( n 2) + F i b ( n 1)
method C o m p u t e F i b ( n : nat ) r e t u r n s
e n s u r e s x == F i b ( n ) ;
{
var
x
:=
:=
(x
: nat )
0;
0;
v a r y := 1 ;
while ( i < n)
invariant 0
invariant x
invariant y
<=
i <= n ;
== F i b ( i ) ;
== F i b ( i + 1 ) ;
{
x ,
17
:=
y ,
x+y ;
:=
1;
}
}
Looking back at what we just did, you may see striking similarities between
loop invariants and mathematical proofs that use induction. At any time, the
loop invariant says what is true after all the loop iterations so far. When no
loop iterations have taken place, you need to check that the loop invariant holds
initially, which corresponds to the base case in typical proofs by induction. To
prove that the loop invariant holds after
gets to assume that it holds after
the inductive hypothesis when doing the inductive step in typical proofs by
induction.
So now we know that if we exit the loop in our ComputeFib method, the
postcondition will hold. But what if we don't exit the loop? It's clearly desirable
to have Dafny assure us that the program will denitely exit the loop.
Dafny can prove termination by using decreases annotations. If we can label each
loop iteration with a natural number and make sure that successive iterations
strictly decrease that label, then it follows that at run time the program can
only execute a nite number of loop iterations, and that is all the information
needed to prove that the loop eventually terminates.
More generally, instead of a natural number, we can use any value as long
as we choose a well-founded relation which induces strictly decreasing chains,
i.e. it does not admit innite descending chains. Dafny predenes a well-founded
relation on each of its types, and it extends these to lexicographically ordered
tuples, which then also form a well-founded relation. The tuple of expressions
that labels a loop iteration is called a variant and is introduced using the keyword
decreases.
while
( i < n). In this case, Dafny infers that what decreases in the loop is
decreases.
Let us start by looking at an example of a simple recursive function that
18 . The decreases
clause in the following example allows Sum to be calling itself with a sequence
18
whose length is decreased by one at each invocation. Since there is a lower bound
on the size of a sequence, this implies ordering on successive calls is well-founded,
and thus the recursion will eventually terminate.
if
x s ==
[]
then
else
xs [ 0 ]
+ Sum ( x s [ 1 . . ] )
19 . As you
can see, the decreases clause has the lexicographic tuple m,n that allows Dafny
to prove termination. It proves this by using size comparisons of the component
values to determine whether the measure has shrunk. In this case it uses two
integers, but in general each component can be of dierent types. The comparison
works lexicographically: if the rst element, in this case m, is smaller, then it
doesn't matter what happens to the other values. They could increase, decrease,
or stay the same. The second element is only considered if the rst element does
not change. Then, the second value needs to decrease. If it doesn't, then the third
element must decrease, etc. For proof of termination to be possible eventually,
one of the elements must decrease, and the values of any subsequent elements
are not taken into consideration.
f u n c t i o n A c k e r m a n n (m : nat ,
d e c r e a s e s m, n ;
{
if
m == 0
: nat ) : nat
then
n + 1
else i f
n == 0
then
A c k e r m a n n (m
1,
1)
A c k e r m a n n (m
1,
A c k e r m a n n (m,
else
1))
Looking more closely at the Ackermann function, there are three recursive
calls. In the rst, m becomes one smaller, but n increases. This makes the decreases clause valid since the rst element of the tuple decreases (and it doesn't
matter what happens to the ones after that). In the second call, m also decreases,
so the second argument is again allowed to be any value. Dafny then needs to
prove that the third call obeys the termination measure. For this call, m remains
the same, but n decreases, so the overall measure decreases as well. Dafny is thus
able to prove the termination of the Ackermann function.
Lemmas
Sometimes, intricate logical steps are required to prove program correctness, but
they are too complex for Dafny to discover and use on its own. An example of
19
this is the Fermat method shown in section 4 on page 7. When this happens, we
can often give Dafny assistance by providing a lemma, which is a theorem used
to prove another result.
Lemmas allow Dafny to break a proof into two: prove the lemma, then use
it to prove the nal result, i.e. the correctness of the program. Splitting up the
proof in this way helps Dafny to see the intermediate steps that make the proof
process easier. Lemmas are particularly useful for inductive arguments, which
are some of the hardest problems for theorem provers.
The most common type of lemma is a method lemma. A method lemma is a
method which has the desired property as a postcondition. The method does not
change any state, and doesn't need to be called at run time. For this reason, it is
declared to be a
of the program, and to help the proof of the program. A typical method lemma
has the following structure:
g h o s t method Lemma ( . . . )
ensures ( d e s i r a b l e property ) ;
{
...
}
e n s u r e s i n d e x < 0 ==>
( forall
i : : 0 <= i
e n s u r e s 0 <= i n d e x ==>
< a . L e n g t h ==> a [ i ]
i n d e x < a . L e n g t h && a [ i n d e x ]
(
forall
::
0 <=
<
!=
0);
== 0 &&
i n d e x ==> a [ i ]
!=
0);
Let's say that the array our method receives has two properties: its elements
are non-negative and its values cannot decrease by more than one unit in consecutive positions. We will add these properties as preconditions to our method:
requires forall
requires forall
i
i
::
::
0 <=
0 <
i
i
1] 1
<= a [ i ] ;
When writing the method, we can take advantage of this property, because
if we know, for example, a [ j ] == 3, then we know that we will not nd a zero
before a [ j +3]. Generalizing this, if a [ j ] is non-zero, we know we will not nd a
zero before a [ j +a[j ]] (due to the property that successive array values decrease
by at most one) and, therefore, we can accordingly jump positions in the array
20 .
while looking for the zeros. We thus have our FindZero method
( index
: int )
:=
0;
w h i l e ( i n d e x < a . Length )
i n v a r i a n t 0 <= i n d e x ;
invariant forall k : : 0
a[k]
if
!=
(a [ index ]
index
:=
<= k <
0;
== 0 )
index
return ;
+ a [ index ] ;
}
index
:= 1;
If you check this code using Dafny, it will report that it cannot prove the
loop invariant, since we are jumping several positions of the array. But we can
write a lemma that allows Dafny to prove that loop invariant.
We want to prove that the elements of the array between a [ j ] and a [ j +a[j ]]
cannot be zero. So we write a method that receives an array with the properties
mentioned previously and an index j and proves that there are no zeros between
a [ j ] and a [ j +a[j ]] .
!=
a[ i ];
==>
0;
{}
Before writing the body of this method, we can try to use it together with
the FindZero method to see if our lemma helps Dafny prove the loop invariant.
(It is not a problem that we didn't write the body yet, since when evaluating
the FindZero method, it will only use the postconditions from the JumpingLemma
method). The key change to the
21 :
while
below
w h i l e ( i n d e x < a . Length )
i n v a r i a n t 0 <= i n d e x ;
invariant forall k : : 0
a[k]
!=
<= k <
0;
21
if
(a [ index ]
== 0 )
return ;
JumpingLemma ( a , i n d e x ) ;
index
:=
index
+ a [ index ] ;
As you can see, Dafny is now able to prove the loop invariant in the FindZero
program. It still complains, but now because it is not able to prove the postcondition of our lemma, and it was not supposed to, since we didn't write the body
of the method yet.
When constructing the body, we want to iterate the array's index from j to
j + a[j ] (making sure that it is still smaller than the size of the array) and prove
that each of those elements is non-zero. The following code will achieve this:
v a r i := j ;
w h i l e ( i < j + a [ j ] && i < a . L e n g t h )
i n v a r i a n t i < a . L e n g t h ==> a [ j ] ( i j )
i n v a r i a n t f o r a l l k : : j <= k < i && k <
a[k]
!=
<= a [ i ] ;
a . L e n g t h ==>
0;
{
i
:=
1;
The rst invariant represents the fact that the value of a [ j ] subtracted by the
distance between j and i is smaller or equal than a [ i ] . This intuitively expresses
the fact that, for example, the value of a [ j +3] is not less than a [ j ]3. This
property can be inferred from the properties of the arrays, step by step. The
other invariant states that the values of the array between a [ j ] and a [ i ] (which
corresponds to the current iteration) are non-zero. Using this code as the method
body will allow Dafny to prove the postconditions of the lemma. In turn, the
postcondition of the lemma method allows the verier to prove that the loop
invariant in FindZero is maintained, which completes the proof of correctness of
22 .
FindZero
Note how the lemma and its proof were themselves expressed as a Dafny
method. Essentially, because Dafny already knows how to reason about calls
and loops, which are related to mathematical induction, these programming
features can also be used to prove lemmas by induction. This is powerful and
useful, and the power is accessible via ordinary constructs used in programming
and program verication.
Abstraction
22
class
{
var
Counter
Value
: int
constructor I n i t ()
modifies this ;
e n s u r e s V a l u e ==
0;
method G e t V a l u e ( ) r e t u r n s
e n s u r e s x == V a l u e ;
(x
: int )
method I n c ( )
modifies this ;
e n s u r e s V a l u e == o l d ( V a l u e )
1;
method Dec ( )
modifies this ;
e n s u r e s V a l u e == o l d ( V a l u e )
1;
We have not shown it here, but it is easy to write implementations for these
23 .
methods and to create a Main method to test the class and its specications
The variable Value is a simple way to explain the operation of the class to
clients. However, suppose the implementer of the class wants to represent Value in
an alternative way? To illustrate this point, suppose the Counter implementation
counts the number of increment and decrement operations; then Value can be
represented as the dierence between those two counts. We declare two more
variables:
var
var
incs
decs
: int
: int
;
;
:=
incs
23
One way to make this relationship explicit would be to add Value == incs
decs
to the postcondition of the constructor and to the pre- and post- conditions of
the methods. This would not be satisfactory, for two reasons. First, if the relation ever were to change, we would have to update the program text in many
places. Second, and more importantly, the details of this relation are to remain
private with the class implementation; clients of the class should not have to be
concerned with the details. So, we instead write the relation in the body of a
function that we shall name Valid :
incs
decs
reads.
to declare which parts of the object store they may update, functions have to
declare which parts of the object store they depend on. We will have more to
say about these so-called frame issues in the next section.
Finally, we need to use and enforce this relationship, which we do by adding
Valid () as postcondition of the constructor and as a pre- and post- condition of
all methods which ensures that if the relationship holds before invocation it must
also do so after execution. Since we now no longer need Value in the compiled
code, we can mark it as ghost.
By applying the suggested changes, we can now verify the program
24 . Because
Value is a ghost variable, it will not be present in the compiled code and hence
it is necessary to have the GetValue method so that we can have access to the
actual value of the Counter instance we will be using.
class
Counter
// p u b l i c v a r i a b l e
ghost var Value : i n t
// p r i v a t e v a r i a b l e s
var i n c s : i n t ;
var decs : i n t ;
incs
decs
constructor I n i t ()
modifies this ;
ensures Valid ( ) ;
e n s u r e s V a l u e ==
24
0;
{
incs ,
decs ,
Value
:=
0,
0,
0;
method G e t V a l u e ( ) r e t u r n s
requires Valid ( ) ;
e n s u r e s x == V a l u e ;
(x
: int )
{
x
:=
incs
decs ;
method I n c ( )
requires Valid ( ) ;
modifies this ;
ensures Valid ( ) ;
e n s u r e s V a l u e == o l d ( V a l u e )
1;
{
incs ,
Value
:=
incs
+ 1,
Value +
1;
method Dec ( )
requires Valid ( ) ;
modifies this ;
ensures Valid ( ) ;
e n s u r e s V a l u e == o l d ( V a l u e )
1;
{
decs ,
Value
:=
decs + 1 ,
Value
1;
}
}
method
{
var
Main ( )
c
:= new
c . Inc ( ) ;
Counter . I n i t ( ) ;
c . Inc ( ) ;
c . Dec ( ) ;
c . Inc ( ) ;
assert
c . V a l u e == 2 ;
Dynamic Frames
reads
and
modies
clauses.
Let us continue with the Counter example from the previous section, but use
our previous Cell class, from section 4 on page 7, for the incs and decs variables
instead of integers:
var
var
incs
decs
:
:
Cell ;
Cell ;
We let the constructor initialize these elds by allocating new Cell objects:
incs ,
:= new
decs
i n c s . data ,
Cell ,
d e c s . data ,
new
Value
Cell ;
:=
0,
0,
0;
In the other methods, we replace incs and decs with incs . data and decs . data,
respectively. We apply that replacement in function Valid , too; in addition, we
need Valid to express that the two Cell references are non-null and distinct:
incs
!=
null
V a l u e ==
&& d e c s
i n c s . data
!=
null
&&
incs
!=
d e c s &&
decs . data
modies
reads
clause allows,
x to this problem is to change these three frame specications to also list the
object referenced by incs and decs
exposes the implementation elds incs and decs in public specications. We need
a way to abstract over these elds in the specications.
A solution to this problem is dynamic frames [6,7]. In Dafny, a dynamic
frame is simply an expression that denotes a set of objects and that is used in
set <object>,
ghost var
Repr
: s e t <o b j e c t
ghost.
>;
We change the constructor to initialize Repr to the desired set, and we change
the frames of methods Inc and Dec to:
modifies
Repr ;
which says that the method is allowed to modify an object in the set Repr. More
precisely, this
object
modies
that, at the time the method is invoked, is in the set denoted by Repr.
Note that the frame for the constructor Init is still just
and is part of the standard idiom. First, at the time the constructor is called,
25
26
the eld Repr has an unknown value, so it would not make sense to list it in the
were created after Init was invoked, and Dafny allows any such newly allocated
objects to be modied, without any need to mention them in the
modies
modies this .
clause.
this ,
as
We have a few more things to address before we are done. If we tried to verify
modies
Inc
Repr. The
reason for this is that the preconditions of these methods do not say anything
about the contents of Repr. To address this problem, we want to change the
denition of Valid to say something about Repr. So, let us change the denition
of Valid to this:
this in
in
incs
incs
R e p r &&
null
R e p r && d e c s
!=
V a l u e ==
i n R e p r &&
i n R e p r &&
d e c s &&
i n c s . data
decs . data
null
this
is in Repr.
but we strongly recommend itin our experience, the verication errors that
it follows that incs and decs are non-null, so we don't need to mention those
properties explicitly. Finally, we changed the
reads
this ?
further explanation.
Why does the frame of Valid explicitly list
reads
Repr
suce?
It may seem that
reads
this
to be included
in Repr. However, when checking that the body of Valid adheres to the reads
frame, the verier does not know anything about Repr. It may help to consider
how the body of Valid would be evaluated from an arbitrary state at run time.
The rst conjunct ( this
false ,
in
27
this
reads
clause.
nished yet, because our postcondition specications are not strong enough to
be useful for clients. Consider some client code, like the Main method we used to
test the class in the previous section
method
{
var
29 :
Main ( )
c
:= new
c . Inc ( ) ;
Counter . I n i t ( ) ;
c . Inc ( ) ;
c . Dec ( ) ;
c . Inc ( ) ;
assert
c . V a l u e == 2 ;
This code gives rise to several frame violation errors. The problem is that the
postconditions of Init and the other methods do not say enough about the objects
in Repr. For example, the specications would allow a method to change incs to
point to a Cell object in use by another Counter object
30 :
method S h a r e M e ( c n t : C o u n t e r )
r e q u i r e s V a l i d ( ) && c n t != n u l l && c n t . V a l i d
m o d i f i e s Repr ;
e n s u r e s V a l i d ( ) && V a l u e == o l d ( V a l u e ) ;
{
if
( i n c s . d a t a == c n t . i n c s . d a t a )
i n c s . data
:=
();
cnt . i n c s . data ;
}
}
Calling this method could lead to two Counter objects sharing the same representation. Dafny allows such specications and implementations, which are
sometimes useful. The situation is ne, because the possibility of sharing does
not escape the verier. Indeed, this is why the verier complains about the Main
client above.
To correct the problem, we show the nal part of the standard dynamicsframe idiom in Dafny. To the constructor, we add the postcondition:
e n s u r e s f r e s h ( Repr
this
});
which says that, upon return from Init , all objects other than
this
in Repr are
ones that were allocated after Init was invoked. In other words, Init sets Repr
to some set of newly allocated objects, except it may also possibly contain
this .
This specication is abstract enough to not explicitly mention the private Counter
implementation, and yet strong enough to allow a client to follow up the call to
Init
modies
Reprafter
all, any object newly allocated in Init is also newly allocated in the caller, Main,
28
29
30
so the caller is allowed to modify the objects that, upon return from Init , are
contained in Repr.
We add a similar postcondition to the mutating methods Inc or Dec:
e n s u r e s f r e s h ( Repr o l d ( Repr ) ) ;
This postcondition says that any objects added to Repr are newly allocated. The
31 :
class
var
Cell
data
: int
class
Counter
// p u b l i c v a r i a b l e
ghost var Value : i n t ;
g h o s t v a r R e p r : s e t < o b j e c t >;
// p r i v a t e v a r i a b l e s
var i n c s : C e l l ;
var decs : C e l l ;
this in
in
incs
incs
R e p r &&
null
R e p r && d e c s
!=
i n R e p r &&
i n R e p r &&
d e c s &&
V a l u e ==
i n c s . data
decs . data
constructor I n i t ()
modifies this ;
e n s u r e s V a l i d ( ) && f r e s h ( R e p r
e n s u r e s V a l u e == 0 ;
{
incs ,
decs
i n c s . data ,
Repr
Repr
:=
:=
:= new
Cell ,
d e c s . data ,
this };
Repr + { i n c s ,
new
Value
this
});
Cell ;
:=
0,
0,
0;
decs };
method G e t V a l u e ( ) r e t u r n s
requires Valid ( ) ;
e n s u r e s x == V a l u e ;
(x
: int )
{
x
:=
i n c s . data
decs . data ;
31
method I n c ( )
requires Valid ( ) ;
m o d i f i e s Repr ;
e n s u r e s V a l i d ( ) && f r e s h ( R e p r o l d ( R e p r ) ) ;
e n s u r e s V a l u e == o l d ( V a l u e ) + 1 ;
{
i n c s . data ,
Value
:=
i n c s . data + 1 ,
Value +
1;
method Dec ( )
requires Valid ( ) ;
m o d i f i e s Repr ;
e n s u r e s V a l i d ( ) && f r e s h ( R e p r o l d ( R e p r ) ) ;
e n s u r e s V a l u e == o l d ( V a l u e ) 1 ;
{
d e c s . data ,
Value
:=
decs . data + 1 ,
Value
1;
}
}
method
{
var
Main ( )
c
:= new
c . Inc ( ) ;
Counter . I n i t ( ) ;
c . Inc ( ) ;
c . Dec ( ) ;
c . Inc ( ) ;
assert
c . V a l u e == 2 ;
Our explanation of the standard dynamic-frames idiom may have been long,
but the idiom is simple to follow: First, declare the ghost variable Repr, and let
Valid describe the contents of Repr as we did above. Then, declare constructors
modifies this ;
ensures Valid ()
&&
f r e s h ( Repr
this
});
declare mutating methods (like Inc and Dec) to include the following specication:
requires Valid ( ) ;
m o d i f i e s Repr ;
e n s u r e s V a l i d ( ) && f r e s h ( R e p r o l d ( R e p r ) ) ;
and declare query methods (like GetValue) to include the following specication:
requires
Valid ( ) ;
10
Conclusion
Dafny is a general-purpose specication language and verier. Through programmerprovided specications and other annotations, it makes possible the verication
of the functional correctness of a program. The symbol manipulation in the
proofs themselves is performed in a mostly automatic fashion, hidden from the
user of the tool.
In these lecture notes, we have introduced Dafny through a series of simple
examples. We have demonstrated a key strength of Dafny, namely abstraction
and dynamic frames, which allow us to scale to larger programs (for further
information on this see, for example, [13]). By learning and using Dafny, you can
construct programs that behave as specied. The concepts you learn also apply
when programming in other languages, even if you don't have a verication tool
and instead do the reasoning informally.
Acknowledgments
We are indebted to the reviewers, who gave a lot of themselves to take us through
various drafts of these lecture notes. Thank you!
References
1.
Barnett, M., Chang, B.-Y. E., DeLine, R., Jacobs, B., and Leino, K.
2.
Barnett, M., Fhndrich, M., Leino, K. R. M., Mller, P., Schulte, W.,
and Venter, H.
3.
4.
5.
6.
7.
8.
Klebanov, V., Mller, P., Shankar, N., Leavens, G. T., Wstholz, V.,
Alkassar, E., Arthan, R., Bronish, D., Chapman, R., Cohen, E., Hillebrand, M., Jacobs, B., Leino, K. R. M., Monahan, R., Piessens, F., Polikarpova, N., Ridge, T., Smans, J., Tobies, S., Tuerk, T., Ulbrich, M.,
and Wei, B.
9.
10.
11.
12.
13.
14.
15.
16.
Koenig, J.
nology, 2011.
Wiles, A. Modular elliptic curves and Fermat's last theorem. The Annals of
Mathematics 141, 3 (May 1995), 443551.