Zulip Chat Archive

Stream: lean4

Topic: kernel invalid projection


David Richter (Mar 23 2024 at 10:58):

Hi, here are two functions ERR and OK, ERR does not compile because of a 'kernel invalid projection' and OK compiles. But I think the two functions ERR and OK should be equivalent?

The problem appears, when I perform a boolean equality over the projection of a term whose type has been rewritten to be a tuple. I'm not sure how to minimize the problem further.

inductive Ty where
  | bool : Ty
  | prod : Ty  Ty  Ty
open Ty

abbrev Ty.de : Ty  Type
  | bool => Bool
  | prod a b => a.de × b.de

instance {t: Ty} : BEq (t.de) where
  beq := let rec go : (t : Ty)  t.de  t.de  Bool
  | .bool => λ a b => a == b
  | .prod α β => λ a b => go α a.1 b.1  go β a.2 b.2
  go t

def FST (ab: (prod a b).de): a.de := ab.1

def ERR (h: t=prod a b) (a: a.de) (e: t.de): Bool := a == (h  e).1 -- (kernel) invalid projection
def OK  (h: t=prod a b) (a: a.de) (e: t.de): Bool := a == FST (h  e) -- ok

#eval OK (a:=bool) (b:=bool) rfl true (true, true) -- OK is computable
#eval OK (a:=bool) (b:=bool) rfl false (true, true) -- OK is computable

EDIT: In particular the error message is

(kernel) invalid projection
  e.1

So it seems, lean thinks that the code contained e.1 directly, which is of course type incorrect, although the code clearly says (h ▸ e).1. Maybe that's related to how this error happens? Or are rewrites just not printed in the error message there?

Kevin Buzzard (Mar 23 2024 at 12:36):

Higher order unification is undecidable so you can't expect everything to work with no clues. I'm guessing that it will work if you write (h \t e : <write the type here>).1 .

David Richter (Mar 23 2024 at 13:14):

Hi, I suppose the ▸ looks so innocent that I forgot that it has to perform higher order unification in the background. Nevertheless, I'm not convinced that's the issue here:

I think lean is able to correctly guess the expected rewriting, otherwise I would expect (h ▸ c).1 to be a type error not a kernel invalid projection.

If I write def ERR₂ (h: γ = prod α β) (a: α.de) (c: γ.de): Bool := let tmp := h ▸ c; a == tmp.1 then I can hover over tmp with the mouse and see that the type (prod α β).de is correctly deduced.

Indeed, writing the type explicitly like def ERR₁ (h: γ = prod α β) (a: α.de) (c: γ.de): Bool := let tmp := (h ▸ c : (prod α β).de); a == tmp.1 doesnt change anything, the kernel invalid projection error still appears.

See:

inductive Ty where
  | bool : Ty
  | prod : Ty  Ty  Ty
open Ty

abbrev Ty.de : Ty  Type
  | bool => Bool
  | prod a b => a.de × b.de

instance {t: Ty} : BEq (t.de) where
  beq := let rec go : (t : Ty)  t.de  t.de  Bool
  | .bool => λ a b => a == b
  | .prod α β => λ a b => go α a.1 b.1  go β a.2 b.2
  go t

def FST (c: (prod α β).de): α.de := c.1

def OK  (h: γ = prod α β) (a: α.de) (c: γ.de): Bool :=
  a == FST (h  c) -- ok
def ERR₁ (h: γ = prod α β) (a: α.de) (c: γ.de): Bool :=
  let tmp := (h  c : (prod α β).de); a == tmp.1 -- (kernel) invalid projection
def ERR₂ (h: γ = prod α β) (a: α.de) (c: γ.de): Bool :=
  let tmp := h  c; a == tmp.1 -- (kernel) invalid projection
def ERR₃ (h: γ = prod α β) (a: α.de) (c: γ.de): Bool :=
  a == (h  c).1 -- (kernel) invalid projection

-- EDIT: here is a very verbose variant
def ERR₄ (h: γ = prod α β) (a: α.de) (c: γ.de): Bool := -- (kernel) invalid projection
  let tmp: α.de × β.de := (h  c : (prod α β).de)
  let tmp1: α.de := tmp.1
  a == tmp1

