summaryrefslogtreecommitdiff
path: root/compiler/main/HscMain.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/main/HscMain.hs')
-rw-r--r--compiler/main/HscMain.hs149
1 files changed, 95 insertions, 54 deletions
diff --git a/compiler/main/HscMain.hs b/compiler/main/HscMain.hs
index bec66f858a..c9baa5ac3e 100644
--- a/compiler/main/HscMain.hs
+++ b/compiler/main/HscMain.hs
@@ -412,19 +412,27 @@ tcRnModule' hsc_env sum save_rn_syntax mod = do
-- end of the safe haskell line, how to respond to user?
if not (safeHaskellOn dflags) || (safeInferOn dflags && not allSafeOK)
-- if safe Haskell off or safe infer failed, mark unsafe
- then markUnsafe tcg_res emptyBag
+ then markUnsafeInfer tcg_res emptyBag
-- module (could be) safe, throw warning if needed
else do
tcg_res' <- hscCheckSafeImports tcg_res
safe <- liftIO $ readIORef (tcg_safeInfer tcg_res')
- when (safe && wopt Opt_WarnSafe dflags)
- (logWarnings $ unitBag $ mkPlainWarnMsg dflags
- (warnSafeOnLoc dflags) $ errSafe tcg_res')
+ when safe $ do
+ case wopt Opt_WarnSafe dflags of
+ True -> (logWarnings $ unitBag $ mkPlainWarnMsg dflags
+ (warnSafeOnLoc dflags) $ errSafe tcg_res')
+ False | safeHaskell dflags == Sf_Trustworthy &&
+ wopt Opt_WarnTrustworthySafe dflags ->
+ (logWarnings $ unitBag $ mkPlainWarnMsg dflags
+ (trustworthyOnLoc dflags) $ errTwthySafe tcg_res')
+ False -> return ()
return tcg_res'
where
pprMod t = ppr $ moduleName $ tcg_mod t
errSafe t = quotes (pprMod t) <+> text "has been inferred as safe!"
+ errTwthySafe t = quotes (pprMod t)
+ <+> text "is marked as Trustworthy but has been inferred as safe!"
-- | Convert a typechecked module to Core
hscDesugar :: HscEnv -> ModSummary -> TcGblEnv -> IO ModGuts
@@ -762,6 +770,18 @@ hscFileFrontEnd mod_summary = do
-- * For modules explicitly marked -XSafe, we throw the errors.
-- * For unmarked modules (inference mode), we drop the errors
-- and mark the module as being Unsafe.
+--
+-- It used to be that we only did safe inference on modules that had no Safe
+-- Haskell flags, but now we perform safe inference on all modules as we want
+-- to allow users to set the `--fwarn-safe`, `--fwarn-unsafe` and
+-- `--fwarn-trustworthy-safe` flags on Trustworthy and Unsafe modules so that a
+-- user can ensure their assumptions are correct and see reasons for why a
+-- module is safe or unsafe.
+--
+-- This is tricky as we must be careful when we should throw an error compared
+-- to just warnings. For checking safe imports we manage it as two steps. First
+-- we check any imports that are required to be safe, then we check all other
+-- imports to see if we can infer them to be safe.
-- | Check that the safe imports of the module being compiled are valid.
@@ -772,21 +792,24 @@ hscCheckSafeImports :: TcGblEnv -> Hsc TcGblEnv
hscCheckSafeImports tcg_env = do
dflags <- getDynFlags
tcg_env' <- checkSafeImports dflags tcg_env
- case safeLanguageOn dflags of
- True -> do
- -- XSafe: we nuke user written RULES
- logWarnings $ warns dflags (tcg_rules tcg_env')
- return tcg_env' { tcg_rules = [] }
- False
- -- SafeInferred: user defined RULES, so not safe
- | safeInferOn dflags && not (null $ tcg_rules tcg_env')
- -> markUnsafe tcg_env' $ warns dflags (tcg_rules tcg_env')
-
- -- Trustworthy OR SafeInferred: with no RULES
- | otherwise
- -> return tcg_env'
+ checkRULES dflags tcg_env'
where
+ checkRULES dflags tcg_env' = do
+ case safeLanguageOn dflags of
+ True -> do
+ -- XSafe: we nuke user written RULES
+ logWarnings $ warns dflags (tcg_rules tcg_env')
+ return tcg_env' { tcg_rules = [] }
+ False
+ -- SafeInferred: user defined RULES, so not safe
+ | safeInferOn dflags && not (null $ tcg_rules tcg_env')
+ -> markUnsafeInfer tcg_env' $ warns dflags (tcg_rules tcg_env')
+
+ -- Trustworthy OR SafeInferred: with no RULES
+ | otherwise
+ -> return tcg_env'
+
warns dflags rules = listToBag $ map (warnRules dflags) rules
warnRules dflags (L loc (HsRule n _ _ _ _ _ _)) =
mkPlainWarnMsg dflags loc $
@@ -808,51 +831,55 @@ hscCheckSafeImports tcg_env = do
checkSafeImports :: DynFlags -> TcGblEnv -> Hsc TcGblEnv
checkSafeImports dflags tcg_env
= do
+ imps <- mapM condense imports'
+ let (safeImps, regImps) = partition (\(_,_,s) -> s) imps
+
-- We want to use the warning state specifically for detecting if safe
-- inference has failed, so store and clear any existing warnings.
oldErrs <- getWarnings
clearWarnings
- imps <- mapM condense imports'
- pkgs <- mapM checkSafe imps
-
- -- grab any safe haskell specific errors and restore old warnings
- errs <- getWarnings
+ -- Check safe imports are correct
+ safePkgs <- mapM checkSafe safeImps
+ safeErrs <- getWarnings
clearWarnings
- logWarnings oldErrs
+ -- Check non-safe imports are correct if inferring safety
-- See the Note [Safe Haskell Inference]
- case (not $ isEmptyBag errs) of
-
- -- We have errors!
- True ->
- -- did we fail safe inference or fail -XSafe?
- case safeInferOn dflags of
- True -> markUnsafe tcg_env errs
- False -> liftIO . throwIO . mkSrcErr $ errs
-
- -- All good matey!
- False -> do
- when (packageTrustOn dflags) $ checkPkgTrust dflags pkg_reqs
- -- add in trusted package requirements for this module
- let new_trust = emptyImportAvails { imp_trust_pkgs = catMaybes pkgs }
- return tcg_env { tcg_imports = imp_info `plusImportAvails` new_trust }
+ (infErrs, infPkgs) <- case (safeInferOn dflags) of
+ False -> return (emptyBag, [])
+ True -> do infPkgs <- mapM checkSafe regImps
+ infErrs <- getWarnings
+ clearWarnings
+ return (infErrs, infPkgs)
+
+ -- restore old errors
+ logWarnings oldErrs
+
+ case (isEmptyBag safeErrs) of
+ -- Failed safe check
+ False -> liftIO . throwIO . mkSrcErr $ safeErrs
+
+ -- Passed safe check
+ True -> do
+ let infPassed = isEmptyBag infErrs
+ tcg_env' <- case (not infPassed) of
+ True -> markUnsafeInfer tcg_env infErrs
+ False -> return tcg_env
+ when (packageTrustOn dflags) $ checkPkgTrust dflags pkgReqs
+ let newTrust = pkgTrustReqs safePkgs infPkgs infPassed
+ return tcg_env' { tcg_imports = impInfo `plusImportAvails` newTrust }
where
- imp_info = tcg_imports tcg_env -- ImportAvails
- imports = imp_mods imp_info -- ImportedMods
+ impInfo = tcg_imports tcg_env -- ImportAvails
+ imports = imp_mods impInfo -- ImportedMods
imports' = moduleEnvToList imports -- (Module, [ImportedModsVal])
- pkg_reqs = imp_trust_pkgs imp_info -- [PackageKey]
+ pkgReqs = imp_trust_pkgs impInfo -- [PackageKey]
condense :: (Module, [ImportedModsVal]) -> Hsc (Module, SrcSpan, IsSafeImport)
condense (_, []) = panic "HscMain.condense: Pattern match failure!"
condense (m, x:xs) = do (_,_,l,s) <- foldlM cond' x xs
- -- we turn all imports into safe ones when
- -- inference mode is on.
- let s' = if safeInferOn dflags &&
- safeHaskell dflags == Sf_None
- then True else s
- return (m, l, s')
+ return (m, l, s)
-- ImportedModsVal = (ModuleName, Bool, SrcSpan, IsSafeImport)
cond' :: ImportedModsVal -> ImportedModsVal -> Hsc ImportedModsVal
@@ -865,8 +892,17 @@ checkSafeImports dflags tcg_env
= return v1
-- easier interface to work with
- checkSafe (_, _, False) = return Nothing
- checkSafe (m, l, True ) = fst `fmap` hscCheckSafe' dflags m l
+ checkSafe (m, l, _) = fst `fmap` hscCheckSafe' dflags m l
+
+ -- what pkg's to add to our trust requirements
+ pkgTrustReqs req inf infPassed | safeInferOn dflags
+ && safeHaskell dflags == Sf_None && infPassed
+ = emptyImportAvails {
+ imp_trust_pkgs = catMaybes req ++ catMaybes inf
+ }
+ pkgTrustReqs _ _ _ | safeHaskell dflags == Sf_Unsafe
+ = emptyImportAvails
+ pkgTrustReqs req _ _ = emptyImportAvails { imp_trust_pkgs = catMaybes req }
-- | Check that a module is safe to import.
--
@@ -1000,11 +1036,16 @@ checkPkgTrust dflags pkgs =
-- | Set module to unsafe and (potentially) wipe trust information.
--
--- Make sure to call this method to set a module to inferred unsafe,
--- it should be a central and single failure method. We only wipe the trust
--- information when we aren't in a specific Safe Haskell mode.
-markUnsafe :: TcGblEnv -> WarningMessages -> Hsc TcGblEnv
-markUnsafe tcg_env whyUnsafe = do
+-- Make sure to call this method to set a module to inferred unsafe, it should
+-- be a central and single failure method. We only wipe the trust information
+-- when we aren't in a specific Safe Haskell mode.
+--
+-- While we only use this for recording that a module was inferred unsafe, we
+-- may call it on modules using Trustworthy or Unsafe flags so as to allow
+-- warning flags for safety to function correctly. See Note [Safe Haskell
+-- Inference].
+markUnsafeInfer :: TcGblEnv -> WarningMessages -> Hsc TcGblEnv
+markUnsafeInfer tcg_env whyUnsafe = do
dflags <- getDynFlags
when (wopt Opt_WarnUnsafe dflags)