Skip to content

Commit

Permalink
fix: make elabTermEnsuringType respect errToSorry when there is a…
Browse files Browse the repository at this point in the history
… type mismatch (#3633)

Floris van Doorn [reported on
Zulip](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/have.20tactic.20error.20recovery/near/425283053)
that it is confusing that the `have : T := e` tactic completely fails if
the body `e` is not of type `T`. This is in contrast to `have : T := by
exact e`, which does not completely fail when `e` is not of type `T`.

This ends up being caused by `elabTermEnsuringType` throwing an error
when it fails to insert a coercion. Now, it detects this case, and it
checks the `errToSorry` flag to decide whether to throw the error or to
log the error and insert a `sorry`.

This is justified by `elabTermEnsuringType` being a frontend to
`elabTerm`, which inserts `sorry` on error.

An alternative would be to make `ensureType` respect `errToSorry`, but
there exists code that expects being able to catch when `ensureType`
fails. Making such code manipulate `errToSorry` seems error prone, and
this function is not a main entry point to the term elaborator, unlike
`elabTermEnsuringType`.
  • Loading branch information
kmill authored Mar 9, 2024
1 parent b39042b commit 3acd77a
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 6 deletions.
21 changes: 17 additions & 4 deletions src/Lean/Elab/Term.lean
Original file line number Diff line number Diff line change
Expand Up @@ -793,10 +793,10 @@ def mkCoe (expectedType : Expr) (e : Expr) (f? : Option Expr := none) (errorMsgH
| _ => throwTypeMismatchError errorMsgHeader? expectedType (← inferType e) e f?

/--
If `expectedType?` is `some t`, then ensure `t` and `eType` are definitionally equal.
If they are not, then try coercions.
If `expectedType?` is `some t`, then ensures `t` and `eType` are definitionally equal by inserting a coercion if necessary.
Argument `f?` is used only for generating error messages. -/
Argument `f?` is used only for generating error messages when inserting coercions fails.
-/
def ensureHasType (expectedType? : Option Expr) (e : Expr)
(errorMsgHeader? : Option String := none) (f? : Option Expr := none) : TermElabM Expr := do
let some expectedType := expectedType? | return e
Expand Down Expand Up @@ -1432,9 +1432,22 @@ def addDotCompletionInfo (stx : Syntax) (e : Expr) (expectedType? : Option Expr)
def elabTerm (stx : Syntax) (expectedType? : Option Expr) (catchExPostpone := true) (implicitLambda := true) : TermElabM Expr :=
withRef stx <| elabTermAux expectedType? catchExPostpone implicitLambda stx

/--
Similar to `Lean.Elab.Term.elabTerm`, but ensures that the type of the elaborated term is `expectedType?`
by inserting coercions if necessary.
If `errToSorry` is true, then if coercion insertion fails, this function returns `sorry` and logs the error.
Otherwise, it throws the error.
-/
def elabTermEnsuringType (stx : Syntax) (expectedType? : Option Expr) (catchExPostpone := true) (implicitLambda := true) (errorMsgHeader? : Option String := none) : TermElabM Expr := do
let e ← elabTerm stx expectedType? catchExPostpone implicitLambda
withRef stx <| ensureHasType expectedType? e errorMsgHeader?
try
withRef stx <| ensureHasType expectedType? e errorMsgHeader?
catch ex =>
if (← read).errToSorry && ex matches .error .. then
exceptionToSorry ex expectedType?
else
throw ex

/-- Execute `x` and return `some` if no new errors were recorded or exceptions were thrown. Otherwise, return `none`. -/
def commitIfNoErrors? (x : TermElabM α) : TermElabM (Option α) := do
Expand Down
7 changes: 7 additions & 0 deletions tests/lean/issue2260.lean.expected.out
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,10 @@ The termination argument has type
DNat i : Type
but is expected to have type
Nat : Type
issue2260.lean:26:15-26:21: error: failed to prove termination, possible solutions:
- Use `have`-expressions to prove the remaining goals
- Use `termination_by` to specify a different well-founded relation
- Use `decreasing_by` to specify your own tactic for discharging this kind of goal
n✝ : Nat
n : DNat n✝
⊢ sorryAx Nat true < 1 + n✝ + sizeOf n
6 changes: 6 additions & 0 deletions tests/lean/mulcommErrorMessage.lean.expected.out
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ has type
true = true : Prop
but is expected to have type
true = false : Prop
mulcommErrorMessage.lean:12:22-12:25: error: type mismatch
rfl
has type
false = false : Prop
but is expected to have type
false = true : Prop
mulcommErrorMessage.lean:16:3-17:47: error: type mismatch
fun a b => ?m a b
has type
Expand Down
24 changes: 24 additions & 0 deletions tests/lean/run/haveTactic.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/-!
# Tests for the `have` tactic.
-/

/-!
If the body of a `have` fails to elaborate, the tactic completes with a `sorry` for the proof.
-/
/--
error: type mismatch
False.elim
has type
False → ?m.6 : Sort ?u.5
but is expected to have type
True : Prop
---
info: h : True
⊢ True
-/
#guard_msgs in
example : True := by
have h : True :=
False.elim
trace_state
assumption
4 changes: 2 additions & 2 deletions tests/lean/struct1.lean
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ structure S :=
structure S extends A Nat :=
(x : Nat) -- error

structure S extends A Nat :=
structure S' extends A Nat :=
(x := true) -- error type mismatch

structure S extends A Nat :=
(x : Bool := true) -- error omit type

structure S :=
structure S'' :=
(x : Nat := true) -- error type mismatch

private structure S :=
Expand Down

0 comments on commit 3acd77a

Please sign in to comment.