This PR introduces a clarifying note to "undefined identifier" error messages when the undefined identifier is in a syntactic position where autobinding might generally apply, but where and autobinding is disabled. A corresponding note is made in the `lean.unknownIdentifier` error explanation. The core intended audience for this error message change is "newcomer who would otherwise be baffled why the thing that works in this Mathlib project gets 'unknown identifier' errors in this non-Mathlib project." ## Modified behavior ### Example 1 ```lean4 set_option autoImplicit true in set_option relaxedAutoImplicit false in def thisBreaks (x : α₂) (y : size₂) := () ``` Before: ``` Unknown identifier `size₂` ``` After: ``` Unknown identifier `size₂` Note: It is not possible to treat `size₂` as an implicitly bound variable here because it has multiple characters while the `relaxedAutoImplicit` option is set to `false`. ``` ### Example 2 ```lean4 set_option autoImplicit false in def thisAlsoBreaks (x : α₃) (y : size₃) := () ``` Before: ``` Unknown identifier `α₃` Unknown identifier `size₃` ``` After: ``` Unknown identifier `α₃` Note: It is not possible to treat `α₃` as an implicitly bound variable here because the `autoImplicit` option is set to `false`. Unknown identifier `size₃` Note: It is not possible to treat `size₃` as an implicitly bound variable here because the `autoImplicit` option is set to `false`. ``` ## How this works The elaboration process knows whether it is considering syntax where we be able to auto-bind implicits thanks to information in the `Lean.Elab.Term.Context`. Before this PR, this contains: * `autoBoundImplicit`, a boolean that is true when we are considering syntax that might be able to auto-bind implicit AND when the `autoImplicit` flag is set to true * `autoBoundImplicits`, an array of `Expr` variables that we've autobound After this PR, this contains: * `autoBoundImplicitCtx`, an option which is `some` **whenever** we are considering syntax that might be able to auto-bind implicit, and carries the array of exprs as well as a copy of the `autoImplicit` flag's value. (The latter lets us re-implement the `autoBoundImplicit` flag for backward compatibility.) Therefore, rather than having access to "elaboration is in an autobinding context && flag is enabled", it's possible to recover both of those individual values, and give different information to the user in cases where we didn't attempt autobinding but would have if different options had been set. ## Rationale The revised error message avoids offering much guidance — it doesn't actively suggest setting the option to a different value or suggest adding an implicit binding. Care needs to be taken here to make sure advice is not misleading; as the accepted RFC in #6462 points out, a substantial portion of autobinding failures are just going to be misspellings. I considered and then rejected a code action here to that would add a local `set_option autoImplicit true`. This seems undesirable or counterproductive — if a project like Mathlib has proactively disabled `autoImplicit`, its odd to be pushing local exceptions. A hint prompting the user to add an implicit binding would be more proper, but only in certain circumstances — we want to be conservative in suggesting specific code actions! In a situation like this one, we'd want to _avoid_ giving the suggestion of adding a `{HasArr}` binding, which I think either requires tricky heuristics or means we'd want the elaboration to play through the consequences of auto-binding and make sure it doesn't cause any follow-on errors before suggesting adding an implicit binding. ``` set_option autoImplicit true set_option relaxedAutoImplicit false instance has_arr : HasArr Preorder := { Arr := Function } ``` Additionally, it seems like it would make the most sense to offer to auto-bind _all_ the relevant unknown identifiers at once. To avoid being misleading, this too would seem to require playing through the consequences of autobinding before being able to safely suggest the change. This is enough additional complexity that I'm leaving it for future work. --------- Co-authored-by: David Thrane Christiansen <david@davidchristiansen.dk>
131 lines
3.1 KiB
Text
131 lines
3.1 KiB
Text
/-!
|
|
# Tests of the `sorry` term elaborator
|
|
-/
|
|
|
|
set_option pp.mvars false
|
|
|
|
/-!
|
|
Basic usage.
|
|
-/
|
|
|
|
/-- warning: declaration uses 'sorry' -/
|
|
#guard_msgs in example : False := sorry
|
|
|
|
/-- warning: declaration uses 'sorry' -/
|
|
#guard_msgs in example : False := by sorry
|
|
|
|
/-!
|
|
Pretty printing
|
|
-/
|
|
|
|
/-- info: sorry : Nat -/
|
|
#guard_msgs in #check (sorry : Nat)
|
|
|
|
/-- info: fun x => sorry : Nat → Nat -/
|
|
#guard_msgs in #check fun x : Nat => (sorry : Nat)
|
|
|
|
/-- info: fun x => sorry (x + 1) : Nat → Nat -/
|
|
#guard_msgs in #check fun x : Nat => (sorry : Nat → Nat) (x + 1)
|
|
|
|
/-!
|
|
Uniqueness
|
|
-/
|
|
|
|
/-- warning: declaration uses 'sorry' -/
|
|
#guard_msgs in
|
|
example : (sorry : Nat) = sorry := by
|
|
fail_if_success rfl
|
|
sorry
|
|
|
|
/-- warning: declaration uses 'sorry' -/
|
|
#guard_msgs in
|
|
def f (n : Nat) : Nat → Nat := sorry
|
|
|
|
example : f 0 0 = f 0 0 := rfl -- succeeds
|
|
|
|
/-!
|
|
If `sorry` is used for a function type, then one gets a family of unique `sorry`s.
|
|
-/
|
|
/--
|
|
error: Type mismatch
|
|
rfl
|
|
has type
|
|
?_ = ?_
|
|
but is expected to have type
|
|
f 0 1 = f 0 0
|
|
-/
|
|
#guard_msgs in example : f 0 1 = f 0 0 := rfl
|
|
|
|
/-!
|
|
It is not completely unique though. The `sorry` did not pay attention to variables in the local context.
|
|
-/
|
|
#guard_msgs in example : f 1 0 = f 0 0 := rfl
|
|
|
|
/-!
|
|
Showing source position when surfacing differences.
|
|
-/
|
|
-- note: the module name is `sorry` and not `lean.run.sorry` in the testing environment,
|
|
-- so this test fails in VS Code.
|
|
/--
|
|
error: Type mismatch
|
|
sorry
|
|
has type
|
|
sorry `«lean.run.sorry:77:43»
|
|
but is expected to have type
|
|
sorry `«lean.run.sorry:77:25»
|
|
-/
|
|
#guard_msgs in example : sorry := (sorry : sorry)
|
|
|
|
/-!
|
|
Elaboration errors are just labeled, not unique, to limit cascading errors.
|
|
-/
|
|
/--
|
|
error: Unknown identifier `a`
|
|
|
|
Note: It is not possible to treat `a` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.
|
|
---
|
|
error: Unknown identifier `b`
|
|
|
|
Note: It is not possible to treat `b` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.
|
|
---
|
|
trace: ⊢ sorry = sorry
|
|
-/
|
|
#guard_msgs in
|
|
set_option autoImplicit false in
|
|
example : a = b := by trace_state; rfl
|
|
|
|
/-!
|
|
Showing that the sorries in the previous test are labeled.
|
|
-/
|
|
/--
|
|
error: Unknown identifier `a`
|
|
|
|
Note: It is not possible to treat `a` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.
|
|
---
|
|
error: Unknown identifier `b`
|
|
|
|
Note: It is not possible to treat `b` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.
|
|
---
|
|
trace: ⊢ sorry `«lean.run.sorry:114:10» = sorry `«lean.run.sorry:114:14»
|
|
-/
|
|
#guard_msgs in
|
|
set_option autoImplicit false in
|
|
set_option pp.sorrySource true in
|
|
example : a = b := by trace_state; rfl
|
|
|
|
/-!
|
|
The local context should show `sorry` without showing its source position.
|
|
This requires `Lean.Widget.ppExprTagged` to have a pretty printing mode that doesn't override any pretty printing options.
|
|
https://github.com/leanprover/lean4/issues/6715
|
|
-/
|
|
/--
|
|
trace: n : Nat := sorry
|
|
⊢ True
|
|
---
|
|
warning: declaration uses 'sorry'
|
|
-/
|
|
#guard_msgs in
|
|
example : True := by
|
|
let n : Nat := sorry
|
|
trace_state
|
|
trivial
|