eager call reduces S; x <@ f(a) ~ x' <@ f'(a'); S' : P ==> Q to the eager premise S, f ~ f', S' plus a wp. On the right, x' := res is executed before S', but the eager premise relates memories after f'; S' with the result not stored, and the only side condition checked is that S does not write the left arguments. If S' writes x', the wp still substitutes x'{2} := res (the substitution happens before generalizing over write(S')); if S' reads x', the premise was proved for an S' that saw the pre-call value. Both cases prove a false equiv.
MRE (write case):
require import AllCore.
module M = { var r : int proc f() : int = { return 1; } }.
lemma eg : eager[M.r <- 0;, M.f ~ M.f, M.r <- 0; : true ==> res{1} = 1 /\ res{2} = 1].
proof. by eager proc; auto. qed.
module G = {
proc left() : int = { M.r <- 0; M.r <@ M.f(); return M.r; } (* returns 1 *)
proc right() : int = { M.r <@ M.f(); M.r <- 0; return M.r; } (* returns 0 *)
}.
lemma eqv : equiv[G.left ~ G.right : true ==> res{1} = res{2}]. (* FALSE *)
proof. proc. eager call eg. by auto. qed.
lemma bad &m : false.
proof.
have h1 : Pr[G.left() @ &m : res = 1] = 1%r by byphoare => //; proc; inline *; auto.
have h2 : Pr[G.right() @ &m : res = 1] = 0%r by byphoare => //; hoare; proc; inline *; auto.
have h3 : Pr[G.left() @ &m : res = 1] = Pr[G.right() @ &m : res = 1] by byequiv eqv.
smt().
qed.
Read case: replace M.r <- 0 by a call M.cp() with proc cp() = { M.t <- M.r; } and return M.t (left returns the old M.r, right returns 1); eager call eg; auto is accepted the same way and false follows through a wrapper that sets M.r <- 0.
Root cause: src/phl/ecPhlEager.ml, t_eager_call_r (l. 395-428) only runs check_a on the left arguments and calls EcPhlCall.compute_equiv_call_post ~mods:(swl, swr) (src/phl/ecPhlCall.ml:78-133), which applies wp_asgn_call for the right lvalue (l. 107) before generalize_mod_ts_inv over write(S') ∪ write(f') (l. 119). Missing side condition: the right lvalue must be disjoint from read(S') ∪ write(S') (reordering the substitution and the generalization would only fix the write case).
eager callreducesS; x <@ f(a) ~ x' <@ f'(a'); S' : P ==> Qto the eager premiseS, f ~ f', S'plus awp. On the right,x' := resis executed beforeS', but the eager premise relates memories afterf'; S'with the result not stored, and the only side condition checked is thatSdoes not write the left arguments. IfS'writesx', thewpstill substitutesx'{2} := res(the substitution happens before generalizing overwrite(S')); ifS'readsx', the premise was proved for anS'that saw the pre-call value. Both cases prove a falseequiv.MRE (write case):
Read case: replace
M.r <- 0by a callM.cp()withproc cp() = { M.t <- M.r; }and returnM.t(left returns the oldM.r, right returns 1);eager call eg; autois accepted the same way andfalsefollows through a wrapper that setsM.r <- 0.Root cause:
src/phl/ecPhlEager.ml,t_eager_call_r(l. 395-428) only runscheck_aon the left arguments and callsEcPhlCall.compute_equiv_call_post ~mods:(swl, swr)(src/phl/ecPhlCall.ml:78-133), which applieswp_asgn_callfor the right lvalue (l. 107) beforegeneralize_mod_ts_invoverwrite(S') ∪ write(f')(l. 119). Missing side condition: the right lvalue must be disjoint fromread(S') ∪ write(S')(reordering the substitution and the generalization would only fix the write case).