-
Notifications
You must be signed in to change notification settings - Fork 93
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
localize / entailer_for_load_tac / unlocalize / Coq 8.17 #756
Comments
#756, just packaged in the same p.r. for convenience
It is still the case that localize/unlocalize is not very robust, in the following sense. The way these tactics are used is,
The problem is that if there are hypotheses above the line before the P.R. #757 patches things back to the status quo up to Coq 8.16. That is, a certain forward symbolic execution using But it's not a very general solution. localize\unlocalize is still fragile, because the forward-symbolic-execution proof between them might entangle the evars. |
What does this mean? Maybe you want to clear all hypotheses that contain evars before running |
I don't know what it means. I find all of this very mysterious. But I don't think your proposal would work. The solution I have adopted is to ensure, whenever possible, that the foward symbolic execution between localize and unlocalize does not instantiate any evars. In the limited situations where localize/unlocalize is used, this is typically just one or two forward statements that load from a record field. |
I mean, This is all running on the assumption that |
The problem is that |
What is the goal and context such that applying the lemma messes with evars? (Does the goal have evars? Is it using an assumption that has evars? Something else?) And regardless, what if you used something like Ltac set_evar ev := let e := fresh "e" in set (e := ev) in *.
(* Alternative if above is too slow *)
(*
Ltac set_evar ev :=
let e := fresh "e" in
pose ev as e;
change ev with e;
repeat match goal with
| [ H : context[ev] |- _ ] => progress change ev with e in H
| [ H := context[ev] |- _ ] => progress change ev with e in (value of H)
end.
*)
Ltac set_evars_in T :=
repeat match T with
| context[?e] => is_evar e; set_evar e
end.
Ltac set_evars :=
match goal with |- ?G => has_evar G; set_evars_in G end;
repeat match goal with
| [ H : ?T |- _ ] => has_evar T; set_evars_in T
| [ H := ?T |- _ ]
=> tryif is_evar T
then fail
else (has_evar T; set_evars_in T)
end.
Ltac generalize_evars :=
set_evars;
repeat match goal with
| [ H := ?e |- _ ] => is_evar e; clearbody H
end. and then replaced |
If you are super interested in this, I can spend an hour and produce instructions for reproducing this so you can see exactly where |
I think I don't have time to dig into it much more than I have, so we should just drop it if the Ltac I gave above for For future reference, I ended up porting this to Ltac2, though, and will leave the Ltac2 code here From Ltac2 Require Import Ltac2.
Ltac2 set_evars_in (t : constr) :=
repeat (match! t with
| context[?e] => if Constr.is_evar e then set $e in * else Control.backtrack_tactic_failure "not evar"
end).
Ltac2 set_evars () :=
set_evars_in (Control.goal ());
List.iter
(fun (_, body, typ)
=> match body with
| Some body => if Constr.is_evar body then () else set_evars_in body
| None => ()
end;
set_evars_in typ)
(List.rev (Control.hyps ())). (* reverse hyps so that we deal with leaf evars before dealing with their dependencies *)
Ltac2 clearbody_evars () :=
Std.clearbody
(List.map
(fun (h, _, _) => h)
(List.filter
(fun (h, body, _)
=> Option.map_default Constr.is_evar false body)
(Control.hyps ()))).
Ltac2 generalize_evars () := set_evars (); clearbody_evars (). (but it's a bit less robust than the Ltac1, I think) |
Thank you for the suggestion. The problem will be, I think, that it's not just a single call to |
It's possible that doing Ltac subst_evars :=
repeat match goal with H := ?e |- _ => is_evar e; subst H end. at |
Remark 1I adjusted the suggestion of @JasonGross to fix a bug and improve its features, as follows (but then see remark 2...)
Remark 2.It probably isn't necessary to search for all evars and protect them. There's one evar in particular that we care about, then one introduced by the |
Improved, more precise fix for issue #756
The
localize / unlocalize
tactics are behaving worse in Coq 8.17 than they were in Coq 8.16. The symptom is that certain hypotheses get cleared from above the line.Initially I thought this was a bug in
refine
, and I reported this as Coq issue 18049. After some very diligent analysis by @JasonGross, I traced it to this:In Coq 8.16, auto does not solve the goal, but
simple apply @TT_right
solves the goal.In Coq 8.17, auto does solve the goal, and info_auto reports
simple apply @TT_right
.In fact, this goal can be solved either by
simple apply @TT_right
or byapply derives_refl
. The former, it turns out, entangles all open existential variables with the current proof goal, and the latter does not. This means that in some other proof goal to be solved later, therefine
tactic will clear a bunch of assumptions from above the line (ifTT_right
were used), or will not do so (ifderives_refl
were used here).Well, it turns out that the
unlocalize
tactic usesrefine
; andrefine
is not really the culprit, the proof goal that's being solved by therefine
is just not solvable if the existentials are entangled. And it turns out thatentailer_for_load_tac
usesquick_typecheck3
which, at some point, usesauto
. In Coq 8.16 (and before), thisauto
did not applyTT_right
, and I suspect the goal was solved some other way (perhaps withderives_refl
). But in Coq 8.17 theauto
appliesTT_right
.An example of
unlocalize
where this matters is at line 162 of this file.One solution that works in that case is to adjust
quick_typecheck3
so that it doestry apply derives_refl
just before it doesauto
. That's not a very general solution, and it does illustrate thatlocalize / unlocalize
is not very robust. But a partial solution might be better than nothing.The text was updated successfully, but these errors were encountered: