summaryrefslogtreecommitdiff
path: root/compiler/GHC/HsToCore.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/GHC/HsToCore.hs')
-rw-r--r--compiler/GHC/HsToCore.hs545
1 files changed, 545 insertions, 0 deletions
diff --git a/compiler/GHC/HsToCore.hs b/compiler/GHC/HsToCore.hs
new file mode 100644
index 0000000000..6802319be2
--- /dev/null
+++ b/compiler/GHC/HsToCore.hs
@@ -0,0 +1,545 @@
+{-
+(c) The University of Glasgow 2006
+(c) The GRASP/AQUA Project, Glasgow University, 1992-1998
+
+
+The Desugarer: turning HsSyn into Core.
+-}
+
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE ViewPatterns #-}
+
+module GHC.HsToCore (
+ -- * Desugaring operations
+ deSugar, deSugarExpr
+ ) where
+
+#include "HsVersions.h"
+
+import GhcPrelude
+
+import GHC.HsToCore.Usage
+import DynFlags
+import HscTypes
+import GHC.Hs
+import TcRnTypes
+import TcRnMonad ( finalSafeMode, fixSafeInstances )
+import TcRnDriver ( runTcInteractive )
+import Id
+import Name
+import Type
+import Avail
+import CoreSyn
+import CoreFVs ( exprsSomeFreeVarsList )
+import CoreOpt ( simpleOptPgm, simpleOptExpr )
+import PprCore
+import GHC.HsToCore.Monad
+import GHC.HsToCore.Expr
+import GHC.HsToCore.Binds
+import GHC.HsToCore.Foreign.Decl
+import PrelNames ( coercibleTyConKey )
+import TysPrim ( eqReprPrimTyCon )
+import Unique ( hasKey )
+import Coercion ( mkCoVarCo )
+import TysWiredIn ( coercibleDataCon )
+import DataCon ( dataConWrapId )
+import MkCore ( mkCoreLet )
+import Module
+import NameSet
+import NameEnv
+import Rules
+import BasicTypes ( Activation(.. ), competesWith, pprRuleName )
+import CoreMonad ( CoreToDo(..) )
+import CoreLint ( endPassIO )
+import VarSet
+import FastString
+import ErrUtils
+import Outputable
+import SrcLoc
+import GHC.HsToCore.Coverage
+import Util
+import MonadUtils
+import OrdList
+import GHC.HsToCore.Docs
+
+import Data.List
+import Data.IORef
+import Control.Monad( when )
+import Plugins ( LoadedPlugin(..) )
+
+{-
+************************************************************************
+* *
+* The main function: deSugar
+* *
+************************************************************************
+-}
+
+-- | Main entry point to the desugarer.
+deSugar :: HscEnv -> ModLocation -> TcGblEnv -> IO (Messages, Maybe ModGuts)
+-- Can modify PCS by faulting in more declarations
+
+deSugar hsc_env
+ mod_loc
+ tcg_env@(TcGblEnv { tcg_mod = id_mod,
+ tcg_semantic_mod = mod,
+ tcg_src = hsc_src,
+ tcg_type_env = type_env,
+ tcg_imports = imports,
+ tcg_exports = exports,
+ tcg_keep = keep_var,
+ tcg_th_splice_used = tc_splice_used,
+ tcg_rdr_env = rdr_env,
+ tcg_fix_env = fix_env,
+ tcg_inst_env = inst_env,
+ tcg_fam_inst_env = fam_inst_env,
+ tcg_merged = merged,
+ tcg_warns = warns,
+ tcg_anns = anns,
+ tcg_binds = binds,
+ tcg_imp_specs = imp_specs,
+ tcg_dependent_files = dependent_files,
+ tcg_ev_binds = ev_binds,
+ tcg_th_foreign_files = th_foreign_files_var,
+ tcg_fords = fords,
+ tcg_rules = rules,
+ tcg_patsyns = patsyns,
+ tcg_tcs = tcs,
+ tcg_insts = insts,
+ tcg_fam_insts = fam_insts,
+ tcg_hpc = other_hpc_info,
+ tcg_complete_matches = complete_matches
+ })
+
+ = do { let dflags = hsc_dflags hsc_env
+ print_unqual = mkPrintUnqualified dflags rdr_env
+ ; withTiming dflags
+ (text "Desugar"<+>brackets (ppr mod))
+ (const ()) $
+ do { -- Desugar the program
+ ; let export_set = availsToNameSet exports
+ target = hscTarget dflags
+ hpcInfo = emptyHpcInfo other_hpc_info
+
+ ; (binds_cvr, ds_hpc_info, modBreaks)
+ <- if not (isHsBootOrSig hsc_src)
+ then addTicksToBinds hsc_env mod mod_loc
+ export_set (typeEnvTyCons type_env) binds
+ else return (binds, hpcInfo, Nothing)
+ ; (msgs, mb_res) <- initDs hsc_env tcg_env $
+ do { ds_ev_binds <- dsEvBinds ev_binds
+ ; core_prs <- dsTopLHsBinds binds_cvr
+ ; (spec_prs, spec_rules) <- dsImpSpecs imp_specs
+ ; (ds_fords, foreign_prs) <- dsForeigns fords
+ ; ds_rules <- mapMaybeM dsRule rules
+ ; let hpc_init
+ | gopt Opt_Hpc dflags = hpcInitCode mod ds_hpc_info
+ | otherwise = empty
+ ; return ( ds_ev_binds
+ , foreign_prs `appOL` core_prs `appOL` spec_prs
+ , spec_rules ++ ds_rules
+ , ds_fords `appendStubC` hpc_init) }
+
+ ; case mb_res of {
+ Nothing -> return (msgs, Nothing) ;
+ Just (ds_ev_binds, all_prs, all_rules, ds_fords) ->
+
+ do { -- Add export flags to bindings
+ keep_alive <- readIORef keep_var
+ ; let (rules_for_locals, rules_for_imps) = partition isLocalRule all_rules
+ final_prs = addExportFlagsAndRules target export_set keep_alive
+ rules_for_locals (fromOL all_prs)
+
+ final_pgm = combineEvBinds ds_ev_binds final_prs
+ -- Notice that we put the whole lot in a big Rec, even the foreign binds
+ -- When compiling PrelFloat, which defines data Float = F# Float#
+ -- we want F# to be in scope in the foreign marshalling code!
+ -- You might think it doesn't matter, but the simplifier brings all top-level
+ -- things into the in-scope set before simplifying; so we get no unfolding for F#!
+
+ ; endPassIO hsc_env print_unqual CoreDesugar final_pgm rules_for_imps
+ ; (ds_binds, ds_rules_for_imps)
+ <- simpleOptPgm dflags mod final_pgm rules_for_imps
+ -- The simpleOptPgm gets rid of type
+ -- bindings plus any stupid dead code
+
+ ; endPassIO hsc_env print_unqual CoreDesugarOpt ds_binds ds_rules_for_imps
+
+ ; let used_names = mkUsedNames tcg_env
+ pluginModules =
+ map lpModule (cachedPlugins (hsc_dflags hsc_env))
+ ; deps <- mkDependencies (thisInstalledUnitId (hsc_dflags hsc_env))
+ (map mi_module pluginModules) tcg_env
+
+ ; used_th <- readIORef tc_splice_used
+ ; dep_files <- readIORef dependent_files
+ ; safe_mode <- finalSafeMode dflags tcg_env
+ ; usages <- mkUsageInfo hsc_env mod (imp_mods imports) used_names
+ dep_files merged pluginModules
+ -- id_mod /= mod when we are processing an hsig, but hsigs
+ -- never desugared and compiled (there's no code!)
+ -- Consequently, this should hold for any ModGuts that make
+ -- past desugaring. See Note [Identity versus semantic module].
+ ; MASSERT( id_mod == mod )
+
+ ; foreign_files <- readIORef th_foreign_files_var
+
+ ; let (doc_hdr, decl_docs, arg_docs) = extractDocs tcg_env
+
+ ; let mod_guts = ModGuts {
+ mg_module = mod,
+ mg_hsc_src = hsc_src,
+ mg_loc = mkFileSrcSpan mod_loc,
+ mg_exports = exports,
+ mg_usages = usages,
+ mg_deps = deps,
+ mg_used_th = used_th,
+ mg_rdr_env = rdr_env,
+ mg_fix_env = fix_env,
+ mg_warns = warns,
+ mg_anns = anns,
+ mg_tcs = tcs,
+ mg_insts = fixSafeInstances safe_mode insts,
+ mg_fam_insts = fam_insts,
+ mg_inst_env = inst_env,
+ mg_fam_inst_env = fam_inst_env,
+ mg_patsyns = patsyns,
+ mg_rules = ds_rules_for_imps,
+ mg_binds = ds_binds,
+ mg_foreign = ds_fords,
+ mg_foreign_files = foreign_files,
+ mg_hpc_info = ds_hpc_info,
+ mg_modBreaks = modBreaks,
+ mg_safe_haskell = safe_mode,
+ mg_trust_pkg = imp_trust_own_pkg imports,
+ mg_complete_sigs = complete_matches,
+ mg_doc_hdr = doc_hdr,
+ mg_decl_docs = decl_docs,
+ mg_arg_docs = arg_docs
+ }
+ ; return (msgs, Just mod_guts)
+ }}}}
+
+mkFileSrcSpan :: ModLocation -> SrcSpan
+mkFileSrcSpan mod_loc
+ = case ml_hs_file mod_loc of
+ Just file_path -> mkGeneralSrcSpan (mkFastString file_path)
+ Nothing -> interactiveSrcSpan -- Presumably
+
+dsImpSpecs :: [LTcSpecPrag] -> DsM (OrdList (Id,CoreExpr), [CoreRule])
+dsImpSpecs imp_specs
+ = do { spec_prs <- mapMaybeM (dsSpec Nothing) imp_specs
+ ; let (spec_binds, spec_rules) = unzip spec_prs
+ ; return (concatOL spec_binds, spec_rules) }
+
+combineEvBinds :: [CoreBind] -> [(Id,CoreExpr)] -> [CoreBind]
+-- Top-level bindings can include coercion bindings, but not via superclasses
+-- See Note [Top-level evidence]
+combineEvBinds [] val_prs
+ = [Rec val_prs]
+combineEvBinds (NonRec b r : bs) val_prs
+ | isId b = combineEvBinds bs ((b,r):val_prs)
+ | otherwise = NonRec b r : combineEvBinds bs val_prs
+combineEvBinds (Rec prs : bs) val_prs
+ = combineEvBinds bs (prs ++ val_prs)
+
+{-
+Note [Top-level evidence]
+~~~~~~~~~~~~~~~~~~~~~~~~~
+Top-level evidence bindings may be mutually recursive with the top-level value
+bindings, so we must put those in a Rec. But we can't put them *all* in a Rec
+because the occurrence analyser doesn't take account of type/coercion variables
+when computing dependencies.
+
+So we pull out the type/coercion variables (which are in dependency order),
+and Rec the rest.
+-}
+
+deSugarExpr :: HscEnv -> LHsExpr GhcTc -> IO (Messages, Maybe CoreExpr)
+
+deSugarExpr hsc_env tc_expr = do {
+ let dflags = hsc_dflags hsc_env
+
+ ; showPass dflags "Desugar"
+
+ -- Do desugaring
+ ; (msgs, mb_core_expr) <- runTcInteractive hsc_env $ initDsTc $
+ dsLExpr tc_expr
+
+ ; case mb_core_expr of
+ Nothing -> return ()
+ Just expr -> dumpIfSet_dyn dflags Opt_D_dump_ds "Desugared"
+ FormatCore (pprCoreExpr expr)
+
+ ; return (msgs, mb_core_expr) }
+
+{-
+************************************************************************
+* *
+* Add rules and export flags to binders
+* *
+************************************************************************
+-}
+
+addExportFlagsAndRules
+ :: HscTarget -> NameSet -> NameSet -> [CoreRule]
+ -> [(Id, t)] -> [(Id, t)]
+addExportFlagsAndRules target exports keep_alive rules prs
+ = mapFst add_one prs
+ where
+ add_one bndr = add_rules name (add_export name bndr)
+ where
+ name = idName bndr
+
+ ---------- Rules --------
+ -- See Note [Attach rules to local ids]
+ -- NB: the binder might have some existing rules,
+ -- arising from specialisation pragmas
+ add_rules name bndr
+ | Just rules <- lookupNameEnv rule_base name
+ = bndr `addIdSpecialisations` rules
+ | otherwise
+ = bndr
+ rule_base = extendRuleBaseList emptyRuleBase rules
+
+ ---------- Export flag --------
+ -- See Note [Adding export flags]
+ add_export name bndr
+ | dont_discard name = setIdExported bndr
+ | otherwise = bndr
+
+ dont_discard :: Name -> Bool
+ dont_discard name = is_exported name
+ || name `elemNameSet` keep_alive
+
+ -- In interactive mode, we don't want to discard any top-level
+ -- entities at all (eg. do not inline them away during
+ -- simplification), and retain them all in the TypeEnv so they are
+ -- available from the command line.
+ --
+ -- isExternalName separates the user-defined top-level names from those
+ -- introduced by the type checker.
+ is_exported :: Name -> Bool
+ is_exported | targetRetainsAllBindings target = isExternalName
+ | otherwise = (`elemNameSet` exports)
+
+{-
+Note [Adding export flags]
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+Set the no-discard flag if either
+ a) the Id is exported
+ b) it's mentioned in the RHS of an orphan rule
+ c) it's in the keep-alive set
+
+It means that the binding won't be discarded EVEN if the binding
+ends up being trivial (v = w) -- the simplifier would usually just
+substitute w for v throughout, but we don't apply the substitution to
+the rules (maybe we should?), so this substitution would make the rule
+bogus.
+
+You might wonder why exported Ids aren't already marked as such;
+it's just because the type checker is rather busy already and
+I didn't want to pass in yet another mapping.
+
+Note [Attach rules to local ids]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Find the rules for locally-defined Ids; then we can attach them
+to the binders in the top-level bindings
+
+Reason
+ - It makes the rules easier to look up
+ - It means that transformation rules and specialisations for
+ locally defined Ids are handled uniformly
+ - It keeps alive things that are referred to only from a rule
+ (the occurrence analyser knows about rules attached to Ids)
+ - It makes sure that, when we apply a rule, the free vars
+ of the RHS are more likely to be in scope
+ - The imported rules are carried in the in-scope set
+ which is extended on each iteration by the new wave of
+ local binders; any rules which aren't on the binding will
+ thereby get dropped
+
+
+************************************************************************
+* *
+* Desugaring transformation rules
+* *
+************************************************************************
+-}
+
+dsRule :: LRuleDecl GhcTc -> DsM (Maybe CoreRule)
+dsRule (L loc (HsRule { rd_name = name
+ , rd_act = rule_act
+ , rd_tmvs = vars
+ , rd_lhs = lhs
+ , rd_rhs = rhs }))
+ = putSrcSpanDs loc $
+ do { let bndrs' = [var | L _ (RuleBndr _ (L _ var)) <- vars]
+
+ ; lhs' <- unsetGOptM Opt_EnableRewriteRules $
+ unsetWOptM Opt_WarnIdentities $
+ dsLExpr lhs -- Note [Desugaring RULE left hand sides]
+
+ ; rhs' <- dsLExpr rhs
+ ; this_mod <- getModule
+
+ ; (bndrs'', lhs'', rhs'') <- unfold_coerce bndrs' lhs' rhs'
+
+ -- Substitute the dict bindings eagerly,
+ -- and take the body apart into a (f args) form
+ ; dflags <- getDynFlags
+ ; case decomposeRuleLhs dflags bndrs'' lhs'' of {
+ Left msg -> do { warnDs NoReason msg; return Nothing } ;
+ Right (final_bndrs, fn_id, args) -> do
+
+ { let is_local = isLocalId fn_id
+ -- NB: isLocalId is False of implicit Ids. This is good because
+ -- we don't want to attach rules to the bindings of implicit Ids,
+ -- because they don't show up in the bindings until just before code gen
+ fn_name = idName fn_id
+ final_rhs = simpleOptExpr dflags rhs'' -- De-crap it
+ rule_name = snd (unLoc name)
+ final_bndrs_set = mkVarSet final_bndrs
+ arg_ids = filterOut (`elemVarSet` final_bndrs_set) $
+ exprsSomeFreeVarsList isId args
+
+ ; rule <- dsMkUserRule this_mod is_local
+ rule_name rule_act fn_name final_bndrs args
+ final_rhs
+ ; when (wopt Opt_WarnInlineRuleShadowing dflags) $
+ warnRuleShadowing rule_name rule_act fn_id arg_ids
+
+ ; return (Just rule)
+ } } }
+dsRule (L _ (XRuleDecl nec)) = noExtCon nec
+
+warnRuleShadowing :: RuleName -> Activation -> Id -> [Id] -> DsM ()
+-- See Note [Rules and inlining/other rules]
+warnRuleShadowing rule_name rule_act fn_id arg_ids
+ = do { check False fn_id -- We often have multiple rules for the same Id in a
+ -- module. Maybe we should check that they don't overlap
+ -- but currently we don't
+ ; mapM_ (check True) arg_ids }
+ where
+ check check_rules_too lhs_id
+ | isLocalId lhs_id || canUnfold (idUnfolding lhs_id)
+ -- If imported with no unfolding, no worries
+ , idInlineActivation lhs_id `competesWith` rule_act
+ = warnDs (Reason Opt_WarnInlineRuleShadowing)
+ (vcat [ hang (text "Rule" <+> pprRuleName rule_name
+ <+> text "may never fire")
+ 2 (text "because" <+> quotes (ppr lhs_id)
+ <+> text "might inline first")
+ , text "Probable fix: add an INLINE[n] or NOINLINE[n] pragma for"
+ <+> quotes (ppr lhs_id)
+ , whenPprDebug (ppr (idInlineActivation lhs_id) $$ ppr rule_act) ])
+
+ | check_rules_too
+ , bad_rule : _ <- get_bad_rules lhs_id
+ = warnDs (Reason Opt_WarnInlineRuleShadowing)
+ (vcat [ hang (text "Rule" <+> pprRuleName rule_name
+ <+> text "may never fire")
+ 2 (text "because rule" <+> pprRuleName (ruleName bad_rule)
+ <+> text "for"<+> quotes (ppr lhs_id)
+ <+> text "might fire first")
+ , text "Probable fix: add phase [n] or [~n] to the competing rule"
+ , whenPprDebug (ppr bad_rule) ])
+
+ | otherwise
+ = return ()
+
+ get_bad_rules lhs_id
+ = [ rule | rule <- idCoreRules lhs_id
+ , ruleActivation rule `competesWith` rule_act ]
+
+-- See Note [Desugaring coerce as cast]
+unfold_coerce :: [Id] -> CoreExpr -> CoreExpr -> DsM ([Var], CoreExpr, CoreExpr)
+unfold_coerce bndrs lhs rhs = do
+ (bndrs', wrap) <- go bndrs
+ return (bndrs', wrap lhs, wrap rhs)
+ where
+ go :: [Id] -> DsM ([Id], CoreExpr -> CoreExpr)
+ go [] = return ([], id)
+ go (v:vs)
+ | Just (tc, [k, t1, t2]) <- splitTyConApp_maybe (idType v)
+ , tc `hasKey` coercibleTyConKey = do
+ u <- newUnique
+
+ let ty' = mkTyConApp eqReprPrimTyCon [k, k, t1, t2]
+ v' = mkLocalCoVar
+ (mkDerivedInternalName mkRepEqOcc u (getName v)) ty'
+ box = Var (dataConWrapId coercibleDataCon) `mkTyApps`
+ [k, t1, t2] `App`
+ Coercion (mkCoVarCo v')
+
+ (bndrs, wrap) <- go vs
+ return (v':bndrs, mkCoreLet (NonRec v box) . wrap)
+ | otherwise = do
+ (bndrs,wrap) <- go vs
+ return (v:bndrs, wrap)
+
+{- Note [Desugaring RULE left hand sides]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+For the LHS of a RULE we do *not* want to desugar
+ [x] to build (\cn. x `c` n)
+We want to leave explicit lists simply as chains
+of cons's. We can achieve that slightly indirectly by
+switching off EnableRewriteRules. See GHC.HsToCore.Expr.dsExplicitList.
+
+That keeps the desugaring of list comprehensions simple too.
+
+Nor do we want to warn of conversion identities on the LHS;
+the rule is precisely to optimise them:
+ {-# RULES "fromRational/id" fromRational = id :: Rational -> Rational #-}
+
+Note [Desugaring coerce as cast]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+We want the user to express a rule saying roughly “mapping a coercion over a
+list can be replaced by a coercion”. But the cast operator of Core (▷) cannot
+be written in Haskell. So we use `coerce` for that (#2110). The user writes
+ map coerce = coerce
+as a RULE, and this optimizes any kind of mapped' casts away, including `map
+MkNewtype`.
+
+For that we replace any forall'ed `c :: Coercible a b` value in a RULE by
+corresponding `co :: a ~#R b` and wrap the LHS and the RHS in
+`let c = MkCoercible co in ...`. This is later simplified to the desired form
+by simpleOptExpr (for the LHS) resp. the simplifiers (for the RHS).
+See also Note [Getting the map/coerce RULE to work] in CoreSubst.
+
+Note [Rules and inlining/other rules]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+If you have
+ f x = ...
+ g x = ...
+ {-# RULES "rule-for-f" forall x. f (g x) = ... #-}
+then there's a good chance that in a potential rule redex
+ ...f (g e)...
+then 'f' or 'g' will inline before the rule can fire. Solution: add an
+INLINE [n] or NOINLINE [n] pragma to 'f' and 'g'.
+
+Note that this applies to all the free variables on the LHS, both the
+main function and things in its arguments.
+
+We also check if there are Ids on the LHS that have competing RULES.
+In the above example, suppose we had
+ {-# RULES "rule-for-g" forally. g [y] = ... #-}
+Then "rule-for-f" and "rule-for-g" would compete. Better to add phase
+control, so "rule-for-f" has a chance to fire before "rule-for-g" becomes
+active; or perhaps after "rule-for-g" has become inactive. This is checked
+by 'competesWith'
+
+Class methods have a built-in RULE to select the method from the dictionary,
+so you can't change the phase on this. That makes id very dubious to
+match on class methods in RULE lhs's. See #10595. I'm not happy
+about this. For example in Control.Arrow we have
+
+{-# RULES "compose/arr" forall f g .
+ (arr f) . (arr g) = arr (f . g) #-}
+
+and similar, which will elicit exactly these warnings, and risk never
+firing. But it's not clear what to do instead. We could make the
+class method rules inactive in phase 2, but that would delay when
+subsequent transformations could fire.
+-}