Skip to content

Commit 60a109e

Browse files
committed
ecPV.form_read: track Fglob reads alongside Fpvar
1 parent dd9bd93 commit 60a109e

2 files changed

Lines changed: 66 additions & 0 deletions

File tree

src/ecPV.ml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,18 @@ let form_read (env : env) (pvs : pmvs) =
708708
|> Option.some)
709709
m pvs
710710

711+
| Fglob (_, m) when Sid.mem m bds ->
712+
pvs
713+
714+
| Fglob (mp, m) ->
715+
Mid.change
716+
(fun pvs ->
717+
pvs
718+
|> Option.value ~default:PV.empty
719+
|> PV.add_glob env (EcPath.mident mp)
720+
|> Option.some)
721+
m pvs
722+
711723
| Fquant (_, subbds, f) ->
712724
let bds =
713725
List.fold_left

tests/procchange.ec

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,3 +669,57 @@ theory ProcChangePostReadYFailTest.
669669
fail by auto.
670670
abort.
671671
end ProcChangePostReadYFailTest.
672+
673+
(* -------------------------------------------------------------------- *)
674+
(* Regression: [proc change {N} [..]] on an equiv goal whose pre contains
675+
[={glob X}] used to leak the opposite-side memory ident into the
676+
local-equivalence subgoal, which would later make [auto] crash with
677+
[LookupError "&_/<stamp>", please report] inside [t_subst]'s
678+
[is_eq_for_subst].
679+
680+
Cause: [EcPV.form_read] did not handle [Fglob] references, so
681+
[={glob X}] (which expands to [f_eq (Fglob X &1) (Fglob X &2)]) was
682+
invisible to the read-tracker and trivially passed [t_change_stmt]'s
683+
frame filter despite reading from both memories. After substitution
684+
of [(fst me)] only, the opposite-side memory remained as a free
685+
reference in goal1's frame. When [auto] later ran [progress] and
686+
then [subst] on a hypothesis whose body inherited that stale memory,
687+
[LDecl.by_id] failed because the stale ident was not in the LDecl.
688+
689+
The trigger requires [auto] to actually invoke [t_subst] on a
690+
hypothesis whose body contains the leaked memory. The minimal
691+
shape that reaches that state involves an abstract module call
692+
inside the replacement, so that [inline; wp; sim; auto] leaves a
693+
residual implication for [progress] to intro. *)
694+
theory ProcChangeGlobFrameTest.
695+
module type Adv = {
696+
proc main() : int
697+
}.
698+
699+
module M = {
700+
var x : int
701+
}.
702+
703+
module Wrap (A : Adv) = {
704+
proc main() : int = {
705+
var r;
706+
M.x <- 0;
707+
r <@ A.main();
708+
return r;
709+
}
710+
}.
711+
712+
section.
713+
declare module A <: Adv {-M}.
714+
715+
local lemma L &m :
716+
equiv[Wrap(A).main ~ Wrap(A).main :
717+
={glob A, glob M} ==> ={res, glob M}].
718+
proof.
719+
proc.
720+
proc change {1} [1..2] : { r <@ Wrap(A).main(); }.
721+
+ inline; wp; sim; auto.
722+
inline; wp; sim.
723+
qed.
724+
end section.
725+
end ProcChangeGlobFrameTest.

0 commit comments

Comments
 (0)