为什么 Dafny 认为在使用 ghost 变量时这个前提条件可能会有问题?

Why does Dafny think there might be a problem with this precondition when using a ghost variable?

假设我有以下 class:

class Testing {
    ghost var myGhostVar: int;

    method Init() 
    modifies this
    ensures this.myGhostVar == -1    
    {
        this.myGhostVar := -1;
        assert this.myGhostVar == -1;
    }


    method MyTestingMethod(list: array<int>, t: int)
    modifies this
    requires list.Length > 1
    requires this.myGhostVar == -1
    requires t == -1
    ensures MyPredicate(list, myGhostVar)
    ensures this.myGhostVar < list.Length
    {
        this.Init();
        assert this.myGhostVar < 0;
        assert list.Length > 0;
        assert this.myGhostVar < list.Length;
    }

    predicate MyPredicate(list: array<int>, startIndex: int)
    requires startIndex < list.Length
    {
        true
    }
}

出于某种原因,Dafny 说对 MyPredicate(...) 的调用可能无法保持。但是,如果我改为使用 t 而不是 myGhostVar 作为参数;达夫尼没有怨言。

两者都有相同的 requires 谓词,这让人有点困惑。使用幻影变量时我是否遗漏了什么?

这不是幽灵变量的问题 - 您可以检查一下,如果删除 ghost 关键字,您会遇到同样的问题。

问题是要调用MyPredicate(list, myGhostVar),你需要满足前置条件,这里是myGhostVar < list.Length

你的 ensures 子句中居然有这个条件,太棒了!

    ensures MyPredicate(list, myGhostVar)
    ensures this.myGhostVar < list.Length

但是您对 MyPredicate 的调用 您需要的条件之前。尝试翻转顺序:

    ensures this.myGhostVar < list.Length
    ensures MyPredicate(list, myGhostVar)

你应该会看到 Dafny 不再抱怨了。

一般来说,Dafny 会尝试使用 previous 确保子句来证明 ensures 子句中的先决条件成立。

Both have the same requires predicate which makes it all a bit confusing. Is it something that I am missing when using ghost variables?

在这里,我想你问的是条款

    requires this.myGhostVar == -1
    requires t == -1

并想知道为什么它不能使用 this.myGhostVar == -1 前置条件来证明 this.myGhostVar < list.Length

答案是 this.myGhostVar == -1 是一个前提条件,这意味着 Dafny 知道它在进入时保持不变,但 this.myGhostVar 的值可能会在方法执行期间发生变化。所以这对 Dafny 检查 post 条件没有帮助。

请注意,Dafny 在检查其 post 条件的格式是否正确时不会查看其方法的主体。为此,Dafny 只知道您在方法签名中告诉它的内容。