Dafny 2.1.0
Dafny version 2.1.0 mainly introduces non-null types, more precise type inference in the presence of subset types, full checks that functions do not depend on the allocation state, and an expanded selection of variance annotations. Some of these changes are not backwards compatible, but the changes required to bring older programs up to version 2.1.0 are usually not large.
Another change is that Visual Studio support has been moved to VS 2017, .NET version 4.5. Building under Mono still works, but only without using Code Contracts (which don't seem to be available on Mono).
In more detail, the changes over version 2.0.0 are as follows:
Language
-
Non-null types.
A declaration of a class or trait
Cgives rise to two types,C?andC.Type
C?is the possibly null reference type forCobjects. Previously in Dafny, this was the only type the class or trait gave rise to and it then had the different nameC.Type
Cis a non-null version ofC?. It is a defined as the subset type:type C = c: C? | c != nullNote that the
?is part of the name of the classC?. It is not a suffix operator, so there cannot be any space between theCand the?. Also, if a classChas type parameters, then these are mentioned after the type namesC?andC, for example,C?<int>andC<int>.In some places in the program text, a class or trait name is expected. In particular, a trait name is expected after the
extendskeyword in aclassdeclaration and a class name is expected in anewallocation. Note that these names, because they are class and trait names, not type names, do not end with a?. -
Dafny has a built-in trait
objectand any number of built-in array classes (array,array2,array3, ...). Each of these also gives rise to two types, as inobject?andobject,array?andarray,array2?andarray2, etc. -
If the literal
nullis compared with an expression of a non-null type, Dafny will emit a warning that the result of the test can be determined at compile time. For example, ifphas a non-null type andSis a set whose element type is a non-null type, then bothp != nullandnull in Swill generate said warning. -
The subset types declared in a program form a hierarchy (that is, a tree), where the parent of a subset type is its given base type. This hierarchy is part of the subtype relation in Dafny. For example,
natis a subtype ofint, and for any class or traitC,Cis a subtype ofC?. In addition, for every class or traitCthat extends a traitTr, typeC?is a subtype ofTr?and typeCis a subtype ofTr.As a consequence of these rules, note that the subtype relation is not just a hierarhcy.
Suppose
Xis a subset type whose base type isC. It then follows thatXis a subtype ofC, which in turn is a subtype ofC?. However, the declaration ofXonly gives rise to one type. In particular, it does not also give rise to a typeX?. In other words, any user-defined subset type ofC?orCis only a subtype of it. -
Type inference is now more precise. Whereas before it would not infer subset types like
nator-->(but instead their base type,intand~>), now it will try to infer such types. In some cases, this will not make a noticeable, but there are two cases where the difference may be important.One case is that type arguments more easily will be inferred as subset types. For example, suppose the usual datatype
Listhas a co-variant type parameter and supposexsis a variable declared to have typeList<nat>. ExpressionCons(55, xs)may now be inferred to have typeList<nat>as well. This is beneficial for verification, because in an assignment likexs := Cons(55, xs);, the verifier only needs to check that55satisfies the constraint ofnat. Previously, it was more likely thatCons(55, xs)would be inferred to have typeList<int>, in which case the assignmentxs := Cons(55, xs);put the burden on the verifier to check thatCons(55, xs)produces a value that actually satisfiesList<nat>.The other case where the more precise type inference can make a noticeable difference is in comprehensions like quantifications and set comprehensions. Suppose
Pis a predicate declared to take a parameter of typenatand consider an expressionforall x :: P(x) ==> 0 <= x. Ifxhas typenat, then this quantifier is a tautology. Ifxhas typeint, then the expression is not well-formed, because the subexpressionP(x)would not satisfy the type conditions ofP. For this example, Dafny will infer the type ofxto benat.As another example, of a quantifier, suppose
PandQare both predicates that take anatargument. Then, Dafny will inferxto have typenatinforall x :: 0 <= x && P(x) ==> Q(x)as well. So, in this example, sincexis inferred to be of typenat, the antecedent0 <= xis redundant and can be omitted.As a third example, suppose
negis a subset type ofintthat stands for the negative integers,Pis a predicate that takes an argument of typenat, andQis a predicate that takes an argument of typeneg. Consider the following expression:forall x :: (0 <= x ==> P(x)) && (0 < x ==> Q(x))For this expression, Dafny will infer that
xhas typeint. In an alternative design, inference might have resulted in the typenatorneg, each of which would render one of the conjuncts trivially satisfied, or resulted in error message like "overconstrained type". -
Allow more RHSs of
constdeclarations. -
The possible variance indicators on type parameters has been expanded. The possibilities are:
-
+(co-variance, strict)
Enclosing type is monotonic in the type parameter.
The parameter is not used in expanding positions (that is, it is not used left of any arrow). -
*(co-variance, relaxed)
Enclosing type is monotonic in the type parameter.
Other than that, use of the parameter is unrestricted (in particular, it may be used to the left of an arrow). -
(default variance, denoted by not giving any of the other variance indicators explicitly) (non-variance, strict)
Different given parameters produce non-comparable types.
The parameter is not used in expanding positions (that is, it is not used left of any arrow). -
!(non-variance, relaxed)
Different given parameters produce non-comparable types.
Other than that, use of the parameter is unrestricted (in particular, it may be used to the left of an arrow). -
-(contra-variance)
Enclosing type is anti-monotonic in the type parameter.
Consequentially, every use of the parameter is to the left of some arrow.
-
-
Strict positivity is used to forbid type definitions where differences in cardinalities would give rise to a logical contradiction.
-
Added type-parameter characteristic
(!new), which says that the type does not include any references, and thus does not depend on the allocation state. It can be used to restrict type parameters so that functions can use the type in unbounded quantifications. -
Opaque types at the very topmost scope are always implicitly
(!new).
Verification
-
Support for customizable error messages (useful for tools built on top of Dafny).
-
Removed the notoriously bad
seqaxiom. (More precisely, rewrote it to have a good trigger.) -
Some performance improvements.
-
Improved and expanded on the syntactic bounds discovery in comprehensions.
IDE
- Visual Studio mode is for VS 2017
Compiler
- Some bug fixes, for example to work around some questionable designs around the use of
nullin the .NET collection libraries.
Command-line options
-
Command-line option
/titracespills out debug trace information from the type inference process. Previously, this debug trace information was hidden under an #if, but now it can be turned on without having to recompile Dafny. -
Added
/noExternsflag, which ignores:externattributes. -
Started implementing a
/allocated:4mode.
Miscellaneous
-
Include Code Contracts for VS 2017. This requires Code Contracts to have been installed (for some version of Visual Studio, like VS 2015) on the machine.
-
Various bug fixes.