Conference PaperPDF Available

Automatic verification of Dafny programs with traits

Authors:

Abstract

This paper describes the design of traits, abstract superclasses, in the verification-aware programming language Dafny. Although there is no inheritance among classes in Dafny, the traits make it possible to describe behavior common to several classes and to write code that abstracts over the particular classes involved. The design incorporates behavioral specifications for a trait's methods and functions, just like for classes in Dafny. The design has been implemented in the Dafny tool.
Automatic Verification of Dafny Programs with Traits
Reza Ahmadi
University of Tampere
reza.ahmadi@uta.fi
K. Rustan M. Leino
Microsoft Research
leino@microsoft.com
Jyrki Nummenmaa
University of Tampere
jyrki@sis.uta.fi
ABSTRACT
This paper describes the design of traits, abstract superclasses, in
the verification-aware programming language Dafny. Although there
is no inheritance among classes in Dafny, the traits make it possi-
ble to describe behavior common to several classes and to write
code that abstracts over the particular classes involved. The de-
sign incorporates behavioral specifications for a trait’s methods and
functions, just like for classes in Dafny. The design has been im-
plemented in the Dafny tool.
Categories and Subject Descriptors
F.3.1 [LOGICS AND MEANINGS OF PROGRAMS]: Speci-
fying and Verifying and Reasoning about Programs—Mechanical
verification
General Terms
Languages, Verification
Keywords
Program Verification, Dafny, Traits, Boogie
0. Introduction
Dafny is a verification-aware programming language with a rela-
tively modest set of features to support classes with dynamically
allocated instances [14]. This makes it possible to write and verify
a large variety of programs that deal with pointers and mutate the
state of objects in the heap. In the original design of the language,
there was no inheritance among classes and no dynamic dispatch of
methods. This ruled out the possibility of writing object-oriented
programs where common functionality can be collected in some
superclass and where code that uses the superclass can be poly-
morphic in the types of the instances that it sees at run time.
In this paper, we remove this limitation by introducingtraits into
the Dafny language. A trait is like a class in that it can declare
member fields, functions, and methods, but a trait itself is not in-
stantiable. Instead, a class can be declared to extend one or more
Permission to make digital or hard copies of all or part of this work for
personal or classroom use is granted without fee provided that copies are
not made or distributed for profit or commercial advantage and that copies
bear this notice and the full citation on the first page. To copy otherwise, to
republish, to post on servers or to redistribute to lists, requires prior specific
permission and/or a fee.
Copyright 2001 ACM X-XXXXX-XX-X/XX/XX ...$5.00.
traits, and the instances of the class can be used where such traits
are expected. Object-oriented languages offer a slue of variations of
traits and these go under many different names, including abstract
superclasses (e.g., Eiffel [18]), opaque types (e.g., Modula-3 [19]),
interfaces (e.g., Java [8] and C# [6]), mix-ins (e.g., Racket [7]), and,
yes, traits (e.g., Scala [20]). Since there seems to be no consensus
about what characteristics each of these names implies, we have
somewhat arbitrarily chosen the name “trait”.
A trait in Dafny can include mutable fields. These are inherited
by the classes that extend the trait. Instance functions and methods
declared in the trait can be given a body in either the trait itself or
in classes that extend the trait. In either case, the trait, just like a
class, associates a behavioral specification with each function and
method and these must be respected by the classes that extend the
trait. A trait can also declare static functions and methods, that is,
functions and methods that do not take a receiver parameter. Since
there is no dynamic dispatch to static functions and methods, a trait
must give these a body.
Figure 0shows a program snippet that illustrates some of the
trait features. We will describe these features in more detail in the
paper.
In this paper, we describe the design of traits in Dafny and give
some examples. The design is implemented in the Dafny tool and
can be tried out online at http://rise4fun.com/dafny. The imple-
mentation consists of not just the compiler, but also a static pro-
gram verifier. We show in this paper that our traits can be specified
in the style of dynamic frames [12], which is also the standard id-
iomatic way to specify classes in Dafny. At the end, we discuss
future extensions of features we have left out in the current version
and compare with related work. The contributions of this paper are:
A specification-aware design of traits.
A logical encoding of traits and overriding that is suitable for
a verifier.
Support for trait specifications in the style of dynamic frames.
A language implementation of traits that includes both a com-
piler and a verifier.
Pointing out a specification problem we have not solved re-
garding termination.
1. Trait Design
In Dafny, there are three kinds of members in classes: fields are
mutable instance variables, functions are mathematical functions
trait J {
var x: int
function method Combine(y: int): int
reads this
method Reset()
modifies this
ensures 0 <= x
method CombineAndReset(y: int)returns (z: int)
modifies this
{
z:= Combine(y);
Reset();
}
}
class Cextends J {
var w: int
constructor ()
modifies this
{x,w:= 0,0; }
function method Combine(y: int): int
reads this
{2*x+w+y}
method Reset()
modifies this
ensures x==0&&w==old(x)
{x,w:= 0, x; }
}
Figure 0. The declaration of a trait with a field, a function, and
two methods, as well as a class that extends the trait and supplies
an implementation of the function and method.
that can take parameters and return a result, and methods are im-
perative code procedures that can take any number of in- and out-
parameters. Unless declared static, functions and methods take
an implicit receiver parameter, denoted this. A function may also
depend on the program state, but must use a reads clause to declare
the set of objects on whose state it may depend. A method may read
and write the program state, but must use a modifies clause to de-
clare the set of objects whose state it may update. The body of a
function is an expression (enclosed in curly braces) and the body of
a method is a statement sequence (also enclosed in curly braces).
There are some different declarations for special kinds of func-
tions and methods. For example, a predicate is a function that
returns a boolean and a lemma is a special kind of method (see
the short tutorial notes [15] for some examples). For the sake of
the present paper, these distinctions are not important, except for
constructor declarations, which are special methods that can only
be invoked when an object is allocated.
Like classes, traits can also declare fields, functions, and meth-
ods. Non-instance functions and methods are declared with the
keyword static, just as for classes. However, since a trait itself is
not instantiable, a trait may not declare any constructor. Figure 0
shows a trait Jwith a field x, a function Combine, and two methods,
Reset and CombineAndReset. Instance functions and methods in a
trait may omit the body, so that different classes get to implement
these in their own ways. For example, in Figure 0,Combine and
Reset do not have a body, whereas CombineAndReset does.
A class can be declared to extend one or more traits. By extend-
ing a trait, the class inherits all fields declared in the trait as well
as all functions and methods with given bodies in the trait. For any
body-less function or method in the trait, the class must also de-
trait Automobile {
ghost var Repr: sethobjecti
predicate Valid()
reads this, Repr
ensures Valid() ==> this in Repr
function method Brand(): string
var position: int
method Drive()
requires Valid()
modifies Repr
ensures Valid() && fresh(Repr - old(Repr))
ensures old(position) <= position
}
Figure 1. A trait specified in the general style of dynamic frames.
clare such a function or method. For example, in Figure 0, class C
extends trait Jand declares Combine and Reset with bodies.
A salient feature of functions and methods in Dafny is that they
can have behavioral specifications. For example, they can declare
preconditions (keyword requires), which say under which con-
ditions they are allowed to be invoked. As we alluded to above,
functions can have reads clauses and methods can have modifies
clauses. Both can also have postconditions (keyword ensures),
which say something about what the implementation of the func-
tion or method guarantees. For example, trait Jin Figure 0declares
that method Reset is allowed to modify the state of the receiver ob-
ject (this) but must establish that field xis non-negative upon ter-
mination. The Dafny program verifier makes sure that the program
respects the behavioral specifications.
When a class redeclares a function or method that was body-less
in the trait, the class must also give a behavioral specification. In
other words, the behavioral specification of the function or method
is not inherited from the trait. We settled on this design because
it means that a programmer only needs to look in one place in the
source text to figure out what specification governs a particular call.
The redeclaration will be subject to an override check, where the
program verifier checks that the specification in the class is at least
as strong as the one given in the trait. Note, for example, how class
Cgives method Reset a stronger postcondition.
2. Traits and Dynamic Frames
Specifying programs that deal with mutable, dynamic data struc-
tures can be difficult. A variety of specification idioms and logics
have been developed (see [9] for a survey). For this purpose, Dafny
uses dynamic frames [12]. From the language perspective, Dafny
supports dynamic-frame specifications simply by supporting reads
and modifies clauses that can list sets of objects. The basic idiom
is for the program to maintain a set Repr of all the objects in the
data structure and a predicate Valid that gives the invariant on that
data structure. For more details of this idiom, see [10,13].
In Figure 1, we show a trait Automobile that is equipped with
idiomatic dynamic-frame specifications. The field Repr is used to
hold the set of all objects that represent an automobile; it is declared
with ghost to say that the field is for specifications only and will
be erased by the compiler. Predicate Valid() holds whenever the
automobile’s representation is in a consistent state. The idea is
that constructors have Valid() as a postcondition and that other
methods have Valid() as a pre- and postcondition.
In our trait, the body of Valid() is elided so that classes that
extend the trait can provide their own specific definitions. However,
method Main() {
var auto: Automobile;
auto := new Fiat(0);
WorkIt(auto);
auto := new Volvo();
WorkIt(auto);
auto := new Catacar();
WorkIt(auto);
}
method WorkIt(auto: Automobile)
requires auto 6=null && auto.Valid()
modifies auto.Repr
{
auto.Drive();
auto.Drive();
assert old(auto.position) <= auto.position;
print auto.Brand(), ": ", auto.position, "\n";
auto.position := 18;
}
Figure 2. A main program that allocates instances of two specific
automobile classes. Method WorkIt is able to operate on any object
whose class extends the Automobile trait.
for our example, we have chosen to say that every class extending
Automobile must define validity to imply this in Repr.
The specification of method Drive() has a postcondition that
says the automobile must not go backwards. The rest of this method’s
specification contains the usual idiomatic parts of dynamic-frame
specifications in Dafny. The postcondition that mentions fresh
says that Drive() is allowed to add newly allocated objects to the
automobile’s representation set (see [10] for more motivation and
details).
In Figure 2, we show a client method that allocates several auto-
mobile instances. More precisely, it allocates instances of classes
that extend the trait Automobile. The constructors of these classes
establish validity (Figures 3,4, and 5) and therefore the precon-
dition of method WorkIt is met. Note that the auto parameter of
WorkIt has type Automobile, so WorkIt operates on any automo-
bile.
Some details in method WorkIt are noteworthy. First, by the
postcondition of Drive() in trait Automobile, the assertion in WorkIt
after the two calls to Drive() can be verified to hold. Second, the
method updates the field position directly. This is possible, since
all Automobile objects have the field. Third, to update auto.position,
the verifier will check that auto is covered by the modifies clause
of WorkIt. This check comes down to verifying that auto is in
Repr, which follows from the postcondition of Valid() that we in-
cluded in Figure 1. Fourth, after the update to auto.position, it is
not possible to prove, for any Automobile, that Valid() still holds.
This is because Valid(), according to its reads clause, is allowed
to depend on the value of the position field, and some class that
extends Automobile may never be valid when position == 18.
Hence, if WorkIt had a postcondition auto.Valid(), then the veri-
fier would complain (but note that the automobile does remain valid
after calling Drive(), as can be witnessed by the second call to
Drive()).
Finally, we show in Figures 3,4, and 5classes Fiat,Volvo, and
Catacar, each of which extends trait Automobile. Each class pro-
vides its own validity condition (that is, class invariant); for exam-
ple, class Fiat places an upper bound on position and class Volvo
says its position is a multiple of 10. Note how the specifications
class Fiat extends Automobile {
predicate Valid()
reads this, Repr
ensures Valid() ==> this in Repr
{
this in Repr && null !in Repr &&
position <= 100
}
constructor (pos: int)
requires pos <= 100
modifies this
ensures Valid() && fresh(Repr - {this})
ensures position == pos
{
position, Repr := pos, {this};
}
function method Brand(): string {
"Fiat"
}
method Drive()
requires Valid()
modifies Repr
ensures Valid() && fresh(Repr - old(Repr))
ensures old(position) <= position
{
position := if position < 97
then position + 3else 100;
}
}
Figure 3. A class that extends trait Automobile. The constructor
allows the initial position to be set, and the class invariant says the
position will never exceed 100.
of the methods overridden in the trait are repeated or, in the case
of Volvo.Drive(), strengthened. Lastly, note a rather subtle point
in the implementation of Fiat.Drive(). If position is initially
not less than 97, then the method sets position to 100. It may
seem from this that the method may fail to live up to the postcon-
dition that says position is not decreased. However, because of
the method precondition Valid() and the definition of Valid() in
class Fiat, the verifier is able to see that setting position to 100
does indeed satisfy the postcondition.
3. Logical Encoding
Our logical encoding for methods is the usual one. A method im-
plementation is checked to satisfy the specification given to the
method. At a call site, the static type of the receiver is used to
retrieve the specification governing the call. For example, with
reference to Figure 0, a call to e.Reset() uses the postcondition
0<= x if the static type of expression eis Jand uses the more
specific postcondition x==0&& w == old(x) if the static type
of eis C. At run time, a call e.Reset() dynamically dispatches to
the implementation in the class corresponding to the allocated type
of e. Since the override check verifies that the specification in the
class is at least as strong as the one in the trait, soundness follows
from the Liskov-Leavens Substitution Principle [17].
For functions, Dafny generates axioms [13]. For an instance
function F(x: X) in a class C, the logical encoding uses a func-
tion with the receiver argument explicated. If the body of Fis some
class Volvo extends Automobile {
var odometer: Odometer
predicate Valid()
reads this, Repr
ensures Valid() ==> this in Repr
{
this in Repr && null !in Repr &&
odometer in Repr &&
position % 10 == 0&&
odometer.value == position
}
constructor ()
modifies this
ensures Valid() && fresh(Repr - {this})
{
position, Repr := 0, {this};
odometer := new Odometer();
Repr := Repr + {odometer};
}
function method Brand(): string {
"Volvo"
}
method Drive()
requires Valid()
modifies Repr
ensures Valid() && fresh(Repr - old(Repr))
ensures old(position) < position
{
position := position + 10;
odometer.Advance(10);
}
}
class Odometer {
var value: int
constructor ()
modifies this
ensures value == 0
{
value := 0;
}
method Advance(d: int)
requires 0 <= d
modifies this
ensures value == old(value) + d
{
value := value + d;
}
}
Figure 4. Another class that extends trait Automobile. This au-
tomobile makes use of a simple auxiliary Odometer object whose
state is updated by the Drive method. The postcondition of the
Drive method promises a definite move forward. The class invari-
ant says the odometer reading is kept in synch with the automo-
bile’s position, which is always a multiple of 10.
class Catacar extends Automobile {
var f: Fiat
var v: Volvo
predicate Valid()
reads this, Repr
ensures Valid() ==> this in Repr
{
this in Repr && null !in Repr &&
fin Repr && this !in f.Repr &&
f.Repr <= Repr && f.Valid() &&
vin Repr && this !in v.Repr &&
v.Repr <= Repr && v.Valid() &&
f.Repr !! v.Repr &&
position == f.position + v.position
}
constructor ()
modifies this
ensures Valid() && fresh(Repr - {this})
{
Repr := {this};
f:= new Fiat(0); Repr := Repr + f.Repr;
v:= new Volvo(); Repr := Repr + v.Repr;
position := v.position;
}
function method Brand(): string {
"Catacar"
}
method Drive()
requires Valid()
modifies Repr
ensures Valid() && fresh(Repr - old(Repr))
ensures old(position) <= position
{
f:= new Fiat(f.position);
f.Drive(); v.Drive();
Repr := Repr + v.Repr + f.Repr;
position := f.position + v.position;
}
}
Figure 5. A more advanced extension of the Automobile trait,
illustrating how aggregate objects are composed in the dynamic-
frame style of specifications. Using Dafny’s sets-are-disjoint oper-
ator, !!, the class invariant says that the representations of the two
component objects are separated. In this contrived example, the
Drive method abandons the old Fiat, allocates a new one at the
same position, and drives it.
expression E, then the axiom has the basic shape:
o:ref, x :X
o6=null dtype(o) = C
=F(o, x) = E
(0)
where dtype is a logical function that returns the allocated type
(that is, the dynamic type) of object oand Cis a term denoting the
class C.
If function Fis declared in a trait but given a body in a class, then
we produce the same axiom. However, if the body is given in the
trait, then there is no single value for dtype(o)that can be used in
the antecedent of the axiom. Instead, for every trait J, we introduce
into the logical encoding a prediate extends_Jand represent the
axiom as:
o:ref, x :X
o6=null extends_J(dtype(o))
=F(o, x) = E
(1)
Furthermore, for every class Cthat extends J, we generate an ax-
iom:
extends_J(C)(2)
The Dafny verifier, and thus our logical encoding, uses Boogie [1]
and proof obligations are discharged using an SMT solver.
4. Compilation
The standard Dafny compiler compiles to .NET bytecode (MSIL).
Internally, the compilation is done by constructing a C# program
and then calling on the C# compiler to generate MSIL. Conve-
niently, C# interface declarations are close to Dafny traits. But not
quite. In this section, we describe the differences.
Unlike a Dafny trait, a C# interface cannot contain field declara-
tions. Instead, we compile fields in traits into setter/getter methods
in the C# interface. In each class that extends the trait, we intro-
duce a field and arrange to override the setter/getter methods to
use this field. C# makes this convenient for us through its proper-
ties, which are setter/getter method pairs with a field-like syntax for
client code.
Despite the fact that MSIL allows some amount of code in inter-
faces, C# does not. Therefore, for instance functions and methods
with a body in a Dafny trait, we omit the body when generating a
method into the C# interface and we include a copy of the com-
piled code in every class that extends the trait. To compile static
functions and methods in a Dafny trait J, we generate not just an
interface Jin the C# code, but also a class _Companion_J. The static
members and then compiled into the companion class and calls to
these static members are compiled into calls to these methods in
the companion class.
5. Future Work
We have omitted some useful features in our current implementa-
tion of traits in Dafny. In particular, traits and the classes that im-
plement them are currently not allowed to take type parameters, a
feature we intend to add soon. Also, traits currently are not allowed
to extend other traits. To add this feature, the language design will
face the diamond problem, where a class can be an extension of a
trait in more than one way. We are optimistic that verification will
not add more issues than compilation does for this problem.
Dafny is currently undergoing a redesign of its type system to
provide a more uniform treatment of subset types (like what the
built-in nat is to the built-in int) and superset types (like what traits
are to classes and what the built-in type object is to all classes).
The hope is that this redesign will also provide a generalized ap-
proach to type inference, including the type inference that is done
for traits.
There is an issue in our design that we have not discussed so far,
namely the issue of termination. Dafny verifies that recursion and
loops terminate, except loops and methods (not functions) that are
specially marked as being possibly divergent. Within each mod-
ule, Dafny constructs a call graph, and any calls within the same
strongly connected component of that call graph are checked to de-
crease some programmer-supplied rank function [14]. To continue
this approach for traits, we add a call-graph edge from body-less
functions and methods in a trait to the corresponding functions and
methods in classes that implement the trait. These edges represent
the dynamic dispatch that takes place.
The import relation between modules in Dafny is acyclic, so calls
into other modules have in the past had the property that they can-
not be part of any recursive cycle. Traits change this. Consider the
desirable case where a trait Jcontaining a method or function Mis
declared in a library module. Consider, further, a method or func-
tion P(j: J) in that library module. If a client module declares a
class Cthat extends J, then a call to P(c), passing an instance cof
class C, could form another recursive cycle (in particular, if Pcalls
j.M).
One modular solution (that is, a solution that allows verification
module by module) to this specification and verification problem
is to require programmer-supplied rank functions across module
boundaries. This can be achieved in an approach that uses multisets
of methods to represent ordinal numbers [11]. We would like to
explore how such an approach can be incorporated into Dafny with
a minimal impact on specification overhead. In the meantime, in
our current design, we take the simple but draconian measure of
disallowing a class to extend a trait that is declared in a different
module. (A tiny step better would be to allow the class to extend
the trait if the trait has no functions and all methods are marked as
being possibly divergent.)
6. Related Work
A salient feature of our traits is that functions and methods have
behavioral specifications, which the Dafny verifier checks. Object-
oriented languages with trait-like features that also support behav-
ioral specifications include Eiffel, Java (with some form of specifi-
cations), Spec#, and Racket. Racket provides only dynamic check-
ing of specifications, whereas the others provide both dynamic and
static checking and can verify adapted versions of the example pro-
grams in this paper.
In Spec#, the specifications of methods in an interface or class
Care inherited by classes that extend C[0]. Spec# has a specifica-
tion methodology that deals with subclasses and method overrides
(see [16]). The overall specification language in Spec# is other-
wise rather limited, including the limited support for user-defined
functions.
Eiffel supports multiple-inheritance classes, which are verified
by AutoProof [25]. To specify and reason about inter-object behav-
ior, AutoProof uses the semantic collaboration methodology [22],
which is more flexible than the Spec# methodology.
Java interfaces and classes can be specified using the Java Mod-
eling Language (JML) [3] and statically checked using tools like
ESC/Java and OpenJML [4]. The design of these tools trades sound
verification for ease of use, in particular with regard to framing (that
is, modifies clauses). The KeY system, based on dynamic logic,
gives a full verifier for Java/JML [2].
Java interfaces and classes can also be specified using separation
logic in the verifier VeriFast [24]. More precisely, VeriFast uses ab-
stract predicate families [21], which allow each subclass to provide
its own specifications for overridden methods. Our logical encod-
ing of traits was inspired by abstract predicate families, but since
Dafny does not support subclasses, our encoding is but a special
case of abstract predicate families.
The vision behind traits is to provide independent and compos-
able units of behavior [23]. Damiani et al. give a proof system that
focuses on the flexible composition of traits [5]. The proof system
is modular in that each trait is verified separately from its uses, and
it also allows some trait specifications to be added incrementally.
However, from what we can tell, the proof system lacks features
for framing that are necessary to deal with aggregate objects.
7. Conclusions
We have presented a design for traits in Dafny. The design allows
specifications to be written in the style of dynamic frames, as we
have demonstrated with an example. We have implemented our de-
sign, not just in the compiler but also in the program verifier. The
most conspicuous issue we have left for future work involves figur-
ing out how best to specify termination in the presence of dynamic
dispatch, while keeping the benefits of modular verification.
Acknowledgments
We are grateful to the expert reviewers for their careful readings and
good suggestions, and to Nadia Polikarpova for help with examples
in AutoProof.
References
[0] Mike Barnett, K. Rustan M. Leino, and Wolfram Schulte.
The Spec# programming system: An overview. In CASSIS
2004, volume 3362 of LNAI, pages 49–69. Springer, 2005.
[1] Mike Barnett, Bor-Yuh Evan Chang, Robert DeLine, Bart
Jacobs, and K. Rustan M. Leino. Boogie: A modular
reusable verifier for object-oriented programs. In FMCO
2005, volume 4111 of LNAI, pages 364–387. Springer,
September 2006.
[2] Bernhard Beckert, Reiner Hähnle, and Peter H. Schmitt.
Verification of Object-Oriented Software: The KeY
Approach, volume 4334. Springer, 2007.
[3] Lilian Burdy, Yoonsik Cheon, David R. Cok, Michael D.
Ernst, Joseph R. Kiniry, Gary T. Leavens, K. Rustan M.
Leino, and Erik Poll. An overview of JML tools and
applications. International Journal on Software Tools for
Technology Transfer, 7 (3): 212–232, June 2005.
[4] David R. Cok. Improved usability and performance of SMT
solvers for debugging specifications. Software Tools for
Technology Transfer (STTT), 12 (6): 467–481, November
2010.
[5] Ferruccio Damiani, Johan Dovland, Einar Broch Johnsen,
and Ina Schaefer. Verifying traits: an incremental proof
system for fine-grained reuse. Formal Aspects of Computing,
26 (4): 761–793, July 2014.
[6] C# Language Specification. ECMA International, June 2006.
[7] Matthew Flatt, Robert Bruce Findler, and Matthias Felleisen.
Scheme with classes, mixins, and traits. In APLAS 2006,
volume 4279 of LNAI, pages 270–289. Springer, November
2006.
[8] James Gosling, Bill Joy, and Guy Steele. The Java Language
Specification. Addison-Wesley, 1996.
[9] John Hatcliff, Gary T. Leavens, K. Rustan M. Leino, Peter
Müller, and Matthew Parkinson. Behavioral interface
specification languages. ACM Computing Surveys, 44 (3),
June 2012. Article 16.
[10] Luke Herbert, K. Rustan M. Leino, and Jose Quaresma.
Using Dafny, an automatic program verifier. In LASER
Summer School 2011, volume 7682 of LNAI, pages 156–181.
Springer, 2012.
[11] Bart Jacobs, Dragan Bosnacki, and Ruurd Kuiper. Modular
termination verification. In ECOOP 2015, 2015.
[12] Ioannis T. Kassios. Dynamic frames: Support for framing,
dependencies and sharing without restrictions. In FM 2006,
volume 4085 of LNAI, pages 268–283. Springer, August
2006.
[13] K. Rustan M. Leino. Specification and verification of
object-oriented software. In Engineering Methods and Tools
for Software Safety and Security, volume 22 ofNATO
Science for Peace and Security Series D: Information and
Communication Security, pages 231–266. IOS Press, 2009.
Summer School Marktoberdorf 2008 lecture notes.
[14] K. Rustan M. Leino. Dafny: An automatic program verifier
for functional correctness. In LPAR-16, volume 6355 of
LNAI, pages 348–370. Springer, April 2010.
[15] K. Rustan M. Leino. Developing verified programs with
Dafny. In ICSE ’13, pages 1488–1490. IEEE/ACM, May
2013.
[16] K. Rustan M. Leino and Peter Müller. Using the Spec#
language, methodology, and tools to write bug-free
programs. In LASER Summer School 2007/2008, volume
6029 of LNAI, pages 91–139. Springer, 2010.
[17] Barbara Liskov and Jeannette M. Wing. A behavioral notion
of subtyping. ACM Transactions on Programming
Languages and Systems, 16 (6), 1994.
[18] Bertrand Meyer. Object-oriented Software Construction.
Series in Computer Science. Prentice-Hall International,
1988.
[19] Greg Nelson, editor. Systems Programming with Modula-3.
Series in Innovative Technology. Prentice-Hall, 1991.
[20] Martin Odersky, Philippe Altherr, Vincent Cremet, Iulian
Dragos, Gilles Dubochet, Burak Emir, Sean McDirmid,
Stéphane Micheloud, Nikolay Mihaylov, Michel Schinz, Erik
Stenman, Lex Spoon, and Matthias Zenger. An overview of
the Scala programming language. Technical Report
LAMP-REPORT-2006-001, EPFL, 2006.
[21] Matthew J. Parkinson and Gavin M. Bierman. Separation
logic, abstraction and inheritance. In POPL 2008, pages
75–86. ACM, January 2008.
[22] Nadia Polikarpova, Julian Tschannen, Carlo A. Furia, and
Bertrand Meyer. Flexible invariants through semantic
collaboration. In FM 2014, volume 8442 of LNAI, pages
514–530. Springer, May 2014.
[23] Nathanael Schärli, Stéphane Ducasse, Oscar Nierstrasz, and
Andrew P. Black. Traits: Composable units of behaviour. In
ECOOP 2003, volume 2743 of LNAI, pages 248–274.
Springer, July 2003.
[24] Jan Smans, Bart Jacobs, and Frank Piessens. VeriFast for
Java: A tutorial. In Aliasing in Object-Oriented
Programming, volume 7850 of LNAI, pages 407–442.
Springer, 2013.
[25] Julian Tschannen, Carlo A. Furia, Martin Nordio, and Nadia
Polikarpova. AutoProof: Auto-active functional verification
of object-oriented programs. In TACAS 2015, volume 9035
of LNAI, pages 566–580. Springer, April 2015.
... refinement of A0, or in fact any other module that structurally is like A0 or a refinement thereof. 2 An "as" import in a module can be tightened up in a refinement module, as illustrated by the following example: ...
... Note that the features we discuss in this paper do not give rise to dynamic dispatch (like the traits feature in Dafny does [2]). There is no relation between refinement modules that can be exploited dynamically at run time. ...
Article
Full-text available
Algorithmic and data refinement are well studied topics that provide a mathematically rigorous approach to gradually introducing details in the implementation of software. Program refinements are performed in the context of some programming language, but mainstream languages lack features for recording the sequence of refinement steps in the program text. To experiment with the combination of refinement, automated verification, and language design, refinement features have been added to the verification-aware programming language Dafny. This paper describes those features and reflects on some initial usage thereof.
... In contrast, Whiley aims to generate code which is, for example, suitable for embedded systems [134,156]. Dafny is an imperative language with simple support for objects and classes without inheritance and, more recently, traits [1]. Like Whiley, Dafny employs unbound arithmetic and distinguishes between pure and impure functions. ...
Article
Full-text available
The quest to develop increasingly sophisticated verification systems continues unabated. Tools such as Dafny, Spec#, ESC/Java, SPARK Ada and Whiley attempt to seamlessly integrate specification and verification into a programming language, in a similar way to type checking. A common integration approach is to generate verification conditions that are handed off to an automated theorem prover. This provides a nice separation of concerns and allows different theorem provers to be used interchangeably. However, generating verification conditions is still a difficult undertaking and the use of more “high-level” intermediate verification languages has become commonplace. In particular, Boogie provides a widely used and understood intermediate verification language. A common difficulty is the potential for an impedance mismatch between the source language and the intermediate verification language. In this paper, we explore the use of Boogie as an intermediate verification language for verifying programs in Whiley. This is noteworthy because the Whiley language has (amongst other things) a rich type system with considerable potential for an impedance mismatch. We provide a comprehensive account of translating Whiley to Boogie which demonstrates that it is possible to model most aspects of the Whiley language. Key challenges posed by the Whiley language included: the encoding of Whiley’s expressive type system and support for flow typing and generics; the implicit assumption that expressions in specifications are well defined; the ability to invoke methods from within expressions; the ability to return multiple values from a function or method; the presence of unrestricted lambda functions; and the limited syntax for framing. We demonstrate that the resulting verification tool can verify significantly more programs than the native Whiley verifier which was custom-built for Whiley verification. Furthermore, our work provides evidence that Boogie is (for the most part) sufficiently general to act as an intermediate language for a wide range of source languages.
... The Dafny language and verification system [29] uses functions to define abstraction predicates which correspond to our model methods. Until recently the Dafny language did not support subtyping [1], and it does not support two-state predicates. In [10], the authors abstract from contract components by predicates to decouple deductive reasoning about programs from the applicability check of contracts. ...
Chapter
Full-text available
Dynamic method dispatch is a core feature of object-oriented programming by which the executed implementation for a polymorphic method is only chosen at runtime. In this paper, we present a specification and verification methodology which extends the concept of dynamic dispatch to design-by-contract specifications. The formal specification language JML has only rudimentary means for polymorphic abstraction in expressions. We promote these to fully flexible specification-only query methods called model methods that can, like ordinary methods, be overridden to give specifications a new semantics in subclasses in a transparent and modular fashion. Moreover, we allow them to refer to more than one program state which give us the possibility to fully abstract and encapsulate two-state specification contexts, i.e., history constraints and method postconditions. Finally, we provide an elegant and flexible mechanism to specify restrictions on specifications in subtypes. Thus behavioural subtyping can be enforced, yet it still allows for other specification paradigms. We provide the semantics for model methods by giving a translation into a first order logic and according proof obligations. We fully implemented this framework in the KeY program verifier and successfully verified relevant examples. We have also implemented an extension to KeY to support permission-based verification of concurrent Java programs. In this context model methods provide a modular specification method to treat code synchronisation through API methods.
Article
Full-text available
Unlimited by the state and space, the formal verification technology based on mechanized theorem proof is an important method to ensure software correctness and avoid serious loss from potential software bugs. LLRB (left-leaning red-black trees) is a variant of binary search trees, and its structure has an additional left-leaning constraint over the traditional red-black trees. During verification, conventional proof strategies cannot be employed, which requires more manual intervention and effort. Thus, the LLRB correctness verification is widely acknowledged as a challenging problem. To this end, based on the Isabelle verification framework for the binary search tree algorithm, this study refines the additional property part of the framework and provides a concrete verification scheme. The LLRB insertion and deletion operations are functionally modeled in Isabelle, with modular treatment of the LLRB invariants. Subsequently, the function correctness is verified. This is the first mechanized verification of functional LLRB insertion and deletion algorithms in Isabelle. Compared to the current Dafny verification of the LLRB algorithm, the theorem number is reduced from 158 to 84, and it is unnecessary for constructing intermediate assertions, which alleviates the verification burden. Meanwhile, this study provides references for
Conference Paper
Full-text available
Modular reasoning about class invariants is challenging in the presence of dependencies among collaborating objects that need to maintain global consistency. This paper presents semantic collaboration: a novel methodology to specify and reason about class invariants of sequential object-oriented programs, which models dependencies between collaborating objects by semantic means. Combined with a simple ownership mechanism and useful default schemes, semantic collaboration achieves the flexibility necessary to reason about complicated inter-object dependencies but requires limited annotation burden when applied to standard specification patterns. The methodology is implemented in AutoProof, our program verifier for the Eiffel programming language (but it is applicable to any language supporting some form of representation invariants). An evaluation on several challenge problems proposed in the literature demonstrates that it can handle a variety of idiomatic collaboration patterns, and is more widely applicable than the existing invariant methodologies.
Article
Full-text available
Traits have been proposed as a more flexible mechanism for code structuring in object-oriented programming than class inheritance, for achieving fine-grained code reuse. A trait originally developed for one purpose can be modified and reused in a completely different context. Formalizations of traits have been extensively studied, and implementations of traits have started to appear in programming languages. However, work on formally establishing properties of trait-based programs has so far mostly concentrated on type systems. This paper proposes the first deductive proof system for a trait-based object-oriented language. If a specification for a trait can be given a priori, covering all actual usage of that trait, our proof system is modular as each trait is analyzed only once. In order to reflect the flexible reuse potential of traits, our proof system additionally allows new specifications to be added to a trait in an incremental way which does not violate established proofs. We formalize and show the soundness of the proof system.
Article
Reasoning about programs is a fundamental skill that every software engineer needs. This tutorial provides participants an opportunity to get hands-on experience with Dafny, a tool that can help develop this skill. Dafny is a programming language and state-of-the-art program verifier. The language is type-safe and sequential, and it includes common imperative features, dynamic object allocation, and inductive datatypes. It also includes specification constructs like pre- and postconditions, which let a programmer record the intended behavior of the program along with the executable code that is supposed to cause that behavior. Because the Dafny verifier runs continuously in the background, the consistency of a program and its specifications is always enforced. In this tutorial, I give a taste of how to use Dafny in program development. This includes an overview of Dafny, basics of writing specifications, how to debug verification attempts, and how to formulate and prove lemmas. Dafny has been used to verify a number of challenging algorithms, including Schorr-Waite graph marking, Floyd's ``tortoise and hare'' cycle-detection algorithm, and snapshotable trees with iterators. Dafny is also being used in teaching, with over 100,000 program-verification attempts submitted to the online version of the tool. Dafny was a popular choice in the VSTTE 2012 program verification competition, where two of the Dafny teams were among the competition's 6 medalists. Its open-source implementation has also been used as a foundation for other verification tools. More information is found from the Dafny project page, http://research.microsoft.com/dafny and in the references below. Binary downloads and sources are available from http://dafny.codeplex.com. The tool can also be run on the web at http://rise4fun.com/dafny, where there is an online version of the tutorial.
Chapter
These lecture notes present Dafny, an automated program verification system that is based on the concept of dynamic frames and is capable of producing .NET executables. These notes overview the basic design, Dafny’s history, and summarizes the environment configuration. The key language constructs, and various system limits, are illustrated through the development of a simple Dafny program. Further examples, linked to online demonstrations, illustrate Dafny’s approach to loop invariants, termination, data abstraction, and heap-related specifications.
Conference Paper
Dafny [2] is a programming language and program verifier. The language is type-safe and sequential, and it includes common imperative features, dynamic object allocation, and inductive datatypes. It also includes specification constructs like pre- and postconditions, which let a programmer record the intended behavior of the program along with the executable code that is supposed to cause that behavior. Because the Dafny verifier runs continuously in the background, the consistency of a program and its specifications is always enforced. Dafny has been used to verify a number of challenging algorithms, including Schorr-Waite graph marking, Floyd’s “tortoise and hare” cycle-detection algorithm, and snapshotable trees with iterators. Dafny is also being used in teaching and it was a popular choice in the VSTTE 2012 program verification competition. Its open-source implementation has also been used as a foundation for other verification tools. In this tutorial, I will give a taste of how to use Dafny in program development. This will include an overview of Dafny, basics of writing specifications, how to debug verification attempts, how to formulate and prove lemmas, and some newer features for staged program development.
Conference Paper
Auto-active verifiers provide a level of automation intermediate between fully automatic and interactive: users supply code with annotations as input while benefiting from a high level of automation in the back-end. This paper presents AutoProof, a state-of-the-art auto-active verifier for object-oriented sequential programs with complex functional specifications. AutoProof fully supports advanced object-oriented features and a powerful methodology for framing and class invariants, which make it applicable in practice to idiomatic object-oriented patterns. The paper focuses on describing AutoProof's interface, design, and implementation features, and demonstrates AutoProof's performance on a rich collection of benchmark problems. The results attest AutoProof's competitiveness among tools in its league on cutting-edge functional verification of object-oriented programs.
Article
In object-oriented programs built in layers, an object at a higher level of abstraction is implemented by objects at lower levels of abstraction. It is usually crucial to correctness that a lower-level object not be shared among several higher-level objects. This paper unveils some difficulties in writing procedure specifications strong enough to guarantee that a lower-level object can be used in the implementation of another object at a higher level of abstraction. To overcome these difficulties, the paper presents virginity, a convenient way of specifying that an object is not globally reachable and thus can safely be used in the implementation of a higher-level abstraction.
Conference Paper
VeriFast is a separation logic-based program verifier for Java. This tutorial introduces the verifier's features step by step.
Conference Paper
Dafny is a programming language and program verifier. The language includes specification constructs and the verifier checks that the program lives up to its specifications. These tutorial notes give some Dafny programs used as examples in the tutorial.