ddc-core-simpl-0.4.3.1: DDC/Core/Transform/Lambdas/Lift.hs
module DDC.Core.Transform.Lambdas.Lift
(liftLambda)
where
import DDC.Core.Transform.Lambdas.Base
import DDC.Core.Fragment
import DDC.Core.Exp.Annot.Context
import DDC.Core.Exp.Annot.Ctx
import DDC.Core.Exp.Annot
import DDC.Core.Collect
import DDC.Data.Pretty
import DDC.Data.Name
import Data.Function
import Data.List
import Data.Set (Set)
import qualified DDC.Core.Check as Check
import qualified DDC.Type.Env as Env
import qualified DDC.Core.Env.EnvX as EnvX
import qualified Data.Set as Set
import qualified Data.Map as Map
---------------------------------------------------------------------------------------------------
-- | Construct the call site, and new lifted binding for a lambda lifted
-- abstraction.
liftLambda
:: (Show a, Show n, Pretty n, Ord n, CompoundName n, Pretty a)
=> Profile n -- ^ Language profile.
-> Context a n -- ^ Context of the original abstraction.
-> Set (Bool, Bound n) -- ^ Free variables in the body of the abstraction.
-> a
-> [(Bool, Bind n)] -- ^ Parameters of the current lambda abstraction.
-> Exp a n -- ^ Body of the current lambda abstraction.
-> S ( Exp a n
, Bind n, Exp a n)
liftLambda p c fusFree a bfsParam xBody
= do
-- Name of the enclosing top-level binding.
let Just nTop = takeTopNameOfCtx (contextCtx c)
-- Names of other supers bound at top-level.
let nsSuper = takeTopLetEnvNamesOfCtx (contextCtx c)
-- The complete abstraction that we're lifting out.
let xLambda = makeXLamFlags a bfsParam xBody
-- Get the list of new parameters we need to add to our super.
-- These bind all the variables that were free in the abstraction
-- group that we're lifting out.
let fusFree_filtered
= filter (needParamForFreeBound nsSuper bfsParam)
$ Set.toList fusFree
-- Lookup the types of those variables from the context,
-- so we can add the correct types for the parameters of the new super.
let joinType (f@True, u)
| Just t <- EnvX.lookupT u (contextEnv c)
= ((f, u), t)
joinType (f@False, u)
| Just t <- EnvX.lookupX u (contextEnv c)
= ((f, u), t)
joinType (f, u)
= error $ unlines
[ "ddc-core-simpl.liftLambda: cannot find type of free variable."
, show (f, u)
, show (Map.keys $ EnvX.envxMap (contextEnv c)) ]
let futsFree_types
= map joinType fusFree_filtered
-- Add in type variables that are free in the types of free
-- value variables. We need to bind these as well in the new super.
let expandFree ((f@True, u), _)
= [(f, u)]
expandFree ((f@False, u), t)
= [(f, u)]
++ [(True, ut) | ut <- Set.toList
$ freeVarsT Env.empty t]
let fusFree_body
= [(True, ut) | ut <- Set.toList
$ freeVarsT Env.empty $ typeOfExp p c xLambda]
let futsFree_expandFree
= map joinType
$ Set.toList $ Set.fromList
$ (concatMap expandFree $ futsFree_types)
++ fusFree_body
-- Sort free vars so the type variables come out the front.
let futsFree
= sortBy (compare `on` (not . fst . fst))
$ futsFree_expandFree
-- Make the new super.
-- For the lifted abstraction, wrap it in new lambdas to bind all of
-- its free variables.
-- ISSUE #330: Lambda lifter doesn't work with anonymous binders.
let makeBind ((True, (UName n)), t) = (True, BName n t)
makeBind ((False, (UName n)), t) = (False, BName n t)
makeBind fut
= error $ "ddc-core-simpl.liftLamba: unhandled binder " ++ show fut
let bsParam = map makeBind futsFree
let xLifted = makeXLamFlags a bsParam xLambda
-- Make a binder for the new super.
(bLifted, uLifted)
<- newVarExtend nTop "L"
$ typeOfExp p c xLifted
-- At the point where the original abstraction group was,
-- call our new lifted super instead.
let makeArg (True, u) = XType a (TVar u)
makeArg (False, u) = XVar a u
let xCall = xApps a (XVar a uLifted)
$ map makeArg $ map fst futsFree
-- All done.
return ( xCall
, bLifted, xLifted)
---------------------------------------------------------------------------------------------------
-- | Decide whether we need to bind the given variable as a new parameter
-- on the lifted super.
needParamForFreeBound
:: (Ord n)
=> Set n -- ^ Names of supers at top level.
-> [(Bool, Bind n)] -- ^ Parameters of the current lambda abstraction.
-> (Bool, Bound n) -- ^ Bound variable, and whether it is a type (True) or not.
-> Bool
needParamForFreeBound nsSuper bfsParam (bType, u)
-- We don't need new parameters for supers that are already
-- at top level, as we can reference those directly.
| not bType
, UName n <- u
= not $ Set.member n nsSuper
-- We don't need new parameters for primitives,
-- as we can reference those directly.
| UPrim{} <- u
= False
-- We don't need new paramters for variables that
-- are already bound by the abstractions that we're lifting.
| any (boundMatchesBind u . snd) bfsParam
= False
-- We need a new parameter for this free variable.
| otherwise
= True
-- Function to get the type of an expression in the given context.
typeOfExp
:: (Pretty a, Show a, Show n, Pretty n, Eq n, Ord n)
=> Profile n -- ^ Language profile.
-> Context a n -- ^ Current context.
-> Exp a n -- ^ Expression we want the type for.
-> Type n
typeOfExp p c x
= case Check.typeOfExp
(Check.configOfProfile p)
(contextEnv c) x
of Left err
-> error $ renderIndent $ vcat
[ text "ddc-core-simpl.liftLambda: type error in lifted expression"
, ppr err]
Right t
-> t