Skip to content

Commit aa35449

Browse files
committed
Test: always give a warning for autobound implicits. This may be more difficult than desired.
1 parent f8e4dce commit aa35449

15 files changed

+138
-27
lines changed

src/Lean/Elab/App.lean

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,11 @@ private def synthesizePendingAndNormalizeFunType : M Unit := do
240240
let validNames ← getFoundNamedArgs
241241
let fnName? := if f.isConst then some f.constName! else none
242242
throwInvalidNamedArg namedArg fnName? validNames
243+
/-
244+
let ctx ← readThe Term.Context
245+
withTraceNode `Elab (fun _ => pure m!"throwinerror {s.f} {ctx.autoBoundImplicitContext.map (·.boundVariables.toArray)}") do
246+
logInfo m!"quirky {repr s.f} {ctx.autoBoundImplicitContext.isSome}"
247+
-/
243248
-- Help users see if this is actually due to an indentation mismatch/other parsing mishaps:
244249
let extra := if let some (arg : Arg) := s.args[0]? then
245250
.note m!"Expected a function because this term is being applied to the argument\
@@ -1338,9 +1343,9 @@ private def resolveLValAux (e : Expr) (eType : Expr) (lval : LVal) : TermElabM L
13381343
fields. The expression{indentExpr e}\nhas type{inlineExpr eType}which has no fields."
13391344
let tupleHint ← mkTupleHint eType idx ref
13401345
throwError m!"Invalid projection: Index `{idx}` is invalid for this structure; \
1341-
{numFields.plural "the only valid index is 1" s!"it must be between 1 and {numFields}"}"
1346+
{plural numFields "the only valid index is 1" s!"it must be between 1 and {numFields}"}"
13421347
++ MessageData.note m!"The expression{indentExpr e}\nhas type{inlineExpr eType}which has only \
1343-
{numFields} field{numFields.plural}"
1348+
{numFields} field{plural numFields}"
13441349
++ tupleHint
13451350
| .const structName _, LVal.fieldName ref fieldName _ _ => withRef ref do
13461351
let env ← getEnv

src/Lean/Elab/ErrorUtils.lean

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ Authors: Rob Simmons
66
module
77

88
prelude
9-
import Init.Notation
10-
import Init.Data.String
9+
import Lean.Message
10+
11+
namespace Lean
1112

1213
/--
1314
Translate numbers (1, 2, 3, 212, 322 ...) to ordinals with appropriate English-language names for
@@ -35,31 +36,63 @@ def _root_.Nat.toOrdinal : Nat -> String
3536
else
3637
s!"{n}th"
3738

39+
class HasOxfordStrings α where
40+
emp : α
41+
and : α
42+
comma : α
43+
commaAnd : α
44+
45+
instance : HasOxfordStrings String where
46+
emp := ""
47+
and := " and "
48+
comma := ", "
49+
commaAnd := ", and "
50+
51+
instance : HasOxfordStrings MessageData where
52+
emp := ""
53+
and := " and "
54+
comma := ", "
55+
commaAnd := ", and "
56+
3857
/--
3958
Make an oxford-comma-separated list of strings.
4059
4160
- `["eats"].toOxford == "eats"`
4261
- `["eats", "shoots"].toOxford == "eats and shoots"`
4362
- `["eats", "shoots", "leaves"] == "eats, shoots, and leaves"`
4463
-/
45-
def _root_.List.toOxford : List String -> String
46-
| [] => ""
64+
def toOxford [Append α] [HasOxfordStrings α] : List α -> α
65+
| [] => HasOxfordStrings.emp
4766
| [a] => a
48-
| [a, b] => a ++ " and " ++ b
49-
| [a, b, c] => a ++ ", " ++ b ++ ", and " ++ c
50-
| a :: as => a ++ ", " ++ toOxford as
67+
| [a, b] => a ++ HasOxfordStrings.and ++ b
68+
| [a, b, c] => a ++ HasOxfordStrings.comma ++ b ++ HasOxfordStrings.commaAnd ++ c
69+
| a :: as => a ++ HasOxfordStrings.comma ++ toOxford as
70+
71+
class HasPluralDefaults α where
72+
singular : α
73+
plural : α → α
74+
75+
instance : HasPluralDefaults String where
76+
singular := ""
77+
plural := (· ++ "s")
78+
79+
instance : HasPluralDefaults MessageData where
80+
singular := .nil
81+
plural := (m!"{·}s")
5182

5283
/--
5384
Give alternative forms of a string if the `count` is 1 or not.
5485
55-
- `(1).plural == ""`
56-
- `(2).plural == "s"`
57-
- `(1).plural "wug" == "wug"`
58-
- `(2).plural "wug" == "wugs"`
59-
- `(1).plural "it" "they" == "it"`
60-
- `(2).plural "it" "they" == "they"`
86+
- `(1) |> plural == ""`
87+
- `(2) |> plural == "s"`
88+
- `(1) |> plural "wug" == "wug"`
89+
- `(2) |> plural "wug" == "wugs"`
90+
- `(1) |> plural "it" "they" == "it"`
91+
- `(2) |> plural "it" "they" == "they"`
6192
-/
62-
def _root_.Nat.plural (count : Nat) (singular : String := "") (plural : String := singular ++ "s") :=
93+
def plural [HasPluralDefaults α] (count : Nat)
94+
(singular : α := HasPluralDefaults.singular)
95+
(plural : α := HasPluralDefaults.plural singular) :=
6396
if count = 1 then
6497
singular
6598
else

src/Lean/Elab/SyntheticMVars.lean

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -262,17 +262,17 @@ def explainStuckTypeclassProblem (typeclassProblem : Expr) : TermElabM (Option M
262262
-- Formulate error message
263263
let containMVars :=
264264
if simpleMVars then
265-
nStuck.plural "is a metavariable" "are metavariables"
265+
plural nStuck "is a metavariable" "are metavariables"
266266
else
267-
nStuck.plural "contains metavariables" "contain metavariables"
267+
plural nStuck "contains metavariables" "contain metavariables"
268268

269269
let theTypeArguments :=
270270
if args.length = 1 then
271271
"the type argument"
272272
else
273-
s!"the {(stuckArguments.toList.map (·.succ.toOrdinal)).toOxford} type {nStuck.plural "argument" "arguments"}"
273+
s!"the {(stuckArguments.toList.map (·.succ.toOrdinal)) |> toOxford} type {plural nStuck "argument" "arguments"}"
274274

275-
return .some (.note m!"Lean will not try to resolve this typeclass instance problem because {theTypeArguments} to `{.ofConstName name}` {containMVars}. {nStuck.plural "This argument" "These arguments"} must be fully determined before Lean will try to resolve the typeclass."
275+
return .some (.note m!"Lean will not try to resolve this typeclass instance problem because {theTypeArguments} to `{.ofConstName name}` {containMVars}. {plural nStuck "This argument" "These arguments"} must be fully determined before Lean will try to resolve the typeclass."
276276
++ .hint' m!"Adding type annotations and supplying implicit arguments to functions can give Lean more information for typeclass resolution. For example, if you have a variable `x` that you intend to be a `{MessageData.ofConstName ``Nat}`, but Lean reports it as having an unresolved type like `?m`, replacing `x` with `(x : Nat)` can get typeclass resolution un-stuck.")
277277

278278
/--

src/Lean/Elab/Term/TermElabM.lean

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public import Lean.Elab.DeclarationRange
1717
public import Lean.Elab.WhereFinally
1818
public import Lean.Elab.InfoTree.InlayHints
1919
public meta import Lean.Parser.Term
20+
import all Lean.Elab.ErrorUtils
2021

2122

2223

@@ -1276,7 +1277,28 @@ private def mkSyntheticSorryFor (expectedType? : Option Expr) : TermElabM Expr :
12761277
elaboration step with exception `ex`.
12771278
-/
12781279
def exceptionToSorry (ex : Exception) (expectedType? : Option Expr) : TermElabM Expr := do
1279-
logException ex
1280+
let autoBoundNames ←
1281+
(← readThe Term.Context).autoBoundImplicitContext
1282+
|>.map (·.boundVariables.toList)
1283+
|>.getD []
1284+
|>.mapM (·.fvarId!.getUserName)
1285+
if autoBoundNames.length > 0 then
1286+
if let .error ref msg := ex then
1287+
if msg.isTagged || msg.hasTag (· = `suppressAutoImplicitWarning) then
1288+
logException ex
1289+
else
1290+
let extra := m!"This error occurred after the identifier{plural autoBoundNames.length} \
1291+
{autoBoundNames.map (m!"`{.ofName ·}`") |> toOxford} had been identified as \
1292+
{plural autoBoundNames.length "a free variable" "free variables"} and automatically \
1293+
given {plural autoBoundNames.length "an implicit binding" "implicit bindings"}. \
1294+
{plural autoBoundNames.length m!"Is `{.ofName autoBoundNames[0]!}`" m!"Are those identifiers all"} \
1295+
supposed to be implicitly bound? Automatic implicit binding can sometimes occur due to \
1296+
typos or missing `import` or `open` statements, and this may cause unexpected errors."
1297+
logException (.error ref (msg ++ .hint' extra))
1298+
else
1299+
logException ex
1300+
else
1301+
logException ex
12801302
mkSyntheticSorryFor expectedType?
12811303

12821304
/-- If `mayPostpone == true`, throw `Exception.postpone`. -/
@@ -1885,6 +1907,32 @@ partial def withAutoBoundImplicit (k : TermElabM α) : TermElabM α := do
18851907
withLocalDecl n .implicit (← mkFreshTypeMVar) fun x => do
18861908
loop (← saveState) (ctx.push x)
18871909
| none => throw ex
1910+
1911+
/-
1912+
withTraceNode `Elab (fun _ => pure m!"withabi {ctx.boundVariables.toList.length}") do
1913+
try
1914+
withReader ({ · with autoBoundImplicitContext := .some ctx }) <|
1915+
withSaveAutoImplicitInfoContext k
1916+
catch
1917+
| ex@(.internal id k) =>
1918+
if id == autoBoundImplicitExceptionId then
1919+
withTraceNode `Elab (fun _ => pure m!"catchInternal {k.getName `localId `x}") do
1920+
-- Restore state, declare a new temporary local variable, and try again
1921+
let n := k.getName `localId `x
1922+
s.restore (restoreInfo := true)
1923+
withLocalDecl n .implicit (← mkFreshTypeMVar) fun x => do
1924+
loop (← saveState) (ctx.push x)
1925+
else throw ex
1926+
| .error ref msg =>
1927+
withTraceNode `Elab (fun _ => pure m!"catchError {ref}") do
1928+
logInfo m!"ERROR AFTER BOUND: {ctx.autoImplicitEnabled} {ctx.boundVariables.toArray}"
1929+
throwErrorAt ref (msg ++ .hint' m!"warning {ref}")
1930+
1931+
| ex => match isAutoBoundImplicitLocalException? ex with
1932+
| some n =>
1933+
| none => throw ex
1934+
-/
1935+
18881936
loop (← saveState) initCtx
18891937
else
18901938
-- Track whether we are in an auto-bound context regardless of whether

src/Lean/Message.lean

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,15 @@ def kind : MessageData → Name
172172
| tagged n _ => n
173173
| _ => .anonymous
174174

175+
/--
176+
Check if the *outermost* constructor is `.tagged`.
177+
To check if a tag exists anywhere in the message, use `MessageData.hasTag (fun _ => true)`.
178+
-/
179+
def isTagged : MessageData → Bool
180+
| .tagged .. => true
181+
| _ => false
182+
183+
175184
def isTrace : MessageData → Bool
176185
| withContext _ msg => msg.isTrace
177186
| withNamingContext _ msg => msg.isTrace

tests/lean/302.lean.expected.out

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ but this term has type
55

66
Note: Expected a function because this term is being applied to the argument
77
0
8+
9+
Hint: This error occurred after the identifiers `m`, `a`, and `t` had been identified as free variables and automatically given implicit bindings. Are those identifiers all supposed to be implicitly bound? Automatic implicit binding can sometimes occur due to typos or missing `import` or `open` statements, and this may cause unexpected errors.

tests/lean/3989_1.lean.expected.out

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@ but this term has type
66

77
Note: Expected a function because this term is being applied to the argument
88
Expr
9+
10+
Hint: This error occurred after the identifiers `Expr` and `MetaM` had been identified as free variables and automatically given implicit bindings. Are those identifiers all supposed to be implicitly bound? Automatic implicit binding can sometimes occur due to typos or missing `import` or `open` statements, and this may cause unexpected errors.

tests/lean/3989_2.lean.expected.out

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@ but this term has type
66

77
Note: Expected a function because this term is being applied to the argument
88
Expr
9+
10+
Hint: This error occurred after the identifiers `Expr` and `MetaM` had been identified as free variables and automatically given implicit bindings. Are those identifiers all supposed to be implicitly bound? Automatic implicit binding can sometimes occur due to typos or missing `import` or `open` statements, and this may cause unexpected errors.
911
Nat : Type

tests/lean/autoBoundImplicits1.lean.expected.out

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
myid 10 : Nat
22
myid true : Bool
33
autoBoundImplicits1.lean:16:4-16:11: warning: declaration uses 'sorry'
4-
autoBoundImplicits1.lean:20:25-20:29: error(lean.unknownIdentifier): Unknown identifier `size`
4+
autoBoundImplicits1.lean:20:25-20:29: error: Unknown identifier `size`
55

66
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`.
7+
8+
Hint: This error occurred after the identifiers `α`, `β`, and `n` had been identified as free variables and automatically given implicit bindings. Are those identifiers all supposed to be implicitly bound? Automatic implicit binding can sometimes occur due to typos or missing `import` or `open` statements, and this may cause unexpected errors.
79
autoBoundImplicits1.lean:24:23-24:24: error(lean.unknownIdentifier): Unknown identifier `α`
810

911
Note: It is not possible to treat `α` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.

tests/lean/autoBoundImplicits3.lean.expected.out

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
autoBoundImplicits3.lean:9:29-9:34: error(lean.unknownIdentifier): Unknown identifier `size₂`
1+
autoBoundImplicits3.lean:9:29-9:34: error: Unknown identifier `size₂`
22

33
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+
5+
Hint: This error occurred after the identifier `α₂` had been identified as a free variable and automatically given an implicit binding. Is `α₂` supposed to be implicitly bound? Automatic implicit binding can sometimes occur due to typos or missing `import` or `open` statements, and this may cause unexpected errors.
46
autoBoundImplicits3.lean:13:24-13:26: error(lean.unknownIdentifier): Unknown identifier `α₃`
57

68
Note: It is not possible to treat `α₃` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.

0 commit comments

Comments
 (0)