Linked List insertion #5073
-
So now I have started having bad dreams about this problem. I followed Rustan Leino's paper and implemented the linked list example from the same. The example clearly explains the concept of dynamic frames but inserting a node in the middle of the queue is not trivial. In the paper, when the node is appended at the end of the list, the footprints of all the nodes in the list are updated to reflect that the new node is reachable from everywhere. A simple "forall" does the trick. However, Inserting in the middle is tricky because we have to tell Dafny that this new node is reachable "only from the nodes before it". I tried the following:
The final assertion gives me an error. As I understood, this error asks me to show Dafny that next node following the new node remains valid. After a bit of reading and later a discussion on stackoverflow, I understood that after the insert operation, Dafny cannot reason about the validity of the nodes already present in the footprint. To overcome this, the solution provided on SO was to collect the nodes parsed along the way and then update the footprint of each node. This is cool but again, Dafny cannot reason about the fact that the footprint of a node is a subset of the footprint of its previous node and therefore an assumption was made. I think using a twostate lemma (or function (or method)) might solve the problem because it will allow me to reason about the fact that, between the updates, the footprint of a node is a subset of the footprint of its previous node holds. I may be wrong and that brings me to my question:
|
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 9 replies
-
This is an excellent problem. So basically, after the while loop, Dafny can only prove about
which, as you can guess, is not useful in any way because curr has no relationship in this formula with the current "this". The solution is to strengthen the invariant so that you know exactly what
and you don't have information loss. Try with the following much stronger invariant: ghost var footprint: seq<Node>
ghost var contents: seq<int>
ghost predicate valid()
reads this, footprint
decreases |footprint|
{
(forall i | 0 <= i < |footprint| ::
&& footprint[i].footprint == footprint[i..])
&& (forall i | 0 < i < |footprint| ::
&& footprint[i].valid())
&& if next == null then
&& contents == []
&& footprint == [this]
else
&& |footprint| >= 2 && footprint[1] == next
&& footprint == [this] + next.footprint
&& contents == [next.data] + next.contents
} To convert a seq to a set, use |
Beta Was this translation helpful? Give feedback.
Whenever verification times out, the following annotation on the method will be your friend:
This will isolate every assertion and assign an arbitrary resource limit to each of them, so that you will quickly see in the gutter which assertions are timing out exactly, and work on them
After doing that, it looks like the assertion it can't prove is the following:
One thing you can do to iterate faster is to use the {:only} attribute like this
so now, everything else will be assumed.
You can now copy-paste this…