Example of Axiomatizaton and Trigger-based rewriting When verifying Insertion sort in Dafny, we found it useful (essential?) to add an assertion: assert xs == [xs[0]] + xs[1..]; in a context where xs != [] When translating to Boogie, Dafny uses an axiomatization of sequence operations (show in simplified syntax here). The above looks like: not (Equal(xs,Empty())) ==> Equal(xs, Append(Build(Empty(),Index(xs,0)), Drop(xs,1))) Here is (part of) the axiomatization, extracted from the generated Boogie code. Triggers are shown in italics within curly braces: type Seq _; function Empty() : Seq T; function Build(s: Seq T, val: T) : Seq T; function Append(Seq T, Seq T) : Seq T; function Index(Seq T, int) : T; function Equal(Seq T, Seq T) : bool; function Length(Seq T) : int; 1 axiom (forall s: Seq T :: { Length(s) } 0 <= Length(s)); 2 axiom (forall :: Length(Empty(): Seq T) == 0); 3 axiom (forall s: Seq T :: { Length(s) } Length(s) == 0 ==> s == Empty()); 4 axiom (forall s0: Seq T, s1: Seq T :: { Length(Append(s0, s1)) } Length(Append(s0, s1)) == Length(s0) + Length(s1)); 5 axiom (forall s: Seq T, v: T :: { Length(Build(s, v)) } Length(Build(s, v)) == 1 + Length(s)); 6 axiom (forall s: Seq T, i: int, v: T :: { Index(Build(s, v), i) } (i == Length(s) ==> Index(Build(s, v), i) == v) && (i != Length(s) ==> Index(Build(s, v), i) == Index(s, i))); 7 axiom (forall s0: Seq T, s1: Seq T :: { Equal(s0, s1) } Equal(s0, s1) <==> Length(s0) == Length(s1) && (forall j: int :: { Index(s0, j) } { Index(s1, j) } 0 <= j && j < Length(s0) ==> Index(s0, j) == Index(s1, j))); 8 axiom (forall s: Seq T, n: int :: { Length(Drop(s, n)) } 0 <= n && n <= Length(s) ==> Length(Drop(s, n)) == Length(s) - n); 9 axiom (forall s: Seq T, n: int, j: int :: {:weight 25} { Index(Drop(s, n), j) } 0 <= n && 0 <= j && j < Length(s) - n ==> Index(Drop(s, n), j) == Index(s, j + n)); 10 axiom (forall s: Seq T, n: int, k: int :: {:weight 25} { Index(s, k), Drop(s, n) } 0 <= n && n <= k && k < Length(s) ==> Index(Drop(s, n), k - n) == Index(s, k)); 11. axiom (forall s0: Seq T, s1: Seq T, n: int :: { Index(Append(s0, s1), n) } (n < Length(s0) ==> Index(Append(s0, s1), n) == Index(s0, n)) && (Length(s0) <= n ==> Index(Append(s0, s1), n) == Index(s1, n - Length(s0)))); You can see that, without writing down the particular decomposition of xs that we want, there is little chance that Dafny will instantiate any of these axioms usefully. Here's how triggering might be used to add clauses that, taken together with our original formula, make it satisfiable. The order of triggerings here is not systematic. --- starting formula (a) not (Equal(xs,Empty())) ==> Equal(xs, Append(Build(Empty(),Index(xs,0)), Drop(xs,1))) --- trigger 7 on (a) Equal(xs,Empty()) (b) Equal(xs,Empty()) <==> Length(xs) == Length(Empty()) && (forall j:int :: { Index(xs, j) } { Index(Empty(), j) } 0 <= j && j < Length(xs) ==> Index(xs, j) == Index(Empty(), j)) --- trigger 1 on (b) Length(xs) (c) 0 <= Length(xs) --- trigger 1 on (b) Length(Empty()) (d) 0 <= Length(Empty()) --- trigger 3 on (b) Length(xs) (e) Length(xs) == 0 ==> xs == Empty() ; this will let us show that Length(xs) > 0 on RHS of (a) --- trigger 3 on (b) Length(Empty()) (f) Length(Empty()) == 0 ==> Empty() == Empty() ; this obviously adds no new information --- trigger 2 on (b) Length(Empty()) (g) Length(Empty()) == 0 --- trigger 7 on (a) Equal (xs,Append ...) (h) Equal(xs,Append(Build(Empty(),Index(xs,0)),Drop(xs,1))) <==> Length(xs) == Length(Append(Build(Empty(),Index(xs,0)),Drop(xs,1))) && (forall j:int :: { Index(xs, j) } { Index(Append(Build(Empty(),Index(xs,0)),Drop(xs,1)), j) } 0 <= j && j < Length(xs) ==> Index(xs,j) == Index(Append(Build(Empty(),Index(xs,0)),Drop(xs,1)),j)) -- trigger 4 on (h) Length(Append...) (i) Length(Append(Build(Empty(),Index(xs,0)),Drop(xs,1))) == Length(Build(Empty(),Index(xs,0))) + Length(Drop(xs,1)) --- trigger 5 on (i) Length(Build ...) (j) Length(Build(Empty(), Index(xs,0))) == 1 + Length(Empty()) --- trigger 8 on (i) Length(Drop ...) (k) 0 <= 1 && 1 <= Length(s) ==> Length(Drop(xs, 1)) == Length(xs) - 1 ; now know enough to validate first clasuse of h --- trigger 11 on (h) Index(Append ...) --- (I believe we don't need to trigger the forall j clause because we are going to use (h) --- right-to-left, so need to show the clause is true for arbitrary integers j (l) (j < Length(Build(Empty(),Index(xs,0))) ==> Index(Append(Build(Empty(),Index(xs,0)),Drop(xs,1)),j) == Index(Build(Empty(),Index(xs,0)),j)) && (Length(Build(Empty(),Index(xs,0))) <= j ==> Index(Append(Build(Empty(),Index(xs,0)),Drop(xs,1)),j) == Index(Drop(xs,1),j-Length(Build(Empty(),Index(xs,0))))) --- trigger 6 on (l) Index(Build ...) (m) (j == Length(Empty()) ==> Index(Build(Empty(),Index(xs,0)),j) == Index(xs,0)) && (j != Length(Empty()) ==> Index(Build(Empty(),Index(xs,0)),j) == Index(Empty(),j)) --- trigger 9 on (l) Index(Drop ...) (n) 0 <= j - Length(Build(Empty(),Index(xs,0))) && j - Length(Build(Empty(),Index(xs,0))) < Length(xs) - 1 ==> Index(Drop(xs, 1), j - Length(Build(empty(),Index(xs,0)))) == Index(xs, j - Length(Build(empty(),Index(xs,0))) + 1) ; Adding (b) through (n) to (a) should produce an (essentially) quantifier-free, valid formula.