lean4-htt/tests/compile/large_closure_bug.lean
Wojciech Różowski 3fc99eef10
feat: add instance validation checks in addInstance (#13389)
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.
2026-04-16 17:48:16 +00:00

258 lines
11 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Lean
import Std
abbrev IteratedProd (ts : List Type) : Type :=
ts.foldr Prod Unit
abbrev IteratedArrow (codomain : Type) (ts : List Type) : Type :=
ts.foldr (· → ·) codomain
section IteratedProdInstances
macro "infer_instance_for_iterated_prod" : tactic =>
`(tactic| repeat' (first | infer_instance | constructor ))
end IteratedProdInstances
section SingleField
variable (fieldDomain : List Type) (FieldCodomain : Type)
local macro "⌞" t1:ident t2:ident* "⌟" : term => `($t1 $(Lean.mkIdent `fieldDomain) $t2:ident*)
local macro "⌞_" t1:ident t2:ident* "⌟" : term => `(⌞ $t1 $(Lean.mkIdent `FieldCodomain) $t2:ident* ⌟)
abbrev FieldUpdatePat : Type := IteratedProd (fieldDomain.map Option)
abbrev CanonicalField : Type := IteratedArrow FieldCodomain fieldDomain
abbrev FieldUpdateDescr := List (⌞ FieldUpdatePat ⌟ × ⌞_ CanonicalField ⌟)
class FieldRepresentation (FieldTypeConcrete : Type) where
set : ⌞_ FieldUpdateDescr ⌟ → FieldTypeConcrete → FieldTypeConcrete
instance canonicalFieldRepresentation {fieldDomain : List Type} {FieldCodomain : Type} :
(⌞_ FieldRepresentation ⌟) (⌞_ CanonicalField ⌟) where
set favs fc := favs.foldr (init := fc) fun (_, v) _ => v
end SingleField
inductive NonDetT (m : Type u -> Type v) : (α : Type u) -> Type _ where
| pure {α} (ret : α) : NonDetT m α
| vis {α} {β} (x : m β) (f : β → NonDetT m α) : NonDetT m α
def NonDetT.bind (x : NonDetT m α) (f : α → NonDetT m β) : NonDetT m β :=
match x with
| pure ret => f ret
| vis x f' => vis x fun y => bind (f' y) f
instance : Monad (NonDetT m) where
pure := NonDetT.pure
bind := NonDetT.bind
instance : MonadLift m (NonDetT m) where
monadLift x := NonDetT.vis x pure
abbrev VeilM (σ α : Type) := NonDetT ((StateT σ (ExceptT Int Option))) α
inductive ExtractNonDet {m} : {α : Type u} -> NonDetT m α -> Type _ where
| pure {α} : ∀ (x : α), ExtractNonDet (NonDetT.pure x)
| vis {α} {β} (x : m β) (f : β → NonDetT m α) :
(∀ y, ExtractNonDet (f y)) → ExtractNonDet (.vis x f)
set_option linter.unusedVariables false in
def ExtractNonDet.bind :
(_ : ExtractNonDet x) -> (_ : ∀ y, ExtractNonDet (f y)) -> ExtractNonDet (x >>= f)
| .pure x, inst => by
dsimp [Bind.bind, NonDetT.bind]; exact (inst x)
| .vis x f inst, inst' => by
dsimp [Bind.bind, NonDetT.bind]; constructor
intro y; apply ExtractNonDet.bind <;> solve_by_elim
def ExtractNonDet.pure' : ExtractNonDet (Pure.pure (f := NonDetT m) x) := by
dsimp [Pure.pure, NonDetT.pure]; constructor
def ExtractNonDet.liftM (x : m α) :
ExtractNonDet (liftM (n := NonDetT m) x) := by
dsimp [_root_.liftM, monadLift, MonadLift.monadLift]; constructor
intro y; apply ExtractNonDet.pure'
macro "extract_step" : tactic =>
`(tactic|
first
| apply ExtractNonDet.bind
| apply ExtractNonDet.pure'
| apply ExtractNonDet.liftM
| split )
macro "extract_tactic" : tactic =>
`(tactic| repeat' (intros; extract_step <;> try dsimp))
abbrev VeilExecM (ε σ α : Type) := σ → Option (Except ε (α × σ))
def NonDetT.extract {α : Type} : (s : VeilM σ α) → (ex : ExtractNonDet s) → VeilExecM Int σ α
| .pure x, _ => fun s => (Option.some (Except.ok (x, s)))
| .vis x f, .vis _ _ _ =>
fun s =>
match x s with
| Option.some (Except.ok (y, s')) =>
extract (f y) (by rename_i a ; exact a y) s'
| Option.none => (Option.none)
| Option.some (Except.error e) => (Option.some (Except.error e))
inductive State.Label : Type where
| m_client_request
| m_marked_client_request
def State.Label.toDomain (l : State.Label) : List Type :=
State.Label.casesOn l [Bool] [Bool, Bool, Bool]
structure State (χ : State.Label → Type) where mk ::
m_client_request : χ State.Label.m_client_request
m_marked_client_request : χ State.Label.m_marked_client_request
def initializer.ext {χ : State.Label → Type}
[χ_rep : (f : State.Label) →
FieldRepresentation (State.Label.toDomain f) Bool (χ f)] : VeilM (State χ) Unit :=
do
let mut __veil_state := (← MonadState.get)
let mut m_client_request_conc := __veil_state.m_client_request
let mut m_marked_client_request_conc := __veil_state.m_marked_client_request
let __veil_bind_m_client_request :=
(χ_rep _).set
[((@id (FieldUpdatePat (State.Label.toDomain State.Label.m_client_request)) (Option.none, ())),
(fun _ => (false)))] m_client_request_conc
m_client_request_conc ←
MonadState.modifyGet
(fun st =>
((__veil_bind_m_client_request, { st with m_client_request := __veil_bind_m_client_request })))
let __veil_bind_m_marked_client_request :=
(χ_rep _).set
[((@id (FieldUpdatePat (State.Label.toDomain State.Label.m_marked_client_request)) (Option.none, Option.none, Option.none, ())),
(fun _ _ _ => (false)))] m_marked_client_request_conc
m_marked_client_request_conc ←
MonadState.modifyGet
(fun st =>
((__veil_bind_m_marked_client_request,
{ st with m_marked_client_request := __veil_bind_m_marked_client_request })))
-- NOTE: the following are just multiple copy & pastes of the do-elements above
let __veil_bind_m_client_request :=
(χ_rep _).set
[((@id (FieldUpdatePat (State.Label.toDomain State.Label.m_client_request)) (Option.none, ())),
(fun _ => (false)))] m_client_request_conc
m_client_request_conc ←
MonadState.modifyGet
(fun st =>
((__veil_bind_m_client_request, { st with m_client_request := __veil_bind_m_client_request })))
let __veil_bind_m_marked_client_request :=
(χ_rep _).set
[((@id (FieldUpdatePat (State.Label.toDomain State.Label.m_marked_client_request)) (Option.none, Option.none, Option.none, ())),
(fun _ _ _ => (false)))] m_marked_client_request_conc
m_marked_client_request_conc ←
MonadState.modifyGet
(fun st =>
((__veil_bind_m_marked_client_request,
{ st with m_marked_client_request := __veil_bind_m_marked_client_request })))
let __veil_bind_m_client_request :=
(χ_rep _).set
[((@id (FieldUpdatePat (State.Label.toDomain State.Label.m_client_request)) (Option.none, ())),
(fun _ => (false)))] m_client_request_conc
m_client_request_conc ←
MonadState.modifyGet
(fun st =>
((__veil_bind_m_client_request, { st with m_client_request := __veil_bind_m_client_request })))
let __veil_bind_m_marked_client_request :=
(χ_rep _).set
[((@id (FieldUpdatePat (State.Label.toDomain State.Label.m_marked_client_request)) (Option.none, Option.none, Option.none, ())),
(fun _ _ _ => (false)))] m_marked_client_request_conc
m_marked_client_request_conc ←
MonadState.modifyGet
(fun st =>
((__veil_bind_m_marked_client_request,
{ st with m_marked_client_request := __veil_bind_m_marked_client_request })))
let __veil_bind_m_client_request :=
(χ_rep _).set
[((@id (FieldUpdatePat (State.Label.toDomain State.Label.m_client_request)) (Option.none, ())),
(fun _ => (false)))] m_client_request_conc
m_client_request_conc ←
MonadState.modifyGet
(fun st =>
((__veil_bind_m_client_request, { st with m_client_request := __veil_bind_m_client_request })))
let __veil_bind_m_marked_client_request :=
(χ_rep _).set
[((@id (FieldUpdatePat (State.Label.toDomain State.Label.m_marked_client_request)) (Option.none, Option.none, Option.none, ())),
(fun _ _ _ => (false)))] m_marked_client_request_conc
m_marked_client_request_conc ←
MonadState.modifyGet
(fun st =>
((__veil_bind_m_marked_client_request,
{ st with m_marked_client_request := __veil_bind_m_marked_client_request })))
let __veil_bind_m_client_request :=
(χ_rep _).set
[((@id (FieldUpdatePat (State.Label.toDomain State.Label.m_client_request)) (Option.none, ())),
(fun _ => (false)))] m_client_request_conc
m_client_request_conc ←
MonadState.modifyGet
(fun st =>
((__veil_bind_m_client_request, { st with m_client_request := __veil_bind_m_client_request })))
let __veil_bind_m_marked_client_request :=
(χ_rep _).set
[((@id (FieldUpdatePat (State.Label.toDomain State.Label.m_marked_client_request)) (Option.none, Option.none, Option.none, ())),
(fun _ _ _ => (false)))] m_marked_client_request_conc
m_marked_client_request_conc ←
MonadState.modifyGet
(fun st =>
((__veil_bind_m_marked_client_request,
{ st with m_marked_client_request := __veil_bind_m_marked_client_request })))
let __veil_bind_m_client_request :=
(χ_rep _).set
[((@id (FieldUpdatePat (State.Label.toDomain State.Label.m_client_request)) (Option.none, ())),
(fun _ => (false)))] m_client_request_conc
m_client_request_conc ←
MonadState.modifyGet
(fun st =>
((__veil_bind_m_client_request, { st with m_client_request := __veil_bind_m_client_request })))
let __veil_bind_m_marked_client_request :=
(χ_rep _).set
[((@id (FieldUpdatePat (State.Label.toDomain State.Label.m_marked_client_request)) (Option.none, Option.none, Option.none, ())),
(fun _ _ _ => (false)))] m_marked_client_request_conc
m_marked_client_request_conc ←
MonadState.modifyGet
(fun st =>
((__veil_bind_m_marked_client_request,
{ st with m_marked_client_request := __veil_bind_m_marked_client_request })))
let __veil_bind_m_client_request :=
(χ_rep _).set
[((@id (FieldUpdatePat (State.Label.toDomain State.Label.m_client_request)) (Option.none, ())),
(fun _ => (false)))] m_client_request_conc
m_client_request_conc ←
MonadState.modifyGet
(fun st =>
((__veil_bind_m_client_request, { st with m_client_request := __veil_bind_m_client_request })))
let __veil_bind_m_marked_client_request :=
(χ_rep _).set
[((@id (FieldUpdatePat (State.Label.toDomain State.Label.m_marked_client_request)) (Option.none, Option.none, Option.none, ())),
(fun _ _ _ => (false)))] m_marked_client_request_conc
m_marked_client_request_conc ←
MonadState.modifyGet
(fun st =>
((__veil_bind_m_marked_client_request,
{ st with m_marked_client_request := __veil_bind_m_marked_client_request })))
def initExec (χ : State.Label → Type) [χ_rep : ∀ f, FieldRepresentation (State.Label.toDomain f) Bool (χ f)]
: VeilExecM Int (State χ) Unit :=
NonDetT.extract (@initializer.ext χ χ_rep) (by (extract_tactic))
def res := initExec (fun f => CanonicalField ( (State.Label.toDomain) f) Bool) {
m_client_request := fun _ => false,
m_marked_client_request := fun _ _ _ => false
}
def main : IO Unit :=
IO.println s!"{[res].length}"