%
% (c) The University of Glasgow 2006
% (c) The GRASP/AQUA Project, Glasgow University, 19921998
%
Patternmatching bindings (HsBinds and MonoBinds)
Handles @HsBinds@; those at the top level require different handling,
in that the @Rec@/@NonRec@/etc structure is thrown away (whereas at
lower levels it is preserved with @let@/@letrec@s).
\begin{code}
module DsBinds ( dsTopLHsBinds, dsLHsBinds, decomposeRuleLhs,
dsCoercion,
AutoScc(..)
) where
#include "HsVersions.h"
import DsExpr( dsLExpr, dsExpr )
import Match( matchWrapper )
import DsMonad
import DsGRHSs
import DsUtils
import OccurAnal
import HsSyn
import CoreSyn
import MkCore
import CoreUtils
import CoreFVs
import TcHsSyn ( mkArbitraryType )
import TcType
import CostCentre
import Module
import Id
import MkId ( seqId )
import Var ( Var, TyVar )
import VarSet
import Rules
import VarEnv
import Outputable
import SrcLoc
import Maybes
import Bag
import BasicTypes hiding ( TopLevel )
import FastString
import StaticFlags ( opt_DsMultiTyVar )
import Util ( mapSnd, mapAndUnzip, lengthExceeds )
import Control.Monad
import Data.List
\end{code}
%************************************************************************
%* *
\subsection[dsMonoBinds]{Desugaring a @MonoBinds@}
%* *
%************************************************************************
\begin{code}
dsTopLHsBinds :: AutoScc -> LHsBinds Id -> DsM [(Id,CoreExpr)]
dsTopLHsBinds auto_scc binds = ds_lhs_binds auto_scc binds
dsLHsBinds :: LHsBinds Id -> DsM [(Id,CoreExpr)]
dsLHsBinds binds = ds_lhs_binds NoSccs binds
ds_lhs_binds :: AutoScc -> LHsBinds Id -> DsM [(Id,CoreExpr)]
ds_lhs_binds auto_scc binds = foldM (dsLHsBind auto_scc) [] (bagToList binds)
dsLHsBind :: AutoScc
-> [(Id,CoreExpr)]
-> LHsBind Id
-> DsM [(Id,CoreExpr)]
dsLHsBind auto_scc rest (L loc bind)
= putSrcSpanDs loc $ dsHsBind auto_scc rest bind
dsHsBind :: AutoScc
-> [(Id,CoreExpr)]
-> HsBind Id
-> DsM [(Id,CoreExpr)]
dsHsBind _ rest (VarBind var expr) = do
core_expr <- dsLExpr expr
core_expr' <- addDictScc var core_expr
return ((var, core_expr') : rest)
dsHsBind _ rest (FunBind { fun_id = L _ fun, fun_matches = matches,
fun_co_fn = co_fn, fun_tick = tick, fun_infix = inf }) = do
(args, body) <- matchWrapper (FunRhs (idName fun) inf) matches
body' <- mkOptTickBox tick body
rhs <- dsCoercion co_fn (return (mkLams args body'))
return ((fun,rhs) : rest)
dsHsBind _ rest (PatBind { pat_lhs = pat, pat_rhs = grhss, pat_rhs_ty = ty }) = do
body_expr <- dsGuarded grhss ty
sel_binds <- mkSelectorBinds pat body_expr
return (sel_binds ++ rest)
dsHsBind auto_scc rest (AbsBinds [] [] exports binds)
= do { core_prs <- ds_lhs_binds NoSccs binds
; let env = mkABEnv exports
do_one (lcl_id, rhs) | Just (_, gbl_id, _, prags) <- lookupVarEnv env lcl_id
= addInlinePrags prags gbl_id $
addAutoScc auto_scc gbl_id rhs
| otherwise = (lcl_id, rhs)
locals' = [(lcl_id, Var gbl_id) | (_, gbl_id, lcl_id, _) <- exports]
; return (map do_one core_prs ++ locals' ++ rest) }
dsHsBind auto_scc rest (AbsBinds tyvars [] exports binds)
| opt_DsMultiTyVar
=
do { core_prs <- ds_lhs_binds NoSccs binds
; arby_env <- mkArbitraryTypeEnv tyvars exports
; let (lg_binds, core_prs') = mapAndUnzip do_one core_prs
bndrs = mkVarSet (map fst core_prs)
add_lets | core_prs `lengthExceeds` 10 = add_some
| otherwise = mkLets lg_binds
add_some rhs = mkLets [ NonRec b r | NonRec b r <- lg_binds
, b `elemVarSet` fvs] rhs
where
fvs = exprSomeFreeVars (`elemVarSet` bndrs) rhs
env = mkABEnv exports
do_one (lcl_id, rhs)
| Just (id_tvs, gbl_id, _, prags) <- lookupVarEnv env lcl_id
= (NonRec lcl_id (mkTyApps (Var gbl_id) (mkTyVarTys id_tvs)),
addInlinePrags prags gbl_id $
addAutoScc auto_scc gbl_id $
mkLams id_tvs $
mkLets [ NonRec tv (Type (lookupVarEnv_NF arby_env tv))
| tv <- tyvars, not (tv `elem` id_tvs)] $
add_lets rhs)
| otherwise
= (NonRec lcl_id (mkTyApps (Var non_exp_gbl_id) (mkTyVarTys tyvars)),
(non_exp_gbl_id, mkLams tyvars (add_lets rhs)))
where
non_exp_gbl_id = setIdType lcl_id (mkForAllTys tyvars (idType lcl_id))
; return (core_prs' ++ rest) }
dsHsBind auto_scc rest
(AbsBinds all_tyvars dicts [(tyvars, global, local, prags)] binds)
= ASSERT( all (`elem` tyvars) all_tyvars ) do
core_prs <- ds_lhs_binds NoSccs binds
let
core_bind = Rec core_prs
mb_specs <- mapM (dsSpec all_tyvars dicts tyvars global local core_bind) prags
let
(spec_binds, rules) = unzip (catMaybes mb_specs)
global' = addIdSpecialisations global rules
rhs' = mkLams tyvars $ mkLams dicts $ Let core_bind (Var local)
bind = addInlinePrags prags global' $ addAutoScc auto_scc global' rhs'
return (bind : spec_binds ++ rest)
dsHsBind auto_scc rest (AbsBinds all_tyvars dicts exports binds)
= do { core_prs <- ds_lhs_binds NoSccs binds
; let env = mkABEnv exports
do_one (lcl_id,rhs) | Just (_, gbl_id, _, prags) <- lookupVarEnv env lcl_id
= addInlinePrags prags lcl_id $
addAutoScc auto_scc gbl_id rhs
| otherwise = (lcl_id,rhs)
core_bind = Rec (map do_one core_prs)
tup_expr = mkBigCoreVarTup locals
tup_ty = exprType tup_expr
poly_tup_expr = mkLams all_tyvars $ mkLams dicts $
Let core_bind tup_expr
locals = [local | (_, _, local, _) <- exports]
local_tys = map idType locals
; poly_tup_id <- newSysLocalDs (exprType poly_tup_expr)
; let mk_bind ((tyvars, global, local, prags), n)
=
do { ty_args <- mapM mk_ty_arg all_tyvars
; let substitute = substTyWith all_tyvars ty_args
; locals' <- newSysLocalsDs (map substitute local_tys)
; tup_id <- newSysLocalDs (substitute tup_ty)
; mb_specs <- mapM (dsSpec all_tyvars dicts tyvars global
local core_bind)
prags
; let (spec_binds, rules) = unzip (catMaybes mb_specs)
global' = addIdSpecialisations global rules
rhs = mkLams tyvars $ mkLams dicts $
mkTupleSelector locals' (locals' !! n) tup_id $
mkVarApps (mkTyApps (Var poly_tup_id) ty_args)
dicts
; return ((global', rhs) : spec_binds) }
where
mk_ty_arg all_tyvar
| all_tyvar `elem` tyvars = return (mkTyVarTy all_tyvar)
| otherwise = dsMkArbitraryType all_tyvar
; export_binds_s <- mapM mk_bind (exports `zip` [0..])
; return ((poly_tup_id, poly_tup_expr) :
(concat export_binds_s ++ rest)) }
mkABEnv :: [([TyVar], Id, Id, [LPrag])] -> VarEnv ([TyVar], Id, Id, [LPrag])
mkABEnv exports = mkVarEnv [ (lcl_id, export) | export@(_, _, lcl_id, _) <- exports]
dsSpec :: [TyVar] -> [DictId] -> [TyVar]
-> Id -> Id
-> CoreBind -> LPrag
-> DsM (Maybe ((Id,CoreExpr),
CoreRule))
dsSpec _ _ _ _ _ _ (L _ (InlinePrag {}))
= return Nothing
dsSpec all_tvs dicts tvs poly_id mono_id mono_bind
(L loc (SpecPrag spec_expr spec_ty inl))
= putSrcSpanDs loc $
do { let poly_name = idName poly_id
; spec_name <- newLocalName poly_name
; ds_spec_expr <- dsExpr spec_expr
; case (decomposeRuleLhs ds_spec_expr) of {
Nothing -> do { warnDs decomp_msg; return Nothing } ;
Just (bndrs, _fn, args) ->
case filter isDeadBinder bndrs of {
bs | not (null bs) -> do { warnDs (dead_msg bs); return Nothing }
| otherwise -> do
{ f_body <- fix_up (Let mono_bind (Var mono_id))
; let local_poly = setIdNotExported poly_id
spec_id = mkLocalId spec_name spec_ty
spec_rhs = Let (NonRec local_poly poly_f_body) ds_spec_expr
poly_f_body = mkLams (tvs ++ dicts) f_body
extra_dict_bndrs = [localiseId d
| d <- varSetElems (exprFreeVars ds_spec_expr)
, isDictId d]
rule = mkLocalRule (mkFastString ("SPEC " ++ showSDoc (ppr poly_name)))
AlwaysActive poly_name
(extra_dict_bndrs ++ bndrs) args
(mkVarApps (Var spec_id) bndrs)
; return (Just (addInlineInfo inl spec_id spec_rhs, rule))
} } } }
where
fix_up body | null void_tvs = return body
| otherwise = do { void_tys <- mapM dsMkArbitraryType void_tvs
; return (mkTyApps (mkLams void_tvs body) void_tys) }
void_tvs = all_tvs \\ tvs
dead_msg bs = vcat [ sep [ptext (sLit "Useless constraint") <> plural bs
<+> ptext (sLit "in specialied type:"),
nest 2 (pprTheta (map get_pred bs))]
, ptext (sLit "SPECIALISE pragma ignored")]
get_pred b = ASSERT( isId b ) expectJust "dsSpec" (tcSplitPredTy_maybe (idType b))
decomp_msg = hang (ptext (sLit "Specialisation too complicated to desugar; ignored"))
2 (ppr spec_expr)
mkArbitraryTypeEnv :: [TyVar] -> [([TyVar], a, b, c)] -> DsM (TyVarEnv Type)
mkArbitraryTypeEnv tyvars exports
= go emptyVarEnv exports
where
go env [] = return env
go env ((ltvs, _, _, _) : exports)
= do { env' <- foldlM extend env [tv | tv <- tyvars
, not (tv `elem` ltvs)
, not (tv `elemVarEnv` env)]
; go env' exports }
extend env tv = do { ty <- dsMkArbitraryType tv
; return (extendVarEnv env tv ty) }
dsMkArbitraryType :: TcTyVar -> DsM Type
dsMkArbitraryType tv = mkArbitraryType warn tv
where
warn span msg = putSrcSpanDs span (warnDs msg)
\end{code}
Note [Unused spec binders]
~~~~~~~~~~~~~~~~~~~~~~~~~~
Consider
f :: a -> a
It's true that this *is* a more specialised type, but the rule
we get is something like this:
f_spec d = f
RULE: f = f_spec d
Note that the rule is bogus, becuase it mentions a 'd' that is
not bound on the LHS! But it's a silly specialisation anyway, becuase
the constraint is unused. We could bind 'd' to (error "unused")
but it seems better to reject the program because it's almost certainly
a mistake. That's what the isDeadBinder call detects.
Note [Const rule dicts]
~~~~~~~~~~~~~~~~~~~~~~~
When the LHS of a specialisation rule, (/\as\ds. f es) has a free dict,
which is presumably in scope at the function definition site, we can quantify
over it too. *Any* dict with that type will do.
So for example when you have
f :: Eq a => a -> a
f = <rhs>
Then we get the SpecPrag
SpecPrag (f Int dInt) Int
And from that we want the rule
RULE forall dInt. f Int dInt = f_spec
f_spec = let f = <rhs> in f Int dInt
But be careful! That dInt might be GHC.Base.$fOrdInt, which is an External
Name, and you can't bind them in a lambda or forall without getting things
confused. Hence the use of 'localiseId' to make it Internal.
%************************************************************************
%* *
\subsection{Adding inline pragmas}
%* *
%************************************************************************
\begin{code}
decomposeRuleLhs :: CoreExpr -> Maybe ([Var], Id, [CoreExpr])
decomposeRuleLhs lhs
= case (decomp emptyVarEnv body) of
Nothing -> Nothing
Just (fn, args) -> Just (bndrs, fn, args)
where
occ_lhs = occurAnalyseExpr lhs
(bndrs, body) = collectBinders occ_lhs
decomp env (Let (NonRec dict rhs) body)
= decomp (extendVarEnv env dict (simpleSubst env rhs)) body
decomp env (Case scrut bndr ty [(DEFAULT, _, body)])
| isDeadBinder bndr
= Just (seqId, [Type (idType bndr), Type ty,
simpleSubst env scrut, simpleSubst env body])
decomp env body
= case collectArgs (simpleSubst env body) of
(Var fn, args) -> Just (fn, args)
_ -> Nothing
simpleSubst :: IdEnv CoreExpr -> CoreExpr -> CoreExpr
simpleSubst subst expr
= go expr
where
go (Var v) = lookupVarEnv subst v `orElse` Var v
go (Cast e co) = Cast (go e) co
go (Type ty) = Type ty
go (Lit lit) = Lit lit
go (App fun arg) = App (go fun) (go arg)
go (Note note e) = Note note (go e)
go (Lam bndr body) = Lam bndr (go body)
go (Let (NonRec bndr rhs) body) = Let (NonRec bndr (go rhs)) (go body)
go (Let (Rec pairs) body) = Let (Rec (mapSnd go pairs)) (go body)
go (Case scrut bndr ty alts) = Case (go scrut) bndr ty
[(c,bs,go r) | (c,bs,r) <- alts]
addInlinePrags :: [LPrag] -> Id -> CoreExpr -> (Id,CoreExpr)
addInlinePrags prags bndr rhs
= case [inl | L _ (InlinePrag inl) <- prags] of
[] -> (bndr, rhs)
(inl:_) -> addInlineInfo inl bndr rhs
addInlineInfo :: InlineSpec -> Id -> CoreExpr -> (Id,CoreExpr)
addInlineInfo (Inline prag is_inline) bndr rhs
= (attach_pragma bndr prag, wrap_inline is_inline rhs)
where
attach_pragma bndr prag
| isDefaultInlinePragma prag = bndr
| otherwise = bndr `setInlinePragma` prag
wrap_inline True body = mkInlineMe body
wrap_inline False body = body
\end{code}
Note [Matching seq]
~~~~~~~~~~~~~~~~~~~
The desugarer turns (seq e r) into (case e of _ -> r), via a specialcase hack
and this code turns it back into an application of seq!
See Note [Rules for seq] in MkId for the details.
%************************************************************************
%* *
\subsection[addAutoScc]{Adding automatic sccs}
%* *
%************************************************************************
\begin{code}
data AutoScc = NoSccs
| AddSccs Module (Id -> Bool)
addAutoScc :: AutoScc
-> Id
-> CoreExpr
-> CoreExpr
addAutoScc NoSccs _ rhs
= rhs
addAutoScc (AddSccs mod add_scc) id rhs
| add_scc id = mkSCC (mkAutoCC id mod NotCafCC) rhs
| otherwise = rhs
\end{code}
If profiling and dealing with a dict binding,
wrap the dict in @_scc_ DICT <dict>@:
\begin{code}
addDictScc :: Id -> CoreExpr -> DsM CoreExpr
addDictScc _ rhs = return rhs
\end{code}
%************************************************************************
%* *
Desugaring coercions
%* *
%************************************************************************
\begin{code}
dsCoercion :: HsWrapper -> DsM CoreExpr -> DsM CoreExpr
dsCoercion WpHole thing_inside = thing_inside
dsCoercion (WpCompose c1 c2) thing_inside = dsCoercion c1 (dsCoercion c2 thing_inside)
dsCoercion (WpCast co) thing_inside = do { expr <- thing_inside
; return (Cast expr co) }
dsCoercion (WpLam id) thing_inside = do { expr <- thing_inside
; return (Lam id expr) }
dsCoercion (WpTyLam tv) thing_inside = do { expr <- thing_inside
; return (Lam tv expr) }
dsCoercion (WpApp v) thing_inside
| isTyVar v = do { expr <- thing_inside
; return (App expr (Type (mkTyVarTy v))) }
| otherwise = do { expr <- thing_inside
; return (App expr (Var v)) }
dsCoercion (WpTyApp ty) thing_inside = do { expr <- thing_inside
; return (App expr (Type ty)) }
dsCoercion WpInline thing_inside = do { expr <- thing_inside
; return (mkInlineMe expr) }
dsCoercion (WpLet bs) thing_inside = do { prs <- dsLHsBinds bs
; expr <- thing_inside
; return (Let (Rec prs) expr) }
\end{code}