This PR refines several error error messages, mostly involving invalid
use of field notation, generalized field notation, and numeric
projection. Provides a new error explanation for field notation.
## Error message changes
In general:
- Uses a slightly different convention for expression-type pairs, where
the expression is always given `indentExpr` and the type is given
`inlineExpr` treatment. This is something of a workaround for the fact
that the `Format` type is awkward for embedding possibly-linebreaking
expressions in not-linebreaking text, which may be a separate issue
worth addressing.
- Tries to give slightly more "why" reasoning — the environment does not
contain `String.parse`, and _therefore you can't project `.parse` from a
`String`_.
Some specific examples:
### No such projection function
```lean4
#check "".parse
```
before:
```
error: Invalid field `parse`: The environment does not contain `String.parse`
""
has type
String
```
after:
```
error: Invalid field `parse`: The environment does not contain `String.parse`, so it is not possible to project the field `parse` from an expression
""
of type `String`
```
### Type does not have the correct form
```lean4
example (x : α) := (foo x).foo
```
before:
```
error: Invalid field notation: Type is not of the form `C ...` where C is a constant
foo x
has type
α
```
after:
```
error: Invalid field notation: Field projection operates on types of the form `C ...` where C is a constant. The expression
foo x
has type `α` which does not have the necessary form.
```
## Refactoring
Includes some refactoring changes as well:
- factors out multiple uses of number (1, 2, 3, 212, 222) to ordinal
("first", "second", "third", "212th", "222nd") conversion into
Lean.Elab.ErrorUtils
- significant refactoring of `resolveLValAux` in `Lean.Elab.App` — in
place of five helper functions, a special-case function case analysis,
and a case analysis on the projection type and structure, there's now a
single case analysis on the projection type and structure. This allows
several error messages to be more explicit (there were a number of cases
where index projection was being described as field projection in an
error messages) and gave the opportunity to slightly improve positining
for several errors: field *notation* errors should appear on `foo.bar`,
but field *projection* errors should appear only on the `bar` part of
`foo.bar`.
108 lines
2.6 KiB
Text
108 lines
2.6 KiB
Text
/-!
|
||
# Tests for generalized field notation through aliases and "top-level" dot notation
|
||
https://github.com/leanprover/lean4/issues/3031
|
||
-/
|
||
|
||
/-!
|
||
Alias dot notation. There used to be a different kind of alias dot notation;
|
||
in the following example, it would have looked for an argument of type `Common.String`.
|
||
Now it looks for one of type `String`, allowing libraries to add "extension methods" from within their own namespaces.
|
||
-/
|
||
def Common.String.a (s : String) : Nat := s.length
|
||
|
||
export Common (String.a)
|
||
|
||
/-- info: String.a "x" : Nat -/
|
||
#guard_msgs in #check String.a "x"
|
||
/-- info: String.a "x" : Nat -/
|
||
#guard_msgs in #check "x".a
|
||
|
||
/-!
|
||
Declarations take precedence over aliases
|
||
-/
|
||
def String.a (s : String) : Nat := s.length + 100
|
||
/-- info: "x".a : Nat -/
|
||
#guard_msgs in #check "x".a
|
||
/-- info: 100 -/
|
||
#guard_msgs in #eval "".a
|
||
|
||
/-!
|
||
Private declarations take precedence over aliases
|
||
-/
|
||
private def String.b (s : String) : Nat := 0
|
||
def Common.String.b (s : String) : Nat := 1
|
||
export Common (String.b)
|
||
/-- info: 0 -/
|
||
#guard_msgs in #eval "".b
|
||
|
||
/-!
|
||
Multiple aliases is an error
|
||
-/
|
||
def Common.String.c (s : String) : Nat := 0
|
||
def Common'.String.c (s : String) : Nat := 0
|
||
export Common (String.c)
|
||
export Common' (String.c)
|
||
/--
|
||
error: Field name `c` is ambiguous: `String.c` has possible interpretations `Common'.String.c`, `Common.String.c`
|
||
-/
|
||
#guard_msgs in #eval "".c
|
||
|
||
/-!
|
||
Aliases work with inheritance
|
||
-/
|
||
namespace Ex1
|
||
structure A
|
||
structure B extends A
|
||
def Common.A.x (_ : A) : Nat := 0
|
||
export Common (A.x)
|
||
/-- info: fun b => A.x b.toA : B → Nat -/
|
||
#guard_msgs in #check fun (b : B) => b.x
|
||
end Ex1
|
||
|
||
/-!
|
||
`open` also works
|
||
-/
|
||
def Common.String.parse (_ : String) : List Nat := []
|
||
|
||
namespace ExOpen1
|
||
/--
|
||
error: Invalid field `parse`: The environment does not contain `String.parse`, so it is not possible to project the field `parse` from an expression
|
||
""
|
||
of type `String`
|
||
-/
|
||
#guard_msgs in #check "".parse
|
||
section
|
||
open Common
|
||
/-- info: String.parse "" : List Nat -/
|
||
#guard_msgs in #check "".parse
|
||
end
|
||
section
|
||
open Common (String.parse)
|
||
/-- info: String.parse "" : List Nat -/
|
||
#guard_msgs in #check "".parse
|
||
end
|
||
end ExOpen1
|
||
|
||
|
||
namespace Ex2
|
||
class A (n : Nat) where
|
||
x : Nat
|
||
|
||
/-!
|
||
Incidental fix: `@` for generalized field notation was failing if there were implicit arguments.
|
||
True projections were ok.
|
||
-/
|
||
def A.x' {n : Nat} (a : A n) := a.x
|
||
|
||
/-- info: fun a => a.x' : A 2 → Nat -/
|
||
#guard_msgs in #check fun (a : A 2) => @a.x'
|
||
end Ex2
|
||
|
||
namespace Ex3
|
||
variable (f : α → β) (g : β → γ)
|
||
/-!
|
||
Functions use the "top-level" dot notation rule: they use the first explicit argument, rather than the first function argument.
|
||
-/
|
||
/-- info: g ∘ f : α → γ -/
|
||
#guard_msgs in #check g.comp f
|
||
end Ex3
|