If you run Dafny with /autoTriggers:1 in the example below, the assertion fails, even though it is just repeating the body of the quantifier in the lemma's ensures clause. The problem is specific to the use of higher-order functions. In particular, when Dafny chooses a trigger for the lemma's ensures clause, it chooses `RefineInt(y)`, which is a good choice. Unfortunately, when this is translated into Boogie, the trigger becomes, essentially, `Apply1(RefineInt, y)`. Meanwhile, in the assertion, the call to Identity is treated as a normal Boogie function call and doesn't mention `Apply1`. If you uncomment the two commented lines, then the assertion succeeds, because the commented lines cause `Apply1` to be mentioned.
Fixing this seems like it will require some thought about how higher-order functions are encoded, at least when it comes to triggers.
```
lemma lemma_ensures(x:int, RefineInt:int->int)
requires forall y :: RefineInt.requires(y);
ensures forall y :: RefineInt(y) + x == RefineInt(x) + y;
function Identity(z:int) : int
lemma test()
{
var v,w:int;
lemma_ensures(w, Identity);
// var RefineInt := Identity;
// assert RefineInt(v) == Identity(v);
assert Identity(v) + w == Identity(w) + v;
}
```
As a side note, if Dafny translated higher-order functions into Boogie maps, it would, at least, solve this particular problem, as illustrated by this Boogie version of the example above:
```
procedure lemma_ensures(x:int, RefineInt:[int]int);
ensures (forall y:int :: RefineInt[y] + x == RefineInt[x] + y);
const Identity:[int]int;
procedure test()
{
var v:int;
var w:int;
call lemma_ensures(w, Identity);
assert Identity[v] + w == Identity[w] + v;
}
```
Comments: Currently, given a Dafny function `function f(x:T) : U`, it gets translated to Boogie as a Boogie-level function `function f(x:T) : U`, as well as a constant structure that contains information about the function (e.g., its preconditions, reads clause, etc.) as something like `const f_prime : Func`. The proposed fix for this issue is to also produce the following Boogie axiom: ``` axiom forall x:T {:trigger f(x)} :: f(x) == Apply1(f_prime, x); ``` As long as this doesn't hurt verification performance of existing code, then it should solve the problem above.
Fixing this seems like it will require some thought about how higher-order functions are encoded, at least when it comes to triggers.
```
lemma lemma_ensures(x:int, RefineInt:int->int)
requires forall y :: RefineInt.requires(y);
ensures forall y :: RefineInt(y) + x == RefineInt(x) + y;
function Identity(z:int) : int
lemma test()
{
var v,w:int;
lemma_ensures(w, Identity);
// var RefineInt := Identity;
// assert RefineInt(v) == Identity(v);
assert Identity(v) + w == Identity(w) + v;
}
```
As a side note, if Dafny translated higher-order functions into Boogie maps, it would, at least, solve this particular problem, as illustrated by this Boogie version of the example above:
```
procedure lemma_ensures(x:int, RefineInt:[int]int);
ensures (forall y:int :: RefineInt[y] + x == RefineInt[x] + y);
const Identity:[int]int;
procedure test()
{
var v:int;
var w:int;
call lemma_ensures(w, Identity);
assert Identity[v] + w == Identity[w] + v;
}
```
Comments: Currently, given a Dafny function `function f(x:T) : U`, it gets translated to Boogie as a Boogie-level function `function f(x:T) : U`, as well as a constant structure that contains information about the function (e.g., its preconditions, reads clause, etc.) as something like `const f_prime : Func`. The proposed fix for this issue is to also produce the following Boogie axiom: ``` axiom forall x:T {:trigger f(x)} :: f(x) == Apply1(f_prime, x); ``` As long as this doesn't hurt verification performance of existing code, then it should solve the problem above.