This implements the first half of #3302: It improves the extensible `apply_rfl` tactic (the one that looks at `refl` attributes, part of the `rfl` macro) to * Check itself and ahead of time that the lhs and rhs are defEq, and give a nice consistent error message when they don't (instead of just passing on the less helpful error message from `apply Foo.refl`), and using the machinery that `apply` uses to elaborate expressions to highlight diffs in implicit arguments. * Also handle `Eq` and `HEq` (built in) and `Iff` (using the attribute) Care is taken that, as before, the current transparency setting affects comparing the lhs and rhs, but not the reduction of the relation So before we had ```lean opaque P : Nat → Nat → Prop @[refl] axiom P.refl (n : Nat) : P n n /-- error: tactic 'apply' failed, failed to unify P ?n ?n with P 42 23 ⊢ P 42 23 -/ #guard_msgs in example : P 42 23 := by apply_rfl opaque withImplicitNat {n : Nat} : Nat /-- error: tactic 'apply' failed, failed to unify P ?n ?n with P withImplicitNat withImplicitNat ⊢ P withImplicitNat withImplicitNat -/ #guard_msgs in example : P (@withImplicitNat 42) (@withImplicitNat 23) := by apply_rfl ``` and with this PR the messages we get are ``` error: tactic 'apply_rfl' failed, The lhs 42 is not definitionally equal to rhs 23 ⊢ P 42 23 ``` resp. ``` error: tactic 'apply_rfl' failed, The lhs @withImplicitNat 42 is not definitionally equal to rhs @withImplicitNat 23 ⊢ P withImplicitNat withImplicitNat ``` A test file checks the various failure modes and error messages. I believe this `apply_rfl` can serve as the only implementation of `rfl`, which would then complete #3302, and actually expose these improved error messages to the user. But as that seems to require a non-trivial bootstrapping dance, it’ll be separate.
58 lines
2 KiB
Text
58 lines
2 KiB
Text
/-
|
||
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
||
Released under Apache 2.0 license as described in the file LICENSE.
|
||
Authors: Leonardo de Moura
|
||
-/
|
||
prelude
|
||
import Lean.Meta.Reduce
|
||
import Lean.Meta.Tactic.Util
|
||
import Lean.Meta.Tactic.Apply
|
||
|
||
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
|
||
mvarId.checkNotAssigned `refl
|
||
let targetType ← mvarId.getType'
|
||
unless targetType.isAppOfArity ``Eq 3 do
|
||
throwTacticEx `rfl mvarId m!"equality expected{indentExpr targetType}"
|
||
let lhs ← instantiateMVars targetType.appFn!.appArg!
|
||
let rhs ← instantiateMVars targetType.appArg!
|
||
let success ← isDefEq lhs rhs
|
||
unless success do
|
||
throwTacticEx `rfl mvarId m!"equality lhs{indentExpr lhs}\nis not definitionally equal to rhs{indentExpr rhs}"
|
||
let us := targetType.getAppFn.constLevels!
|
||
let α := targetType.appFn!.appFn!.appArg!
|
||
mvarId.assign (mkApp2 (mkConst ``Eq.refl us) α lhs)
|
||
|
||
/--
|
||
Try to apply `heq_of_eq`. If successful, then return new goal, otherwise return `mvarId`.
|
||
-/
|
||
def _root_.Lean.MVarId.heqOfEq (mvarId : MVarId) : MetaM MVarId :=
|
||
mvarId.withContext do
|
||
let some [mvarId] ← observing? do mvarId.apply (mkConst ``heq_of_eq [← mkFreshLevelMVar]) | return mvarId
|
||
return mvarId
|
||
|
||
/--
|
||
Try to apply `eq_of_heq`. If successful, then return new goal, otherwise return `mvarId`.
|
||
-/
|
||
def _root_.Lean.MVarId.eqOfHEq (mvarId : MVarId) : MetaM MVarId :=
|
||
mvarId.withContext do
|
||
let some [mvarId] ← observing? do mvarId.apply (mkConst ``eq_of_heq [← mkFreshLevelMVar]) | return mvarId
|
||
return mvarId
|
||
|
||
/--
|
||
Close given goal using `HEq.refl`.
|
||
-/
|
||
def _root_.Lean.MVarId.hrefl (mvarId : MVarId) : MetaM Unit := do
|
||
mvarId.withContext do
|
||
let some [] ← observing? do mvarId.apply (mkConst ``HEq.refl [← mkFreshLevelMVar])
|
||
| throwTacticEx `hrefl mvarId
|
||
|
||
end Lean.Meta
|