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
C
gives rise to two types,C?
andC
.Type
C?
is the possibly null reference type forC
objects. Previously in Dafny, this was the only type the class or trait gave rise to and it then had the different nameC
.Type
C
is a non-null version ofC?
. It is a defined as the subset type:type C = c: C? | c != null
Note that the
?
is part of the name of the classC?
. It is not a suffix operator, so there cannot be any space between theC
and the?
. Also, if a classC
has 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
extends
keyword in aclass
declaration and a class name is expected in anew
allocation. 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
object
and 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
null
is 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, ifp
has a non-null type andS
is a set whose element type is a non-null type, then bothp != null
andnull in S
will 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,
nat
is a subtype ofint
, and for any class or traitC
,C
is a subtype ofC?
. In addition, for every class or traitC
that extends a traitTr
, typeC?
is a subtype ofTr?
and typeC
is a subtype ofTr
.As a consequence of these rules, note that the subtype relation is not just a hierarhcy.
Suppose
X
is a subset type whose base type isC
. It then follows thatX
is a subtype ofC
, which in turn is a subtype ofC?
. However, the declaration ofX
only 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?
orC
is only a subtype of it. -
Type inference is now more precise. Whereas before it would not infer subset types like
nat
or-->
(but instead their base type,int
and~>
), 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
List
has a co-variant type parameter and supposexs
is 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 that55
satisfies 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
P
is a predicate declared to take a parameter of typenat
and consider an expressionforall x :: P(x) ==> 0 <= x
. Ifx
has typenat
, then this quantifier is a tautology. Ifx
has 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 ofx
to benat
.As another example, of a quantifier, suppose
P
andQ
are both predicates that take anat
argument. Then, Dafny will inferx
to have typenat
inforall x :: 0 <= x && P(x) ==> Q(x)
as well. So, in this example, sincex
is inferred to be of typenat
, the antecedent0 <= x
is redundant and can be omitted.As a third example, suppose
neg
is a subset type ofint
that stands for the negative integers,P
is a predicate that takes an argument of typenat
, andQ
is 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
x
has typeint
. In an alternative design, inference might have resulted in the typenat
orneg
, each of which would render one of the conjuncts trivially satisfied, or resulted in error message like "overconstrained type". -
Allow more RHSs of
const
declarations. -
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
seq
axiom. (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
null
in the .NET collection libraries.
Command-line options
-
Command-line option
/titrace
spills 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
/noExterns
flag, which ignores:extern
attributes. -
Started implementing a
/allocated:4
mode.
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.