/- Copyright (c) 2019 Microsoft Corporation. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Leonardo de Moura -/ module prelude public import Lean.Meta.InferType public section namespace Lean.Meta private structure FunInfoEnvCacheKey where c : Name ls : List Level maxArgs? : Option Nat deriving BEq, Hashable, TypeName @[inline] private def checkFunInfoCache (fn : Expr) (maxArgs? : Option Nat) (k : MetaM FunInfo) : MetaM FunInfo := do let key ← mkInfoCacheKey fn maxArgs? match (← get).cache.funInfo.find? key with | some finfo => return finfo | none => do let finfo ← match fn with | .const c ls => -- If `fn` is only a single constant, we can share the result with any thread that can see `c` -- as well. if ls.any (·.hasMVar) then -- However, if any level mvars are present, other threads should not be able to encounter -- the same `fn` and sharing would just waste time. k else realizeValue c { c, ls, maxArgs? : FunInfoEnvCacheKey } k | _ => k modify fun s => { s with cache := { s.cache with funInfo := s.cache.funInfo.insert key finfo } } return finfo @[inline] private def whenHasVar {α} (e : Expr) (deps : α) (k : α → α) : α := if e.hasFVar then k deps else deps private def collectDeps (fvars : Array Expr) (e : Expr) : Array Nat := let rec visit (e : Expr) (deps : Array Nat) : Array Nat := match e with | .app f a => whenHasVar e deps (visit a ∘ visit f) | .forallE _ d b _ => whenHasVar e deps (visit b ∘ visit d) | .lam _ d b _ => whenHasVar e deps (visit b ∘ visit d) | .letE _ t v b _ => whenHasVar e deps (visit b ∘ visit v ∘ visit t) | .proj _ _ e => visit e deps | .mdata _ e => visit e deps | .fvar .. => match fvars.idxOf? e with | none => deps | some i => if deps.contains i then deps else deps.push i | _ => deps let deps := visit e #[] deps.qsort (fun i j => i < j) /-- Update `hasFwdDeps` fields using new `backDeps` -/ private def updateHasFwdDeps (pinfo : Array ParamInfo) (backDeps : Array Nat) : Array ParamInfo := if backDeps.size == 0 then pinfo else -- update hasFwdDeps fields pinfo.mapIdx fun i info => if info.hasFwdDeps then info else if backDeps.contains i then { info with hasFwdDeps := true } else info private def getFunInfoAux (fn : Expr) (maxArgs? : Option Nat) : MetaM FunInfo := checkFunInfoCache fn maxArgs? do let fnType ← inferType fn withAtLeastTransparency TransparencyMode.default do forallBoundedTelescope fnType maxArgs? fun fvars type => do let mut paramInfo := #[] let mut higherOrderOutParams : FVarIdSet := {} for h : i in *...fvars.size do let fvar := fvars[i] let decl ← getFVarLocalDecl fvar let backDeps := collectDeps fvars decl.type let dependsOnHigherOrderOutParam := !higherOrderOutParams.isEmpty && Option.isSome (decl.type.find? fun e => e.isFVar && higherOrderOutParams.contains e.fvarId!) paramInfo := updateHasFwdDeps paramInfo backDeps paramInfo := paramInfo.push { backDeps, dependsOnHigherOrderOutParam binderInfo := decl.binderInfo isProp := (← isProp decl.type) isDecInst := (← forallTelescopeReducing decl.type fun _ type => return type.isAppOf ``Decidable) } if decl.binderInfo == .instImplicit then /- Collect higher order output parameters of this class -/ if let some className ← isClass? decl.type then if let some outParamPositions := getOutParamPositions? (← getEnv) className then unless outParamPositions.isEmpty do let args := decl.type.getAppArgs for h2 : i in *...args.size do if outParamPositions.contains i then let arg := args[i] if let some idx := fvars.idxOf? arg then if (← whnf (← inferType arg)).isForall then paramInfo := paramInfo.modify idx fun info => { info with higherOrderOutParam := true } higherOrderOutParams := higherOrderOutParams.insert arg.fvarId! let resultDeps := collectDeps fvars type paramInfo := updateHasFwdDeps paramInfo resultDeps return { resultDeps, paramInfo } def getFunInfo (fn : Expr) (maxArgs? : Option Nat := none) : MetaM FunInfo := getFunInfoAux fn maxArgs? def getFunInfoNArgs (fn : Expr) (nargs : Nat) : MetaM FunInfo := getFunInfoAux fn (some nargs) def FunInfo.getArity (info : FunInfo) : Nat := info.paramInfo.size end Lean.Meta