summaryrefslogtreecommitdiff
path: root/compiler/main/HscMain.lhs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/main/HscMain.lhs')
-rw-r--r--compiler/main/HscMain.lhs180
1 files changed, 125 insertions, 55 deletions
diff --git a/compiler/main/HscMain.lhs b/compiler/main/HscMain.lhs
index a8bb18d510..2603d21bc4 100644
--- a/compiler/main/HscMain.lhs
+++ b/compiler/main/HscMain.lhs
@@ -309,9 +309,12 @@ hscRnImportDecls
-- because tcRnImports will force-load any orphan modules necessary, making extra
-- instances/family instances visible (GHC #4832)
hscRnImportDecls hsc_env this_mod import_decls
- = runHsc hsc_env $ ioMsgMaybe $ initTc hsc_env HsSrcFile False this_mod $
- fmap tcg_rdr_env $ tcRnImports hsc_env this_mod import_decls
-
+ = runHsc hsc_env $ ioMsgMaybe $
+ initTc hsc_env HsSrcFile False this_mod $
+ fmap tcg_rdr_env $
+ tcRnImports hsc_env this_mod loc import_decls
+ where
+ loc = mkGeneralSrcSpan (mkFastString "In a call to hscRnImportDecls")
#endif
-- -----------------------------------------------------------------------------
@@ -484,7 +487,7 @@ type InteractiveResult = (InteractiveStatus, ModIface, ModDetails)
-- 'interactive' mode. They should be removed from 'oneshot' mode.
type Compiler result = HscEnv
-> ModSummary
- -> Bool -- True <=> source unchanged
+ -> SourceModified
-> Maybe ModIface -- Old interface, if available
-> Maybe (Int,Int) -- Just (i,n) <=> module i of n (for msgs)
-> IO result
@@ -512,38 +515,64 @@ data HsCompiler a
}
genericHscCompile :: HsCompiler a
- -> (HscEnv -> Maybe (Int,Int) -> Bool -> ModSummary -> IO ())
- -> HscEnv -> ModSummary -> Bool
+ -> (HscEnv -> Maybe (Int,Int) -> RecompReason -> ModSummary -> IO ())
+ -> HscEnv -> ModSummary -> SourceModified
-> Maybe ModIface -> Maybe (Int, Int)
-> IO a
genericHscCompile compiler hscMessage hsc_env
- mod_summary source_unchanged
+ mod_summary source_modified
mb_old_iface0 mb_mod_index
= do
(recomp_reqd, mb_checked_iface)
<- {-# SCC "checkOldIface" #-}
checkOldIface hsc_env mod_summary
- source_unchanged mb_old_iface0
+ source_modified mb_old_iface0
-- save the interface that comes back from checkOldIface.
-- In one-shot mode we don't have the old iface until this
-- point, when checkOldIface reads it from the disk.
let mb_old_hash = fmap mi_iface_hash mb_checked_iface
+
+ let
+ skip iface = do
+ hscMessage hsc_env mb_mod_index RecompNotRequired mod_summary
+ runHsc hsc_env $ hscNoRecomp compiler iface
+
+ compile reason = do
+ hscMessage hsc_env mb_mod_index reason mod_summary
+ runHsc hsc_env $ hscRecompile compiler mod_summary mb_old_hash
+
+ stable = case source_modified of
+ SourceUnmodifiedAndStable -> True
+ _ -> False
+
+ -- If the module used TH splices when it was last compiled,
+ -- then the recompilation check is not accurate enough (#481)
+ -- and we must ignore it. However, if the module is stable
+ -- (none of the modules it depends on, directly or indirectly,
+ -- changed), then we *can* skip recompilation. This is why
+ -- the SourceModified type contains SourceUnmodifiedAndStable,
+ -- and it's pretty important: otherwise ghc --make would
+ -- always recompile TH modules, even if nothing at all has
+ -- changed. Stability is just the same check that make is
+ -- doing for us in one-shot mode.
+
case mb_checked_iface of
- Just iface | not recomp_reqd
- -> do hscMessage hsc_env mb_mod_index False mod_summary
- runHsc hsc_env $ hscNoRecomp compiler iface
- _otherwise
- -> do hscMessage hsc_env mb_mod_index True mod_summary
- runHsc hsc_env $ hscRecompile compiler mod_summary mb_old_hash
+ Just iface | not recomp_reqd ->
+ if mi_used_th iface && not stable
+ then compile RecompForcedByTH
+ else skip iface
+ _otherwise ->
+ compile RecompRequired
+
hscCheckRecompBackend :: HsCompiler a -> TcGblEnv -> Compiler a
hscCheckRecompBackend compiler tc_result
- hsc_env mod_summary source_unchanged mb_old_iface _m_of_n
+ hsc_env mod_summary source_modified mb_old_iface _m_of_n
= do
(recomp_reqd, mb_checked_iface)
<- {-# SCC "checkOldIface" #-}
checkOldIface hsc_env mod_summary
- source_unchanged mb_old_iface
+ source_modified mb_old_iface
let mb_old_hash = fmap mi_iface_hash mb_checked_iface
case mb_checked_iface of
@@ -746,24 +775,31 @@ genModDetails old_iface
-- Progress displayers.
--------------------------------------------------------------
-oneShotMsg :: HscEnv -> Maybe (Int,Int) -> Bool -> ModSummary -> IO ()
-oneShotMsg hsc_env _mb_mod_index recomp _mod_summary =
- if recomp
- then return ()
- else compilationProgressMsg (hsc_dflags hsc_env) $
- "compilation IS NOT required"
+data RecompReason = RecompNotRequired | RecompRequired | RecompForcedByTH
+ deriving Eq
-batchMsg :: HscEnv -> Maybe (Int,Int) -> Bool -> ModSummary -> IO ()
+oneShotMsg :: HscEnv -> Maybe (Int,Int) -> RecompReason -> ModSummary -> IO ()
+oneShotMsg hsc_env _mb_mod_index recomp _mod_summary =
+ case recomp of
+ RecompNotRequired ->
+ compilationProgressMsg (hsc_dflags hsc_env) $
+ "compilation IS NOT required"
+ _other ->
+ return ()
+
+batchMsg :: HscEnv -> Maybe (Int,Int) -> RecompReason -> ModSummary -> IO ()
batchMsg hsc_env mb_mod_index recomp mod_summary
- = do
- let showMsg msg = compilationProgressMsg (hsc_dflags hsc_env) $
- (showModuleIndex mb_mod_index ++
- msg ++ showModMsg (hscTarget (hsc_dflags hsc_env)) recomp mod_summary)
- if recomp
- then showMsg "Compiling "
- else if verbosity (hsc_dflags hsc_env) >= 2
- then showMsg "Skipping "
- else return ()
+ = case recomp of
+ RecompRequired -> showMsg "Compiling "
+ RecompNotRequired
+ | verbosity (hsc_dflags hsc_env) >= 2 -> showMsg "Skipping "
+ | otherwise -> return ()
+ RecompForcedByTH -> showMsg "Compiling [TH] "
+ where
+ showMsg msg =
+ compilationProgressMsg (hsc_dflags hsc_env) $
+ (showModuleIndex mb_mod_index ++
+ msg ++ showModMsg (hscTarget (hsc_dflags hsc_env)) (recomp == RecompRequired) mod_summary)
--------------------------------------------------------------
-- FrontEnds
@@ -778,7 +814,7 @@ hscFileFrontEnd mod_summary = do
ioMsgMaybe $
tcRnModule hsc_env (ms_hsc_src mod_summary) False rdr_module
dflags <- getDynFlags
- -- XXX: See Note [SafeHaskell API]
+ -- XXX: See Note [Safe Haskell API]
if safeHaskellOn dflags
then do
tcg_env1 <- checkSafeImports dflags hsc_env tcg_env
@@ -805,24 +841,53 @@ hscFileFrontEnd mod_summary = do
warnRules (L loc (HsRule n _ _ _ _ _ _)) =
mkPlainWarnMsg loc $
text "Rule \"" <> ftext n <> text "\" ignored" $+$
- text "User defined rules are disabled under SafeHaskell"
+ text "User defined rules are disabled under Safe Haskell"
--------------------------------------------------------------
--- SafeHaskell
+-- Safe Haskell
--------------------------------------------------------------
+-- Note [Safe Haskell API]
+-- ~~~~~~~~~~~~~~~~~~~~~~
+-- XXX: We only call this in hscFileFrontend and don't expose
+-- it to the GHC API. External users of GHC can't properly use
+-- the GHC API and Safe Haskell.
+
+
+-- Note [Safe Haskell Trust Check]
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+-- Safe Haskell checks that an import is trusted according to the following
+-- rules for an import of module M that resides in Package P:
+--
+-- * If M is recorded as Safe and all its trust dependencies are OK
+-- then M is considered safe.
+-- * If M is recorded as Trustworthy and P is considered trusted and
+-- all M's trust dependencies are OK then M is considered safe.
+--
+-- By trust dependencies we mean that the check is transitive. So if
+-- a module M that is Safe relies on a module N that is trustworthy,
+-- importing module M will first check (according to the second case)
+-- that N is trusted before checking M is trusted.
+--
+-- This is a minimal description, so please refer to the user guide
+-- for more details. The user guide is also considered the authoritative
+-- source in this matter, not the comments or code.
+
+
-- | Validate that safe imported modules are actually safe.
-- For modules in the HomePackage (the package the module we
-- are compiling in resides) this just involves checking its
-- trust type is 'Safe' or 'Trustworthy'. For modules that
-- reside in another package we also must check that the
--- external pacakge is trusted.
+-- external pacakge is trusted. See the Note [Safe Haskell
+-- Trust Check] above for more information.
--
--- Note [SafeHaskell API]
--- ~~~~~~~~~~~~~~~~~~~~~~
--- XXX: We only call this in hscFileFrontend and don't expose
--- it to the GHC API. External users of GHC can't properly use
--- the GHC API and SafeHaskell.
+-- The code for this is quite tricky as the whole algorithm
+-- is done in a few distinct phases in different parts of the
+-- code base. See RnNames.rnImportDecl for where package trust
+-- dependencies for a module are collected and unioned.
+-- Specifically see the Note [RnNames . Tracking Trust Transitively]
+-- and the Note [RnNames . Trust Own Package].
checkSafeImports :: DynFlags -> HscEnv -> TcGblEnv -> Hsc TcGblEnv
checkSafeImports dflags hsc_env tcg_env
= do
@@ -873,9 +938,9 @@ checkSafeImports dflags hsc_env tcg_env
-- that their package is trusted. For trustworthy modules,
-- modules in the home package are trusted but otherwise
-- we check the package trust flag.
- packageTrusted :: SafeHaskellMode -> Module -> Bool
- packageTrusted Sf_Safe _ = True
- packageTrusted _ m
+ packageTrusted :: SafeHaskellMode -> Bool -> Module -> Bool
+ packageTrusted Sf_Safe False _ = True
+ packageTrusted _ _ m
| isHomePkg m = True
| otherwise = trusted $ getPackageDetails (pkgState dflags)
(modulePackageId m)
@@ -894,11 +959,11 @@ checkSafeImports dflags hsc_env tcg_env
-- got iface, check trust
Just iface' -> do
let trust = getSafeMode $ mi_trust iface'
+ trust_own_pkg = mi_trust_pkg iface'
-- check module is trusted
- safeM = trust `elem` [Sf_Safe, Sf_Trustworthy,
- Sf_TrustworthyWithSafeLanguage]
+ safeM = trust `elem` [Sf_Safe, Sf_Trustworthy]
-- check package is trusted
- safeP = packageTrusted trust m
+ safeP = packageTrusted trust trust_own_pkg m
if safeM && safeP
then return Nothing
else return $ Just $ if safeM
@@ -1025,6 +1090,7 @@ hscGenHardCode cgguts mod_summary
cg_dep_pkgs = dependencies,
cg_hpc_info = hpc_info } = cgguts
dflags = hsc_dflags hsc_env
+ platform = targetPlatform dflags
location = ms_location mod_summary
data_tycons = filter isDataTyCon tycons
-- cg_tycons includes newtypes, for the benefit of External Core,
@@ -1060,7 +1126,7 @@ hscGenHardCode cgguts mod_summary
-- unless certain dflags are on, the identity function
------------------ Code output -----------------------
rawcmms <- cmmToRawCmm cmms
- dumpIfSet_dyn dflags Opt_D_dump_raw_cmm "Raw Cmm" (ppr rawcmms)
+ dumpIfSet_dyn dflags Opt_D_dump_raw_cmm "Raw Cmm" (pprPlatform platform rawcmms)
(_stub_h_exists, stub_c_exists)
<- codeOutput dflags this_mod location foreign_stubs
dependencies rawcmms
@@ -1131,10 +1197,11 @@ tryNewCodeGen :: HscEnv -> Module -> [TyCon]
tryNewCodeGen hsc_env this_mod data_tycons
cost_centre_info stg_binds hpc_info =
do { let dflags = hsc_dflags hsc_env
+ platform = targetPlatform dflags
; prog <- StgCmm.codeGen dflags this_mod data_tycons
cost_centre_info stg_binds hpc_info
; dumpIfSet_dyn dflags Opt_D_dump_cmmz "Cmm produced by new codegen"
- (pprCmms prog)
+ (pprCmms platform prog)
-- We are building a single SRT for the entire module, so
-- we must thread it through all the procedures as we cps-convert them.
@@ -1143,7 +1210,7 @@ tryNewCodeGen hsc_env this_mod data_tycons
; (topSRT, prog) <- foldM (cmmPipeline hsc_env) (initTopSRT, []) prog
; let prog' = map cmmOfZgraph (srtToData topSRT : prog)
- ; dumpIfSet_dyn dflags Opt_D_dump_cmmz "Output Cmm" (ppr prog')
+ ; dumpIfSet_dyn dflags Opt_D_dump_cmmz "Output Cmm" (pprPlatform platform prog')
; return prog' }
@@ -1160,11 +1227,12 @@ optionallyConvertAndOrCPS hsc_env cmms =
testCmmConversion :: HscEnv -> Cmm -> IO Cmm
testCmmConversion hsc_env cmm =
do let dflags = hsc_dflags hsc_env
+ platform = targetPlatform dflags
showPass dflags "CmmToCmm"
- dumpIfSet_dyn dflags Opt_D_dump_cvt_cmm "C-- pre-conversion" (ppr cmm)
+ dumpIfSet_dyn dflags Opt_D_dump_cvt_cmm "C-- pre-conversion" (pprPlatform platform cmm)
--continuationC <- cmmCPS dflags abstractC >>= cmmToRawCmm
us <- mkSplitUniqSupply 'C'
- let zgraph = initUs_ us (cmmToZgraph cmm)
+ let zgraph = initUs_ us (cmmToZgraph platform cmm)
chosen_graph <-
if dopt Opt_RunCPSZ dflags
then do us <- mkSplitUniqSupply 'S'
@@ -1172,10 +1240,10 @@ testCmmConversion hsc_env cmm =
(_, [zgraph]) <- cmmPipeline hsc_env (topSRT, []) zgraph
return zgraph
else return (runCmmContFlowOpts zgraph)
- dumpIfSet_dyn dflags Opt_D_dump_cmmz "C-- Zipper Graph" (ppr chosen_graph)
+ dumpIfSet_dyn dflags Opt_D_dump_cmmz "C-- Zipper Graph" (pprPlatform platform chosen_graph)
showPass dflags "Convert from Z back to Cmm"
let cvt = cmmOfZgraph chosen_graph
- dumpIfSet_dyn dflags Opt_D_dump_cvt_cmm "C-- post-conversion" (ppr cvt)
+ dumpIfSet_dyn dflags Opt_D_dump_cvt_cmm "C-- post-conversion" (pprPlatform platform cvt)
return cvt
myCoreToStg :: DynFlags -> Module -> [CoreBind]
@@ -1378,6 +1446,7 @@ mkModGuts mod binds = ModGuts {
mg_deps = noDependencies,
mg_dir_imps = emptyModuleEnv,
mg_used_names = emptyNameSet,
+ mg_used_th = False,
mg_rdr_env = emptyGlobalRdrEnv,
mg_fix_env = emptyFixityEnv,
mg_types = emptyTypeEnv,
@@ -1393,7 +1462,8 @@ mkModGuts mod binds = ModGuts {
mg_modBreaks = emptyModBreaks,
mg_vect_info = noVectInfo,
mg_inst_env = emptyInstEnv,
- mg_fam_inst_env = emptyFamInstEnv
+ mg_fam_inst_env = emptyFamInstEnv,
+ mg_trust_pkg = False
}
\end{code}