130 lines
5.3 KiB
Text
130 lines
5.3 KiB
Text
/-
|
||
Copyright (c) 2019 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.Util
|
||
|
||
namespace Lean.Meta
|
||
|
||
@[inline] private partial def introNImp {σ} (mvarId : MVarId) (n : Nat) (mkName : LocalContext → Name → Bool → σ → MetaM (Name × σ)) (s : σ)
|
||
: MetaM (Array FVarId × MVarId) := withMVarContext mvarId do
|
||
checkNotAssigned mvarId `introN
|
||
let mvarType ← getMVarType mvarId
|
||
let lctx ← getLCtx
|
||
let rec @[specialize] loop : Nat → LocalContext → Array Expr → Nat → σ → Expr → MetaM (Array Expr × MVarId)
|
||
| 0, lctx, fvars, j, _, type =>
|
||
let type := type.instantiateRevRange j fvars.size fvars
|
||
withReader (fun ctx => { ctx with lctx := lctx }) do
|
||
withNewLocalInstances fvars j do
|
||
let tag ← getMVarTag mvarId
|
||
let type := type.headBeta
|
||
let newMVar ← mkFreshExprSyntheticOpaqueMVar type tag
|
||
let lctx ← getLCtx
|
||
let newVal ← mkLambdaFVars fvars newMVar
|
||
assignExprMVar mvarId newVal
|
||
pure $ (fvars, newMVar.mvarId!)
|
||
| (i+1), lctx, fvars, j, s, Expr.letE n type val body _ => do
|
||
let type := type.instantiateRevRange j fvars.size fvars
|
||
let type := type.headBeta
|
||
let val := val.instantiateRevRange j fvars.size fvars
|
||
let fvarId ← mkFreshFVarId
|
||
let (n, s) ← mkName lctx n true s
|
||
let lctx := lctx.mkLetDecl fvarId n type val
|
||
let fvar := mkFVar fvarId
|
||
let fvars := fvars.push fvar
|
||
loop i lctx fvars j s body
|
||
| (i+1), lctx, fvars, j, s, Expr.forallE n type body c => do
|
||
let type := type.instantiateRevRange j fvars.size fvars
|
||
let type := type.headBeta
|
||
let fvarId ← mkFreshFVarId
|
||
let (n, s) ← mkName lctx n c.binderInfo.isExplicit s
|
||
let lctx := lctx.mkLocalDecl fvarId n type c.binderInfo
|
||
let fvar := mkFVar fvarId
|
||
let fvars := fvars.push fvar
|
||
loop i lctx fvars j s body
|
||
| (i+1), lctx, fvars, j, s, type =>
|
||
let type := type.instantiateRevRange j fvars.size fvars
|
||
withReader (fun ctx => { ctx with lctx := lctx }) do
|
||
withNewLocalInstances fvars j do
|
||
let newType ← whnf type
|
||
if newType.isForall then
|
||
loop (i+1) lctx fvars fvars.size s newType
|
||
else
|
||
throwTacticEx `introN mvarId "insufficient number of binders"
|
||
let (fvars, mvarId) ← loop n lctx #[] 0 s mvarType
|
||
pure (fvars.map Expr.fvarId!, mvarId)
|
||
|
||
register_builtin_option tactic.hygienic : Bool := {
|
||
defValue := true
|
||
group := "tactic"
|
||
descr := "make sure tactics are hygienic"
|
||
}
|
||
|
||
def getHygienicIntro : MetaM Bool := do
|
||
return tactic.hygienic.get (← getOptions)
|
||
|
||
private def mkAuxNameImp (preserveBinderNames : Bool) (hygienic : Bool) (useNamesForExplicitOnly : Bool)
|
||
(lctx : LocalContext) (binderName : Name) (isExplicit : Bool) : List Name → MetaM (Name × List Name)
|
||
| [] => mkAuxNameWithoutGivenName []
|
||
| n :: rest => do
|
||
if useNamesForExplicitOnly && !isExplicit then
|
||
mkAuxNameWithoutGivenName (n :: rest)
|
||
else if n != Name.mkSimple "_" then
|
||
pure (n, rest)
|
||
else
|
||
mkAuxNameWithoutGivenName rest
|
||
where
|
||
mkAuxNameWithoutGivenName (rest : List Name) : MetaM (Name × List Name) := do
|
||
if preserveBinderNames then
|
||
pure (binderName, rest)
|
||
else if hygienic then
|
||
let binderName ← mkFreshUserName binderName
|
||
pure (binderName, rest)
|
||
else
|
||
pure (lctx.getUnusedName binderName, rest)
|
||
|
||
def introNCore (mvarId : MVarId) (n : Nat) (givenNames : List Name) (useNamesForExplicitOnly : Bool) (preserveBinderNames : Bool)
|
||
: MetaM (Array FVarId × MVarId) := do
|
||
let hygienic ← getHygienicIntro
|
||
if n == 0 then
|
||
pure (#[], mvarId)
|
||
else
|
||
introNImp mvarId n (mkAuxNameImp preserveBinderNames hygienic useNamesForExplicitOnly) givenNames
|
||
|
||
abbrev introN (mvarId : MVarId) (n : Nat) (givenNames : List Name := []) (useNamesForExplicitOnly := false) : MetaM (Array FVarId × MVarId) :=
|
||
introNCore mvarId n givenNames (useNamesForExplicitOnly := useNamesForExplicitOnly) (preserveBinderNames := false)
|
||
|
||
abbrev introNP (mvarId : MVarId) (n : Nat) : MetaM (Array FVarId × MVarId) :=
|
||
introNCore mvarId n [] (useNamesForExplicitOnly := false) (preserveBinderNames := true)
|
||
|
||
def intro (mvarId : MVarId) (name : Name) : MetaM (FVarId × MVarId) := do
|
||
let (fvarIds, mvarId) ← introN mvarId 1 [name]
|
||
pure (fvarIds.get! 0, mvarId)
|
||
|
||
def intro1Core (mvarId : MVarId) (preserveBinderNames : Bool) : MetaM (FVarId × MVarId) := do
|
||
let (fvarIds, mvarId) ← introNCore mvarId 1 [] (useNamesForExplicitOnly := false) preserveBinderNames
|
||
pure (fvarIds.get! 0, mvarId)
|
||
|
||
abbrev intro1 (mvarId : MVarId) : MetaM (FVarId × MVarId) :=
|
||
intro1Core mvarId false
|
||
|
||
abbrev intro1P (mvarId : MVarId) : MetaM (FVarId × MVarId) :=
|
||
intro1Core mvarId true
|
||
|
||
private def getIntrosSize : Expr → Nat
|
||
| Expr.forallE _ _ b _ => getIntrosSize b + 1
|
||
| Expr.letE _ _ _ b _ => getIntrosSize b + 1
|
||
| Expr.mdata _ b _ => getIntrosSize b
|
||
| _ => 0
|
||
|
||
def intros (mvarId : MVarId) : MetaM (Array FVarId × MVarId) := do
|
||
let type ← Meta.getMVarType mvarId
|
||
let type ← instantiateMVars type
|
||
let n := getIntrosSize type
|
||
if n == 0 then
|
||
return (#[], mvarId)
|
||
else
|
||
Meta.introN mvarId n
|
||
|
||
end Lean.Meta
|