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 ` 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