lean4-htt/src/Lean/Meta/Tactic/Intro.lean
2021-09-07 19:06:50 -07:00

130 lines
5.3 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.

/-
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