在 C# 代码合约中使用纯函数时的静态验证限制?

发布于 2024-09-19 00:23:29 字数 1504 浏览 2 评论 0原文

我正在尝试使用代码契约静态验证以下基于数组的堆栈的部分实现。 Pop() 方法使用纯函数 IsNotEmpty() 来确保后续数组访问将位于/高于下限。静态验证器失败并建议我添加前提条件 Contract.Requires(0 <= this.top)

谁能解释为什么验证者无法证明数组访问相对于给定合约 IsNotEmpty() 的下限是有效的?

起初,我认为 Contract.Requires(IsNotEmpty()) 方法可能不正确,因为子类可以重写 IsNotEmpty()。但是,如果我将类标记为 sealed,验证程序仍然无法证明数组访问是有效的。

更新:如果我将 IsNotEmpty() 更改为只读属性,验证将按预期成功。这就提出了一个问题:如何/为什么对只读属性和纯函数进行不同的处理?

class StackAr<T>
{
    private T[] data;
    private int capacity;

    /// <summary>
    /// the index of the top array element
    /// </summary>
    private int top;

    [ContractInvariantMethod]
    private void ObjectInvariant()
    {
        Contract.Invariant(data != null);
        Contract.Invariant(top < capacity);
        Contract.Invariant(top >= -1);
        Contract.Invariant(capacity == data.Length);
    }

    public StackAr(int capacity)
    {
        Contract.Requires(capacity > 0);
        this.capacity = capacity;
        this.data = new T[capacity];
        top = -1;
    }

    [Pure]
    public bool IsNotEmpty()
    {
        return 0 <= this.top;
    }

    public T Pop()
    {
        Contract.Requires(IsNotEmpty());

        //CodeContracts: Suggested precondition: 
        //Contract.Requires(0 <= this.top);

        T element = data[top];
        top--;
        return element;
    }
}

I'm trying to statically verify the following partial implementation of an array-based stack with code contracts. The method Pop() uses the pure function IsNotEmpty() to ensure that the subsequent array access will be at/above the lower bound. The static verifier fails and suggests that I add the precondition Contract.Requires(0 <= this.top).

Can anyone explain why the verifier cannot prove the array access is valid with respect to the lower bound given the contract IsNotEmpty()?

At first I thought the Contract.Requires(IsNotEmpty()) approach might be incorrect because a subclass could override IsNotEmpty(). However, the verifier still can't prove the array access is valid if I mark the class as sealed.

Update: If I change IsNotEmpty() to a read-only property, the verification succeeds as expected. This raises the question: how / why are read-only properties and pure functions treated differently?

class StackAr<T>
{
    private T[] data;
    private int capacity;

    /// <summary>
    /// the index of the top array element
    /// </summary>
    private int top;

    [ContractInvariantMethod]
    private void ObjectInvariant()
    {
        Contract.Invariant(data != null);
        Contract.Invariant(top < capacity);
        Contract.Invariant(top >= -1);
        Contract.Invariant(capacity == data.Length);
    }

    public StackAr(int capacity)
    {
        Contract.Requires(capacity > 0);
        this.capacity = capacity;
        this.data = new T[capacity];
        top = -1;
    }

    [Pure]
    public bool IsNotEmpty()
    {
        return 0 <= this.top;
    }

    public T Pop()
    {
        Contract.Requires(IsNotEmpty());

        //CodeContracts: Suggested precondition: 
        //Contract.Requires(0 <= this.top);

        T element = data[top];
        top--;
        return element;
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

两仪 2024-09-26 00:23:29

这应该可以解决问题:

[Pure]
public bool IsNotEmpty() {
    Contract.Ensures(Contract.Result<bool>() && 0 <= this.top || !Contract.Result<bool>() && 0 > this.top);
    return 0 <= this.top;
}

请参阅代码合同论坛上的此线程以获取更多信息:调用 Contract.Requires 中的方法

编辑:

此问题的另一个解决方案,如上面链接的线程中提到的,是 -infer 命令行选项:

现在,推断此方法的后置条件是可能的,事实上我们确实可以选择这样做:尝试在静态检查器的合约属性窗格中的额外选项行上添加 -infer Ensures。

我检查过,这确实解决了问题。如果您查看代码合约手册,您会看到默认选项是仅推断属性后置条件。通过使用此开关,您也可以告诉它尝试推断方法的后置条件:

-infer(需要 + propertyensures + methodensures)(默认=propertyensures)

This should solve the problem:

[Pure]
public bool IsNotEmpty() {
    Contract.Ensures(Contract.Result<bool>() && 0 <= this.top || !Contract.Result<bool>() && 0 > this.top);
    return 0 <= this.top;
}

See this thread on the code contracts forum for more information: Calling a method in Contract.Requires

Edit:

Another solution to this problem, as mentioned in the thread linked above, is the -infer command line option:

Now, inferring the post condition of this method is possible, and in fact we do have the option to do so: try to add -infer ensures on the extra options line in the contract property pane for the static checker.

I have checked, and this does resolve the problem. If you look in the code contracts manual, you'll see that the default option is to only infer property post-conditions. By using this switch, you can tell it to attempt to infer the post-conditions of methods too:

-infer (requires + propertyensures + methodensures) (default=propertyensures)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文