Skip to content

Commit f81e649

Browse files
feat: improve error when an identifier is unbound because autoImplicit is off (#11119)
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 <[email protected]>
1 parent 5bb9839 commit f81e649

File tree

9 files changed

+231
-52
lines changed

9 files changed

+231
-52
lines changed

src/Lean/Elab/AutoBound.lean

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ module
77

88
prelude
99
public import Lean.Data.Options
10+
public import Lean.Message
11+
public import Lean.Meta.Hint
1012

1113
public section
1214

@@ -24,7 +26,14 @@ register_builtin_option relaxedAutoImplicit : Bool := {
2426
descr := "When \"relaxed\" mode is enabled, any atomic nonempty identifier is eligible for auto bound implicit locals (see option `autoImplicit`)."
2527
}
2628

29+
/--
30+
Checks whether a string is a name that can be auto-bound when the `relaxedAutoImplicit` option is
31+
set to `false`.
2732
33+
In "strict" auto implicit mode, a identifier can only be auto-bound if it is a single character (`α`
34+
or `x`) or has an arbitrary postfix sequence of numbers, subscripts, and underscores. Therefore,
35+
both `αᵣₒₛₑ₂₁₁'''` and `X123_45` can be auto-bound even with `relaxedAutoBound` set to `false`.
36+
-/
2837
private def isValidAutoBoundSuffix (s : String) : Bool :=
2938
s.toRawSubstring.drop 1 |>.all fun c => c.isDigit || isSubScriptAlnum c || c == '_' || c == '\''
3039

@@ -39,17 +48,56 @@ Thus, in the example above, when `A` is expanded, a `x` with a fresh macro scope
3948
`x`+macros-scope is not in scope and is a valid auto-bound implicit name after macro scopes are erased.
4049
So, an auto-bound exception would be thrown, and `x`+macro-scope would be added as a new implicit.
4150
When, we try again, a `x` with a new macro scope is created and this process keeps repeating.
42-
Therefore, we do consider identifier with macro scopes anymore.
51+
Therefore, we don't consider identifier with macro scopes anymore.
52+
53+
An `.error` value should be treated as a `false`—this is not a valid auto-bound implicit name—
54+
but it contains additional notes (above and beyond `Unknown identifier`) to attach to
55+
an error message.
4356
-/
4457

45-
def isValidAutoBoundImplicitName (n : Name) (relaxed : Bool) : Bool :=
58+
def checkValidAutoBoundImplicitName (n : Name) (allowed : Bool) (relaxed : Bool) : Except MessageData Bool :=
4659
match n with
47-
| .str .anonymous s => s.length > 0 && (relaxed || isValidAutoBoundSuffix s)
48-
| _ => false
60+
| .str .anonymous s =>
61+
if s.length = 0 then
62+
.ok false
63+
else if allowed && (relaxed || isValidAutoBoundSuffix s) then
64+
.ok true
65+
else if !allowed then
66+
.error <| .note m!"It is not possible to treat `{.ofConstName n}` as an implicitly bound variable here because the `autoImplicit` option is set to `{.ofConstName ``false}`."
67+
else
68+
.error <| .note m!"It is not possible to treat `{.ofConstName n}` as an implicitly bound variable here because it has multiple characters while the `relaxedAutoImplicit` option is set to `{.ofConstName ``false}`."
69+
| _ => .ok false
4970

5071
def isValidAutoBoundLevelName (n : Name) (relaxed : Bool) : Bool :=
5172
match n with
5273
| .str .anonymous s => s.length > 0 && (relaxed || (s.front.isLower && isValidAutoBoundSuffix s))
5374
| _ => false
5475

76+
/--
77+
Tracks extra context needed within the scope of `Lean.Elab.Term.withAutoBoundImplicit`
78+
-/
79+
public structure AutoBoundImplicitContext where
80+
/--
81+
This always matches the `autoImplicit` option; it is duplicated here in
82+
order to support the behavior of the deprecated `Lean.Elab.Term.Context.autoImplicit`
83+
method.
84+
-/
85+
autoImplicitEnabled : Bool
86+
/--
87+
Tracks a working set of variables that the auto-binding process currently
88+
anticipates adding implicit binding for.
89+
-/
90+
boundVariables : PArray Expr := {}
91+
deriving Inhabited
92+
93+
instance : EmptyCollection AutoBoundImplicitContext where
94+
emptyCollection := AutoBoundImplicitContext.mk (autoImplicitEnabled := false) (boundVariables := {})
95+
96+
/--
97+
Pushes a new variable onto the autoImplicit context, indicating that it needs
98+
to be bound as an implicit parameter.
99+
-/
100+
public def AutoBoundImplicitContext.push (ctx : AutoBoundImplicitContext) (x : Expr) :=
101+
{ ctx with boundVariables := ctx.boundVariables.push x }
102+
55103
end Lean.Elab

src/Lean/Elab/Term/TermElabM.lean

Lines changed: 75 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -258,33 +258,41 @@ structure Context where
258258
declName? : Option Name := none
259259
macroStack : MacroStack := []
260260
/--
261-
When `mayPostpone == true`, an elaboration function may interrupt its execution by throwing `Exception.postpone`.
262-
The function `elabTerm` catches this exception and creates fresh synthetic metavariable `?m`, stores `?m` in
263-
the list of pending synthetic metavariables, and returns `?m`. -/
261+
When `mayPostpone == true`, an elaboration function may interrupt its execution by throwing `Exception.postpone`.
262+
The function `elabTerm` catches this exception and creates fresh synthetic metavariable `?m`, stores `?m` in
263+
the list of pending synthetic metavariables, and returns `?m`.
264+
-/
264265
mayPostpone : Bool := true
265266
/--
266-
When `errToSorry` is set to true, the method `elabTerm` catches
267-
exceptions and converts them into synthetic `sorry`s.
268-
The implementation of choice nodes and overloaded symbols rely on the fact
269-
that when `errToSorry` is set to false for an elaboration function `F`, then
270-
`errToSorry` remains `false` for all elaboration functions invoked by `F`.
271-
That is, it is safe to transition `errToSorry` from `true` to `false`, but
272-
we must not set `errToSorry` to `true` when it is currently set to `false`. -/
267+
When `errToSorry` is set to true, the method `elabTerm` catches
268+
exceptions and converts them into synthetic `sorry`s.
269+
The implementation of choice nodes and overloaded symbols rely on the fact
270+
that when `errToSorry` is set to false for an elaboration function `F`, then
271+
`errToSorry` remains `false` for all elaboration functions invoked by `F`.
272+
That is, it is safe to transition `errToSorry` from `true` to `false`, but
273+
we must not set `errToSorry` to `true` when it is currently set to `false`.
274+
-/
273275
errToSorry : Bool := true
274276
/--
275-
When `autoBoundImplicit` is set to true, instead of producing
276-
an "unknown identifier" error for unbound variables, we generate an
277-
internal exception. This exception is caught at `withAutoBoundImplicit`
278-
which adds an implicit declaration for the unbound variable and tries again. -/
279-
autoBoundImplicit : Bool := false
280-
autoBoundImplicits : PArray Expr := {}
277+
During elaboration we track the current context for adding auto-bound
278+
implicit variables. (We mark the entry to such a region with
279+
`withAutoBoundImplicit` and leave it with `withoutAutoBoundImplicit`.)
280+
281+
When the `autoImplicit` option is `false`, this context is only used to
282+
affect error messages. When `autoImplicit` is `true` and an identifier is
283+
unbound and potentially an auto-bound implicit, an internal exception is
284+
thrown and caught at the closest surrounding `withAutoBoundImplicit`,
285+
which adds an implicit declaration for the unbound variable and tries
286+
again.
287+
-/
288+
autoBoundImplicitContext : Option AutoBoundImplicitContext := .none
281289
/--
282-
A name `n` is only eligible to be an auto implicit name if `autoBoundImplicitForbidden n = false`.
283-
We use this predicate to disallow `f` to be considered an auto implicit name in a definition such
284-
as
285-
```
286-
def f : f → Bool := fun _ => true
287-
```
290+
A name `n` is only eligible to be an auto implicit name if `autoBoundImplicitForbidden n = false`.
291+
We use this predicate to disallow `f` to be considered an auto implicit name in a definition such
292+
as
293+
```
294+
def f : f → Bool := fun _ => true
295+
```
288296
-/
289297
autoBoundImplicitForbidden : Name → Bool := fun _ => false
290298
/-- Map from user name to internal unique name -/
@@ -333,6 +341,12 @@ structure Context where
333341
abbrev TermElabM := ReaderT Context $ StateRefT State MetaM
334342
abbrev TermElab := Syntax → Option Expr → TermElabM Expr
335343

344+
@[deprecated "replace with a check of autoBoundImplicitContext" (since := "2025-11-11")]
345+
def Context.autoBoundImplicit (ctx : Context) : Bool :=
346+
match ctx.autoBoundImplicitContext with
347+
| .none => false
348+
| .some subCtx => subCtx.autoImplicitEnabled
349+
336350
abbrev FixedTermElab := Option Expr → TermElabM Expr
337351

338352
unsafe def FixedTermElab.toFixedTermElabRefImpl (m : FixedTermElab) : FixedTermElabRef :=
@@ -635,7 +649,11 @@ instance : MonadParentDecl TermElabM where
635649
getParentDeclName? := getDeclName?
636650

637651
instance : MonadAutoImplicits TermElabM where
638-
getAutoImplicits := return (← read).autoBoundImplicits.toArray
652+
getAutoImplicits := do
653+
if let .some implicits := (← read).autoBoundImplicitContext then
654+
pure implicits.boundVariables.toArray
655+
else
656+
pure #[]
639657

640658
/--
641659
Executes `x` in the context of the given declaration name. Ensures that the info tree is set up
@@ -751,7 +769,11 @@ def liftLevelM (x : LevelElabM α) : TermElabM α := do
751769
let ctx ← read
752770
let mctx ← getMCtx
753771
let ngen ← getNGen
754-
let lvlCtx : Level.Context := { options := (← getOptions), ref := (← getRef), autoBoundImplicit := ctx.autoBoundImplicit }
772+
let lvlCtx : Level.Context := {
773+
options := (← getOptions),
774+
ref := (← getRef),
775+
autoBoundImplicit := ctx.autoBoundImplicitContext.map (·.autoImplicitEnabled) |>.getD false
776+
}
755777
match (x lvlCtx).run { ngen := ngen, mctx := mctx, levelNames := (← getLevelNames) } with
756778
| .ok a newS => setMCtx newS.mctx; setNGen newS.ngen; setLevelNames newS.levelNames; pure a
757779
| .error ex _ => throw ex
@@ -1812,28 +1834,30 @@ def elabType (stx : Syntax) : TermElabM Expr := do
18121834
Enable auto-bound implicits, and execute `k` while catching auto bound implicit exceptions. When an exception is caught,
18131835
a new local declaration is created, registered, and `k` is tried to be executed again. -/
18141836
partial def withAutoBoundImplicit (k : TermElabM α) : TermElabM α := do
1815-
let flag := autoImplicit.get (← getOptions)
1816-
if flag then
1817-
withReader (fun ctx => { ctx with autoBoundImplicit := flag, autoBoundImplicits := {} }) do
1818-
let rec loop (s : SavedState) : TermElabM α := withIncRecDepth do
1819-
checkSystem "auto-implicit"
1820-
try
1837+
let autoImplicitEnabled := autoImplicit.get (← getOptions)
1838+
let initCtx : AutoBoundImplicitContext := { autoImplicitEnabled }
1839+
if autoImplicitEnabled then
1840+
let rec loop (s : SavedState) (ctx : AutoBoundImplicitContext) : TermElabM α := withIncRecDepth do
1841+
checkSystem "auto-implicit"
1842+
try
1843+
withReader ({ · with autoBoundImplicitContext := .some ctx }) <|
18211844
withSaveAutoImplicitInfoContext k
1822-
catch
1823-
| ex => match isAutoBoundImplicitLocalException? ex with
1824-
| some n =>
1825-
-- Restore state, declare `n`, and try again
1826-
s.restore (restoreInfo := true)
1827-
withLocalDecl n .implicit (← mkFreshTypeMVar) fun x =>
1828-
withReader (fun ctx => { ctx with autoBoundImplicits := ctx.autoBoundImplicits.push x } ) do
1829-
loop (← saveState)
1830-
| none => throw ex
1831-
loop (← saveState)
1845+
catch
1846+
| ex => match isAutoBoundImplicitLocalException? ex with
1847+
| some n =>
1848+
-- Restore state, declare `n`, and try again
1849+
s.restore (restoreInfo := true)
1850+
withLocalDecl n .implicit (← mkFreshTypeMVar) fun x => do
1851+
loop (← saveState) (ctx.push x)
1852+
| none => throw ex
1853+
loop (← saveState) initCtx
18321854
else
1833-
k
1855+
-- Track whether we are in an auto-bound context regardless of whether
1856+
-- the `autoImplicit` flag is enabled; this can influence error messages.
1857+
withReader ({ · with autoBoundImplicitContext := .some initCtx}) k
18341858

18351859
def withoutAutoBoundImplicit (k : TermElabM α) : TermElabM α := do
1836-
withReader (fun ctx => { ctx with autoBoundImplicit := false, autoBoundImplicits := {} }) k
1860+
withReader (fun ctx => { ctx with autoBoundImplicitContext := .none }) k
18371861

18381862
partial def withAutoBoundImplicitForbiddenPred (p : Name → Bool) (x : TermElabM α) : TermElabM α := do
18391863
withReader (fun ctx => { ctx with autoBoundImplicitForbidden := fun n => p n || ctx.autoBoundImplicitForbidden n }) x
@@ -1922,7 +1946,7 @@ def addAutoBoundImplicitsInlayHint (autos : Array Expr) (inlayHintPos : String.P
19221946
use-case may not be able to handle the metavariables in the array being given to `k`.
19231947
-/
19241948
def addAutoBoundImplicits (xs : Array Expr) (inlayHintPos? : Option String.Pos.Raw) : TermElabM (Array Expr) := do
1925-
let autos := (← read).autoBoundImplicits
1949+
let autos ← getAutoImplicits
19261950
go autos.toList #[]
19271951
where
19281952
go (todo : List Expr) (autos : Array Expr) : TermElabM (Array Expr) := do
@@ -2034,10 +2058,14 @@ where
20342058
if env.isExporting then
20352059
if let [(npriv, _)] ← withoutExporting <| resolveGlobalName (enableLog := false) n then
20362060
throwUnknownIdentifierAt (declHint := npriv) stx m!"Unknown identifier `{.ofConstName n}`"
2037-
if (← read).autoBoundImplicit &&
2038-
!(← read).autoBoundImplicitForbidden n &&
2039-
isValidAutoBoundImplicitName n (relaxedAutoImplicit.get (← getOptions)) then
2040-
throwAutoBoundImplicitLocal n
2061+
if !(← read).autoBoundImplicitForbidden n then
2062+
if (← read).autoBoundImplicitContext.isSome then
2063+
let allowed := autoImplicit.get (← getOptions)
2064+
let relaxed := relaxedAutoImplicit.get (← getOptions)
2065+
match checkValidAutoBoundImplicitName n (allowed := allowed) (relaxed := relaxed) with
2066+
| .ok true => throwAutoBoundImplicitLocal n
2067+
| .ok false => throwUnknownIdentifierAt (declHint := n) stx m!"Unknown identifier `{.ofConstName n}`"
2068+
| .error msg => throwUnknownIdentifierAt (declHint := n) stx (m!"Unknown identifier `{.ofConstName n}`" ++ msg)
20412069
throwUnknownIdentifierAt (declHint := n) stx m!"Unknown identifier `{.ofConstName n}`"
20422070

20432071
/--

src/Lean/ErrorExplanations/UnknownIdentifier.lean

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,51 @@ the expected type of the expression in which it occurs, which—due to the type
184184
this code seemingly intended—use *generalized field notation* as shown in the first corrected
185185
example. Alternatively, the correct namespace can be explicitly specified by writing the fully
186186
qualified function name.
187+
188+
## Auto-bound variables
189+
190+
```lean broken
191+
set autoImplicit false in
192+
def thisBreaks (x : α₁) (y : size₁) := ()
193+
194+
set relaxedAutoImplicit false in
195+
def thisBreaks (x : α₂) (y : size₂) := ()
196+
```
197+
```output
198+
Unknown identifier `size₁`
199+
200+
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`.
201+
Unknown identifier `α₂`
202+
203+
Note: It is not possible to treat `α₂` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.
204+
Unknown identifier `size₂`
205+
206+
Note: It is not possible to treat `size₂` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.
207+
```
208+
```lean fixed (title := "Fixed (modifying options)")
209+
set autoImplicit true in
210+
def thisBreaks (x : α₁) (y : size₁) := ()
211+
212+
set relaxedAutoImplicit true in
213+
def thisBreaks (x : α₂) (y : size₂) := ()
214+
```
215+
```lean fixed (title := "Fixed (add implicit bindings for the unknown identifiers)")
216+
set autoImplicit false in
217+
def thisBreaks {size₁} (x : α₁) (y : size₁) := ()
218+
219+
set relaxedAutoImplicit false in
220+
def thisBreaks {α₂ size₂} (x : α₂) (y : size₂) := ()
221+
```
222+
223+
Lean's default behavior, when it encounters an identifier it can't identify in the type of a
224+
definition, is to add [automatic implicit parameters](lean-manual://section/automatic-implicit-parameters)
225+
for those unknown identifiers. However, many files or projects disable this feature by setting the
226+
`autoImplicit` or `relaxedAutoImplicit` options to `false`.
227+
228+
Without re-enabling the `autoImplicit` or `relaxedAutoImplicit` options, the easiest way to fix
229+
this error is to add the unknown identifiers as [ordinary implicit parameters](lean-manual://section/implicit-functions)
230+
as shown in the example above.
231+
187232
-/
188233
register_error_explanation lean.unknownIdentifier {
189234
summary := "Failed to resolve identifier to variable or constant."

tests/lean/1011.lean.expected.out

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
1011.lean:6:11-6:13: error(lean.unknownIdentifier): Unknown identifier `AA`
2+
3+
Note: It is not possible to treat `AA` as an implicitly bound variable here because it has multiple characters while the `relaxedAutoImplicit` option is set to `false`.

tests/lean/autoBoundImplicits1.lean.expected.out

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,26 @@ myid 10 : Nat
22
myid true : Bool
33
autoBoundImplicits1.lean:16:4-16:11: warning: declaration uses 'sorry'
44
autoBoundImplicits1.lean:20:25-20:29: error(lean.unknownIdentifier): Unknown identifier `size`
5+
6+
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`.
57
autoBoundImplicits1.lean:24:23-24:24: error(lean.unknownIdentifier): Unknown identifier `α`
8+
9+
Note: It is not possible to treat `α` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.
610
autoBoundImplicits1.lean:24:25-24:26: error(lean.unknownIdentifier): Unknown identifier `n`
11+
12+
Note: It is not possible to treat `n` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.
713
autoBoundImplicits1.lean:24:33-24:34: error(lean.unknownIdentifier): Unknown identifier `α`
14+
15+
Note: It is not possible to treat `α` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.
816
autoBoundImplicits1.lean:24:37-24:38: error(lean.unknownIdentifier): Unknown identifier `β`
17+
18+
Note: It is not possible to treat `β` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.
919
autoBoundImplicits1.lean:24:46-24:47: error(lean.unknownIdentifier): Unknown identifier `β`
20+
21+
Note: It is not possible to treat `β` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.
1022
autoBoundImplicits1.lean:24:48-24:49: error(lean.unknownIdentifier): Unknown identifier `n`
23+
24+
Note: It is not possible to treat `n` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.
1125
f {α : Type} {n : Nat} (xs : Vec α n) : Vec α n
1226
f mkVec : Vec ?m 0
1327
f mkVec : Vec Nat 0
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
set_option autoImplicit true in
3+
set_option relaxedAutoImplicit true in
4+
set_option linter.unusedVariables false in
5+
def thisWorks (x : α₁) (y : size₁) := ()
6+
7+
set_option autoImplicit true in
8+
set_option relaxedAutoImplicit false in
9+
def thisBreaks (x : α₂) (y : size₂) := ()
10+
11+
set_option autoImplicit false in
12+
set_option relaxedAutoImplicit true in
13+
def thisAlsoBreaks (x : α₃) (y : size₃) := ()
14+
15+
set_option autoImplicit false in
16+
set_option relaxedAutoImplicit false in
17+
def thisDefinitelyBreaks (x : α₄) (y : size₄) := ()
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
autoBoundImplicits3.lean:9:29-9:34: error(lean.unknownIdentifier): Unknown identifier `size₂`
2+
3+
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`.
4+
autoBoundImplicits3.lean:13:24-13:26: error(lean.unknownIdentifier): Unknown identifier `α₃`
5+
6+
Note: It is not possible to treat `α₃` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.
7+
autoBoundImplicits3.lean:13:33-13:38: error(lean.unknownIdentifier): Unknown identifier `size₃`
8+
9+
Note: It is not possible to treat `size₃` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.
10+
autoBoundImplicits3.lean:17:30-17:32: error(lean.unknownIdentifier): Unknown identifier `α₄`
11+
12+
Note: It is not possible to treat `α₄` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.
13+
autoBoundImplicits3.lean:17:39-17:44: error(lean.unknownIdentifier): Unknown identifier `size₄`
14+
15+
Note: It is not possible to treat `size₄` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.

0 commit comments

Comments
 (0)