lean4-htt/src/Lean/Expr.lean
Leonardo de Moura fc79b794ba fix: missing error messages
Issue reported by @JasonGross
2021-03-05 17:20:04 -08:00

1015 lines
36 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) 2018 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
import Lean.Data.KVMap
import Lean.Level
namespace Lean
inductive Literal where
| natVal (val : Nat)
| strVal (val : String)
deriving Inhabited, BEq
protected def Literal.hash : Literal → USize
| Literal.natVal v => hash v
| Literal.strVal v => hash v
instance : Hashable Literal := ⟨Literal.hash⟩
def Literal.lt : Literal → Literal → Bool
| Literal.natVal _, Literal.strVal _ => true
| Literal.natVal v₁, Literal.natVal v₂ => v₁ < v₂
| Literal.strVal v₁, Literal.strVal v₂ => v₁ < v₂
| _, _ => false
instance : HasLess Literal := ⟨fun a b => a.lt b⟩
instance (a b : Literal) : Decidable (a < b) :=
inferInstanceAs (Decidable (a.lt b))
inductive BinderInfo where
| default | implicit | strictImplicit | instImplicit | auxDecl
deriving Inhabited, BEq
def BinderInfo.hash : BinderInfo → USize
| BinderInfo.default => 947
| BinderInfo.implicit => 1019
| BinderInfo.strictImplicit => 1087
| BinderInfo.instImplicit => 1153
| BinderInfo.auxDecl => 1229
def BinderInfo.isExplicit : BinderInfo → Bool
| BinderInfo.implicit => false
| BinderInfo.strictImplicit => false
| BinderInfo.instImplicit => false
| _ => true
instance : Hashable BinderInfo := ⟨BinderInfo.hash⟩
def BinderInfo.isInstImplicit : BinderInfo → Bool
| BinderInfo.instImplicit => true
| _ => false
def BinderInfo.isAuxDecl : BinderInfo → Bool
| BinderInfo.auxDecl => true
| _ => false
abbrev MData := KVMap
abbrev MData.empty : MData := {}
/--
Cached hash code, cached results, and other data for `Expr`.
hash : 32-bits
hasFVar : 1-bit
hasExprMVar : 1-bit
hasLevelMVar : 1-bit
hasLevelParam : 1-bit
nonDepLet : 1-bit
binderInfo : 3-bits
looseBVarRange : 24-bits -/
def Expr.Data := UInt64
instance: Inhabited Expr.Data :=
inferInstanceAs (Inhabited UInt64)
def Expr.Data.hash (c : Expr.Data) : USize :=
c.toUInt32.toUSize
instance : BEq Expr.Data where
beq (a b : UInt64) := a == b
def Expr.Data.looseBVarRange (c : Expr.Data) : UInt32 :=
(c.shiftRight 40).toUInt32
def Expr.Data.hasFVar (c : Expr.Data) : Bool :=
((c.shiftRight 32).land 1) == 1
def Expr.Data.hasExprMVar (c : Expr.Data) : Bool :=
((c.shiftRight 33).land 1) == 1
def Expr.Data.hasLevelMVar (c : Expr.Data) : Bool :=
((c.shiftRight 34).land 1) == 1
def Expr.Data.hasLevelParam (c : Expr.Data) : Bool :=
((c.shiftRight 35).land 1) == 1
def Expr.Data.nonDepLet (c : Expr.Data) : Bool :=
((c.shiftRight 36).land 1) == 1
@[extern c inline "(uint8_t)((#1 << 24) >> 61)"]
def Expr.Data.binderInfo (c : Expr.Data) : BinderInfo :=
let bi := (c.shiftLeft 24).shiftRight 61
if bi == 0 then BinderInfo.default
else if bi == 1 then BinderInfo.implicit
else if bi == 2 then BinderInfo.strictImplicit
else if bi == 3 then BinderInfo.instImplicit
else BinderInfo.auxDecl
@[extern c inline "(uint64_t)#1"]
def BinderInfo.toUInt64 : BinderInfo → UInt64
| BinderInfo.default => 0
| BinderInfo.implicit => 1
| BinderInfo.strictImplicit => 2
| BinderInfo.instImplicit => 3
| BinderInfo.auxDecl => 4
@[inline] private def Expr.mkDataCore
(h : USize) (looseBVarRange : Nat)
(hasFVar hasExprMVar hasLevelMVar hasLevelParam nonDepLet : Bool) (bi : BinderInfo)
: Expr.Data :=
if looseBVarRange > Nat.pow 2 24 - 1 then panic! "bound variable index is too big"
else
let r : UInt64 :=
h.toUInt32.toUInt64 +
hasFVar.toUInt64.shiftLeft 32 +
hasExprMVar.toUInt64.shiftLeft 33 +
hasLevelMVar.toUInt64.shiftLeft 34 +
hasLevelParam.toUInt64.shiftLeft 35 +
nonDepLet.toUInt64.shiftLeft 36 +
bi.toUInt64.shiftLeft 37 +
looseBVarRange.toUInt64.shiftLeft 40
r
def Expr.mkData (h : USize) (looseBVarRange : Nat := 0) (hasFVar hasExprMVar hasLevelMVar hasLevelParam : Bool := false) : Expr.Data :=
Expr.mkDataCore h looseBVarRange hasFVar hasExprMVar hasLevelMVar hasLevelParam false BinderInfo.default
def Expr.mkDataForBinder (h : USize) (looseBVarRange : Nat) (hasFVar hasExprMVar hasLevelMVar hasLevelParam : Bool) (bi : BinderInfo) : Expr.Data :=
Expr.mkDataCore h looseBVarRange hasFVar hasExprMVar hasLevelMVar hasLevelParam false bi
def Expr.mkDataForLet (h : USize) (looseBVarRange : Nat) (hasFVar hasExprMVar hasLevelMVar hasLevelParam nonDepLet : Bool) : Expr.Data :=
Expr.mkDataCore h looseBVarRange hasFVar hasExprMVar hasLevelMVar hasLevelParam nonDepLet BinderInfo.default
open Expr
abbrev MVarId := Name
abbrev FVarId := Name
/- We use the `E` suffix (short for `Expr`) to avoid collision with keywords.
We considered using «...», but it is too inconvenient to use. -/
inductive Expr where
| bvar : Nat → Data → Expr -- bound variables
| fvar : FVarId → Data → Expr -- free variables
| mvar : MVarId → Data → Expr -- meta variables
| sort : Level → Data → Expr -- Sort
| const : Name → List Level → Data → Expr -- constants
| app : Expr → Expr → Data → Expr -- application
| lam : Name → Expr → Expr → Data → Expr -- lambda abstraction
| forallE : Name → Expr → Expr → Data → Expr -- (dependent) arrow
| letE : Name → Expr → Expr → Expr → Data → Expr -- let expressions
| lit : Literal → Data → Expr -- literals
| mdata : MData → Expr → Data → Expr -- metadata
| proj : Name → Nat → Expr → Data → Expr -- projection
deriving Inhabited
namespace Expr
@[inline] def data : Expr → Data
| bvar _ d => d
| fvar _ d => d
| mvar _ d => d
| sort _ d => d
| const _ _ d => d
| app _ _ d => d
| lam _ _ _ d => d
| forallE _ _ _ d => d
| letE _ _ _ _ d => d
| lit _ d => d
| mdata _ _ d => d
| proj _ _ _ d => d
def ctorName : Expr → String
| bvar _ _ => "bvar"
| fvar _ _ => "fvar"
| mvar _ _ => "mvar"
| sort _ _ => "sort"
| const _ _ _ => "const"
| app _ _ _ => "app"
| lam _ _ _ _ => "lam"
| forallE _ _ _ _ => "forallE"
| letE _ _ _ _ _ => "letE"
| lit _ _ => "lit"
| mdata _ _ _ => "mdata"
| proj _ _ _ _ => "proj"
protected def hash (e : Expr) : USize :=
e.data.hash
instance : Hashable Expr := ⟨Expr.hash⟩
def hasFVar (e : Expr) : Bool :=
e.data.hasFVar
def hasExprMVar (e : Expr) : Bool :=
e.data.hasExprMVar
def hasLevelMVar (e : Expr) : Bool :=
e.data.hasLevelMVar
def hasMVar (e : Expr) : Bool :=
let d := e.data
d.hasExprMVar || d.hasLevelMVar
def hasLevelParam (e : Expr) : Bool :=
e.data.hasLevelParam
def looseBVarRange (e : Expr) : Nat :=
e.data.looseBVarRange.toNat
def binderInfo (e : Expr) : BinderInfo :=
e.data.binderInfo
@[export lean_expr_hash] def hashEx : Expr → USize := hash
@[export lean_expr_has_fvar] def hasFVarEx : Expr → Bool := hasFVar
@[export lean_expr_has_expr_mvar] def hasExprMVarEx : Expr → Bool := hasExprMVar
@[export lean_expr_has_level_mvar] def hasLevelMVarEx : Expr → Bool := hasLevelMVar
@[export lean_expr_has_mvar] def hasMVarEx : Expr → Bool := hasMVar
@[export lean_expr_has_level_param] def hasLevelParamEx : Expr → Bool := hasLevelParam
@[export lean_expr_loose_bvar_range] def looseBVarRangeEx (e : Expr) : UInt32 := e.data.looseBVarRange
@[export lean_expr_binder_info] def binderInfoEx : Expr → BinderInfo := binderInfo
end Expr
def mkLit (l : Literal) : Expr :=
Expr.lit l $ mkData (mixHash 3 (hash l))
def mkNatLit (n : Nat) : Expr :=
mkLit (Literal.natVal n)
def mkStrLit (s : String) : Expr :=
mkLit (Literal.strVal s)
def mkConst (n : Name) (lvls : List Level := []) : Expr :=
Expr.const n lvls $ mkData (mixHash 5 $ mixHash (hash n) (hash lvls)) 0 false false (lvls.any Level.hasMVar) (lvls.any Level.hasParam)
def Literal.type : Literal → Expr
| Literal.natVal _ => mkConst `Nat
| Literal.strVal _ => mkConst `String
@[export lean_lit_type]
def Literal.typeEx : Literal → Expr := Literal.type
def mkBVar (idx : Nat) : Expr :=
Expr.bvar idx $ mkData (mixHash 7 $ hash idx) (idx+1)
def mkSort (lvl : Level) : Expr :=
Expr.sort lvl $ mkData (mixHash 11 $ hash lvl) 0 false false lvl.hasMVar lvl.hasParam
def mkFVar (fvarId : FVarId) : Expr :=
Expr.fvar fvarId $ mkData (mixHash 13 $ hash fvarId) 0 true
def mkMVar (fvarId : MVarId) : Expr :=
Expr.mvar fvarId $ mkData (mixHash 17 $ hash fvarId) 0 false true
def mkMData (d : MData) (e : Expr) : Expr :=
Expr.mdata d e $ mkData (mixHash 19 $ hash e) e.looseBVarRange e.hasFVar e.hasExprMVar e.hasLevelMVar e.hasLevelParam
def mkProj (s : Name) (i : Nat) (e : Expr) : Expr :=
Expr.proj s i e $ mkData (mixHash 23 $ mixHash (hash s) $ mixHash (hash i) (hash e))
e.looseBVarRange e.hasFVar e.hasExprMVar e.hasLevelMVar e.hasLevelParam
def mkApp (f a : Expr) : Expr :=
Expr.app f a $ mkData (mixHash 29 $ mixHash (hash f) (hash a))
(Nat.max f.looseBVarRange a.looseBVarRange)
(f.hasFVar || a.hasFVar)
(f.hasExprMVar || a.hasExprMVar)
(f.hasLevelMVar || a.hasLevelMVar)
(f.hasLevelParam || a.hasLevelParam)
def mkLambda (x : Name) (bi : BinderInfo) (t : Expr) (b : Expr) : Expr :=
-- let x := x.eraseMacroScopes
Expr.lam x t b $ mkDataForBinder (mixHash 31 $ mixHash (hash t) (hash b))
(Nat.max t.looseBVarRange (b.looseBVarRange - 1))
(t.hasFVar || b.hasFVar)
(t.hasExprMVar || b.hasExprMVar)
(t.hasLevelMVar || b.hasLevelMVar)
(t.hasLevelParam || b.hasLevelParam)
bi
def mkForall (x : Name) (bi : BinderInfo) (t : Expr) (b : Expr) : Expr :=
-- let x := x.eraseMacroScopes
Expr.forallE x t b $ mkDataForBinder (mixHash 37 $ mixHash (hash t) (hash b))
(Nat.max t.looseBVarRange (b.looseBVarRange - 1))
(t.hasFVar || b.hasFVar)
(t.hasExprMVar || b.hasExprMVar)
(t.hasLevelMVar || b.hasLevelMVar)
(t.hasLevelParam || b.hasLevelParam)
bi
/- Return `Unit -> type`. Do not confuse with `Thunk type` -/
def mkSimpleThunkType (type : Expr) : Expr :=
mkForall Name.anonymous BinderInfo.default (Lean.mkConst `Unit) type
/- Return `fun (_ : Unit), e` -/
def mkSimpleThunk (type : Expr) : Expr :=
mkLambda `_ BinderInfo.default (Lean.mkConst `Unit) type
def mkLet (x : Name) (t : Expr) (v : Expr) (b : Expr) (nonDep : Bool := false) : Expr :=
-- let x := x.eraseMacroScopes
Expr.letE x t v b $ mkDataForLet (mixHash 41 $ mixHash (hash t) $ mixHash (hash v) (hash b))
(Nat.max (Nat.max t.looseBVarRange v.looseBVarRange) (b.looseBVarRange - 1))
(t.hasFVar || v.hasFVar || b.hasFVar)
(t.hasExprMVar || v.hasExprMVar || b.hasExprMVar)
(t.hasLevelMVar || v.hasLevelMVar || b.hasLevelMVar)
(t.hasLevelParam || v.hasLevelParam || b.hasLevelParam)
nonDep
@[export lean_expr_mk_bvar] def mkBVarEx : Nat → Expr := mkBVar
@[export lean_expr_mk_fvar] def mkFVarEx : FVarId → Expr := mkFVar
@[export lean_expr_mk_mvar] def mkMVarEx : MVarId → Expr := mkMVar
@[export lean_expr_mk_sort] def mkSortEx : Level → Expr := mkSort
@[export lean_expr_mk_const] def mkConstEx (c : Name) (lvls : List Level) : Expr := mkConst c lvls
@[export lean_expr_mk_app] def mkAppEx : Expr → Expr → Expr := mkApp
@[export lean_expr_mk_lambda] def mkLambdaEx (n : Name) (d b : Expr) (bi : BinderInfo) : Expr := mkLambda n bi d b
@[export lean_expr_mk_forall] def mkForallEx (n : Name) (d b : Expr) (bi : BinderInfo) : Expr := mkForall n bi d b
@[export lean_expr_mk_let] def mkLetEx (n : Name) (t v b : Expr) : Expr := mkLet n t v b
@[export lean_expr_mk_lit] def mkLitEx : Literal → Expr := mkLit
@[export lean_expr_mk_mdata] def mkMDataEx : MData → Expr → Expr := mkMData
@[export lean_expr_mk_proj] def mkProjEx : Name → Nat → Expr → Expr := mkProj
def mkAppN (f : Expr) (args : Array Expr) : Expr :=
args.foldl mkApp f
private partial def mkAppRangeAux (n : Nat) (args : Array Expr) (i : Nat) (e : Expr) : Expr :=
if i < n then mkAppRangeAux n args (i+1) (mkApp e (args.get! i)) else e
/-- `mkAppRange f i j #[a_1, ..., a_i, ..., a_j, ... ]` ==> the expression `f a_i ... a_{j-1}` -/
def mkAppRange (f : Expr) (i j : Nat) (args : Array Expr) : Expr :=
mkAppRangeAux j args i f
def mkAppRev (fn : Expr) (revArgs : Array Expr) : Expr :=
revArgs.foldr (fun a r => mkApp r a) fn
namespace Expr
-- TODO: implement it in Lean
@[extern "lean_expr_dbg_to_string"]
constant dbgToString (e : @& Expr) : String
@[extern "lean_expr_quick_lt"]
constant quickLt (a : @& Expr) (b : @& Expr) : Bool
@[extern "lean_expr_lt"]
constant lt (a : @& Expr) (b : @& Expr) : Bool
/- Return true iff `a` and `b` are alpha equivalent.
Binder annotations are ignored. -/
@[extern "lean_expr_eqv"]
constant eqv (a : @& Expr) (b : @& Expr) : Bool
instance : BEq Expr where
beq := Expr.eqv
/- Return true iff `a` and `b` are equal.
Binder names and annotations are taking into account. -/
@[extern "lean_expr_equal"]
constant equal (a : @& Expr) (b : @& Expr) : Bool
def isSort : Expr → Bool
| sort _ _ => true
| _ => false
def isProp : Expr → Bool
| sort (Level.zero ..) _ => true
| _ => false
def isBVar : Expr → Bool
| bvar _ _ => true
| _ => false
def isMVar : Expr → Bool
| mvar _ _ => true
| _ => false
def isFVar : Expr → Bool
| fvar _ _ => true
| _ => false
def isApp : Expr → Bool
| app .. => true
| _ => false
def isProj : Expr → Bool
| proj .. => true
| _ => false
def isConst : Expr → Bool
| const .. => true
| _ => false
def isConstOf : Expr → Name → Bool
| const n _ _, m => n == m
| _, _ => false
def isForall : Expr → Bool
| forallE .. => true
| _ => false
def isLambda : Expr → Bool
| lam .. => true
| _ => false
def isBinding : Expr → Bool
| lam .. => true
| forallE .. => true
| _ => false
def isLet : Expr → Bool
| letE .. => true
| _ => false
def isMData : Expr → Bool
| mdata .. => true
| _ => false
def isLit : Expr → Bool
| lit .. => true
| _ => false
def getAppFn : Expr → Expr
| app f a _ => getAppFn f
| e => e
def getAppNumArgsAux : Expr → Nat → Nat
| app f a _, n => getAppNumArgsAux f (n+1)
| e, n => n
def getAppNumArgs (e : Expr) : Nat :=
getAppNumArgsAux e 0
private def getAppArgsAux : Expr → Array Expr → Nat → Array Expr
| app f a _, as, i => getAppArgsAux f (as.set! i a) (i-1)
| _, as, _ => as
@[inline] def getAppArgs (e : Expr) : Array Expr :=
let dummy := mkSort levelZero
let nargs := e.getAppNumArgs
getAppArgsAux e (mkArray nargs dummy) (nargs-1)
private def getAppRevArgsAux : Expr → Array Expr → Array Expr
| app f a _, as => getAppRevArgsAux f (as.push a)
| _, as => as
@[inline] def getAppRevArgs (e : Expr) : Array Expr :=
getAppRevArgsAux e (Array.mkEmpty e.getAppNumArgs)
@[specialize] def withAppAux (k : Expr → Array Expr → α) : Expr → Array Expr → Nat → α
| app f a _, as, i => withAppAux k f (as.set! i a) (i-1)
| f, as, i => k f as
@[inline] def withApp (e : Expr) (k : Expr → Array Expr → α) : α :=
let dummy := mkSort levelZero
let nargs := e.getAppNumArgs
withAppAux k e (mkArray nargs dummy) (nargs-1)
@[specialize] private def withAppRevAux (k : Expr → Array Expr → α) : Expr → Array Expr → α
| app f a _, as => withAppRevAux k f (as.push a)
| f, as => k f as
@[inline] def withAppRev (e : Expr) (k : Expr → Array Expr → α) : α :=
withAppRevAux k e (Array.mkEmpty e.getAppNumArgs)
def getRevArgD : Expr → Nat → Expr → Expr
| app f a _, 0, _ => a
| app f _ _, i+1, v => getRevArgD f i v
| _, _, v => v
def getRevArg! : Expr → Nat → Expr
| app f a _, 0 => a
| app f _ _, i+1 => getRevArg! f i
| _, _ => panic! "invalid index"
@[inline] def getArg! (e : Expr) (i : Nat) (n := e.getAppNumArgs) : Expr :=
getRevArg! e (n - i - 1)
@[inline] def getArgD (e : Expr) (i : Nat) (v₀ : Expr) (n := e.getAppNumArgs) : Expr :=
getRevArgD e (n - i - 1) v₀
def isAppOf (e : Expr) (n : Name) : Bool :=
match e.getAppFn with
| const c _ _ => c == n
| _ => false
def isAppOfArity : Expr → Name → Nat → Bool
| const c _ _, n, 0 => c == n
| app f _ _, n, a+1 => isAppOfArity f n a
| _, _, _ => false
def appFn! : Expr → Expr
| app f _ _ => f
| _ => panic! "application expected"
def appArg! : Expr → Expr
| app _ a _ => a
| _ => panic! "application expected"
def isNatLit : Expr → Bool
| lit (Literal.natVal _) _ => true
| _ => false
def natLit? : Expr → Option Nat
| lit (Literal.natVal v) _ => v
| _ => none
def isStringLit : Expr → Bool
| lit (Literal.strVal _) _ => true
| _ => false
def isCharLit (e : Expr) : Bool :=
e.isAppOfArity `Char.ofNat 1 && e.appArg!.isNatLit
def constName! : Expr → Name
| const n _ _ => n
| _ => panic! "constant expected"
def constName? : Expr → Option Name
| const n _ _ => some n
| _ => none
def constLevels! : Expr → List Level
| const _ ls _ => ls
| _ => panic! "constant expected"
def bvarIdx! : Expr → Nat
| bvar idx _ => idx
| _ => panic! "bvar expected"
def fvarId! : Expr → FVarId
| fvar n _ => n
| _ => panic! "fvar expected"
def mvarId! : Expr → MVarId
| mvar n _ => n
| _ => panic! "mvar expected"
def bindingName! : Expr → Name
| forallE n _ _ _ => n
| lam n _ _ _ => n
| _ => panic! "binding expected"
def bindingDomain! : Expr → Expr
| forallE _ d _ _ => d
| lam _ d _ _ => d
| _ => panic! "binding expected"
def bindingBody! : Expr → Expr
| forallE _ _ b _ => b
| lam _ _ b _ => b
| _ => panic! "binding expected"
def bindingInfo! : Expr → BinderInfo
| forallE _ _ _ c => c.binderInfo
| lam _ _ _ c => c.binderInfo
| _ => panic! "binding expected"
def letName! : Expr → Name
| letE n _ _ _ _ => n
| _ => panic! "let expression expected"
def consumeMData : Expr → Expr
| mdata _ e _ => consumeMData e
| e => e
def hasLooseBVars (e : Expr) : Bool :=
e.looseBVarRange > 0
/- Remark: the following function assumes `e` does not have loose bound variables. -/
def isArrow (e : Expr) : Bool :=
match e with
| forallE _ _ b _ => !b.hasLooseBVars
| _ => false
@[extern "lean_expr_has_loose_bvar"]
constant hasLooseBVar (e : @& Expr) (bvarIdx : @& Nat) : Bool
/-- Return true if `e` contains the loose bound variable `bvarIdx` in an explicit parameter, or in the range if `tryRange == true`. -/
def hasLooseBVarInExplicitDomain : Expr → Nat → Bool → Bool
| Expr.forallE _ d b c, bvarIdx, tryRange => (c.binderInfo.isExplicit && hasLooseBVar d bvarIdx) || hasLooseBVarInExplicitDomain b (bvarIdx+1) tryRange
| e, bvarIdx, tryRange => tryRange && hasLooseBVar e bvarIdx
/--
Lower the loose bound variables `>= s` in `e` by `d`.
That is, a loose bound variable `bvar i`.
`i >= s` is mapped into `bvar (i-d)`.
Remark: if `s < d`, then result is `e` -/
@[extern "lean_expr_lower_loose_bvars"]
constant lowerLooseBVars (e : @& Expr) (s d : @& Nat) : Expr
/--
Lift loose bound variables `>= s` in `e` by `d`. -/
@[extern "lean_expr_lift_loose_bvars"]
constant liftLooseBVars (e : @& Expr) (s d : @& Nat) : Expr
/--
`inferImplicit e numParams considerRange` updates the first `numParams` parameter binder annotations of the `e` forall type.
It marks any parameter with an explicit binder annotation if there is another explicit arguments that depends on it or
the resulting type if `considerRange == true`.
Remark: we use this function to infer the bind annotations of inductive datatype constructors, and structure projections.
When the `{}` annotation is used in these commands, we set `considerRange == false`.
-/
def inferImplicit : Expr → Nat → Bool → Expr
| Expr.forallE n d b c, i+1, considerRange =>
let b := inferImplicit b i considerRange
let newInfo := if c.binderInfo.isExplicit && hasLooseBVarInExplicitDomain b 0 considerRange then BinderInfo.implicit else c.binderInfo
mkForall n newInfo d b
| e, 0, _ => e
| e, _, _ => e
/-- Instantiate the loose bound variables in `e` using `subst`.
That is, a loose `Expr.bvar i` is replaced with `subst[i]`. -/
@[extern "lean_expr_instantiate"]
constant instantiate (e : @& Expr) (subst : @& Array Expr) : Expr
@[extern "lean_expr_instantiate1"]
constant instantiate1 (e : @& Expr) (subst : @& Expr) : Expr
/-- Similar to instantiate, but `Expr.bvar i` is replaced with `subst[subst.size - i - 1]` -/
@[extern "lean_expr_instantiate_rev"]
constant instantiateRev (e : @& Expr) (subst : @& Array Expr) : Expr
/-- Similar to `instantiate`, but consider only the variables `xs` in the range `[beginIdx, endIdx)`.
Function panics if `beginIdx <= endIdx <= xs.size` does not hold. -/
@[extern "lean_expr_instantiate_range"]
constant instantiateRange (e : @& Expr) (beginIdx endIdx : @& Nat) (xs : @& Array Expr) : Expr
/-- Similar to `instantiateRev`, but consider only the variables `xs` in the range `[beginIdx, endIdx)`.
Function panics if `beginIdx <= endIdx <= xs.size` does not hold. -/
@[extern "lean_expr_instantiate_rev_range"]
constant instantiateRevRange (e : @& Expr) (beginIdx endIdx : @& Nat) (xs : @& Array Expr) : Expr
/-- Replace free variables `xs` with loose bound variables. -/
@[extern "lean_expr_abstract"]
constant abstract (e : @& Expr) (xs : @& Array Expr) : Expr
/-- Similar to `abstract`, but consider only the first `min n xs.size` entries in `xs`. -/
@[extern "lean_expr_abstract_range"]
constant abstractRange (e : @& Expr) (n : @& Nat) (xs : @& Array Expr) : Expr
/-- Replace occurrences of the free variable `fvar` in `e` with `v` -/
def replaceFVar (e : Expr) (fvar : Expr) (v : Expr) : Expr :=
(e.abstract #[fvar]).instantiate1 v
/-- Replace occurrences of the free variable `fvarId` in `e` with `v` -/
def replaceFVarId (e : Expr) (fvarId : FVarId) (v : Expr) : Expr :=
replaceFVar e (mkFVar fvarId) v
/-- Replace occurrences of the free variables `fvars` in `e` with `vs` -/
def replaceFVars (e : Expr) (fvars : Array Expr) (vs : Array Expr) : Expr :=
(e.abstract fvars).instantiateRev vs
instance : ToString Expr where
toString := Expr.dbgToString
def isAtomic : Expr → Bool
| Expr.const _ _ _ => true
| Expr.sort _ _ => true
| Expr.bvar _ _ => true
| Expr.lit _ _ => true
| Expr.mvar _ _ => true
| Expr.fvar _ _ => true
| _ => false
end Expr
def mkAppB (f a b : Expr) := mkApp (mkApp f a) b
def mkApp2 (f a b : Expr) := mkAppB f a b
def mkApp3 (f a b c : Expr) := mkApp (mkAppB f a b) c
def mkApp4 (f a b c d : Expr) := mkAppB (mkAppB f a b) c d
def mkApp5 (f a b c d e : Expr) := mkApp (mkApp4 f a b c d) e
def mkApp6 (f a b c d e₁ e₂ : Expr) := mkAppB (mkApp4 f a b c d) e₁ e₂
def mkApp7 (f a b c d e₁ e₂ e₃ : Expr) := mkApp3 (mkApp4 f a b c d) e₁ e₂ e₃
def mkApp8 (f a b c d e₁ e₂ e₃ e₄ : Expr) := mkApp4 (mkApp4 f a b c d) e₁ e₂ e₃ e₄
def mkApp9 (f a b c d e₁ e₂ e₃ e₄ e₅ : Expr) := mkApp5 (mkApp4 f a b c d) e₁ e₂ e₃ e₄ e₅
def mkApp10 (f a b c d e₁ e₂ e₃ e₄ e₅ e₆ : Expr) := mkApp6 (mkApp4 f a b c d) e₁ e₂ e₃ e₄ e₅ e₆
def mkDecIsTrue (pred proof : Expr) :=
mkAppB (mkConst `Decidable.isTrue) pred proof
def mkDecIsFalse (pred proof : Expr) :=
mkAppB (mkConst `Decidable.isFalse) pred proof
open Std (HashMap HashSet PHashMap PHashSet)
abbrev ExprMap (α : Type) := HashMap Expr α
abbrev PersistentExprMap (α : Type) := PHashMap Expr α
abbrev ExprSet := HashSet Expr
abbrev PersistentExprSet := PHashSet Expr
abbrev PExprSet := PersistentExprSet
/- Auxiliary type for forcing `==` to be structural equality for `Expr` -/
structure ExprStructEq where
val : Expr
deriving Inhabited
instance : Coe Expr ExprStructEq := ⟨ExprStructEq.mk⟩
namespace ExprStructEq
protected def beq : ExprStructEq → ExprStructEq → Bool
| ⟨e₁⟩, ⟨e₂⟩ => Expr.equal e₁ e₂
protected def hash : ExprStructEq → USize
| ⟨e⟩ => e.hash
instance : BEq ExprStructEq := ⟨ExprStructEq.beq⟩
instance : Hashable ExprStructEq := ⟨ExprStructEq.hash⟩
instance : ToString ExprStructEq := ⟨fun e => toString e.val⟩
end ExprStructEq
abbrev ExprStructMap (α : Type) := HashMap ExprStructEq α
abbrev PersistentExprStructMap (α : Type) := PHashMap ExprStructEq α
namespace Expr
private partial def mkAppRevRangeAux (revArgs : Array Expr) (start : Nat) (b : Expr) (i : Nat) : Expr :=
if i == start then b
else
let i := i - 1
mkAppRevRangeAux revArgs start (mkApp b (revArgs.get! i)) i
/-- `mkAppRevRange f b e args == mkAppRev f (revArgs.extract b e)` -/
def mkAppRevRange (f : Expr) (beginIdx endIdx : Nat) (revArgs : Array Expr) : Expr :=
mkAppRevRangeAux revArgs beginIdx f endIdx
private def betaRevAux (revArgs : Array Expr) (sz : Nat) : Expr → Nat → Expr
| Expr.lam _ _ b _, i =>
if i + 1 < sz then
betaRevAux revArgs sz b (i+1)
else
let n := sz - (i + 1)
mkAppRevRange (b.instantiateRange n sz revArgs) 0 n revArgs
| Expr.mdata _ b _, i => betaRevAux revArgs sz b i
| b, i =>
let n := sz - i
mkAppRevRange (b.instantiateRange n sz revArgs) 0 n revArgs
/-- If `f` is a lambda expression, than "beta-reduce" it using `revArgs`.
This function is often used with `getAppRev` or `withAppRev`.
Examples:
- `betaRev (fun x y => t x y) #[]` ==> `fun x y => t x y`
- `betaRev (fun x y => t x y) #[a]` ==> `fun y => t a y`
- `betaRev (fun x y => t x y) #[a, b]` ==> t b a`
- `betaRev (fun x y => t x y) #[a, b, c, d]` ==> t d c b a`
Suppose `t` is `(fun x y => t x y) a b c d`, then
`args := t.getAppRev` is `#[d, c, b, a]`,
and `betaRev (fun x y => t x y) #[d, c, b, a]` is `t a b c d`. -/
def betaRev (f : Expr) (revArgs : Array Expr) : Expr :=
if revArgs.size == 0 then f
else betaRevAux revArgs revArgs.size f 0
def isHeadBetaTargetFn : Expr → Bool
| Expr.lam _ _ _ _ => true
| Expr.mdata _ b _ => isHeadBetaTargetFn b
| _ => false
def headBeta (e : Expr) : Expr :=
let f := e.getAppFn
if f.isHeadBetaTargetFn then betaRev f e.getAppRevArgs else e
def isHeadBetaTarget (e : Expr) : Bool :=
e.getAppFn.isHeadBetaTargetFn
private def etaExpandedBody : Expr → Nat → Nat → Option Expr
| app f (bvar j _) _, n+1, i => if j == i then etaExpandedBody f n (i+1) else none
| _, n+1, _ => none
| f, 0, _ => if f.hasLooseBVars then none else some f
private def etaExpandedAux : Expr → Nat → Option Expr
| lam _ _ b _, n => etaExpandedAux b (n+1)
| e, n => etaExpandedBody e n 0
/--
If `e` is of the form `(fun x₁ ... xₙ => f x₁ ... xₙ)` and `f` does not contain `x₁`, ..., `xₙ`,
then return `some f`. Otherwise, return `none`.
It assumes `e` does not have loose bound variables.
Remark: `ₙ` may be 0 -/
def etaExpanded? (e : Expr) : Option Expr :=
etaExpandedAux e 0
/-- Similar to `etaExpanded?`, but only succeeds if `ₙ ≥ 1`. -/
def etaExpandedStrict? : Expr → Option Expr
| lam _ _ b _ => etaExpandedAux b 1
| _ => none
def getOptParamDefault? (e : Expr) : Option Expr :=
if e.isAppOfArity `optParam 2 then
some e.appArg!
else
none
def getAutoParamTactic? (e : Expr) : Option Expr :=
if e.isAppOfArity `autoParam 2 then
some e.appArg!
else
none
def isOptParam (e : Expr) : Bool :=
e.isAppOfArity `optParam 2
def isAutoParam (e : Expr) : Bool :=
e.isAppOfArity `autoParam 2
/-- Return true iff `e` contains a free variable which statisfies `p`. -/
@[inline] def hasAnyFVar (e : Expr) (p : FVarId → Bool) : Bool :=
let rec @[specialize] visit (e : Expr) := if !e.hasFVar then false else
match e with
| Expr.forallE _ d b _ => visit d || visit b
| Expr.lam _ d b _ => visit d || visit b
| Expr.mdata _ e _ => visit e
| Expr.letE _ t v b _ => visit t || visit v || visit b
| Expr.app f a _ => visit f || visit a
| Expr.proj _ _ e _ => visit e
| e@(Expr.fvar fvarId _) => p fvarId
| e => false
visit e
def containsFVar (e : Expr) (fvarId : FVarId) : Bool :=
e.hasAnyFVar (· == fvarId)
/- The update functions here are defined using C code. They will try to avoid
allocating new values using pointer equality.
The hypotheses `(h : e.is... = true)` are used to ensure Lean will not crash
at runtime.
The `update*!` functions are inlined and provide a convenient way of using the
update proofs without providing proofs.
Note that if they are used under a match-expression, the compiler will eliminate
the double-match. -/
@[extern "lean_expr_update_app"]
def updateApp (e : Expr) (newFn : Expr) (newArg : Expr) (h : e.isApp = true) : Expr :=
mkApp newFn newArg
@[inline] def updateApp! (e : Expr) (newFn : Expr) (newArg : Expr) : Expr :=
match e with
| app fn arg c => updateApp (app fn arg c) newFn newArg rfl
| _ => panic! "application expected"
@[extern "lean_expr_update_const"]
def updateConst (e : Expr) (newLevels : List Level) (h : e.isConst = true) : Expr :=
mkConst e.constName! newLevels
@[inline] def updateConst! (e : Expr) (newLevels : List Level) : Expr :=
match e with
| const n ls c => updateConst (const n ls c) newLevels rfl
| _ => panic! "constant expected"
@[extern "lean_expr_update_sort"]
def updateSort (e : Expr) (newLevel : Level) (h : e.isSort = true) : Expr :=
mkSort newLevel
@[inline] def updateSort! (e : Expr) (newLevel : Level) : Expr :=
match e with
| sort l c => updateSort (sort l c) newLevel rfl
| _ => panic! "level expected"
@[extern "lean_expr_update_proj"]
def updateProj (e : Expr) (newExpr : Expr) (h : e.isProj = true) : Expr :=
match e with
| proj s i _ _ => mkProj s i newExpr
| _ => e -- unreachable because of `h`
@[extern "lean_expr_update_mdata"]
def updateMData (e : Expr) (newExpr : Expr) (h : e.isMData = true) : Expr :=
match e with
| mdata d _ _ => mkMData d newExpr
| _ => e -- unreachable because of `h`
@[inline] def updateMData! (e : Expr) (newExpr : Expr) : Expr :=
match e with
| mdata d e c => updateMData (mdata d e c) newExpr rfl
| _ => panic! "mdata expected"
@[inline] def updateProj! (e : Expr) (newExpr : Expr) : Expr :=
match e with
| proj s i e c => updateProj (proj s i e c) newExpr rfl
| _ => panic! "proj expected"
@[extern "lean_expr_update_forall"]
def updateForall (e : Expr) (newBinfo : BinderInfo) (newDomain : Expr) (newBody : Expr) (h : e.isForall = true) : Expr :=
mkForall e.bindingName! newBinfo newDomain newBody
@[inline] def updateForall! (e : Expr) (newBinfo : BinderInfo) (newDomain : Expr) (newBody : Expr) : Expr :=
match e with
| forallE n d b c => updateForall (forallE n d b c) newBinfo newDomain newBody rfl
| _ => panic! "forall expected"
@[inline] def updateForallE! (e : Expr) (newDomain : Expr) (newBody : Expr) : Expr :=
match e with
| forallE n d b c => updateForall (forallE n d b c) c.binderInfo newDomain newBody rfl
| _ => panic! "forall expected"
@[extern "lean_expr_update_lambda"]
def updateLambda (e : Expr) (newBinfo : BinderInfo) (newDomain : Expr) (newBody : Expr) (h : e.isLambda = true) : Expr :=
mkLambda e.bindingName! newBinfo newDomain newBody
@[inline] def updateLambda! (e : Expr) (newBinfo : BinderInfo) (newDomain : Expr) (newBody : Expr) : Expr :=
match e with
| lam n d b c => updateLambda (lam n d b c) newBinfo newDomain newBody rfl
| _ => panic! "lambda expected"
@[inline] def updateLambdaE! (e : Expr) (newDomain : Expr) (newBody : Expr) : Expr :=
match e with
| lam n d b c => updateLambda (lam n d b c) c.binderInfo newDomain newBody rfl
| _ => panic! "lambda expected"
@[extern "lean_expr_update_let"]
def updateLet (e : Expr) (newType : Expr) (newVal : Expr) (newBody : Expr) (h : e.isLet = true) : Expr :=
mkLet e.letName! newType newVal newBody
@[inline] def updateLet! (e : Expr) (newType : Expr) (newVal : Expr) (newBody : Expr) : Expr :=
match e with
| letE n t v b c => updateLet (letE n t v b c) newType newVal newBody rfl
| _ => panic! "let expression expected"
def updateFn : Expr → Expr → Expr
| e@(app f a _), g => e.updateApp! (updateFn f g) a
| _, g => g
/- Instantiate level parameters -/
@[inline] def instantiateLevelParamsCore (s : Name → Option Level) (e : Expr) : Expr :=
let rec @[specialize] visit (e : Expr) : Expr :=
if !e.hasLevelParam then e
else match e with
| lam n d b _ => e.updateLambdaE! (visit d) (visit b)
| forallE n d b _ => e.updateForallE! (visit d) (visit b)
| letE n t v b _ => e.updateLet! (visit t) (visit v) (visit b)
| app f a _ => e.updateApp! (visit f) (visit a)
| proj _ _ s _ => e.updateProj! (visit s)
| mdata _ b _ => e.updateMData! (visit b)
| const _ us _ => e.updateConst! (us.map (fun u => u.instantiateParams s))
| sort u _ => e.updateSort! (u.instantiateParams s)
| e => e
visit e
private def getParamSubst : List Name → List Level → Name → Option Level
| p::ps, u::us, p' => if p == p' then some u else getParamSubst ps us p'
| _, _, _ => none
def instantiateLevelParams (e : Expr) (paramNames : List Name) (lvls : List Level) : Expr :=
instantiateLevelParamsCore (getParamSubst paramNames lvls) e
private partial def getParamSubstArray (ps : Array Name) (us : Array Level) (p' : Name) (i : Nat) : Option Level :=
if h : i < ps.size then
let p := ps.get ⟨i, h⟩
if h : i < us.size then
let u := us.get ⟨i, h⟩
if p == p' then some u else getParamSubstArray ps us p' (i+1)
else none
else none
def instantiateLevelParamsArray (e : Expr) (paramNames : Array Name) (lvls : Array Level) : Expr :=
instantiateLevelParamsCore (fun p => getParamSubstArray paramNames lvls p 0) e
/- Annotate `e` with the given option. -/
def setOption (e : Expr) (optionName : Name) [KVMap.Value α] (val : α) : Expr :=
mkMData (MData.empty.set optionName val) e
/- Annotate `e` with `pp.explicit := true`
The delaborator uses `pp` options. -/
def setPPExplicit (e : Expr) (flag : Bool) :=
e.setOption `pp.explicit flag
/- If `e` is an application `f a_1 ... a_n` annotate `f`, `a_1` ... `a_n` with `pp.explicit := false`,
and annotate `e` with `pp.explicit := true`. -/
def setAppPPExplicit (e : Expr) : Expr :=
match e with
| app .. =>
let f := e.getAppFn.setPPExplicit false
let args := e.getAppArgs.map (·.setPPExplicit false)
mkAppN f args |>.setPPExplicit true
| _ => e
/- Similar for `setAppPPExplicit`, but only annotate children with `pp.explicit := false` if
`e` does not contain metavariables. -/
def setAppPPExplicitForExposingMVars (e : Expr) : Expr :=
match e with
| app .. =>
let f := e.getAppFn.setPPExplicit false
let args := e.getAppArgs.map fun arg => if arg.hasMVar then arg else arg.setPPExplicit false
mkAppN f args |>.setPPExplicit true
| _ => e
end Expr
def mkAnnotation (kind : Name) (e : Expr) : Expr :=
mkMData (KVMap.empty.insert kind (DataValue.ofBool true)) e
def annotation? (kind : Name) (e : Expr) : Option Expr :=
match e with
| Expr.mdata d b _ => if d.size == 1 && d.getBool kind false then some b else none
| _ => none
def mkFreshFVarId {m : Type → Type} [Monad m] [MonadNameGenerator m] : m FVarId :=
mkFreshId
def mkFreshMVarId {m : Type → Type} [Monad m] [MonadNameGenerator m] : m FVarId :=
mkFreshId
end Lean