#eval OK (α := bool) (β := bool) rfl true  (true, true) -- OK is computable
#eval OK (α := bool) (β := bool) rfl false (true, true) -- OK is computable

Mario Carneiro (Mar 24 2024 at 05:27):

Note that your type ascriptions are doing nothing for the kernel, they are only seen by the elaborator. Here are some variants that work:

def OK₄ (h: γ = prod α β) (a: α.de) (c: γ.de): Bool :=
  let tmp: α.de × β.de := (show (prod α β).de from h  c)
  let tmp1: α.de := tmp.1
  a == tmp1

def hc (h: γ = prod α β) (c: γ.de) : (prod α β).de := (h  c)
def OK  (h: γ = prod α β) (a: α.de) (c: γ.de): Bool :=
  a == (hc h c).1

Mario Carneiro (Mar 24 2024 at 05:34):

The use of show and hc in these examples is forcing an intermediate term whose type is (prod α β).de. My guess is that without this, the term h ▸ c gets the type acquired from its underlying term @Eq.rec Ty γ (fun x h ↦ de x) c (prod α β) h, which is (fun x h ↦ de x) (prod α β) h, but ascribing this type explicitly also seems to work...

Mario Carneiro (Mar 24 2024 at 05:36):

Also, my guess is that the c.1 you see in the error message is not actually the (h ▸ c).1 term you wrote itself, but rather a subterm generated during the typechecking of this term: the type of a projection can refer to other projections, and after whnf it can be a bit difficult to say exactly what terms it decided to synthesize here. I'll see if I can reproduce with lean4lean

Mario Carneiro (Mar 24 2024 at 05:48):

oh, extra weird:

theorem OK  {γ α β} (h: γ = prod α β) (a: α.de) (c: γ.de): Bool := a == (h  c).1 -- ok
def     ERR {γ α β} (h: γ = prod α β) (a: α.de) (c: γ.de): Bool := a == (h  c).1 -- invalid projection

so somehow being a definition is relevant here; perhaps the error is actually occurring in the equation lemma?

Mario Carneiro (Mar 24 2024 at 05:51):

Using #whatsnew in on the def reveals

#whatsnew in
def OK {γ α β} (h: γ = prod α β) (a: α.de) (c: γ.de): Bool :=
  a == (@id _ <| h  c).1
unsafe def OK._rarg._cstage2 : _obj  _obj  _neutral  _obj  _obj  _obj :=
fun α β h a c 
  let _x_1 := c.1;
  instBEqDe.go α a _x_1

unsafe def OK._cstage2 : _obj  _obj :=
fun γ  OK._rarg

def OK : {γ α β : Ty}  γ = prod α β  de α  de γ  Bool :=
fun {γ α β} h a c  a == (id (h  c)).fst

unsafe def OK._cstage1 : {γ α β : Ty}  γ = prod α β  de α  de γ  Bool :=
fun {γ α β} h a c 
  let _x_3 := c.1;
  instBEqDe.go α a _x_3

where in the last definition we indeed have a c.1 subterm for which hovering it says "invalid projection"

Mario Carneiro (Mar 24 2024 at 05:53):

So it seems like this is actually an ill-formed definition OK._cstage1 being produced by the compiler. (unsafe def turns off some checks in the kernel, but not this one)

Mario Carneiro (Mar 24 2024 at 05:55):

this does make some sense, because the compiler erases Eq.rec terms, which can produce ill formed expressions, and this has been the source of several compiler bugs. I think you should report this. The new compiler uses a custom typechecker instead of reusing lean's typechecker in order to better handle these kind of weakly typed terms

Mario Carneiro (Mar 24 2024 at 05:57):

As added evidence that this is the issue,

noncomputable def OK {γ α β} (h: γ = prod α β) (a: α.de) (c: γ.de): Bool :=
  a == (h  c).1

compiles fine (using noncomputable to tell the compiler not to try to produce the bad auxiliary defs)

David Richter (Mar 25 2024 at 09:17):

I think you should report this

Like this https://github.com/leanprover/lean4/issues/3761 ?


Last updated: May 02 2025 at 03:31 UTC