Zulip Chat Archive
Stream: lean4
Topic: Kernel error in list index notation
Eric Wieser (Sep 18 2025 at 00:16):
This is a regression, but I'm not entirely sure when it was introduced:
import Mathlib
open scoped ProbabilityTheory
theorem kernel_error
(L : List ℝ) (hL : L.length = 2 ∧ ∀ i : Fin L.length, L[i] = 0) (i : ℕ) :
L[i % 2] = 0 := hL.2 ⟨i%2, by simp [hL.1, Nat.mod_lt]⟩
#print axioms kernel_error -- no sorry
gives
(kernel) declaration has free variables 'kernel_error._proof_1'
Eric Wieser (Sep 18 2025 at 00:19):
It looks like the overloaded notation is being partially elaborated and some incomplete state is left behind when switching to the other elaborator
Eric Wieser (Sep 18 2025 at 00:44):
(deleted)
Eric Wieser (Sep 18 2025 at 00:49):
Mathlib-free:
structure Wrapper (α) where val : α
instance {α : Type} [NatCast α] : NatCast (Wrapper α) := sorry
def foo {α : Type} (μ : Wrapper α) (f : α → Nat) : Nat := sorry
macro:max P:term noWs "[" term "]" : term => `(foo $P fun x => 0)
theorem kernel_error
(L : List Nat) (hL : L.length = 2 ∧ ∀ i : Fin L.length, L[i] = 0) (i : Nat) :
L[i % 2] = 0 := hL.2 ⟨i%2, by simp [hL.1, Nat.mod_lt]⟩
#print axioms kernel_error -- no sorry
Notification Bot (Sep 18 2025 at 00:53):
This topic was moved here from #mathlib4 > Kernel error in list index notation by Eric Wieser.
Eric Wieser (Sep 18 2025 at 11:59):
One thing that would probably help debug this is if the kernel could tell me what the statement of kernel_error._proof_1 is; it doesn't ever seem to make it into the environment
Eric Wieser (Sep 18 2025 at 12:01):
set_option trace.Kernel true provides [Kernel] ❌️ typechecking declarations [kernel_error._proof_1] where the name is not hoverable
Eric Wieser (Sep 18 2025 at 12:01):
@Sebastian Ullrich , setting set_option Elab.async false seems to make the kernel error go away
Eric Wieser (Sep 18 2025 at 12:02):
(I assume you're a sensible person to ping on async-related things?)
Sebastian Ullrich (Sep 18 2025 at 12:23):
Thanks, lean#10438
Eric Wieser (Sep 24 2025 at 15:37):
It seems that Elab.async is still misbehaving here; turning the setting off makes elaboration fail:
structure Wrapper (α) where val : α
instance {α : Type} [NatCast α] : NatCast (Wrapper α) := sorry
def foo {α : Type} (μ : Wrapper α) (f : α → Nat) : Nat := sorry
macro:max P:term noWs "[" term "]" : term => `(foo $P fun x => 0)
set_option Elab.async false -- remove this and things pass
theorem kernel_error
(L : List Nat) (hL : L.length = 2 ∧ ∀ i : Fin L.length, L[i] = 0) (i : Nat) :
L[i % 2] = 0 := hL.2 ⟨i%2, by simp [hL.1, Nat.mod_lt]⟩
-- ^^^^^^^^ ambiguous notation here only with synchronous elaboration
Eric Wieser (Sep 25 2025 at 08:45):
Is there a future where Elab.async is removed and effectively always true, making the option increasingly vestigial? Or is it reasonable to expect behavior parity with the two setting values?
Sebastian Ullrich (Sep 25 2025 at 08:47):
In the above, it appears the behavior under Elab.async false is the incorrect one? I think it's fair to say that such bugs will be regarded as low priority, yes.
Sebastian Ullrich (Sep 25 2025 at 08:48):
Perhaps the option should be moved into the debug namespace to make that expectation explicit
Eric Wieser (Sep 25 2025 at 08:56):
I claim the true version is correct, since it's consistent with what happens when hL is simpler
Eric Wieser (Sep 25 2025 at 08:57):
Perhaps the syntax overloading of get_elem_tactic is to blame here?
Sebastian Ullrich (Sep 25 2025 at 08:59):
It could be something about how postponement of tactic mvars interacts with overloading
Eric Wieser (Sep 25 2025 at 09:22):
Regarding Elab.async; if the intended path is for this always to be true, and the default is false, I guess every custom frontend is expected to set this option?
Sebastian Ullrich (Sep 25 2025 at 09:26):
Good question. I would say that for simple custom frontends, it is unlikely they should run into such divergences and if that's not the case, we might want to fix them with slightly higher priority. For more ambitious frontends, it is likely worth the performance boost alone.
Eric Wieser (Sep 25 2025 at 09:33):
I mean, presumably even the simple frontends would crash if provided the code snippets above, which could otherwise appear without error in a regular Lean project built using the usual frontend
Paul Lezeau (Sep 25 2025 at 14:21):
In case it's helpful, here's a further minimisation of the issue:
structure Wrapper (α) where val : α
instance {α : Type} [NatCast α] : NatCast (Wrapper α) := sorry
def foo {α : Type} (μ : Wrapper α) (f : α → Nat) : Nat := sorry
macro:max P:term noWs "|[" s:term "]|" : term => `(
let h : $s ≤ List.length $P := by grind
0)
macro:max P:term noWs "|[" term "]|" : term => `(foo $P fun x => 0)
set_option Elab.async false in -- remove this and things pass
theorem kernel_error
(L : List Nat) (hL : L.length = 2 ∧ L|[0]| = 0) :
L|[0]| = 0 := by sorry
-- ^^^^^^^^ ambiguous notation here only with synchronous elaboration
Interestingly, replacing grind by omega still results in an error, but replacing with simp fixes things.
Eric Wieser (Sep 25 2025 at 15:07):
(the above is for the lean nightly)
Eric Wieser (Sep 25 2025 at 15:11):
Here's some with fewer grinds (replacing the last part of Paul's):
set_option Elab.async false in -- remove this and things pass
theorem ambiguous_synchronously
(L : List Nat) (hL : L.length = 2 ∧ let : True := (by trivial); True) : -- remove the `by` and things pass
L|[0]| = 0 := by sorry
theorem ambiguous_synchronously
(L : List Nat) (hL : L.length = 2 ∧ let : True := (by trivial); True) : -- remove the `by` and things pass
set_option Elab.async false in -- remove this and things pass
L|[0]| = 0 := by sorry
Eric Wieser (Oct 03 2025 at 10:27):
How can I get a list of all the grind components here in order to disable each one in turn to diagnose which one is causing the issue?
Robin Arnez (Oct 05 2025 at 16:16):
grind (config := { <trigger completion here> }) should show you every config (including all the modules you can disable)
Robin Arnez (Oct 05 2025 at 16:19):
Or grind (config := Lean.Grind.NoopConfig.toConfig {}) if you simply want to disable everything
Kenny Lau (Oct 05 2025 at 20:54):
is that documented anywhere?
Robin Arnez (Oct 05 2025 at 21:14):
Well, the first one is basically old config notation + regular structure autocompletion. The second one is just me happening to know it from the commit log (923c3d10a2457eb1413dc282b98cbdb50be5c5ee).
Eric Wieser (Oct 06 2025 at 12:23):
grind (config := Lean.Grind.NoopConfig.toConfig {}) seems sufficient to trigger the bug
Eric Wieser (Oct 06 2025 at 22:49):
Digging a little deeper, I think the cause is actually in the thread title; there is a kernel error occurring in the asynchronous code, which is being silenced by these lines. Should that catch be finer-grained?
Eric Wieser (Oct 06 2025 at 22:55):
Sebastian Ullrich said:
Which is to say; I think this is just masking the problem, and notation should never give a kernel error; and if it does, it shouldn't be hidden from the user.
Eric Wieser (Oct 07 2025 at 13:17):
It seems that mkAuxTheorem is being called with a synthetic opaque metavariable in the proof, which Closure.mkValueTypeClosure is incorrectly converting into an unbound free variable. I think this only happens when inside synthesizeSyntheticMVars (postpone := .no)
Eric Wieser (Oct 07 2025 at 13:26):
In particular, I think mkValueTypeClosure can't handle a metavariable whose type is itself a metavariable
Eric Wieser (Oct 07 2025 at 15:07):
looks to be the cause, and async is just affecting when there are unassigned metavariables
Last updated: Dec 20 2025 at 21:32 UTC