Dafny

Last updated
Dafny
Dafny logo.jpg
Paradigm Imperative, functional
Designed by K. Rustan M. Leino
Developer Microsoft Research, AWS [ citation needed ]
First appeared2009;14 years ago (2009)
Stable release
3.7.2 / July 14, 2022;8 months ago (2022-07-14)
Typing discipline Static, strong, safe
License MIT License
Filename extensions .dfy
Website dafny.org

Dafny is an imperative and functional compiled language that compiles to other programming languages, such as C#, Java, JavaScript, Go and Python. It supports formal specification through preconditions, postconditions, loop invariants, loop variants, termination specifications and read/write framing specifications. The language combines ideas from the functional and imperative paradigms; it includes support for object-oriented programming. Features include generic classes, dynamic allocation, inductive datatypes and a variation of separation logic known as implicit dynamic frames [1] for reasoning about side effects. [2] Dafny was created by Rustan Leino at Microsoft Research after his previous work on developing ESC/Modula-3, ESC/Java, and Spec#.

Contents

Dafny is widely used in teaching[ citation needed ] because it provides a simple, integrated introduction to formal specification and verification; it is regularly featured in software verification competitions (e.g. VSTTE'08, [3] VSCOMP'10, [4] COST'11, [5] and VerifyThis'12 [6] ).

Dafny was designed as a verification-aware programming language, requiring verification along with code development. It thus fits the "Correct by Construction" software development paradigm. Verification proofs are supported by a mathematical toolbox that includes mathematical integers and reals, bit-vectors, sequences, sets, multisets, infinite sequences and sets, induction, co-induction, and calculational proofs. Verification obligations are discharged automatically, given sufficient specification. Dafny uses some program analysis to infer many specification assertions, reducing the burden on the user of writing specifications. The general proof framework is that of Hoare logic.

Dafny builds on the Boogie intermediate language which uses the Z3 automated theorem prover for discharging proof obligations. [7] [8]

Data types

Dafny provides methods for implementation which may have side-effects and functions for use in specification which are pure. [9] Methods consist of sequences of statements following a familiar imperative style whilst, in contrast, the body of a function is simply an expression. Any side-effecting statements in a method (e.g. assigning an element of an array parameter) must be accounted for by noting which parameters can be mutated, using the modifies clause. Dafny also provides a range of immutable collection types including: sequences (e.g. seq<int>), sets (e.g. set<int>), maps (map<int,int>), tuples, inductive datatypes and mutable arrays (e.g. array<int>).

Imperative features

The following illustrates many of the features in Dafny, including the use of preconditions, postconditions, loop invariants and loop variants.

methodmax(arr:array<int>)returns(max:int)// Array must have at least one elementrequiresarr.Length>0// Return cannot be smaller than any element in arrayensuresforallj:int::j>=0&&j<arr.Length==>max>=arr[j]// Return must match some element in arrayensuresexistsj:int::j>=0&&j<arr.Length&&max==arr[j]{max:=arr[0];vari:int:=1;//while(i<arr.Length)// Index at most arr.Length (needed to show i==arr.Length after loop)invarianti<=arr.Length// No element seen so far larger than maxinvariantforallj:int::j>=0&&j<i==>max>=arr[j]// Some element seen so far matches maxinvariantexistsj:int::j>=0&&j<i&&max==arr[j]// arr.Length - i decreases at every step and is lower-bounded by 0decreasesarr.Length-i{// Update max if larger element encounteredif(arr[i]>max){max:=arr[i];}// Continue through arrayi:=i+1;}}

This example computes the maximum element of an array. The method's precondition and postcondition are given with the requires and ensures clauses (respectively). Likewise, the loop invariant and loop variant are given through the invariant and decreases clauses (respectively).

Loop invariants

The treatment of loop invariants in Dafny differs from traditional Hoare logic. Variables mutated in a loop are treated such that (most) information known about them prior to the loop is discarded. Information required to prove properties of such variables must be expressed explicitly in the loop invariant. In contrast, variables not mutated in the loop retain all information known about them beforehand. The following example illustrates using loops:

