From 38faeea1a94072ffd9f459d9fe570f06bc1da84a Mon Sep 17 00:00:00 2001 From: Matthew Pickering Date: Wed, 5 May 2021 13:48:19 +0100 Subject: Remove transitive information about modules and packages from interface files This commit modifies interface files so that *only* direct information about modules and packages is stored in the interface file. * Only direct module and direct package dependencies are stored in the interface files. * Trusted packages are now stored separately as they need to be checked transitively. * hs-boot files below the compiled module in the home module are stored so that eps_is_boot can be calculated in one-shot mode without loading all interface files in the home package. * The transitive closure of signatures is stored separately This is important for two reasons * Less recompilation is needed, as motivated by #16885, a lot of redundant compilation was triggered when adding new imports deep in the module tree as all the parent interface files had to be redundantly updated. * Checking an interface file is cheaper because you don't have to perform a transitive traversal to check the dependencies are up-to-date. In the code, places where we would have used the transitive closure, we instead compute the necessary transitive closure. The closure is not computed very often, was already happening in checkDependencies, and was already happening in getLinkDeps. Fixes #16885 ------------------------- Metric Decrease: MultiLayerModules T13701 T13719 ------------------------- --- compiler/GHC/Core/Opt/Pipeline.hs | 2 +- compiler/GHC/Core/Opt/Specialise.hs | 2 +- compiler/GHC/Driver/Env.hs | 55 ++++++++++++-- compiler/GHC/Driver/Main.hs | 3 +- compiler/GHC/Driver/Pipeline.hs | 2 +- compiler/GHC/HsToCore/Usage.hs | 33 ++++++--- compiler/GHC/Iface/Load.hs | 19 +++-- compiler/GHC/Iface/Recomp.hs | 119 +++++++----------------------- compiler/GHC/Iface/Tidy.hs | 2 +- compiler/GHC/Linker/Loader.hs | 10 +-- compiler/GHC/Rename/Module.hs | 4 +- compiler/GHC/Rename/Names.hs | 83 +++++++++++++-------- compiler/GHC/Tc/Module.hs | 20 +++-- compiler/GHC/Tc/Types.hs | 90 +++++++++++++---------- compiler/GHC/Types/Unique/FM.hs | 16 ++++ compiler/GHC/Unit/Module/Deps.hs | 137 ++++++++++++++++++++++++++++++----- compiler/GHC/Unit/Module/ModIface.hs | 2 +- 17 files changed, 366 insertions(+), 233 deletions(-) (limited to 'compiler') diff --git a/compiler/GHC/Core/Opt/Pipeline.hs b/compiler/GHC/Core/Opt/Pipeline.hs index ba75cab359..90b5968a2f 100644 --- a/compiler/GHC/Core/Opt/Pipeline.hs +++ b/compiler/GHC/Core/Opt/Pipeline.hs @@ -109,7 +109,7 @@ core2core hsc_env guts@(ModGuts { mg_module = mod where logger = hsc_logger hsc_env dflags = hsc_dflags hsc_env - home_pkg_rules = hptRules hsc_env (dep_mods deps) + home_pkg_rules = hptRules hsc_env (dep_direct_mods deps) hpt_rule_base = mkRuleBase home_pkg_rules print_unqual = mkPrintUnqualified (hsc_unit_env hsc_env) rdr_env -- mod: get the module out of the current HscEnv so we can retrieve it from the monad. diff --git a/compiler/GHC/Core/Opt/Specialise.hs b/compiler/GHC/Core/Opt/Specialise.hs index 459e3fe43c..68b16ea753 100644 --- a/compiler/GHC/Core/Opt/Specialise.hs +++ b/compiler/GHC/Core/Opt/Specialise.hs @@ -2592,7 +2592,7 @@ data CallInfoSet = CIS Id (Bag CallInfo) data CallInfo = CI { ci_key :: [SpecArg] -- All arguments , ci_fvs :: IdSet -- Free Ids of the ci_key call - -- *not* including the main id itself, of course + -- _not_ including the main id itself, of course -- NB: excluding tyvars: -- See Note [Specialising polymorphic dictionaries] } diff --git a/compiler/GHC/Driver/Env.hs b/compiler/GHC/Driver/Env.hs index 27e250b68c..756d8eaff0 100644 --- a/compiler/GHC/Driver/Env.hs +++ b/compiler/GHC/Driver/Env.hs @@ -13,7 +13,8 @@ module GHC.Driver.Env , hscEPS , hscInterp , hptCompleteSigs - , hptInstances + , hptAllInstances + , hptInstancesBelow , hptAnns , hptAllThings , hptSomeThingsBelowUs @@ -64,9 +65,10 @@ import GHC.Utils.Outputable import GHC.Utils.Monad import GHC.Utils.Panic import GHC.Utils.Misc +import GHC.Types.Unique.FM -import Control.Monad ( guard ) import Data.IORef +import qualified Data.Set as Set runHsc :: HscEnv -> Hsc a -> IO a runHsc hsc_env (Hsc hsc) = do @@ -180,14 +182,28 @@ hptCompleteSigs = hptAllThings (md_complete_matches . hm_details) -- the Home Package Table filtered by the provided predicate function. -- Used in @tcRnImports@, to select the instances that are in the -- transitive closure of imports from the currently compiled module. -hptInstances :: HscEnv -> (ModuleName -> Bool) -> ([ClsInst], [FamInst]) -hptInstances hsc_env want_this_module +hptAllInstances :: HscEnv -> ([ClsInst], [FamInst]) +hptAllInstances hsc_env = let (insts, famInsts) = unzip $ flip hptAllThings hsc_env $ \mod_info -> do - guard (want_this_module (moduleName (mi_module (hm_iface mod_info)))) let details = hm_details mod_info return (md_insts details, md_fam_insts details) in (concat insts, concat famInsts) +-- | Find instances visible from the given set of imports +hptInstancesBelow :: HscEnv -> ModuleName -> [ModuleNameWithIsBoot] -> ([ClsInst], [FamInst]) +hptInstancesBelow hsc_env mn mns = + let (insts, famInsts) = + unzip $ hptSomeThingsBelowUs (\mod_info -> + let details = hm_details mod_info + -- Don't include instances for the current module + in if moduleName (mi_module (hm_iface mod_info)) == mn + then [] + else [(md_insts details, md_fam_insts details)]) + True -- Include -hi-boot + hsc_env + mns + in (concat insts, concat famInsts) + -- | Get rules from modules "below" this one (in the dependency sense) hptRules :: HscEnv -> [ModuleNameWithIsBoot] -> [CoreRule] hptRules = hptSomeThingsBelowUs (md_rules . hm_details) False @@ -201,10 +217,33 @@ hptAnns hsc_env Nothing = hptAllThings (md_anns . hm_details) hsc_env hptAllThings :: (HomeModInfo -> [a]) -> HscEnv -> [a] hptAllThings extract hsc_env = concatMap extract (eltsHpt (hsc_HPT hsc_env)) +hptModulesBelow :: HscEnv -> [ModuleNameWithIsBoot] -> Set.Set ModuleNameWithIsBoot +hptModulesBelow hsc_env mn = Set.fromList (eltsUFM $ go mn emptyUFM) + where + hpt = hsc_HPT hsc_env + + go [] seen = seen + go (mn:mns) seen + | Just mn' <- lookupUFM seen (gwib_mod mn) + -- Already seen the module before + , gwib_isBoot mn' == gwib_isBoot mn = go mns seen + | otherwise = + case lookupHpt hpt (gwib_mod mn) of + -- Not a home module + Nothing -> go mns seen + Just hmi -> + let + comb m@(GWIB { gwib_isBoot = NotBoot }) _ = m + comb (GWIB { gwib_isBoot = IsBoot }) x = x + in + go (dep_direct_mods (mi_deps (hm_iface hmi)) ++ mns) + (addToUFM_C comb seen (gwib_mod mn) mn) + + -- | Get things from modules "below" this one (in the dependency sense) -- C.f Inst.hptInstances hptSomeThingsBelowUs :: (HomeModInfo -> [a]) -> Bool -> HscEnv -> [ModuleNameWithIsBoot] -> [a] -hptSomeThingsBelowUs extract include_hi_boot hsc_env deps +hptSomeThingsBelowUs extract include_hi_boot hsc_env mod | isOneShot (ghcMode (hsc_dflags hsc_env)) = [] | otherwise @@ -212,7 +251,7 @@ hptSomeThingsBelowUs extract include_hi_boot hsc_env deps in [ thing | -- Find each non-hi-boot module below me - GWIB { gwib_mod = mod, gwib_isBoot = is_boot } <- deps + GWIB { gwib_mod = mod, gwib_isBoot = is_boot } <- Set.toList (hptModulesBelow hsc_env mod) , include_hi_boot || (is_boot == NotBoot) -- unsavoury: when compiling the base package with --make, we @@ -243,7 +282,7 @@ prepareAnnotations hsc_env mb_guts = do -- Extract dependencies of the module if we are supplied one, -- otherwise load annotations from all home package table -- entries regardless of dependency ordering. - home_pkg_anns = (mkAnnEnv . hptAnns hsc_env) $ fmap (dep_mods . mg_deps) mb_guts + home_pkg_anns = (mkAnnEnv . hptAnns hsc_env) $ fmap (dep_direct_mods . mg_deps) mb_guts other_pkg_anns = eps_ann_env eps ann_env = foldl1' plusAnnEnv $ catMaybes [mb_this_module_anns, Just home_pkg_anns, diff --git a/compiler/GHC/Driver/Main.hs b/compiler/GHC/Driver/Main.hs index 210c8644da..2d3e1e3925 100644 --- a/compiler/GHC/Driver/Main.hs +++ b/compiler/GHC/Driver/Main.hs @@ -1362,7 +1362,7 @@ hscCheckSafe' m l = do -- check package is trusted safeP = packageTrusted dflags (hsc_units hsc_env) home_unit trust trust_own_pkg m -- pkg trust reqs - pkgRs = S.fromList . map fst $ filter snd $ dep_pkgs $ mi_deps iface' + pkgRs = S.fromList (dep_trusted_pkgs $ mi_deps iface') -- warn if Safe module imports Safe-Inferred module. warns = if wopt Opt_WarnInferredSafeImports dflags && safeLanguageOn dflags @@ -1518,7 +1518,6 @@ markUnsafeInfer tcg_env whyUnsafe = do text "overlap mode isn't allowed in Safe Haskell"] | otherwise = [] - -- | Figure out the final correct safe haskell mode hscGetSafeMode :: TcGblEnv -> Hsc SafeHaskellMode hscGetSafeMode tcg_env = do diff --git a/compiler/GHC/Driver/Pipeline.hs b/compiler/GHC/Driver/Pipeline.hs index 02ebfbb5ce..e766fda3c7 100644 --- a/compiler/GHC/Driver/Pipeline.hs +++ b/compiler/GHC/Driver/Pipeline.hs @@ -554,7 +554,7 @@ link' logger tmpfs dflags unit_env batch_attempt_linking hpt home_mod_infos = eltsHpt hpt -- the packages we depend on - pkg_deps = concatMap (map fst . dep_pkgs . mi_deps . hm_iface) home_mod_infos + pkg_deps = concatMap (dep_direct_pkgs . mi_deps . hm_iface) home_mod_infos -- the linkables to link linkables = map (expectJust "link".hm_linkable) home_mod_infos diff --git a/compiler/GHC/HsToCore/Usage.hs b/compiler/GHC/HsToCore/Usage.hs index 139ec05167..0da8f59070 100644 --- a/compiler/GHC/HsToCore/Usage.hs +++ b/compiler/GHC/HsToCore/Usage.hs @@ -38,11 +38,12 @@ import GHC.Unit.Module.Deps import GHC.Data.Maybe import Control.Monad (filterM) -import Data.List (sort, sortBy, nub) +import Data.List (sortBy, sort, nub) import Data.IORef import Data.Map (Map) import qualified Data.Map as Map import qualified Data.Set as Set + import System.Directory import System.FilePath @@ -80,8 +81,7 @@ mkDependencies iuid pluginModules let (dep_plgins, ms) = unzip [ (moduleName mn, mn) | mn <- pluginModules ] plugin_dep_pkgs = filter (/= iuid) (map (toUnitId . moduleUnit) ms) th_used <- readIORef th_var - let dep_mods = modDepsElts (delFromUFM (imp_dep_mods imports) - (moduleName mod)) + let direct_mods = modDepsElts (delFromUFM (imp_direct_dep_mods imports) (moduleName mod)) -- M.hi-boot can be in the imp_dep_mods, but we must remove -- it before recording the modules on which this one depends! -- (We want to retain M.hi-boot in imp_dep_mods so that @@ -93,19 +93,28 @@ mkDependencies iuid pluginModules -- We must also remove self-references from imp_orphs. See -- Note [Module self-dependency] - raw_pkgs = foldr Set.insert (imp_dep_pkgs imports) plugin_dep_pkgs + direct_pkgs_0 = foldr Set.insert (imp_dep_direct_pkgs imports) plugin_dep_pkgs - pkgs | th_used = Set.insert thUnitId raw_pkgs - | otherwise = raw_pkgs + direct_pkgs + | th_used = Set.insert thUnitId direct_pkgs_0 + | otherwise = direct_pkgs_0 -- Set the packages required to be Safe according to Safe Haskell. -- See Note [Tracking Trust Transitively] in GHC.Rename.Names - sorted_pkgs = sort (Set.toList pkgs) + sorted_direct_pkgs = sort (Set.toList direct_pkgs) trust_pkgs = imp_trust_pkgs imports - dep_pkgs' = map (\x -> (x, x `Set.member` trust_pkgs)) sorted_pkgs - - return Deps { dep_mods = dep_mods, - dep_pkgs = dep_pkgs', + -- If there's a non-boot import, then it shadows the boot import + -- coming from the dependencies + source_mods = + modDepsElts $ (imp_boot_mods imports) + + sig_mods = filter (/= (moduleName mod)) $ imp_sig_mods imports + + return Deps { dep_direct_mods = direct_mods, + dep_direct_pkgs = sorted_direct_pkgs, + dep_sig_mods = sort sig_mods, + dep_trusted_pkgs = sort (Set.toList trust_pkgs), + dep_boot_mods = sort source_mods, dep_orphs = dep_orphs, dep_plgins = dep_plgins, dep_finsts = sortBy stableModuleCmp (imp_finsts imports) } @@ -235,7 +244,7 @@ mkPluginUsage hsc_env pluginModule pNm = moduleName $ mi_module pluginModule pPkg = moduleUnit $ mi_module pluginModule deps = map gwib_mod $ - dep_mods $ mi_deps pluginModule + dep_direct_mods $ mi_deps pluginModule -- Lookup object file for a plugin dependency, -- from the same package as the plugin. diff --git a/compiler/GHC/Iface/Load.hs b/compiler/GHC/Iface/Load.hs index 7b2a659161..89480c6112 100644 --- a/compiler/GHC/Iface/Load.hs +++ b/compiler/GHC/Iface/Load.hs @@ -1179,18 +1179,25 @@ pprUsageImport usage usg_mod' -- | Pretty-print unit dependencies pprDeps :: UnitState -> Dependencies -> SDoc -pprDeps unit_state (Deps { dep_mods = mods, dep_pkgs = pkgs, dep_orphs = orphs, - dep_finsts = finsts }) +pprDeps unit_state (Deps { dep_direct_mods = dmods + , dep_boot_mods = bmods + , dep_orphs = orphs + , dep_direct_pkgs = pkgs + , dep_trusted_pkgs = tps + , dep_finsts = finsts + , dep_plgins = plugins }) = pprWithUnitState unit_state $ - vcat [text "module dependencies:" <+> fsep (map ppr_mod mods), - text "package dependencies:" <+> fsep (map ppr_pkg pkgs), + vcat [text "direct module dependencies:" <+> fsep (map ppr_mod dmods), + text "boot module dependencies:" <+> fsep (map ppr bmods), + text "direct package dependencies:" <+> fsep (map ppr_pkg pkgs), + if null tps then empty else text "trusted package dependencies:" <+> fsep (map ppr_pkg pkgs), text "orphans:" <+> fsep (map ppr orphs), + text "plugins:" <+> fsep (map ppr plugins), text "family instance modules:" <+> fsep (map ppr finsts) ] where ppr_mod (GWIB { gwib_mod = mod_name, gwib_isBoot = boot }) = ppr mod_name <+> ppr_boot boot - ppr_pkg (pkg,trust_req) = ppr pkg <> - (if trust_req then text "*" else Outputable.empty) + ppr_pkg pkg = ppr pkg ppr_boot IsBoot = text "[boot]" ppr_boot NotBoot = Outputable.empty diff --git a/compiler/GHC/Iface/Recomp.hs b/compiler/GHC/Iface/Recomp.hs index e033a6628a..9bccffab3d 100644 --- a/compiler/GHC/Iface/Recomp.hs +++ b/compiler/GHC/Iface/Recomp.hs @@ -63,9 +63,8 @@ import GHC.Unit.Module.Deps import Control.Monad import Data.Function -import Data.List (find, sortBy, sort) +import Data.List (sortBy, sort) import qualified Data.Map as Map -import qualified Data.Set as Set import Data.Word (Word64) --Qualified import so we can define a Semigroup instance @@ -265,11 +264,10 @@ checkVersions hsc_env mod_summary iface -- It's just temporary because either the usage check will succeed -- (in which case we are done with this module) or it'll fail (in which -- case we'll compile the module from scratch anyhow). - -- - -- We do this regardless of compilation mode, although in --make mode - -- all the dependent modules should be in the HPT already, so it's - -- quite redundant - ; updateEps_ $ \eps -> eps { eps_is_boot = mod_deps } + + when (isOneShot (ghcMode (hsc_dflags hsc_env))) $ do { + ; updateEps_ $ \eps -> eps { eps_is_boot = mkModDeps $ (dep_boot_mods (mi_deps iface)) } + } ; recomp <- checkList [checkModUsage (homeUnitAsUnit home_unit) u | u <- mi_usages iface] ; return (recomp, Just iface) @@ -278,9 +276,9 @@ checkVersions hsc_env mod_summary iface logger = hsc_logger hsc_env dflags = hsc_dflags hsc_env home_unit = hsc_home_unit hsc_env - -- This is a bit of a hack really - mod_deps :: ModuleNameEnv ModuleNameWithIsBoot - mod_deps = mkModDeps (dep_mods (mi_deps iface)) + + + -- | Check if any plugins are requesting recompilation checkPlugins :: HscEnv -> ModIface -> IfG RecompileRequired @@ -450,38 +448,19 @@ checkMergedSignatures hsc_env mod_summary iface = do -- - a new home module has been added that shadows a package module -- See bug #1372. -- --- In addition, we also check if the union of dependencies of the imported --- modules has any difference to the previous set of dependencies. We would need --- to recompile in that case also since the `mi_deps` field of ModIface needs --- to be updated to match that information. This is one of the invariants --- of interface files (see https://gitlab.haskell.org/ghc/ghc/wikis/commentary/compiler/recompilation-avoidance#interface-file-invariants). --- See bug #16511. --- -- Returns (RecompBecause ) if recompilation is required. checkDependencies :: HscEnv -> ModSummary -> ModIface -> IfG RecompileRequired checkDependencies hsc_env summary iface - = - checkList $ - [ liftIO $ checkList (map dep_missing (ms_imps summary ++ ms_srcimps summary)) - , do - (recomp, mnames_seen) <- runUntilRecompRequired $ map - checkForNewHomeDependency - (ms_home_imps summary) - liftIO $ case recomp of - UpToDate -> do - let - seen_home_deps = Set.unions $ map Set.fromList mnames_seen - checkIfAllOldHomeDependenciesAreSeen seen_home_deps - _ -> return recomp] + = liftIO $ checkList (map dep_missing (ms_imps summary ++ ms_srcimps summary)) where dflags = hsc_dflags hsc_env logger = hsc_logger hsc_env fc = hsc_FC hsc_env home_unit = hsc_home_unit hsc_env units = hsc_units hsc_env - prev_dep_mods = dep_mods (mi_deps iface) + prev_dep_mods = dep_direct_mods (mi_deps iface) prev_dep_plgn = dep_plgins (mi_deps iface) - prev_dep_pkgs = dep_pkgs (mi_deps iface) + prev_dep_pkgs = dep_direct_pkgs (mi_deps iface) dep_missing (mb_pkg, L _ mod) = do find_res <- findImportedModule fc units home_unit dflags mod (mb_pkg) @@ -497,7 +476,7 @@ checkDependencies hsc_env summary iface else return UpToDate | otherwise - -> if toUnitId pkg `notElem` (map fst prev_dep_pkgs) + -> if toUnitId pkg `notElem` prev_dep_pkgs then do trace_hi_diffs logger dflags $ text "imported module " <> quotes (ppr mod) <> text " is from package " <> quotes (ppr pkg) <> @@ -508,58 +487,6 @@ checkDependencies hsc_env summary iface where pkg = moduleUnit mod _otherwise -> return (RecompBecause reason) - projectNonBootNames = map gwib_mod . filter ((== NotBoot) . gwib_isBoot) - old_deps = Set.fromList - $ projectNonBootNames prev_dep_mods - isOldHomeDeps = flip Set.member old_deps - checkForNewHomeDependency (L _ mname) = do - let - mod = mkHomeModule home_unit mname - str_mname = moduleNameString mname - reason = str_mname ++ " changed" - -- We only want to look at home modules to check if any new home dependency - -- pops in and thus here, skip modules that are not home. Checking - -- membership in old home dependencies suffice because the `dep_missing` - -- check already verified that all imported home modules are present there. - if not (isOldHomeDeps mname) - then return (UpToDate, []) - else do - mb_result <- getFromModIface "need mi_deps for" mod $ \imported_iface -> do - let mnames = mname:(map gwib_mod $ filter ((== NotBoot) . gwib_isBoot) $ - dep_mods $ mi_deps imported_iface) - case find (not . isOldHomeDeps) mnames of - Nothing -> return (UpToDate, mnames) - Just new_dep_mname -> do - trace_hi_diffs logger dflags $ - text "imported home module " <> quotes (ppr mod) <> - text " has a new dependency " <> quotes (ppr new_dep_mname) - return (RecompBecause reason, []) - return $ fromMaybe (MustCompile, []) mb_result - - -- Performs all recompilation checks in the list until a check that yields - -- recompile required is encountered. Returns the list of the results of - -- all UpToDate checks. - runUntilRecompRequired [] = return (UpToDate, []) - runUntilRecompRequired (check:checks) = do - (recompile, value) <- check - if recompileRequired recompile - then return (recompile, []) - else do - (recomp, values) <- runUntilRecompRequired checks - return (recomp, value:values) - - checkIfAllOldHomeDependenciesAreSeen seen_deps = do - let unseen_old_deps = Set.difference - old_deps - seen_deps - if not (null unseen_old_deps) - then do - let missing_dep = Set.elemAt 0 unseen_old_deps - trace_hi_diffs logger dflags $ - text "missing old home dependency " <> quotes (ppr missing_dep) - return $ RecompBecause "missing old dependency" - else return UpToDate - needInterface :: Module -> (ModIface -> IO RecompileRequired) -> IfG RecompileRequired needInterface mod continue @@ -1043,17 +970,22 @@ addFingerprints hsc_env iface0 orphan_hash <- computeFingerprint (mk_put_name local_env) (map ifDFun orph_insts, orph_rules, orph_fis) + -- Hash of the transitive things in dependencies + dep_hash <- computeFingerprint putNameLiterally + (dep_sig_mods (mi_deps iface0), + dep_boot_mods (mi_deps iface0), + -- Trusted packages are like orphans + dep_trusted_pkgs (mi_deps iface0), + -- See Note [Export hash depends on non-orphan family instances] + dep_finsts (mi_deps iface0) ) + -- the export list hash doesn't depend on the fingerprints of -- the Names it mentions, only the Names themselves, hence putNameLiterally. export_hash <- computeFingerprint putNameLiterally (mi_exports iface0, orphan_hash, + dep_hash, dep_orphan_hashes, - dep_pkgs (mi_deps iface0), - -- See Note [Export hash depends on non-orphan family instances] - dep_finsts (mi_deps iface0), - -- dep_pkgs: see "Package Version Changes" on - -- wiki/commentary/compiler/recompilation-avoidance mi_trust iface0) -- Make sure change of Safe Haskell mode causes recomp. @@ -1209,8 +1141,11 @@ getOrphanHashes hsc_env mods = do sortDependencies :: Dependencies -> Dependencies sortDependencies d - = Deps { dep_mods = sortBy (lexicalCompareFS `on` (moduleNameFS . gwib_mod)) (dep_mods d), - dep_pkgs = sortBy (compare `on` fst) (dep_pkgs d), + = Deps { dep_direct_mods = sortBy (lexicalCompareFS `on` (moduleNameFS . gwib_mod)) (dep_direct_mods d), + dep_direct_pkgs = sort (dep_direct_pkgs d), + dep_sig_mods = sort (dep_sig_mods d), + dep_trusted_pkgs = sort (dep_trusted_pkgs d), + dep_boot_mods = sort (dep_boot_mods d), dep_orphs = sortBy stableModuleCmp (dep_orphs d), dep_finsts = sortBy stableModuleCmp (dep_finsts d), dep_plgins = sortBy (lexicalCompareFS `on` moduleNameFS) (dep_plgins d) } diff --git a/compiler/GHC/Iface/Tidy.hs b/compiler/GHC/Iface/Tidy.hs index 71e93671b9..96da0ce2c0 100644 --- a/compiler/GHC/Iface/Tidy.hs +++ b/compiler/GHC/Iface/Tidy.hs @@ -459,7 +459,7 @@ tidyProgram hsc_env (ModGuts { mg_module = mod cg_binds = all_tidy_binds, cg_foreign = add_spt_init_code foreign_stubs, cg_foreign_files = foreign_files, - cg_dep_pkgs = map fst $ dep_pkgs deps, + cg_dep_pkgs = dep_direct_pkgs deps, cg_hpc_info = hpc_info, cg_modBreaks = modBreaks, cg_spt_entries = spt_entries }, diff --git a/compiler/GHC/Linker/Loader.hs b/compiler/GHC/Linker/Loader.hs index 1258034fb5..ccd3879910 100644 --- a/compiler/GHC/Linker/Loader.hs +++ b/compiler/GHC/Linker/Loader.hs @@ -692,20 +692,20 @@ getLinkDeps hsc_env hpt pls replace_osuf span mods deps = mi_deps iface home_unit = hsc_home_unit hsc_env - pkg_deps = dep_pkgs deps - (boot_deps, mod_deps) = flip partitionWith (dep_mods deps) $ + pkg_deps = dep_direct_pkgs deps + (boot_deps, mod_deps) = flip partitionWith (dep_direct_mods deps) $ \ (GWIB { gwib_mod = m, gwib_isBoot = is_boot }) -> m & case is_boot of IsBoot -> Left NotBoot -> Right - boot_deps' = filter (not . (`elementOfUniqDSet` acc_mods)) boot_deps + mod_deps' = filter (not . (`elementOfUniqDSet` acc_mods)) (boot_deps ++ mod_deps) acc_mods' = addListToUniqDSet acc_mods (moduleName mod : mod_deps) - acc_pkgs' = addListToUniqDSet acc_pkgs $ map fst pkg_deps + acc_pkgs' = addListToUniqDSet acc_pkgs pkg_deps -- if not (isHomeUnit home_unit pkg) then follow_deps mods acc_mods (addOneToUniqDSet acc_pkgs' (toUnitId pkg)) - else follow_deps (map (mkHomeModule home_unit) boot_deps' ++ mods) + else follow_deps (map (mkHomeModule home_unit) mod_deps' ++ mods) acc_mods' acc_pkgs' where msg = text "need to link module" <+> ppr mod <+> diff --git a/compiler/GHC/Rename/Module.hs b/compiler/GHC/Rename/Module.hs index 8bb8557186..2eb048f3f6 100644 --- a/compiler/GHC/Rename/Module.hs +++ b/compiler/GHC/Rename/Module.hs @@ -763,10 +763,10 @@ rnFamEqn doc atfi extra_kvars -- See Note [Renaming associated types]. -- Per that Note, the LHS type variables consist of: -- - -- * The variables mentioned in the instance's type patterns + -- - The variables mentioned in the instance's type patterns -- (pat_fvs), and -- - -- * The variables mentioned in an outermost kind signature on the + -- - The variables mentioned in an outermost kind signature on the -- RHS. This is a subset of `rhs_fvs`. To compute it, we look up -- each RdrName in `extra_kvars` to find its corresponding Name in -- the LocalRdrEnv. diff --git a/compiler/GHC/Rename/Names.hs b/compiler/GHC/Rename/Names.hs index 8daf355ab4..ee545b9132 100644 --- a/compiler/GHC/Rename/Names.hs +++ b/compiler/GHC/Rename/Names.hs @@ -70,6 +70,7 @@ import GHC.Types.Basic ( TopLevelFlag(..) ) import GHC.Types.SourceText import GHC.Types.Id import GHC.Types.HpcInfo +import GHC.Types.Unique.FM import GHC.Unit import GHC.Unit.Module.Warnings @@ -197,9 +198,20 @@ rnImports imports = do stuff2 <- mapAndReportM (rnImportDecl this_mod) source -- Safe Haskell: See Note [Tracking Trust Transitively] let (decls, rdr_env, imp_avails, hpc_usage) = combine (stuff1 ++ stuff2) - return (decls, rdr_env, imp_avails, hpc_usage) + -- Update imp_boot_mods if imp_direct_mods mentions any of them + let final_import_avail = clobberSourceImports imp_avails + return (decls, rdr_env, final_import_avail, hpc_usage) where + clobberSourceImports imp_avails = + imp_avails { imp_boot_mods = imp_boot_mods' } + where + imp_boot_mods' = mergeUFM combJ id (const mempty) + (imp_boot_mods imp_avails) + (imp_direct_dep_mods imp_avails) + + combJ (GWIB _ IsBoot) x = Just x + combJ r _ = Just r -- See Note [Combining ImportAvails] combine :: [(LImportDecl GhcRn, GlobalRdrEnv, ImportAvails, AnyHpcUsage)] -> ([LImportDecl GhcRn], GlobalRdrEnv, ImportAvails, AnyHpcUsage) @@ -421,6 +433,7 @@ calculateAvails home_unit iface mod_safe' want_boot imported_by = deps = mi_deps iface trust = getSafeMode $ mi_trust iface trust_pkg = mi_trust_pkg iface + is_sig = mi_hsc_src iface == HsigFile -- If the module exports anything defined in this module, just -- ignore it. Reason: otherwise it looks as if there are two @@ -456,53 +469,61 @@ calculateAvails home_unit iface mod_safe' want_boot imported_by = imp_sem_mod : dep_finsts deps | otherwise = dep_finsts deps + -- Trusted packages are a lot like orphans. + trusted_pkgs | mod_safe' = S.fromList (dep_trusted_pkgs deps) + | otherwise = S.empty + + pkg = moduleUnit (mi_module iface) ipkg = toUnitId pkg -- Does this import mean we now require our own pkg -- to be trusted? See Note [Trust Own Package] ptrust = trust == Sf_Trustworthy || trust_pkg + pkg_trust_req + | isHomeUnit home_unit pkg = ptrust + | otherwise = False + + dependent_pkgs = if isHomeUnit home_unit pkg + then S.empty + else S.fromList [ipkg] + + direct_mods = mkModDeps $ if isHomeUnit home_unit pkg + then [GWIB (moduleName imp_mod) want_boot] + else [] + + dep_boot_mods_map = mkModDeps (dep_boot_mods deps) + + boot_mods + -- If we are looking for a boot module, it must be HPT + | IsBoot <- want_boot = addToUFM dep_boot_mods_map (moduleName imp_mod) (GWIB (moduleName imp_mod) IsBoot) + -- Now we are importing A properly, so don't go looking for + -- A.hs-boot + | isHomeUnit home_unit pkg = dep_boot_mods_map + -- There's no boot files to find in external imports + | otherwise = emptyUFM + + sig_mods = + if is_sig + then moduleName imp_mod : dep_sig_mods deps + else dep_sig_mods deps - (dependent_mods, dependent_pkgs, pkg_trust_req) - | isHomeUnit home_unit pkg = - -- Imported module is from the home package - -- Take its dependent modules and add imp_mod itself - -- Take its dependent packages unchanged - -- - -- NB: (dep_mods deps) might include a hi-boot file - -- for the module being compiled, CM. Do *not* filter - -- this out (as we used to), because when we've - -- finished dealing with the direct imports we want to - -- know if any of them depended on CM.hi-boot, in - -- which case we should do the hi-boot consistency - -- check. See GHC.Iface.Load.loadHiBootInterface - ( GWIB { gwib_mod = moduleName imp_mod, gwib_isBoot = want_boot } : dep_mods deps - , dep_pkgs deps - , ptrust - ) - - | otherwise = - -- Imported module is from another package - -- Dump the dependent modules - -- Add the package imp_mod comes from to the dependent packages - assertPpr (not (ipkg `elem` (map fst $ dep_pkgs deps))) - (ppr ipkg <+> ppr (dep_pkgs deps)) - ([], (ipkg, False) : dep_pkgs deps, False) in ImportAvails { imp_mods = unitModuleEnv (mi_module iface) [imported_by], imp_orphs = orphans, imp_finsts = finsts, - imp_dep_mods = mkModDeps dependent_mods, - imp_dep_pkgs = S.fromList . map fst $ dependent_pkgs, + imp_sig_mods = sig_mods, + imp_direct_dep_mods = direct_mods, + imp_dep_direct_pkgs = dependent_pkgs, + imp_boot_mods = boot_mods, + -- Add in the imported modules trusted package -- requirements. ONLY do this though if we import the -- module as a safe import. -- See Note [Tracking Trust Transitively] -- and Note [Trust Transitive Property] - imp_trust_pkgs = if mod_safe' - then S.fromList . map fst $ filter snd dependent_pkgs - else S.empty, + imp_trust_pkgs = trusted_pkgs, -- Do we require our own pkg to be trusted? -- See Note [Trust Own Package] imp_trust_own_pkg = pkg_trust_req diff --git a/compiler/GHC/Tc/Module.hs b/compiler/GHC/Tc/Module.hs index 5e3f0b3501..b04ab96e43 100644 --- a/compiler/GHC/Tc/Module.hs +++ b/compiler/GHC/Tc/Module.hs @@ -366,7 +366,7 @@ tcRnImports hsc_env import_decls ; this_mod <- getModule ; let { dep_mods :: ModuleNameEnv ModuleNameWithIsBoot - ; dep_mods = imp_dep_mods imports + ; dep_mods = imp_direct_dep_mods imports -- We want instance declarations from all home-package -- modules below this one, including boot modules, except @@ -375,17 +375,15 @@ tcRnImports hsc_env import_decls -- filtering also ensures that we don't see instances from -- modules batch (@--make@) compiled before this one, but -- which are not below this one. - ; want_instances :: ModuleName -> Bool - ; want_instances mod = mod `elemUFM` dep_mods - && mod /= moduleName this_mod - ; (home_insts, home_fam_insts) = hptInstances hsc_env - want_instances + ; (home_insts, home_fam_insts) = hptInstancesBelow hsc_env (moduleName this_mod) (eltsUFM dep_mods) } ; -- Record boot-file info in the EPS, so that it's -- visible to loadHiBootInterface in tcRnSrcDecls, -- and any other incrementally-performed imports - ; updateEps_ (\eps -> eps { eps_is_boot = dep_mods }) ; + ; when (isOneShot (ghcMode (hsc_dflags hsc_env))) $ do { + updateEps_ $ \eps -> eps { eps_is_boot = imp_boot_mods imports } + } -- Update the gbl env ; updGblEnv ( \ gbl -> @@ -399,7 +397,7 @@ tcRnImports hsc_env import_decls tcg_hpc = hpc_info }) $ do { - ; traceRn "rn1" (ppr (imp_dep_mods imports)) + ; traceRn "rn1" (ppr (imp_direct_dep_mods imports)) -- Fail if there are any errors so far -- The error printing (if needed) takes advantage -- of the tcg_env we have now set @@ -2070,7 +2068,7 @@ runTcInteractive hsc_env thing_inside ; setEnvs (gbl_env', lcl_env') thing_inside } where - (home_insts, home_fam_insts) = hptInstances hsc_env (\_ -> True) + (home_insts, home_fam_insts) = hptAllInstances hsc_env icxt = hsc_IC hsc_env (ic_insts, ic_finsts) = ic_instances icxt @@ -2952,9 +2950,9 @@ pprTcGblEnv (TcGblEnv { tcg_type_env = type_env, , ppr_fam_insts fam_insts , ppr_rules rules , text "Dependent modules:" <+> - pprUFM (imp_dep_mods imports) (ppr . sort) + pprUFM (imp_direct_dep_mods imports) (ppr . sort) , text "Dependent packages:" <+> - ppr (S.toList $ imp_dep_pkgs imports)] + ppr (S.toList $ imp_dep_direct_pkgs imports)] -- The use of sort is just to reduce unnecessary -- wobbling in testsuite output diff --git a/compiler/GHC/Tc/Types.hs b/compiler/GHC/Tc/Types.hs index 0145ee9b43..2d80039234 100644 --- a/compiler/GHC/Tc/Types.hs +++ b/compiler/GHC/Tc/Types.hs @@ -1346,31 +1346,11 @@ data ImportAvails -- different packages. (currently not the case, but might be in the -- future). - imp_dep_mods :: ModuleNameEnv ModuleNameWithIsBoot, - -- ^ Home-package modules needed by the module being compiled - -- - -- It doesn't matter whether any of these dependencies - -- are actually /used/ when compiling the module; they - -- are listed if they are below it at all. For - -- example, suppose M imports A which imports X. Then - -- compiling M might not need to consult X.hi, but X - -- is still listed in M's dependencies. - - imp_dep_pkgs :: Set UnitId, - -- ^ Packages needed by the module being compiled, whether directly, - -- or via other modules in this package, or via modules imported - -- from other packages. + imp_direct_dep_mods :: ModuleNameEnv ModuleNameWithIsBoot, + -- ^ Home-package modules directly imported by the module being compiled. - imp_trust_pkgs :: Set UnitId, - -- ^ This is strictly a subset of imp_dep_pkgs and records the - -- packages the current module needs to trust for Safe Haskell - -- compilation to succeed. A package is required to be trusted if - -- we are dependent on a trustworthy module in that package. - -- While perhaps making imp_dep_pkgs a tuple of (UnitId, Bool) - -- where True for the bool indicates the package is required to be - -- trusted is the more logical design, doing so complicates a lot - -- of code not concerned with Safe Haskell. - -- See Note [Tracking Trust Transitively] in "GHC.Rename.Names" + imp_dep_direct_pkgs :: Set UnitId, + -- ^ Packages directly needed by the module being compiled imp_trust_own_pkg :: Bool, -- ^ Do we require that our own package is trusted? @@ -1378,6 +1358,23 @@ data ImportAvails -- a Trustworthy module that resides in the same package as it. -- See Note [Trust Own Package] in "GHC.Rename.Names" + -- Transitive information below here + + imp_trust_pkgs :: Set UnitId, + -- ^ This records the + -- packages the current module needs to trust for Safe Haskell + -- compilation to succeed. A package is required to be trusted if + -- we are dependent on a trustworthy module in that package. + -- See Note [Tracking Trust Transitively] in "GHC.Rename.Names" + + imp_boot_mods :: ModuleNameEnv ModuleNameWithIsBoot, + -- ^ Domain is all modules which have hs-boot files, and whether + -- we should import the boot version of interface file. Only used + -- in one-shot mode to populate eps_is_boot. + + imp_sig_mods :: [ModuleName], + -- ^ Signature modules below this one + imp_orphs :: [Module], -- ^ Orphan modules below us in the import tree (and maybe including -- us for imported modules) @@ -1393,6 +1390,20 @@ mkModDeps deps = foldl' add emptyUFM deps where add env elt = addToUFM env (gwib_mod elt) elt +plusModDeps :: ModuleNameEnv ModuleNameWithIsBoot + -> ModuleNameEnv ModuleNameWithIsBoot + -> ModuleNameEnv ModuleNameWithIsBoot +plusModDeps = plusUFM_C plus_mod_dep + where + plus_mod_dep r1@(GWIB { gwib_mod = m1, gwib_isBoot = boot1 }) + r2@(GWIB {gwib_mod = m2, gwib_isBoot = boot2}) + | assertPpr (m1 == m2) ((ppr m1 <+> ppr m2) $$ (ppr (boot1 == IsBoot) <+> ppr (boot2 == IsBoot))) + boot1 == IsBoot = r2 + | otherwise = r1 + -- If either side can "see" a non-hi-boot interface, use that + -- Reusing existing tuples saves 10% of allocations on test + -- perf/compiler/MultiLayerModules + modDepsElts :: ModuleNameEnv ModuleNameWithIsBoot -> [ModuleNameWithIsBoot] @@ -1402,10 +1413,12 @@ modDepsElts = sort . nonDetEltsUFM emptyImportAvails :: ImportAvails emptyImportAvails = ImportAvails { imp_mods = emptyModuleEnv, - imp_dep_mods = emptyUFM, - imp_dep_pkgs = S.empty, + imp_direct_dep_mods = emptyUFM, + imp_dep_direct_pkgs = S.empty, + imp_sig_mods = [], imp_trust_pkgs = S.empty, imp_trust_own_pkg = False, + imp_boot_mods = emptyUFM, imp_orphs = [], imp_finsts = [] } @@ -1417,29 +1430,28 @@ emptyImportAvails = ImportAvails { imp_mods = emptyModuleEnv, plusImportAvails :: ImportAvails -> ImportAvails -> ImportAvails plusImportAvails (ImportAvails { imp_mods = mods1, - imp_dep_mods = dmods1, imp_dep_pkgs = dpkgs1, + imp_direct_dep_mods = ddmods1, + imp_dep_direct_pkgs = ddpkgs1, + imp_boot_mods = srs1, + imp_sig_mods = sig_mods1, imp_trust_pkgs = tpkgs1, imp_trust_own_pkg = tself1, imp_orphs = orphs1, imp_finsts = finsts1 }) (ImportAvails { imp_mods = mods2, - imp_dep_mods = dmods2, imp_dep_pkgs = dpkgs2, + imp_direct_dep_mods = ddmods2, + imp_dep_direct_pkgs = ddpkgs2, + imp_boot_mods = srcs2, + imp_sig_mods = sig_mods2, imp_trust_pkgs = tpkgs2, imp_trust_own_pkg = tself2, imp_orphs = orphs2, imp_finsts = finsts2 }) = ImportAvails { imp_mods = plusModuleEnv_C (++) mods1 mods2, - imp_dep_mods = plusUFM_C plus_mod_dep dmods1 dmods2, - imp_dep_pkgs = dpkgs1 `S.union` dpkgs2, + imp_direct_dep_mods = ddmods1 `plusModDeps` ddmods2, + imp_dep_direct_pkgs = ddpkgs1 `S.union` ddpkgs2, imp_trust_pkgs = tpkgs1 `S.union` tpkgs2, imp_trust_own_pkg = tself1 || tself2, + imp_boot_mods = srs1 `plusModDeps` srcs2, + imp_sig_mods = sig_mods1 `unionLists` sig_mods2, imp_orphs = orphs1 `unionLists` orphs2, imp_finsts = finsts1 `unionLists` finsts2 } - where - plus_mod_dep r1@(GWIB { gwib_mod = m1, gwib_isBoot = boot1 }) - r2@(GWIB {gwib_mod = m2, gwib_isBoot = boot2}) - | assertPpr (m1 == m2) ((ppr m1 <+> ppr m2) $$ (ppr (boot1 == IsBoot) <+> ppr (boot2 == IsBoot))) $ - boot1 == IsBoot = r2 - | otherwise = r1 - -- If either side can "see" a non-hi-boot interface, use that - -- Reusing existing tuples saves 10% of allocations on test - -- perf/compiler/MultiLayerModules {- ************************************************************************ diff --git a/compiler/GHC/Types/Unique/FM.hs b/compiler/GHC/Types/Unique/FM.hs index 102025bda2..35a8a2b3fc 100644 --- a/compiler/GHC/Types/Unique/FM.hs +++ b/compiler/GHC/Types/Unique/FM.hs @@ -54,6 +54,7 @@ module GHC.Types.Unique.FM ( plusUFM_C, plusUFM_CD, plusUFM_CD2, + mergeUFM, plusMaybeUFM_C, plusUFMList, minusUFM, @@ -88,6 +89,7 @@ import qualified Data.IntSet as S import Data.Data import qualified Data.Semigroup as Semi import Data.Functor.Classes (Eq1 (..)) +import Data.Coerce -- | A finite map from @uniques@ of one type to -- elements in another type. @@ -273,6 +275,20 @@ plusUFM_CD2 f (UFM xm) (UFM ym) (MS.map (\y -> Nothing `f` Just y)) xm ym +mergeUFM + :: (elta -> eltb -> Maybe eltc) + -> (UniqFM key elta -> UniqFM key eltc) -- map X + -> (UniqFM key eltb -> UniqFM key eltc) -- map Y + -> UniqFM key elta + -> UniqFM key eltb + -> UniqFM key eltc +mergeUFM f g h (UFM xm) (UFM ym) + = UFM $ MS.mergeWithKey + (\_ x y -> (x `f` y)) + (coerce g) + (coerce h) + xm ym + plusMaybeUFM_C :: (elt -> elt -> Maybe elt) -> UniqFM key elt -> UniqFM key elt -> UniqFM key elt plusMaybeUFM_C f (UFM xm) (UFM ym) diff --git a/compiler/GHC/Unit/Module/Deps.hs b/compiler/GHC/Unit/Module/Deps.hs index 5bdd23239b..2de3fe710d 100644 --- a/compiler/GHC/Unit/Module/Deps.hs +++ b/compiler/GHC/Unit/Module/Deps.hs @@ -17,25 +17,41 @@ import GHC.Utils.Fingerprint import GHC.Utils.Binary -- | Dependency information about ALL modules and packages below this one --- in the import hierarchy. +-- in the import hierarchy. This is the serialisable version of `ImportAvails`. -- -- Invariant: the dependencies of a module @M@ never includes @M@. -- -- Invariant: none of the lists contain duplicates. +-- +-- See Note [Transitive Information in Dependencies] data Dependencies = Deps - { dep_mods :: [ModuleNameWithIsBoot] - -- ^ All home-package modules transitively below this one - -- I.e. modules that this one imports, or that are in the - -- dep_mods of those directly-imported modules - - , dep_pkgs :: [(UnitId, Bool)] - -- ^ All packages transitively below this module - -- I.e. packages to which this module's direct imports belong, - -- or that are in the dep_pkgs of those modules - -- The bool indicates if the package is required to be - -- trusted when the module is imported as a safe import + { dep_direct_mods :: [ModuleNameWithIsBoot] + -- ^ All home-package modules which are directly imported by this one. + + , dep_direct_pkgs :: [UnitId] + -- ^ All packages directly imported by this module + -- I.e. packages to which this module's direct imports belong. + -- + , dep_plgins :: [ModuleName] + -- ^ All the plugins used while compiling this module. + + + -- Transitive information below here + , dep_sig_mods :: ![ModuleName] + -- ^ Transitive closure of hsig files in the home package + + + , dep_trusted_pkgs :: [UnitId] + -- Packages which we are required to trust + -- when the module is imported as a safe import -- (Safe Haskell). See Note [Tracking Trust Transitively] in GHC.Rename.Names + , dep_boot_mods :: [ModuleNameWithIsBoot] + -- ^ All modules which have boot files below this one, and whether we + -- should use the boot file or not. + -- This information is only used to populate the eps_is_boot field. + -- See Note [Structure of dep_boot_mods] + , dep_orphs :: [Module] -- ^ Transitive closure of orphan modules (whether -- home or external pkg). @@ -53,30 +69,39 @@ data Dependencies = Deps -- does NOT include us, unlike 'imp_finsts'. See Note -- [The type family instance consistency story]. - , dep_plgins :: [ModuleName] - -- ^ All the plugins used while compiling this module. } deriving( Eq ) -- Equality used only for old/new comparison in GHC.Iface.Recomp.addFingerprints -- See 'GHC.Tc.Utils.ImportAvails' for details on dependencies. instance Binary Dependencies where - put_ bh deps = do put_ bh (dep_mods deps) - put_ bh (dep_pkgs deps) + put_ bh deps = do put_ bh (dep_direct_mods deps) + put_ bh (dep_direct_pkgs deps) + put_ bh (dep_trusted_pkgs deps) + put_ bh (dep_sig_mods deps) + put_ bh (dep_boot_mods deps) put_ bh (dep_orphs deps) put_ bh (dep_finsts deps) put_ bh (dep_plgins deps) - get bh = do ms <- get bh - ps <- get bh + get bh = do dms <- get bh + dps <- get bh + tps <- get bh + hsigms <- get bh + sms <- get bh os <- get bh fis <- get bh pl <- get bh - return (Deps { dep_mods = ms, dep_pkgs = ps, dep_orphs = os, + return (Deps { dep_direct_mods = dms + , dep_direct_pkgs = dps + , dep_sig_mods = hsigms + , dep_boot_mods = sms + , dep_trusted_pkgs = tps + , dep_orphs = os, dep_finsts = fis, dep_plgins = pl }) noDependencies :: Dependencies -noDependencies = Deps [] [] [] [] [] +noDependencies = Deps [] [] [] [] [] [] [] [] -- | Records modules for which changes may force recompilation of this module -- See wiki: https://gitlab.haskell.org/ghc/ghc/wikis/commentary/compiler/recompilation-avoidance @@ -193,3 +218,75 @@ instance Binary Usage where hash <- get bh return UsageMergedRequirement { usg_mod = mod, usg_mod_hash = hash } i -> error ("Binary.get(Usage): " ++ show i) + + +{- +Note [Transitive Information in Dependencies] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is important to be careful what information we put in 'Dependencies' because +ultimately it ends up serialised in an interface file. Interface files must always +be kept up-to-date with the state of the world, so if `Dependencies` needs to be updated +then the module had to be recompiled just to update `Dependencies`. + +Before #16885, the dependencies used to contain the transitive closure of all +home modules. Therefore, if you added an import somewhere low down in the home package +it would recompile nearly every module in your project, just to update this information. + +Now, we are a bit more careful about what we store and +explicitly store transitive information only if it is really needed. + +# Direct Information + +* dep_direct_mods - Directly imported home package modules +* dep_direct_pkgs - Directly imported packages +* dep_plgins - Directly used plugins + +# Transitive Information + +Some features of the compiler require transitive information about what is currently +being compiled, so that is explicitly stored separately in the form they need. + +* dep_trusted_pkgs - Only used for the -fpackage-trust feature +* dep_boot_mods - Only used to populate eps_is_boot in -c mode +* dep_orphs - Modules with orphan instances +* dep_finsts - Modules with type family instances + +Important note: If you add some transitive information to the interface file then +you need to make sure recompilation is triggered when it could be out of date. +The correct way to do this is to include the transitive information in the export +hash of the module. The export hash is computed in `GHC.Iface.Recomp.addFingerprints`. +-} + +{- +Note [Structure of mod_boot_deps] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In `-c` mode we always need to know whether to load the normal or boot version of +an interface file, and this can't be determined from just looking at the direct imports. + +Consider modules with dependencies: + +``` +A -(S)-> B +A -> C -> B -(S)-> B +``` + +Say when compiling module `A` that we need to load the interface for `B`, do we load +`B.hi` or `B.hi-boot`? Well, `A` does directly {-# SOURCE #-} import B, so you might think +that we would load the `B.hi-boot` file, however this is wrong because `C` imports +`B` normally. Therefore in the interface file for `C` we still need to record that +there is a hs-boot file for `B` below it but that we now want `B.hi` rather than +`B.hi-boot`. When `C` is imported, the fact that it needs `B.hi` clobbers the `{- SOURCE -}` +import for `B`. + +Therefore in mod_boot_deps we store the names of any modules which have hs-boot files, +and whether we want to import the .hi or .hi-boot version of the interface file. + +If you get this wrong, then GHC fails to compile, so there is a test but you might +not make it that far if you get this wrong! + +Question: does this happen even across packages? +No: if I need to load the interface for module X from package P I always look for p:X.hi. + +-} diff --git a/compiler/GHC/Unit/Module/ModIface.hs b/compiler/GHC/Unit/Module/ModIface.hs index b7e0235730..18101e309b 100644 --- a/compiler/GHC/Unit/Module/ModIface.hs +++ b/compiler/GHC/Unit/Module/ModIface.hs @@ -282,7 +282,7 @@ mi_free_holes iface = -> renameFreeHoles (mkUniqDSet cands) (instUnitInsts (moduleUnit indef)) _ -> emptyUniqDSet where - cands = map gwib_mod $ dep_mods $ mi_deps iface + cands = dep_sig_mods $ mi_deps iface -- | Given a set of free holes, and a unit identifier, rename -- the free holes according to the instantiation of the unit -- cgit v1.2.1