/- 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.AppBuilder import Lean.Meta.Tactic.Clear import Lean.Meta.Tactic.Assert import Lean.Meta.Tactic.Intro namespace Lean namespace Meta inductive InjectionResultCore | solved | subgoal (mvarId : MVarId) (numNewEqs : Nat) def injectionCore (mvarId : MVarId) (fvarId : FVarId) : MetaM InjectionResultCore := do withMVarContext mvarId $ do checkNotAssigned mvarId `injection; decl ← getLocalDecl fvarId; type ← whnf decl.type; match type.eq? with | none => throwTacticEx `injection mvarId "equality expected" | some (α, a, b) => do a ← whnf a; b ← whnf b; target ← getMVarType mvarId; env ← getEnv; match a.isConstructorApp? env, b.isConstructorApp? env with | some aCtor, some bCtor => do val ← mkNoConfusionApp target (mkFVar fvarId); if aCtor.name != bCtor.name then do assignExprMVar mvarId val; pure InjectionResultCore.solved else do valType ← inferType val; valType ← whnf valType; match valType with | Expr.forallE _ newTarget _ _ => do let newTarget := newTarget.headBeta; tag ← getMVarTag mvarId; newMVar ← mkFreshExprSyntheticOpaqueMVar newTarget tag; assignExprMVar mvarId (mkApp val newMVar); mvarId ← tryClear newMVar.mvarId! fvarId; pure $ InjectionResultCore.subgoal mvarId aCtor.nfields | _ => throwTacticEx `injection mvarId "ill-formed noConfusion auxiliary construction" | _, _ => throwTacticEx `injection mvarId "equality of constructor applications expected" inductive InjectionResult | solved | subgoal (mvarId : MVarId) (newEqs : Array FVarId) (remainingNames : List Name) private def heqToEq (mvarId : MVarId) (fvarId : FVarId) : MetaM (FVarId × MVarId) := withMVarContext mvarId $ do decl ← getLocalDecl fvarId; type ← whnf decl.type; match type.heq? with | none => pure (fvarId, mvarId) | some (α, a, β, b) => do pr ← mkEqOfHEq (mkFVar fvarId); eq ← mkEq a b; mvarId ← assert mvarId decl.userName eq pr; mvarId ← clear mvarId fvarId; (fvarId, mvarId) ← intro1 mvarId false; pure (fvarId, mvarId) def injectionIntro : Nat → MVarId → Array FVarId → List Name → MetaM InjectionResult | 0, mvarId, fvarIds, remainingNames => pure $ InjectionResult.subgoal mvarId fvarIds remainingNames | n+1, mvarId, fvarIds, name::remainingNames => do (fvarId, mvarId) ← intro mvarId name; (fvarId, mvarId) ← heqToEq mvarId fvarId; injectionIntro n mvarId (fvarIds.push fvarId) remainingNames | n+1, mvarId, fvarIds, [] => do (fvarId, mvarId) ← intro1 mvarId true; (fvarId, mvarId) ← heqToEq mvarId fvarId; injectionIntro n mvarId (fvarIds.push fvarId) [] def injection (mvarId : MVarId) (fvarId : FVarId) (newNames : List Name := []) (useUnusedNames : Bool := true) : MetaM InjectionResult := do r ← injectionCore mvarId fvarId; match r with | InjectionResultCore.solved => pure InjectionResult.solved | InjectionResultCore.subgoal mvarId numEqs => injectionIntro numEqs mvarId #[] newNames end Meta end Lean