methodsumAndZero(arr:array<int>)returns(sum:nat)requiresforalli::0<=i<arr.Length==>arr[i]>=0modifiesarr{vari:int:=0;sum:=0;//while(i<arr.Length){sum:=sum+arr[i];arr[i]:=arr[i];i:=i+1;}}

This fails verification because Dafny cannot establish that (sum + arr[i]) >= 0 holds at the assignment. From the precondition, intuitively, forall i :: 0 <= i < arr.Length ==> arr[i] >= 0 holds in the loop since arr[i] := arr[i]; is a NOP. However, this assignment causes Dafny to treat arr as a mutable variable and drop information known about it from before the loop. To verify this program in Dafny we can either (a) remove the redundant assignment arr[i] := arr[i];; or (b) add the loop invariant invariant forall i :: 0 <= i < arr.Length ==> arr[i] >= 0

Dafny additionally employs limited static analysis to infer simple loop invariants where possible. In the example above, it would seem that the loop invariant invariant i >= 0 is also required as variable i is mutated within the loop. Whilst the underlying logic does require such an invariant, Dafny automatically infers this and, hence, it can be omitted at the source level.

Proof features

Dafny includes features which further support its use as a proof assistant. Although proofs of simple properties within a function or method (as shown above) are not unusual for tools of this nature, Dafny also allows the proof of properties between one function and another. As is common for a proof assistant, such proofs are often inductive in nature. Dafny is perhaps unusual in employing method invocation as a mechanism for applying the inductive hypothesis. The following illustrates:

datatypeList=Nil|Link(data:int,next:List)functionsum(l:List):int{matchlcaseNil=>0caseLink(d,n)=>d+sum(n)}predicateisNatList(l:List){matchlcaseNil=>truecaseLink(d,n)=>d>=0&&isNatList(n)}lemmaNatSumLemma(l:List,n:int)requiresisNatList(l)&&n==sum(l)ensuresn>=0{matchlcaseNil=>// Discharged AutomaticallycaseLink(data,next)=>{// Apply Inductive HypothesisNatSumLemma(next,sum(next));// Check what known by Dafnyassertdata>=0;}}

Here, NatSumLemma proves a useful property betweensum() and isNatList() (i.e. that isNatList(l) ==> (sum(l) >= 0)). The use of a ghost method for encoding lemmas and theorems is standard in Dafny with recursion employed for induction (typically, structural induction). Case analysis is performed using match statements and non-inductive cases are often discharged automatically. The verifier must also have complete access to the contents of a function or predicate in order to unroll them as necessary. This has implications when used in conjunction with access modifiers. Specifically, hiding the contents of a function using the protected modifier can limit what properties can be established about it.

See also

Related Research Articles

<span class="mw-page-title-main">Mathematical induction</span> Form of mathematical proof

Mathematical induction is a method for proving that a statement is true for every natural number , that is, that the infinitely many cases   all hold. Informal metaphors help to explain this technique, such as falling dominoes or climbing a ladder:

Mathematical induction proves that we can climb as high as we like on a ladder, by proving that we can climb onto the bottom rung and that from each rung we can climb up to the next one.

In computer science, selection sort is an in-place comparison sorting algorithm. It has an O(n2) time complexity, which makes it inefficient on large lists, and generally performs worse than the similar insertion sort. Selection sort is noted for its simplicity and has performance advantages over more complicated algorithms in certain situations, particularly where auxiliary memory is limited.

The Vienna Development Method (VDM) is one of the longest-established formal methods for the development of computer-based systems. Originating in work done at the IBM Laboratory Vienna in the 1970s, it has grown to include a group of techniques and tools based on a formal specification language—the VDM Specification Language (VDM-SL). It has an extended form, VDM++, which supports the modeling of object-oriented and concurrent systems. Support for VDM includes commercial and academic tools for analyzing models, including support for testing and proving properties of models and generating program code from validated VDM models. There is a history of industrial usage of VDM and its tools and a growing body of research in the formalism has led to notable contributions to the engineering of critical systems, compilers, concurrent systems and in logic for computer science.

In computer science, formal methods are mathematically rigorous techniques for the specification, development, analysis, and verification of software and hardware systems. The use of formal methods for software and hardware design is motivated by the expectation that, as in other engineering disciplines, performing appropriate mathematical analysis can contribute to the reliability and robustness of a design.

In axiomatic set theory and the branches of mathematics and philosophy that use it, the axiom of infinity is one of the axioms of Zermelo–Fraenkel set theory. It guarantees the existence of at least one infinite set, namely a set containing the natural numbers. It was first published by Ernst Zermelo as part of his set theory in 1908.

In the context of hardware and software systems, formal verification is the act of proving or disproving the correctness of intended algorithms underlying a system with respect to a certain formal specification or property, using formal methods of mathematics.

<span class="mw-page-title-main">Liskov substitution principle</span> Object-oriented programming principle

The Liskov substitution principle (LSP) is a particular definition of a subtyping relation, called strong behavioral subtyping, that was initially introduced by Barbara Liskov in a 1987 conference keynote address titled Data abstraction and hierarchy. It is based on the concept of "substitutability" – a principle in object-oriented programming stating that an object may be replaced by a sub-object without breaking the program. It is a semantic rather than merely syntactic relation, because it intends to guarantee semantic interoperability of types in a hierarchy, object types in particular. Barbara Liskov and Jeannette Wing described the principle succinctly in a 1994 paper as follows:

Subtype Requirement: Let be a property provable about objects of type T. Then should be true for objects of type S where S is a subtype of T.

In computer science, program synthesis is the task to construct a program that provably satisfies a given high-level formal specification. In contrast to program verification, the program is to be constructed rather than given; however, both fields make use of formal proof techniques, and both comprise approaches of different degrees of automatization. In contrast to automatic programming techniques, specifications in program synthesis are usually non-algorithmic statements in an appropriate logical calculus.

In computer science, a loop invariant is a property of a program loop that is true before each iteration. It is a logical assertion, sometimes checked with a code assertion. Knowing its invariant(s) is essential in understanding the effect of a loop.

In computer programming, specifically object-oriented programming, a class invariant is an invariant used for constraining objects of a class. Methods of the class should preserve the invariant. The class invariant constrains the state stored in the object.

In computing, aliasing describes a situation in which a data location in memory can be accessed through different symbolic names in the program. Thus, modifying the data through one name implicitly modifies the values associated with all aliased names, which may not be expected by the programmer. As a result, aliasing makes it particularly difficult to understand, analyze and optimize programs. Aliasing analysers intend to make and compute useful information for understanding aliasing in programs.

Predicate transformer semantics were introduced by Edsger Dijkstra in his seminal paper "Guarded commands, nondeterminacy and formal derivation of programs". They define the semantics of an imperative programming paradigm by assigning to each statement in this language a corresponding predicate transformer: a total function between two predicates on the state space of the statement. In this sense, predicate transformer semantics are a kind of denotational semantics. Actually, in guarded commands, Dijkstra uses only one kind of predicate transformer: the well-known weakest preconditions.

The Java Modeling Language (JML) is a specification language for Java programs, using Hoare style pre- and postconditions and invariants, that follows the design by contract paradigm. Specifications are written as Java annotation comments to the source files, which hence can be compiled with any Java compiler.

<span class="mw-page-title-main">KeY</span>

The KeY tool is used in formal verification of Java programs. It accepts specifications written in the Java Modeling Language to Java source files. These are transformed into theorems of dynamic logic and then compared against program semantics that are likewise defined in terms of dynamic logic. KeY is significantly powerful in that it supports both interactive and fully automated correctness proofs. Failed proof attempts can be used for a more efficient debugging or verification-based testing. There have been several extensions to KeY in order to apply it to the verification of C programs or hybrid systems. KeY is jointly developed by Karlsruhe Institute of Technology, Germany; Technische Universität Darmstadt, Germany; and Chalmers University of Technology in Gothenburg, Sweden and is licensed under the GPL.

<span class="mw-page-title-main">ATS (programming language)</span> Programming language

ATS is a programming language designed to unify programming with formal specification. ATS has support for combining theorem proving with practical programming through the use of advanced type systems. A past version of The Computer Language Benchmarks Game has demonstrated that the performance of ATS is comparable to that of the C and C++ programming languages. By using theorem proving and strict type checking, the compiler can detect and prove that its implemented functions are not susceptible to bugs such as division by zero, memory leaks, buffer overflow, and other forms of memory corruption by verifying pointer arithmetic and reference counting before the program compiles. Additionally, by using the integrated theorem-proving system of ATS (ATS/LF), the programmer may make use of static constructs that are intertwined with the operative code to prove that a function attains its specification.

Extended static checking (ESC) is a collective name in computer science for a range of techniques for statically checking the correctness of various program constraints. ESC can be thought of as an extended form of type checking. As with type checking, ESC is performed automatically at compile time. This distinguishes it from more general approaches to the formal verification of software, which typically rely on human-generated proofs. Furthermore, it promotes practicality over soundness, in that it aims to dramatically reduce the number of false positives at the cost of introducing some false negatives. ESC can identify a range of errors which are currently outside the scope of a type checker, including division by zero, array out of bounds, integer overflow and null dereferences.

TLA<sup>+</sup> Formal specification language

TLA+ is a formal specification language developed by Leslie Lamport. It is used for designing, modelling, documentation, and verification of programs, especially concurrent systems and distributed systems. TLA+ is considered to be exhaustively-testable pseudocode, and its use likened to drawing blueprints for software systems; TLA is an acronym for Temporal Logic of Actions.

Whiley is an experimental programming language that combines features from the functional and imperative paradigms, and supports formal specification through function preconditions, postconditions and loop invariants. The language uses flow-sensitive typing also known as "flow typing."

In computer science, interference freedom is a technique for proving partial correctness of concurrent programs with shared variables. Hoare logic had been introduced earlier to prove correctness of sequential programs. In her PhD thesis under advisor David Gries, Susan Owicki extended this work to apply to concurrent programs.

Deepak Kapur is a Distinguished Professor in the Department of Computer Science at the University of New Mexico.

References

  1. Smans, Jan; Jacobs, Bart; Piessens, Frank (2009). Implicit Dynamic Frames: Combining Dynamic Frames and Separation Logic (PDF). Proceedings of the Conference on European Conference on Object Oriented Programming. pp. 148–172. doi:10.1007/978-3-642-03013-0_8.
  2. Leino, Rustan (2010). Dafny: An Automatic Program Verifier for Functional Correctness. Proceedings of the Conference on Logic for Programming, Artificial Intelligence, and Reasoning. pp. 348–370. doi:10.1007/978-3-642-17511-4_20.
  3. Leino, Rustan; Monahan, Rosemary (2010). Dafny Meets the Verification Benchmarks Challenge (PDF). International Conference on Verified Software: Theories, Tools, and Experiments. pp. 112–116. doi:10.1007/978-3-642-15057-9_8.
  4. Klebanov, Vladimir; et al. (2011). The 1st Verified Software Competition: Experience Report. Proceedings of the Conference on Formal Methods. pp. 154–168. CiteSeerX   10.1.1.221.6890 . doi:10.1007/978-3-642-21437-0_14.
  5. Bormer, Thorsten; et al. (2011). The COST IC0701 Verification Competition 2011. Proceedings of the Conference on Formal Verification of Object-Oriented Software. pp. 3–21. CiteSeerX   10.1.1.396.6170 . doi:10.1007/978-3-642-31762-0_2.
  6. Huisman, Marieke; Klebanov, Vladimir; Monahan, Rosemary (2015). "VerifyThis 2012" (PDF). International Journal on Software Tools for Technology Transfer. 17 (6): 647–657. doi:10.1007/s10009-015-0396-8. S2CID   14301377.
  7. "Z3 Homepage". GitHub . 2019-02-07.
  8. de Moura, Leonardo; Bjørner, Nikolaj (2008). Z3: An Efficient SMT Solver. Proceedings of the Conference on Tools and Algorithms for the Construction and Analysis. pp. 337–340. doi: 10.1007/978-3-540-78800-3_24 .
  9. "Dafny Programming Language". 2022-07-14.

Further reading