This PR adds two validation checks to `addInstance` that provide early feedback for common mistakes in instance declarations: 1. **Non-class instance check**: errors when an instance target type is not a type class. This catches the common mistake of writing `instance` for a plain structure. Previously handled by the `nonClassInstance` linter in Batteries (`Batteries.Tactic.Lint.TypeClass`), this is now checked directly at declaration time. 2. **Impossible argument check**: errors when an instance has arguments that cannot be inferred by instance synthesis. Specifically, it flags arguments that are not instance-implicit and do not appear in any subsequent instance-implicit argument or in the return type. Previously such instances would be silently accepted but could never be synthesised. Supersedes #13237 and #13333.
105 lines
3.1 KiB
Text
105 lines
3.1 KiB
Text
import Lean
|
|
open Lean
|
|
|
|
-- We need a structure as this is related to isDefEq problems of the form `e1.proj =?= e2.proj`.
|
|
structure Test where
|
|
x : Nat
|
|
|
|
-- We need a data structure with functions that are not meant for reduction purposes.
|
|
abbrev Cache := Std.HashMap Nat Test
|
|
|
|
def Cache.insert (cache : Cache) (key : Nat) (val : Test) : Cache :=
|
|
Std.HashMap.insert cache key val
|
|
|
|
def Cache.find? (cache : Cache) (key : Nat) : Option Test :=
|
|
cache[key]?
|
|
|
|
-- This function just contains a call to a function that we definitely do not want to reduce.
|
|
-- To illustrate that the problem is actually noticeable there are multiple implementations provided.
|
|
-- Each of these implementations does additional modifications on the cache before looking things up,
|
|
-- as one might expect in irl functions.
|
|
-- Each version has a lot of additional complexity from the type checkers POV.
|
|
def barImpl1 (cache : Cache) (key : Nat) : Test :=
|
|
match cache.find? key with
|
|
| some val => val
|
|
| none => ⟨0⟩
|
|
|
|
def barImpl2 (cache : Cache) (key : Nat) : Test :=
|
|
match (cache.insert key ⟨0⟩).find? key with
|
|
| some val => val
|
|
| none => ⟨0⟩
|
|
|
|
def barImpl3 (cache : Cache) (key : Nat) : Test :=
|
|
match ((cache.insert key ⟨0⟩).insert 0 ⟨0⟩).find? key with
|
|
| some val => val
|
|
| none => ⟨0⟩
|
|
|
|
def barImpl4 (cache : Cache) (key : Nat) : Test :=
|
|
match (((cache.insert key ⟨0⟩).insert 0 ⟨0⟩).insert key ⟨key⟩).find? key with
|
|
| some val => val
|
|
| none => ⟨0⟩
|
|
|
|
def bar := barImpl4
|
|
|
|
set_option maxHeartbeats 400 in
|
|
def test (c1 : Cache) (key : Nat) : Nat :=
|
|
go c1 key
|
|
where
|
|
go (c1 : Cache) (key : Nat) : Nat :=
|
|
let val : Test := bar c1 key
|
|
have : val.x = (bar c1 key).x := rfl
|
|
val.x
|
|
|
|
/--
|
|
error: (deterministic) timeout at `whnf`, maximum number of heartbeats (400) has been reached
|
|
|
|
Note: Use `set_option maxHeartbeats <num>` to set the limit.
|
|
|
|
Hint: Additional diagnostic information may be available using the `set_option diagnostics true` command.
|
|
-/
|
|
#guard_msgs in
|
|
set_option backward.isDefEq.lazyWhnfCore false in
|
|
set_option maxHeartbeats 400 in
|
|
def test' (c1 : Cache) (key : Nat) : Nat :=
|
|
go c1 key
|
|
where
|
|
go (c1 : Cache) (key : Nat) : Nat :=
|
|
let val : Test := bar c1 key
|
|
have : val.x = (bar c1 key).x := rfl
|
|
val.x
|
|
|
|
def ack : Nat → Nat → Nat
|
|
| 0, y => y+1
|
|
| x+1, 0 => ack x 1
|
|
| x+1, y+1 => ack x (ack (x+1) y)
|
|
|
|
class Foo where
|
|
x : Nat
|
|
y : Nat
|
|
|
|
@[reducible]
|
|
def f (x : Nat) : Foo :=
|
|
{ x, y := ack 10 10 }
|
|
|
|
@[reducible]
|
|
def g (x : Nat) : Foo :=
|
|
{ x, y := ack 10 11 }
|
|
|
|
open Lean Meta
|
|
set_option maxHeartbeats 500 in
|
|
run_meta do
|
|
withLocalDeclD `x (mkConst ``Nat) fun x => do
|
|
let lhs := Expr.proj ``Foo 0 <| mkApp (mkConst ``f) x
|
|
let rhs := Expr.proj ``Foo 0 <| mkApp (mkConst ``g) x
|
|
assert! (← isDefEq lhs rhs)
|
|
|
|
run_meta do
|
|
withLocalDeclD `x (mkConst ``Nat) fun x => do
|
|
let lhs := Expr.proj ``Foo 0 <| mkApp (mkConst ``f) x
|
|
let rhs := Expr.proj ``Foo 0 <| mkApp (mkConst ``g) x
|
|
match Kernel.isDefEq (← getEnv) {} lhs rhs with
|
|
| .ok b => assert! b
|
|
| .error _ => throwError "failed"
|
|
|
|
example : (f x).1 = (g x).1 :=
|
|
rfl
|