我想要一个分配两个迭代器的函数,然后进入一个循环,只要MoveNext()返回true,该循环便会在迭代器上重复调用MoveNext()。但是,在迭代器上调用MoveNext()会违反(或阻止Dafny验证)另一个迭代器所需的不变式:
iterator Iter1()
{ yield; }
iterator Iter2()
{ yield; }
method main()
decreases *
{
var iter1 := new Iter1();
var iter2 := new Iter2();
var iter1More := true;
var iter2More := true;
while (iter1More || iter2More)
invariant iter1More ==> iter1.Valid() && fresh(iter1._new)
// The following invariant is not verified
invariant iter2More ==> iter2.Valid() && fresh(iter2._new)
decreases *
{
if (iter1More) {
iter1More := iter1.MoveNext();
}
}
}
Dafny无法验证iter2More ==> iter2.Valid() && fresh(iter2._new)
,即使Iter1的修改子句为空。我需要添加一个循环不变式吗?
答案 0 :(得分:1)
好的,这个很有趣:)
这是您需要验证的其他不变式
invariant {iter1} + iter1._new !! {iter2} + iter2._new
这个不变量也足以证明调用两个迭代器的更复杂的循环,如下所示:
if (iterMore1) {
iterMore1 := iter1.MoveNext();
}
if (iterMore2) {
iterMore2 := iter2.MoveNext();
}
我发现这个问题的方式是,达夫妮抱怨iter2.Valid()
在调用iter1.MoveNext()
之后可能不成立。任何时候在调用之前为真的谓词可能在调用之后都不为真时,您就会知道问题是Dafny无法证明该函数的reads子句与该方法的Modifys子句不重叠。不幸的是,没有记录迭代器的Valid()
谓词的reads子句,也没有记录迭代器的MoveNext()
方法的Modifys子句。我深入研究了Dafny的Boogie输出,并能够如下对它们进行反向工程
predicate Valid()
reads this, this._reads, this._new
method MoveNext()
modifies this, this._modifies, old(this._new)
我还可以看到Dafny可以证明Iter1
和Iter2
都具有空的_reads
和_modifies
集。因此,实际上只是引用本身及其_new
集。这解释了不变性。