I have my test code (to study the WP loop invariants) which adds two long integers with each digit's representation in an array cell:
int main(int argc, const char * argv[]) {
char a[32], b[32];//size can be very big
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
scanf("%s %s", a, b);
unsigned int size1 = strlen(a);
unsigned int size2 = strlen(b);
//code to reverse a string. currently proved
reverse(a, size1);
reverse(b, size2);
for (unsigned int i = 0; i < size1; i++) a[i]-='0'; //move from chars to integers
for (unsigned int j = 0; j < size2; j++) b[j]-='0';
unsigned int maxsize = size1;
if (size2 > maxsize) maxsize = size2;
int over = 0;
//actual computation code
/*@
loop invariant maxsize == \max(size1, size2);
loop invariant bound: 0 <= k <= maxsize;
loop invariant ov: \forall integer i; 0 < i < k ==> \at(a[i], Here) == (\at(a[i], Pre) + b[i]+ Over((char *)a, (char *)b, i)) % 10;
loop assigns k, a[0..maxsize-1],over;
loop variant maxsize - k;
*/
for (unsigned int k = 0; k < maxsize; k++)
{
char sum=a[k] + b[k] + over;
//over=overflow is for 9+9 = 18, result=8, over=1
a[k] = sum % 10;
over = sum / 10;
}
if (over != 0) a[maxsize++] = over;
//...
return 0;
}
I want to specify only the last loop invariant. I have already tried some pieces of ACSL code with \at(a[i], LoopCurrent). I received Unknown status or Timeout. Finally, I finished with an axiomatic recursive solution without any success.
I have no ideas now what else should I try to verify this. Help?
UPDATE: Created a function for actual calculations. Improved axioms. Still got Timeout status. Ran with Frama-C latest release from opam with default settings (timeout=10, prover Alt-Ergo).
/*@
@predicate
Unchanged{K,L}(char* a, integer first, integer last) = \forall integer i; first <= i < last ==> \at(a[i],K) == \at(a[i],L);
axiomatic ReminderAxiomatic
{
logic integer Reminder {l} (integer x, integer y);
axiom ReminderEmpty: \forall integer x, integer y; x < y ==> Reminder(x, y) == x;
axiom ReminderNext: \forall integer x, integer y; x>=y ==> Reminder(x, y) == Reminder(x-y, y)+0;
}
axiomatic DivAxiomatic
{
logic integer Div {l} (integer x, integer y);
axiom DivEmpty: \forall integer x, integer y; x < y ==> Div(x, y) == 0 ;
axiom DivNext: \forall integer x, integer y; x >= y ==> Div(x, y) == Div(x - y, y) + 1 ;
}
axiomatic OverAxiomatic
{
logic integer Over {L}(char *a, char * b, integer step) reads a[0..step-1], b[0..step-1];
axiom OverEmpty: \forall char *a, char * b, integer step; step <= 0 ==> Over(a, b, step) == 0;
axiom OverNext: \forall char *a, char * b, integer step; step > 0 && a[step-1]<10 && a[step-1]>=0 && b[step-1]<10 && b[step-1]>=0
==> Over(a, b, step) == Div((int) a[step-1]+(int)b[step-1]+(int)Over(a,b,step-1), 10);
}
*/
/*@
requires 0 <= maxsize < 10;
requires \valid (a+(0..maxsize-1));
requires \valid (b+(0..maxsize-1));
requires \valid (res+(0..maxsize));
requires \forall integer i; 0 <= i < maxsize ==> 0 <= (int)a[i] < 10;
requires \forall integer i; 0 <= i < maxsize ==> 0 <= (int)b[i] < 10;
*/
void summ(char *a, char *b, char *res, unsigned int maxsize) {
char over = 0;
/*@
loop invariant bound: 0 <= k <=maxsize;
loop invariant step: \forall integer i; 0 <= i < k ==> res[i] == (char) Reminder((int)a[i]+ (int)b[i]+ (int)Over((char *)a, (char *)b, i) , 10);
loop invariant unch: Unchanged{Here,LoopEntry}((char *)res, k, maxsize);
loop assigns k, over, res[0..maxsize-1];
loop variant maxsize-k;
*/
for (unsigned int k = 0; k < maxsize; k++)
{
char sum = a[k] + b[k] + over;
res[k] = sum % 10;
over = sum / 10;
}
//
if (over != 0) res[maxsize++] = over;
}
First of all, it would be much better if your question contained a MCVE, which in the present case include the C function you're currently working on (instead of only its body), and the ACSL annotations you've written, at their exact place in the code. The command line used to launch Frama-C wouldn't hurt either, as well as the list of annotations for which you have trouble.
Apart from that, here are a few things that might be related to your problem (again, without an exact description, it's difficult to tell for sure).
loop assigns
is wrong: you're assigningover
in the loop, but it is not mentioned there.\max
is well supported by WP.loop invariant
does not say anything about the values ofa[i]
fork <= i< maxsize
. Sinceloop assigns
indicates that all cells ofa
might have been modified, you have to add an invariant telling that the ones with higher indices have not been touched so far.\at(a[i],Pre)
:Pre
indicates the beginning of the current function. Thus, ifscanf
andreverse
are in the same function as the loop (again, a MCVE would have clarified this), this is not true. You might want to speak about\at(a[i],Loop_entry)
to refer to the value of the cell in the state in which the loop is entered for the first time.UPDATE
I'm afraid WP will not be able to completely prove your annotations, but I managed to obtain a version in which only a single
lemma
, that is basically an expanded version of thereads
clause for theOver
function is left unproved (and I believe that there is no way to prove it under WP's actual representation of C memory). Note that it implies playing with the TIP feature of WP. Code and scripts are provided below, but I'll start by commenting on your original version:Div
andReminder
. If anything, they will confuse the prover. You can stick withxxx/10
andxxx%10
. In fact, my remark on division and modulo was a bit too cautious. In the present case, everything seems to be fine at this level.Unchanged
could be inlined, but it shouldn't hurt to keep it that way.unsigned char
instead ofchar
, as it avoids some spurious conversions due to integer promotions, and removed any cast in the logic. You normally don't have to introduce such casts in ACSL annotations, except perhaps to indicate that some computation stays within an appropriate range (as in\let x = y + z; x == (int) x;
). Such casts are translated into function calls in the proof obligation, and again it can confuse the provers.over
does indeed contain the potential carry from the previous step (again, a good hint that you're missing an invariant is that a location mentioned inloop assigns
does not appear in anyloop invariant
).over
can only be0
or1
, there's nothing that prevent the addition to overflow theunsigned char
(making it impossible to prove that a C computation withunsigned char
and its ACSL counterpart with unbounded integers give the same result). Due to the recursive nature of theOver
function, this can be established by a so-called lemma function in which the mathematical induction is carried out by a simple for loop with appropriate invariantsa
andb
are left untouched. This is normally implied by theloop assigns
, but having these explicit hypotheses makes it easier during the scripts.To sum up, here is the final code:
The two TIP scripts are available here. To use them, put them in a directory
scripts/typed
next to the code (sayfile.c
) and use this command line:You can change the name of the
scripts
directory if you wish (it must match the argument of-wp-session
), buttyped
and the filenames of the scripts must be as given since WP will use them to detect which proof obligation they are supposed to prove. In GUI mode, you can have a look at the script, but it is probably difficult to understand and explaining each step will not fit in a SO's answer.