Skip to content

Dependent elimination failed due to constructor not matched #10892

@nomeata

Description

@nomeata

Consider this attempt at pattern matching on a redex in an intrinsically typed lambda calculus:

inductive Ty where
  | unit : Ty
  | arrow (t₁ t₂ : Ty) : Ty

def Env (n : Nat) := Fin n → Ty

@[reducible] def Env.add (Γ : Env n) (t : Ty) : Env (n + 1) :=
  Fin.cases t Γ

inductive Term : Env n → Ty → Type where
  | var (i : Fin n) : Term Γ (Γ i)
  | app (e₁ : Term Γ (.arrow t₁ t₂)) (e₂ : Term Γ t₁) : Term Γ t₂
  | lam (e : Term (Γ.add t₁) t₂) : Term Γ (.arrow t₁ t₂)

axiom Term.subst : Term (Γ.add t₁) t₂ → Term Γ t₁ → Term Γ t₂ 

/--
error: Tactic `cases` failed with a nested error:
Dependent elimination failed: Failed to solve equation
  t₁✝.arrow t✝ = Γ i✝
at case `var` after processing
  _, (app _ _ _ _ _ _)
the dependent pattern matcher can solve the following kinds of equations
- <var> = <term> and <term> = <var>
- <term> = <term> where the terms are definitionally equal
- <constructor> = <constructor>, examples: List.cons x xs = List.cons y ys, and List.cons x xs = List.nil
-/
#guard_msgs in
def Term.step : (e : Term Γ t) → Option (Term Γ t)
  | .var i => none
  | .app (.lam e₁) e₂ => some (e₁.subst e₂)
  | .app _ _ => none
  | .lam e => none

-- Minimized

/--
error: Tactic `cases` failed with a nested error:
Dependent elimination failed: Failed to solve equation
  t₁.arrow t₂ = Γ i✝
at case `Term.var` after processing
  _
the dependent pattern matcher can solve the following kinds of equations
- <var> = <term> and <term> = <var>
- <term> = <term> where the terms are definitionally equal
- <constructor> = <constructor>, examples: List.cons x xs = List.cons y ys, and List.cons x xs = List.nil
-/
#guard_msgs in
def getLamBody? : Term Γ (.arrow t₁ t₂) → Option (Term (Γ.add t₁) t₂)
    | .lam e' => some e'
    | _ => none

-- Workaround 1: Manual generalization
def getLamBody?' : Term Γ (.arrow t₁ t₂) → Option (Term (Γ.add t₁) t₂) := aux rfl
where
  aux {t} : (t = Ty.arrow t₁ t₂) → Term Γ t → Option (Term (Γ.add t₁) t₂)
    | h, .lam e' => Ty.noConfusion h fun h₁ h₂ => some (h₁ ▸ h₂ ▸ e')
    | _, _ => none

-- Non-Workaround 2: Boolean predicate (did not actually work)

@[reducible] def isLam : Term Γ t → Bool
  | .lam _ => true
  | _ => false

def getLamBody : (e : Term Γ (.arrow t₁ t₂)) → isLam e → Term (Γ.add t₁) t₂
  | .lam e, rfl => e

This during match compilation in the inner match, see the more minimal example getLamBody?.

It is a workaround to manually generalize the index. I was hoping that adding a reducible predicate that asserts we are in the case would help, but it doesn't.

I wonder if this should work: The pattern does not mention .var, so why is it trying to solve for it.

I’ll wager (but didn't check yet) that using sparse casesOn (#10823) might fix this issue – but accidentialy so, or for good reasons?

Also, should the “discriminant refinement procedure” auto-generalize the indices here, or is that out of scope?

Version

Lean 4.25.0-nightly-2025-10-21

Metadata

Metadata

Assignees

No one assigned

    Labels

    P-mediumWe may work on this issue if we find the time

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions