在 C# 代码合约中使用纯函数时的静态验证限制?
我正在尝试使用代码契约静态验证以下基于数组的堆栈的部分实现。 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这应该可以解决问题:
请参阅代码合同论坛上的此线程以获取更多信息:调用 Contract.Requires 中的方法
编辑:
此问题的另一个解决方案,如上面链接的线程中提到的,是
-infer
命令行选项:我检查过,这确实解决了问题。如果您查看代码合约手册,您会看到默认选项是仅推断属性后置条件。通过使用此开关,您也可以告诉它尝试推断方法的后置条件:
This should solve the problem:
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: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: