2

I'm using Frama-C version Silicon-20161101. Every time a reference a pointer value *x in an ensures clause the preprocessor inserts *\old(x) unnecessarily. For example

// File swap.c:

/*@ requires \valid(a) && \valid(b);
  @ ensures A: *a == \old(*b) ;
  @ ensures B: *\at(\old(b),Post) == \old(*a) ;
  @ assigns *a,*b ;
  @*/
void swap(int *a,int *b)
{
   int tmp = *a ;
   *a = *b ;
   *b = tmp ;
   return ;
} 

when processed with frama-c swap.c -print outputs

/* Generated by Frama-C */
/*@ requires \valid(a) ∧ \valid(b);
    ensures A: *\old(a) ≡ \old(*b);
    ensures B: *\old(b) ≡ \old(*a);
    assigns *a, *b;
 */
void swap(int *a, int *b)
{
  int tmp;
  tmp = *a;
  *a = *b;
  *b = tmp;
  return;
}

Interestingly enough, this is still verified as correct by the WP plugin! I assume this is because *\old(a) is the post value of \old(a) (which is still the same pointer since it hasn't been changed)? Is this a bug? Is there any quick user end fix for this?

byako
  • 3,372
  • 2
  • 21
  • 36
cdalves
  • 21
  • 3

1 Answers1

2

There are two points in your question. First, *\old(a) should not be confused with \old(*a): in both cases, we evaluate the pointer a in the Old state, but in *\old(a), we dereference it in the current state, while in \old(*a), the dereference is also done in the Old state.

Second, formal parameters are a bit special: as mentioned in the ACSL manual, in the post-state of the function they have the same the value as in the pre-state, even if they have been modified by the function, such as in the following:

/*@ 
     requires \valid(a + 0 .. 1);
     ensures *a == \old(*a) + 1;
     ensures a[1] == \old(a[1]) + 1;
*/
void f(int *a) {
  *a = *a + 1;
  a++;
  *a = *a + 1;
}

The rationale here is that since C has a call-by-value semantics, the outside world cannot see any internal modification made to the formal (which acts as a local variable for this purpose). In other words, as long as the contract is concerned, the value tied to a in the example above is the one of the actual argument passed to it, no matter what happens in the implementation of the function. Note that in the case of a pointer, this of course applies only to the value of the pointer itself, not to the values stored in the memory block it points to.

In order to make this semantics explicit, the type-checker wraps all occurrences of a formal parameter in an ensures clause inside \old.

You may also want to see this answer to a related, albeit not completely similar question.

Virgile
  • 9,724
  • 18
  • 42