Frama-C/WP не может доказать цикл, инвариантный с \at
У меня проблемы с проверкой двух инвариантов цикла:
loop invariant \forall integer i; 0 <= i < (\at(n, Pre) - n) ==> ((char*)m2)[i] == \at(((char*)m1)[i], Pre);
loop invariant \forall integer i; 0 <= i < (\at(n, Pre) - n) ==> ((char*)m1)[i] == \at(((char*)m2)[i], Pre);
Я предполагаю, что \at не работает для массивов, как я ожидаю.
Существует аналогичная функция в ACSL в Примере (стр. 68, swap_ranges), которая использует это, но, как указано, они не смогли доказать эту конкретную функцию с помощью плагина WP. Я попробовал это на своей машине, и он действительно не может доказать тот же инвариант.
Полный код
/*
* memswap()
*
* Swaps the contents of two nonoverlapping memory areas.
* This really could be done faster...
*/
#include "string.h"
/*@
requires n >= 1;
requires \valid(((char*)m1)+(0..n-1));
requires \valid(((char*)m2)+(0..n-1));
requires \separated(((char*)m1)+(0..n-1), ((char*)m2)+(0..n-1));
assigns ((char*)m1)[0..n-1];
assigns ((char*)m2)[0..n-1];
ensures \forall integer i; 0 <= i < n ==> ((char*)m1)[i] == \old(((char*)m2)[i]);
ensures \forall integer i; 0 <= i < n ==> ((char*)m2)[i] == \old(((char*)m1)[i]);
@*/
void memswap(void *m1, void *m2, size_t n)
{
char *p = m1;
char *q = m2;
char tmp;
/*@
loop invariant 0 <= n <= \at(n, Pre);
loop invariant p == m1+(\at(n, Pre) - n);
loop invariant q == m2+(\at(n, Pre) - n);
loop invariant (char*)m1 <= p <= (char*)m1+\at(n, Pre);
loop invariant (char*)m2 <= q <= (char*)m2+\at(n, Pre);
loop invariant \forall integer i; 0 <= i < (\at(n, Pre) - n) ==> ((char*)m2)[i] == \at(((char*)m1)[i], Pre);
loop invariant \forall integer i; 0 <= i < (\at(n, Pre) - n) ==> ((char*)m1)[i] == \at(((char*)m2)[i], Pre);
loop assigns n, tmp, ((char*)m1)[0..\at(n,Pre)-1], ((char*)um2)[0..\at(n, Pre)-1], p, q;
loop variant n;
@*/
while (/*n--*/ n) {
tmp = *p;
*p = *q;
*q = tmp;
p++;
q++;
n--; // inserted code
}
}
РЕДАКТИРОВАТЬ
Я использую Frama-C Oxygen Release и пробовал автоматическое тестирование с alt-ergo(0.94) и cvc3(2.4.1)
вывод из frama-c:
cvc3:
[wp] [Cvc3] Goal store_memswap_loop_inv_7_established : Valid
[wp] [Cvc3] Goal store_memswap_loop_inv_6_established : Valid
[wp] [Cvc3] Goal store_memswap_loop_inv_7_preserved : Unknown
[wp] [Cvc3] Goal store_memswap_loop_inv_6_preserved : Unknown
альт-эрго:
[wp] [Alt-Ergo] Goal store_memswap_loop_inv_7_established : Valid
[wp] [Alt-Ergo] Goal store_memswap_loop_inv_6_established : Valid
[wp] [Alt-Ergo] Goal store_memswap_loop_inv_7_preserved : Timeout
[wp] [Alt-Ergo] Goal store_memswap_loop_inv_6_preserved : Timeout
1 ответ
/*@
…
loop invariant \forall integer i; 0 <= i < (\at(n, Pre) - n) ==> ((char*)m2)[i] == \at(((char*)m1)[i], Pre);
loop invariant \forall integer i; 0 <= i < (\at(n, Pre) - n) ==> ((char*)m1)[i] == \at(((char*)m2)[i], Pre);
loop assigns n, tmp, ((char*)m1)[0..\at(n,Pre)-1], ((char*)um2)[0..\at(n, Pre)-1], p, q;
…
@*/
Вы ошиблись loop assigns
, loop assigns
аннотации рассказывают, какие области памяти были изменены на каждой итерации. Количество мест должно обычно увеличиваться по мере прохождения цикла (в вашем случае, как n
уменьшается). Это что-то вроде:
loop assigns n, tmp, ((char*)m1)[0..(\at(n, Pre) - n - 1)], ((char*)um2)[0..(\at(n, Pre) - n - 1)], p, q;
Но моё собственное предложение, приведенное выше, может быть отклонено тем или иным направлением. Мне трудно вспомнить, как именно работают эти "движущиеся" предложения о назначении цикла.
С другой стороны, вы можете написать более простой, "статический" loop assigns
аннотации (как ваша), и добавить информацию о том, что еще не изменилось в инварианте цикла. Это то, что я обычно делаю, чтобы обойти мою неспособность вспомнить, насколько сложным loop assigns
пункты работают. Это было бы что-то вроде (не проверено):
/*@
…
loop invariant \forall integer i; 0 <= i < (\at(n, Pre) - n) ==> ((char*)m2)[i] == \at(((char*)m1)[i], Pre);
loop invariant \forall integer i; 0 <= i < (\at(n, Pre) - n) ==> ((char*)m1)[i] == \at(((char*)m2)[i], Pre);
loop invariant \forall integer i; (\at(n, Pre) - n) <= i < \at(n, Pre) ==> ((char*)m1)[i] == \at(((char*)m1)[i], Pre);
loop invariant \forall integer i; (\at(n, Pre) - n) <= i < \at(n, Pre) ==> ((char*)m2)[i] == \at(((char*)m2)[i], Pre);
loop assigns n, tmp, ((char*)m1)[0..\at(n,Pre)-1], ((char*)um2)[0..\at(n, Pre)-1], p, q;
…
@*/