Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions doc/changelog/04-tactics/10760-more-rapply.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
- The tactic :tacn:`rapply` in :g:`Coq.Program.Tactics` now handles
arbitrary numbers of underscores and takes in a :g:`uconstr`. In
rare cases where users were relying on :tacn:`rapply` inserting
exactly 15 underscores and no more, due to the lemma having a
completely unspecified codomain (and thus allowing for any number of
underscores), the tactic will now instead loop. (`#10760
<https://github.com/coq/coq/pull/10760>`_, by Jason Gross)
22 changes: 22 additions & 0 deletions doc/sphinx/proof-engine/tactics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,28 @@ Applying theorems
instantiate (see :ref:`Existential-Variables`). The instantiation is
intended to be found later in the proof.

.. tacv:: rapply @term
:name: rapply

The tactic :tacn:`rapply` behaves like :tacn:`eapply` but it
uses the proof engine of :tacn:`refine` for dealing with
existential variables, holes, and conversion problems. This may
result in slightly different behavior regarding which conversion
problems are solvable. However, like :tacn:`apply` but unlike
:tacn:`eapply`, :tacn:`rapply` will fail if there are any holes
which remain in :n:`@term` itself after typechecking and
typeclass resolution but before unification with the goal. More
technically, :n:`@term` is first parsed as a
:production:`constr` rather than as a :production:`uconstr` or
:production:`open_constr` before being applied to the goal. Note
that :tacn:`rapply` prefers to instantiate as many hypotheses of
:n:`@term` as possible. As a result, if it is possible to apply
:n:`@term` to arbitrarily many arguments without getting a type
error, :tacn:`rapply` will loop.

Note that you need to :n:`Require Import Coq.Program.Tactics` to
make use of :tacn:`rapply`.

.. tacv:: simple apply @term.

This behaves like :tacn:`apply` but it reasons modulo conversion only on subterms
Expand Down
27 changes: 27 additions & 0 deletions test-suite/success/rapply.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Require Import Coq.Program.Tactics.

(** We make a version of [rapply] that takes [uconstr]; we do not
currently test what scope [rapply] interprets terms in. *)

Tactic Notation "urapply" uconstr(p) := rapply p.

Ltac test n :=
(*let __ := match goal with _ => idtac n end in*)
lazymatch n with
| O => let __ := match goal with _ => assert True by urapply I; clear end in
uconstr:(fun _ => I)
| S ?n'
=> let lem := test n' in
let __ := match goal with _ => assert True by (unshelve urapply lem; try exact I); clear end in
uconstr:(fun _ : True => lem)
end.

Goal True.
assert True by urapply I.
assert True by (unshelve urapply (fun _ => I); try exact I).
assert True by (unshelve urapply (fun _ _ => I); try exact I).
assert True by (unshelve urapply (fun _ _ _ => I); try exact I).
clear.
Time let __ := test 50 in idtac.
urapply I.
Qed.
30 changes: 9 additions & 21 deletions theories/Program/Tactics.v
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,12 @@ Ltac destruct_pairs := repeat (destruct_one_pair).

Ltac destruct_one_ex :=
let tac H := let ph := fresh "H" in (destruct H as [H ph]) in
let tac2 H := let ph := fresh "H" in let ph' := fresh "H" in
(destruct H as [H ph ph'])
let tac2 H := let ph := fresh "H" in let ph' := fresh "H" in
(destruct H as [H ph ph'])
in
let tacT H := let ph := fresh "X" in (destruct H as [H ph]) in
let tacT2 H := let ph := fresh "X" in let ph' := fresh "X" in
(destruct H as [H ph ph'])
let tacT2 H := let ph := fresh "X" in let ph' := fresh "X" in
(destruct H as [H ph ph'])
in
match goal with
| [H : (ex _) |- _] => tac H
Expand Down Expand Up @@ -140,7 +140,7 @@ Ltac clear_dups := repeat clear_dup.

(** Try to clear everything except some hyp *)

Ltac clear_except hyp :=
Ltac clear_except hyp :=
repeat match goal with [ H : _ |- _ ] =>
match H with
| hyp => fail 1
Expand Down Expand Up @@ -173,22 +173,10 @@ Ltac on_application f tac T :=
(** A variant of [apply] using [refine], doing as much conversion as necessary. *)

Ltac rapply p :=
refine (p _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) ||
refine (p _ _ _ _ _ _ _ _ _ _ _ _ _ _) ||
refine (p _ _ _ _ _ _ _ _ _ _ _ _ _) ||
refine (p _ _ _ _ _ _ _ _ _ _ _ _) ||
refine (p _ _ _ _ _ _ _ _ _ _ _) ||
refine (p _ _ _ _ _ _ _ _ _ _) ||
refine (p _ _ _ _ _ _ _ _ _) ||
refine (p _ _ _ _ _ _ _ _) ||
refine (p _ _ _ _ _ _ _) ||
refine (p _ _ _ _ _ _) ||
refine (p _ _ _ _ _) ||
refine (p _ _ _ _) ||
refine (p _ _ _) ||
refine (p _ _) ||
refine (p _) ||
refine p.
(** before we try to add more underscores, first ensure that adding such underscores is valid *)
(assert_succeeds (idtac; let __ := open_constr:(p _) in idtac);
rapply uconstr:(p _))
|| refine p.

(** Tactical [on_call f tac] applies [tac] on any application of [f] in the hypothesis or goal. *)

Expand Down