In the example provided by the official manual of Aorai plugin https://frama-c.com/download/frama-c-aorai-manual.pdf the example.ya file is the following
%accept: S1, S2, S3, S4, S5, S6, S7;
S1 : { CALL(main) } -> S2
;
S2 : { opa().r>=0 } -> S3
;
S3 : { opa().return<=5000 } -> S4
;
S4 : { !RETURN(opa) } -> S5
;
S5 : { RETURN(opb) } -> S6
;
S6 : { RETURN(main) } -> S7
;
S7 : -> S7
;
My question is why do we have to specify at the state S4 that {!RETURN(opa)}
why don't we replace it by { CALL(opb) }
?
The main function corresponding to this automaton is
int main() {
if (rr<5000) rr=opa(rr);
opb();
goto L6;
opc();
L6:
return 1;
}
!RETURN(opa)
indicates that the current event must not be a return
from the opa
function. If you have a look at the whole automaton:
%init: S0;
%accept: S0, S1, S2,S3,S4,S5,S6;
S0 : { CALL(main) } -> S1;
;
S1 : { opa().r<5000 }
-> S2
;
S2 : { opa().return<=5000 } -> S3
;
S3 : { !RETURN(opa) } -> S4
;
S4 : { RETURN(opb) } -> S5
;
S5 : { RETURN(main)} -> S6
;
S6 : -> S6
;
and taking into account that a transition must be crossed at each call or return event, we can see that in fact this event must be a call to opb
(so that we can return from opb
during the transition from S4
to S5
. In fact, if the code is correct with respect to the automaton, there is a call to opa
when in state S1
(with an argument less than 5000
), followed by a return from opa
(with a result less than or equal to 5000
), bringing the program back into main
(the very first transition from S0
to S1
is done at the start of the program).
EDIT following the update to the original question:
As should be clear from the description above, yes, the guard between S3
and S4
could have been written {CALL(opb)}
without really changing the specification.