/- Copyright (c) 2020 Microsoft Corporation. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Leonardo de Moura -/ import Lean.Meta.Tactic.Subst import Lean.Meta.Tactic.Clear namespace Lean.Meta structure CaseValueSubgoal where mvarId : MVarId newH : FVarId subst : FVarSubst := {} instance : Inhabited CaseValueSubgoal := ⟨{ mvarId := arbitrary, newH := arbitrary }⟩ /-- Split goal `... |- C x` into two subgoals `..., (h : x = value) |- C value` `..., (h : x != value) |- C x` where `fvarId` is `x`s id. The type of `x` must have decidable equality. Remark: `subst` field of the second subgoal is equal to the input `subst`. -/ def caseValueAux (mvarId : MVarId) (fvarId : FVarId) (value : Expr) (hName : Name := `h) (subst : FVarSubst := {}) : MetaM (CaseValueSubgoal × CaseValueSubgoal) := withMVarContext mvarId do let tag ← getMVarTag mvarId checkNotAssigned mvarId `caseValue let target ← getMVarType mvarId let xEqValue ← mkEq (mkFVar fvarId) value let xNeqValue := mkApp (mkConst `Not) xEqValue let thenTarget := Lean.mkForall hName BinderInfo.default xEqValue target let elseTarget := Lean.mkForall hName BinderInfo.default xNeqValue target let thenMVar ← mkFreshExprSyntheticOpaqueMVar thenTarget tag let elseMVar ← mkFreshExprSyntheticOpaqueMVar elseTarget tag let val ← mkAppOptM `dite #[none, xEqValue, none, thenMVar, elseMVar] assignExprMVar mvarId val let (elseH, elseMVarId) ← intro1P elseMVar.mvarId! let elseSubgoal := { mvarId := elseMVarId, newH := elseH, subst := subst : CaseValueSubgoal } let (thenH, thenMVarId) ← intro1P thenMVar.mvarId! let symm := false let clearH := false let (thenSubst, thenMVarId) ← substCore thenMVarId thenH symm subst clearH withMVarContext thenMVarId do trace[Meta]! "subst domain: {thenSubst.domain}" let thenH := (thenSubst.get thenH).fvarId! trace[Meta]! "searching for decl" let decl ← getLocalDecl thenH trace[Meta]! "found decl" let thenSubgoal := { mvarId := thenMVarId, newH := (thenSubst.get thenH).fvarId!, subst := thenSubst : CaseValueSubgoal } pure (thenSubgoal, elseSubgoal) def caseValue (mvarId : MVarId) (fvarId : FVarId) (value : Expr) : MetaM (CaseValueSubgoal × CaseValueSubgoal) := do let s ← caseValueAux mvarId fvarId value appendTagSuffix s.1.mvarId `thenBranch appendTagSuffix s.2.mvarId `elseBranch pure s structure CaseValuesSubgoal where mvarId : MVarId newHs : Array FVarId := #[] subst : FVarSubst := {} instance : Inhabited CaseValuesSubgoal := ⟨{ mvarId := arbitrary }⟩ /-- Split goal `... |- C x` into values.size + 1 subgoals 1) `..., (h_1 : x = value[0]) |- C value[0]` ... n) `..., (h_n : x = value[n - 1]) |- C value[n - 1]` n+1) `..., (h_1 : x != value[0]) ... (h_n : x != value[n-1]) |- C x` where `n = values.size` where `fvarId` is `x`s id. The type of `x` must have decidable equality. Remark: the last subgoal is for the "else" catchall case, and its `subst` is `{}`. Remark: the fiels `newHs` has size 1 forall but the last subgoal. -/ def caseValues (mvarId : MVarId) (fvarId : FVarId) (values : Array Expr) (hNamePrefix := `h) : MetaM (Array CaseValuesSubgoal) := let rec loop : Nat → MVarId → List Expr → Array FVarId → Array CaseValuesSubgoal → MetaM (Array CaseValuesSubgoal) | i, mvarId, [], hs, subgoals => throwTacticEx `caseValues mvarId "list of values must not be empty" | i, mvarId, v::vs, hs, subgoals => do let (thenSubgoal, elseSubgoal) ← caseValueAux mvarId fvarId v (hNamePrefix.appendIndexAfter i) {} appendTagSuffix thenSubgoal.mvarId ((`case).appendIndexAfter i) let thenMVarId ← hs.foldlM (fun thenMVarId h => match thenSubgoal.subst.get h with | Expr.fvar fvarId _ => tryClear thenMVarId fvarId | _ => pure thenMVarId) thenSubgoal.mvarId let subgoals := subgoals.push { mvarId := thenMVarId, newHs := #[thenSubgoal.newH], subst := thenSubgoal.subst } match vs with | [] => do appendTagSuffix elseSubgoal.mvarId ((`case).appendIndexAfter (i+1)) pure $ subgoals.push { mvarId := elseSubgoal.mvarId, newHs := hs.push elseSubgoal.newH, subst := {} } | vs => loop (i+1) elseSubgoal.mvarId vs (hs.push elseSubgoal.newH) subgoals loop 1 mvarId values.toList #[] #[] end Lean.Meta