次の C コードがあるとします。
int my_main(int x){
if (x > 5){
x++;
if (x > 8){
x++;
if (x < 15){
//@(x >= 9 && x <= 14);
}
}
}
return 0;
}
初期化時に変数 x の境界を静的分析を使用して計算したいと思います。これにより、満足のいく述語が得られます。この例では、メインの開始時の 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;
}
次の述語が代替エルゴ ソルバーに送信されます。
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 の境界に従うことで、入力に必要な間隔を識別することができます。私はこれらの境界をマークしました。これはあまり堅牢ではありません。たとえば、アサーションが&& n <= 13に変更された場合、暗黙の述語の「左側」は変更された条件がないため同じままです。また、関数を呼び出すときにアサーションを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))