假设我们有以下C代码:
int my_main(int x){
if (x > 5){
x++;
if (x > 8){
x++;
if (x < 15){
//@(x >= 9 && x <= 14);
}
}
}
return 0;
}
我想在初始化时使用静态分析计算变量x的边界,从而得到满意的谓词。在这个例子中,main开头的x的间隔应该是[8,12]。
TL; DR:在代码中的某个位置给出断言,计算这些范围的最佳方法是什么?
到目前为止我尝试了什么:
我认为解决这个问题的最佳方法是使用最弱的前提条件计算器。我试图使用frama-c的wp插件,但由于它是为验证目的而构建的,我不确定它在这个用例中有多大用处。在以下代码上应用插件时:
int main(void){
int n = 0;
int x;
if (x > 5){
x++;
if (x > 8){
x++;
if (x < 15){
n = x;
}
}
}
//@ assert p: n >= 9 && n <= 14;
return 0;
}
我将以下谓词发送给alt-ergo求解器:
goal main_assert_p:
forall i_1,i : int.
is_sint32(i) ->
is_sint32(i_1) ->
(((i < 6) -> (0 = i_1)) and
(**(6 <= i)** ->
(((i < 8) -> (0 = i_1)) and
(**(8 <= i)** ->
(((12 < i) -> (0 = i_1)) and (**(i <= 12)** -> (i_1 = (2 + i)))))))) ->
((9 <= i_1) and (i_1 <= 14))
如果仔细观察,可以通过遵循不导致(i_1 = 0)的变量i的界限来确定输入所需的间隔。我注意到了这些界限。这不是很强大,例如,如果断言变为&amp;&amp; n <= 13 ,&#39;左侧&#39;暗示谓词保持不变,没有任何条件改变。此外,我不确定这在其他场景中有多大用处,例如在调用函数时将我的断言更改为require语句,我无法理解结果谓词:
if (x < 15){
sum(x);
}
在函数中添加一个require语句:
//@requires (n >= 6 && n <= 11);
int sum(int n){
我明白了:
goal main_call_sum_pre:
forall i : int.
(6 <= i) ->
(8 <= i) ->
(i <= 12) ->
is_sint32(i) ->
is_sint32(1 + i) ->
is_sint32(2 + i) ->
((4 <= i) and (i <= 9))
答案 0 :(得分:3)
你是对的,基于“最弱前提条件”范式的WP(或Jessie)是在这里使用的正确工具。然而,他们所做的是建立暗示:
规范给出的前提条件==&gt;计算最弱的前提条件
外部证明者然后尝试证明上述含义,(在一般情况下)仅提供真/假/超时答案。
您可以通过反复试验,使用“LOWER_BOUND≤x≤UPPER_BOUND”作为user_input
(*)的后置条件,并查看暗示是否得到证实。使用你作为黑盒子的工具,你可以通过几分之二到达间隔。你永远不会知道你是否有最佳间隔,或者证明者是否已经不能证明一个仍然存在的财产,但这就是生命。
或者你可以让证明者为你做简化工作,但这需要一种更复杂的交互,而不仅仅是“这个属性是真的吗?”。有些证明文件可以让您更轻松地访问其他信息。在WP完成其工作之后,这一切都在证明者的手中,而且你的问题实际上是“一个证明将一个逻辑公式减少到x
以使公式成立的证据”,而不是关于Frama-下进行。
这个study涉及到“在某些地方给你最好的间隔”的问题。它是关于浮点的,但由于浮点只是整数推理的难度,因此使用的工具和技术也可能适用于您的问题。特别是,“Gappa”证明者,其特点是浮点,本地间隔工作,IIRC是在该研究中提供必要的“最佳”间隔的证明者(第11页,“例如,我们是怎么做的在我们的说明性例子中确定界限1/16?“)
(*)请注意,在将调用添加到user_input()
以澄清含义之后,您要查找的内容实际上是该函数的后置条件,而不是主函数的前置条件。
答案 1 :(得分:-1)
assert
采用布尔表达式,如果FALSE
,则使用消息中止应用程序。 assert
通常是一个宏,在程序的非调试版本中,在预处理过程中会删除对这些宏的调用。
您的布尔表达式包含常量。如果用变量替换那些变量,那么就可以使用灵活的断言。