Skip to content
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

feat: apply_rfl tactic: handle Eq, HEq, better error messages #3714

Merged
merged 21 commits into from
Sep 20, 2024
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
3 changes: 3 additions & 0 deletions src/Lean/Meta/Tactic/Refl.lean
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ namespace Lean.Meta

/--
Close given goal using `Eq.refl`.

See `Lean.MVarId.applyRfl` for the variant that also consults `@[refl]` lemmas, and which
backs the `rfl` tactic.
-/
def _root_.Lean.MVarId.refl (mvarId : MVarId) : MetaM Unit := do
mvarId.withContext do
Expand Down
50 changes: 38 additions & 12 deletions src/Lean/Meta/Tactic/Rfl.lean
Original file line number Diff line number Diff line change
Expand Up @@ -50,33 +50,59 @@ open Elab Tactic

/-- `MetaM` version of the `rfl` tactic.

This tactic applies to a goal whose target has the form `x ~ x`,
where `~` is a reflexive relation other than `=`,
that is, a relation which has a reflexive lemma tagged with the attribute @[refl].
This tactic applies to a goal whose target has the form `x ~ x`, where `~` is a reflexive
relation, that is, equality or another relation which has a reflexive lemma tagged with the
attribute [refl].
-/
def _root_.Lean.MVarId.applyRfl (goal : MVarId) : MetaM Unit := do
let .app (.app rel _) _ ← whnfR <|← instantiateMVars <|← goal.getType
| throwError "reflexivity lemmas only apply to binary relations, not{
indentExpr (← goal.getType)}"
if let .app (.const ``Eq [_]) _ := rel then
throwError "MVarId.applyRfl does not solve `=` goals. Use `MVarId.refl` instead."
def _root_.Lean.MVarId.applyRfl (goal : MVarId) : MetaM Unit := goal.withContext do
-- NB: uses whnfR, we do not want to unfold the relation itself
let t ← whnfR <|← instantiateMVars <|← goal.getType
if t.getAppNumArgs < 2 then
throwError "rfl can only be used on binary relations, not{indentExpr (← goal.getType)}"

-- Special case HEq here as it has a different argument order.
if t.isAppOfArity ``HEq 4 then
let gs ← goal.applyConst ``HEq.refl
unless gs.isEmpty do
throwError MessageData.tagged `Tactic.unsolvedGoals <| m!"unsolved goals\n{
goalsToMessageData gs}"
Comment on lines +67 to +68
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems like it should be a function somewhere

return

let rel := t.appFn!.appFn!
let lhs := t.appFn!.appArg!
let rhs := t.appArg!

let success ← approxDefEq <| isDefEqGuarded lhs rhs
unless success do
let explanation := MessageData.ofLazyM (es := #[lhs, rhs]) do
let (lhs, rhs) ← addPPExplicitToExposeDiff lhs rhs
return m!"The lhs{indentExpr lhs}\nis not definitionally equal to rhs{indentExpr rhs}"
throwTacticEx `apply_rfl goal explanation

if rel.isAppOfArity `Eq 1 then
-- The common case is equality: just use `Eq.refl`
let us := rel.appFn!.constLevels!
let α := rel.appArg!
goal.assign (mkApp2 (mkConst ``Eq.refl us) α lhs)
else
-- Else search through `@refl` keyed by the relation
let s ← saveState
let mut ex? := none
for lem in ← (reflExt.getState (← getEnv)).getMatch rel reflExt.config do
try
let gs ← goal.apply (← mkConstWithFreshMVarLevels lem)
if gs.isEmpty then return () else
logError <| MessageData.tagged `Tactic.unsolvedGoals <| m!"unsolved goals\n{
throwError MessageData.tagged `Tactic.unsolvedGoals <| m!"unsolved goals\n{
goalsToMessageData gs}"
catch e =>
ex? := ex? <|> (some (← saveState, e)) -- stash the first failure of `apply`
unless ex?.isSome do
ex? := some (← saveState, e) -- stash the first failure of `apply`
s.restore
if let some (sErr, e) := ex? then
sErr.restore
throw e
else
throwError "rfl failed, no lemma with @[refl] applies"
throwError "rfl failed, no @[refl] lemma registered for relation{indentExpr rel}"

/-- Helper theorem for `Lean.MVarId.liftReflToEq`. -/
private theorem rel_of_eq_and_refl {α : Sort _} {R : α → α → Prop}
Expand Down
2 changes: 2 additions & 0 deletions stage0/src/stdlib_flags.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ options get_default_options() {
return opts;
}
}

// please update stage0
15 changes: 15 additions & 0 deletions tests/lean/run/rflApplyFoApprox.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

opaque f : Nat → Nat
-- A rewrite with a free variable on the RHS

opaque P : Nat → (Nat → Nat) → Prop
opaque Q : Nat → Prop
opaque foo (g : Nat → Nat) (x : Nat) : P x f ↔ Q (g x) := sorry

example : P x f ↔ Q (x + 10) := by
rewrite [foo]
-- we have an mvar now
with_reducible rfl -- should should instantiate it with the lambda on the RHS and close the goal
-- same as
-- with_reducible (apply (Iff.refl _))
-- NB: apply, not exact! Different defEq flags.
Loading
Loading