%
+% (c) The University of Glasgow 2006
% (c) The GRASP/AQUA Project, Glasgow University, 1993-1998
%
\begin{code}
-module MkIface (
- mkUsageInfo, -- Construct the usage info for a module
+{-# OPTIONS -w #-}
+-- The above warning supression flag is a temporary kludge.
+-- While working on this module you are encouraged to remove it and fix
+-- any warnings in the module. See
+-- http://hackage.haskell.org/trac/ghc/wiki/Commentary/CodingStyle#Warnings
+-- for details
+module MkIface (
+ mkUsedNames,
+ mkDependencies,
mkIface, -- Build a ModIface from a ModGuts,
-- including computing version information
+ mkIfaceTc,
+
writeIfaceFile, -- Write the interface file
checkOldIface, -- See if recompilation is required, by
compiled:
import B <n> ;
to record the fact that A does import B indirectly. This is used to decide
-to look to look for B.hi rather than B.hi-boot when compiling a module that
+to look for B.hi rather than B.hi-boot when compiling a module that
imports A. This line says that A imports B, but uses nothing in it.
So we'll get an early bale-out when compiling A if B's version changes.
haul in all the unfoldings for B, in case the module that imports A *is*
compiled with -O. I think this is the case.]
+SimonM [30/11/2007]: I believe the above is all out of date; the
+current implementation doesn't do it this way. Instead, when any of
+the dependencies of a declaration changes, the version of the
+declaration itself changes.
\begin{code}
#include "HsVersions.h"
-import IfaceSyn -- All of it
+import IfaceSyn
import IfaceType
-import LoadIface ( readIface, loadInterface, pprModIface )
-import Id ( Id, idName, idType, idInfo, idArity, isDataConWorkId_maybe, isFCallId_maybe )
-import IdInfo ( IdInfo, CafInfo(..), WorkerInfo(..),
- arityInfo, cafInfo, newStrictnessInfo,
- workerInfo, unfoldingInfo, inlinePragInfo )
-import NewDemand ( isTopSig )
+import LoadIface
+import Id
+import IdInfo
+import NewDemand
import CoreSyn
-import Class ( classExtraBigSig, classTyCon )
-import TyCon ( TyCon, AlgTyConRhs(..), SynTyConRhs(..),
- isRecursiveTyCon, isForeignTyCon,
- isSynTyCon, isAlgTyCon, isPrimTyCon, isFunTyCon,
- isTupleTyCon, tupleTyConBoxity, tyConStupidTheta,
- tyConHasGenerics, synTyConRhs, isGadtSyntaxTyCon,
- tyConArity, tyConTyVars, algTyConRhs, tyConExtName,
- tyConFamInst_maybe )
-import DataCon ( dataConName, dataConFieldLabels, dataConStrictMarks,
- dataConTyCon, dataConIsInfix, dataConUnivTyVars,
- dataConExTyVars, dataConEqSpec, dataConTheta,
- dataConOrigArgTys )
-import Type ( TyThing(..), splitForAllTys, funResultTy )
-import TcType ( deNoteType )
-import TysPrim ( alphaTyVars )
-import InstEnv ( Instance(..) )
-import FamInstEnv ( FamInst(..) )
+import CoreFVs
+import Class
+import TyCon
+import DataCon
+import Type
+import TcType
+import InstEnv
+import FamInstEnv
import TcRnMonad
-import HscTypes ( ModIface(..), ModDetails(..),
- ModGuts(..), HscEnv(..), hscEPS, Dependencies(..),
- FixItem(..),
- ModSummary(..), msHiFilePath,
- mkIfaceDepCache, mkIfaceFixCache, mkIfaceVerCache,
- typeEnvElts,
- GenAvailInfo(..), availName, AvailInfo,
- ExternalPackageState(..),
- Usage(..), IsBootInterface,
- Deprecs(..), IfaceDeprecs, Deprecations,
- lookupIfaceByModule, isImplicitTyThing
- )
-
-
-import DynFlags ( GhcMode(..), DynFlags(..), DynFlag(..), dopt )
-import Name ( Name, nameModule, nameModule_maybe, nameOccName,
- isExternalName, isInternalName, isWiredInName,
- NamedThing(..) )
+import HscTypes
+import Finder
+import DynFlags
+import VarEnv
+import Var
+import Name
import NameEnv
import NameSet
-import OccName ( OccName, OccEnv, mkOccEnv, lookupOccEnv, emptyOccEnv,
- extendOccEnv_C,
- OccSet, emptyOccSet, elemOccSet, occSetElts,
- extendOccSet, extendOccSetList, mkOccSet,
- isEmptyOccSet, intersectOccSet, intersectsOccSet,
- unionOccSets, unitOccSet,
- occNameFS, isTcOcc )
+import OccName
import Module
-import BinIface ( readBinIface, writeBinIface, v_IgnoreHiWay )
-import Unique ( Unique, Uniquable(..) )
-import ErrUtils ( dumpIfSet_dyn, showPass )
-import Digraph ( stronglyConnComp, SCC(..) )
-import SrcLoc ( SrcSpan )
-import PackageConfig ( PackageId )
+import BinIface
+import Unique
+import ErrUtils
+import Digraph
+import SrcLoc
+import PackageConfig hiding ( Version )
import Outputable
import BasicTypes hiding ( SuccessFlag(..) )
-import UniqFM
+import LazyUniqFM
import Util hiding ( eqListBy )
import FiniteMap
import FastString
+import Maybes
+import ListSetOps
-import Data.List ( partition )
-import DATA_IOREF ( writeIORef )
-import Monad ( when )
-import List ( insert )
-import Maybes ( orElse, mapCatMaybes, isNothing, isJust,
- expectJust, catMaybes, MaybeErr(..) )
+import Control.Monad
+import Data.List
+import Data.IORef
+import System.FilePath
\end{code}
\begin{code}
mkIface :: HscEnv
-> Maybe ModIface -- The old interface, if we have it
- -> ModGuts -- Usages, deprecations, etc
-> ModDetails -- The trimmed, tidied interface
+ -> ModGuts -- Usages, deprecations, etc
-> IO (ModIface, -- The new one, complete with decls and versions
Bool) -- True <=> there was an old Iface, and the new one
-- is identical, so no need to write it
-mkIface hsc_env maybe_old_iface
- (ModGuts{ mg_module = this_mod,
+mkIface hsc_env maybe_old_iface mod_details
+ ModGuts{ mg_module = this_mod,
mg_boot = is_boot,
- mg_usages = usages,
+ mg_used_names = used_names,
mg_deps = deps,
+ mg_dir_imps = dir_imp_mods,
mg_rdr_env = rdr_env,
mg_fix_env = fix_env,
- mg_deprecs = src_deprecs })
- (ModDetails{ md_insts = insts,
+ mg_deprecs = deprecs,
+ mg_hpc_info = hpc_info }
+ = mkIface_ hsc_env maybe_old_iface
+ this_mod is_boot used_names deps rdr_env
+ fix_env deprecs hpc_info dir_imp_mods mod_details
+
+-- | make an interface from the results of typechecking only. Useful
+-- for non-optimising compilation, or where we aren't generating any
+-- object code at all ('HscNothing').
+mkIfaceTc :: HscEnv
+ -> Maybe ModIface -- The old interface, if we have it
+ -> ModDetails -- gotten from mkBootModDetails, probably
+ -> TcGblEnv -- Usages, deprecations, etc
+ -> IO (ModIface,
+ Bool)
+mkIfaceTc hsc_env maybe_old_iface mod_details
+ tc_result@TcGblEnv{ tcg_mod = this_mod,
+ tcg_src = hsc_src,
+ tcg_imports = imports,
+ tcg_rdr_env = rdr_env,
+ tcg_fix_env = fix_env,
+ tcg_deprecs = deprecs,
+ tcg_hpc = other_hpc_info
+ }
+ = do
+ used_names <- mkUsedNames tc_result
+ deps <- mkDependencies tc_result
+ let hpc_info = emptyHpcInfo other_hpc_info
+ mkIface_ hsc_env maybe_old_iface
+ this_mod (isHsBoot hsc_src) used_names deps rdr_env
+ fix_env deprecs hpc_info (imp_mods imports) mod_details
+
+
+mkUsedNames :: TcGblEnv -> IO NameSet
+mkUsedNames
+ TcGblEnv{ tcg_inst_uses = dfun_uses_var,
+ tcg_dus = dus
+ }
+ = do
+ dfun_uses <- readIORef dfun_uses_var -- What dfuns are used
+ return (allUses dus `unionNameSets` dfun_uses)
+
+mkDependencies :: TcGblEnv -> IO Dependencies
+mkDependencies
+ TcGblEnv{ tcg_mod = mod,
+ tcg_imports = imports,
+ tcg_th_used = th_var
+ }
+ = do
+ th_used <- readIORef th_var -- Whether TH is used
+ let
+ dep_mods = eltsUFM (delFromUFM (imp_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
+ -- loadHiBootInterface can see if M's direct imports depend
+ -- on M.hi-boot, and hence that we should do the hi-boot consistency
+ -- check.)
+
+ dir_imp_mods = imp_mods imports
+
+ -- Modules don't compare lexicographically usually,
+ -- but we want them to do so here.
+ le_mod :: Module -> Module -> Bool
+ le_mod m1 m2 = moduleNameFS (moduleName m1)
+ <= moduleNameFS (moduleName m2)
+
+ le_dep_mod :: (ModuleName, IsBootInterface)
+ -> (ModuleName, IsBootInterface) -> Bool
+ le_dep_mod (m1,_) (m2,_) = moduleNameFS m1 <= moduleNameFS m2
+
+
+ pkgs | th_used = insertList thPackageId (imp_dep_pkgs imports)
+ | otherwise = imp_dep_pkgs imports
+
+ return Deps { dep_mods = sortLe le_dep_mod dep_mods,
+ dep_pkgs = sortLe (<=) pkgs,
+ dep_orphs = sortLe le_mod (imp_orphs imports),
+ dep_finsts = sortLe le_mod (imp_finsts imports) }
+ -- sort to get into canonical order
+
+
+mkIface_ hsc_env maybe_old_iface
+ this_mod is_boot used_names deps rdr_env fix_env src_deprecs hpc_info
+ dir_imp_mods
+ ModDetails{ md_insts = insts,
md_fam_insts = fam_insts,
md_rules = rules,
+ md_vect_info = vect_info,
md_types = type_env,
- md_exports = exports })
-
+ md_exports = exports }
-- NB: notice that mkIface does not look at the bindings
-- only at the TypeEnv. The previous Tidy phase has
-- put exactly the info into the TypeEnv that we want
-- to expose in the interface
- = do { eps <- hscEPS hsc_env
+ = do {eps <- hscEPS hsc_env
+
+ ; usages <- mkUsageInfo hsc_env dir_imp_mods (dep_mods deps) used_names
+
; let { entities = typeEnvElts type_env ;
decls = [ tyThingToIfaceDecl entity
| entity <- entities,
- not (isImplicitTyThing entity
- || isWiredInName (getName entity)) ]
- -- Don't put implicit Ids and class tycons in
- -- the interface file, Nor wired-in things; the
- -- compiler knows about them anyhow
-
- ; fixities = [(occ,fix) | FixItem occ fix _ <- nameEnvElts fix_env]
- ; deprecs = mkIfaceDeprec src_deprecs
- ; iface_rules = map coreRuleToIfaceRule rules
+ let name = getName entity,
+ not (isImplicitTyThing entity),
+ -- No implicit Ids and class tycons in the interface file
+ not (isWiredInName name),
+ -- Nor wired-in things; the compiler knows about them anyhow
+ nameIsLocalOrFrom this_mod name ]
+ -- Sigh: see Note [Root-main Id] in TcRnDriver
+
+ ; fixities = [(occ,fix) | FixItem occ fix <- nameEnvElts fix_env]
+ ; deprecs = src_deprecs
+ ; iface_rules = map (coreRuleToIfaceRule this_mod) rules
; iface_insts = map instanceToIfaceInst insts
; iface_fam_insts = map famInstToIfaceFamInst fam_insts
+ ; iface_vect_info = flattenVectInfo vect_info
; intermediate_iface = ModIface {
mi_module = this_mod,
mi_deps = deps,
mi_usages = usages,
mi_exports = mkIfaceExports exports,
+
+ -- Sort these lexicographically, so that
+ -- the result is stable across compilations
mi_insts = sortLe le_inst iface_insts,
mi_fam_insts= sortLe le_fam_inst iface_fam_insts,
mi_rules = sortLe le_rule iface_rules,
+
+ mi_vect_info = iface_vect_info,
+
mi_fixities = fixities,
mi_deprecs = deprecs,
mi_globals = Just rdr_env,
mi_rule_vers = initialVersion,
mi_orphan = False, -- Always set by addVersionInfo, but
-- it's a strict field, so we can't omit it.
+ mi_finsts = False, -- Ditto
mi_decls = deliberatelyOmitted "decls",
mi_ver_fn = deliberatelyOmitted "ver_fn",
+ mi_hpc = isHpcUsed hpc_info,
-- And build the cached values
mi_dep_fn = mkIfaceDepCache deprecs,
-- Add version information
; ext_ver_fn = mkParentVerFun hsc_env eps
; (new_iface, no_change_at_all, pp_diffs, pp_orphs)
- = _scc_ "versioninfo"
+ = {-# SCC "versioninfo" #-}
addVersionInfo ext_ver_fn maybe_old_iface
intermediate_iface decls
}
; dumpIfSet_dyn dflags Opt_D_dump_hi "FINAL INTERFACE"
(pprModIface new_iface)
- ; return (new_iface, no_change_at_all) }
+ -- bug #1617: on reload we weren't updating the PrintUnqualified
+ -- correctly. This stems from the fact that the interface had
+ -- not changed, so addVersionInfo returns the old ModIface
+ -- with the old GlobalRdrEnv (mi_globals).
+ ; let final_iface = new_iface{ mi_globals = Just rdr_env }
+
+ ; return (final_iface, no_change_at_all) }
where
- r1 `le_rule` r2 = ifRuleName r1 <= ifRuleName r2
- i1 `le_inst` i2 = ifDFun i1 <= ifDFun i2
- i1 `le_fam_inst` i2 = ifFamInstTyConOcc i1 <= ifFamInstTyConOcc i2
+ r1 `le_rule` r2 = ifRuleName r1 <= ifRuleName r2
+ i1 `le_inst` i2 = ifDFun i1 `le_occ` ifDFun i2
+ i1 `le_fam_inst` i2 = ifFamInstTcName i1 `le_occ` ifFamInstTcName i2
+
+ le_occ :: Name -> Name -> Bool
+ -- Compare lexicographically by OccName, *not* by unique, because
+ -- the latter is not stable across compilations
+ le_occ n1 n2 = nameOccName n1 <= nameOccName n2
dflags = hsc_dflags hsc_env
deliberatelyOmitted x = panic ("Deliberately omitted: " ++ x)
- ifFamInstTyConOcc = nameOccName . ifaceTyConName . ifFamInstTyCon
+ ifFamInstTcName = ifaceTyConName . ifFamInstTyCon
+
+ flattenVectInfo (VectInfo { vectInfoVar = vVar
+ , vectInfoTyCon = vTyCon
+ }) =
+ IfaceVectInfo {
+ ifaceVectInfoVar = [ Var.varName v
+ | (v, _) <- varEnvElts vVar],
+ ifaceVectInfoTyCon = [ tyConName t
+ | (t, t_v) <- nameEnvElts vTyCon
+ , t /= t_v],
+ ifaceVectInfoTyConReuse = [ tyConName t
+ | (t, t_v) <- nameEnvElts vTyCon
+ , t == t_v]
+ }
-
-----------------------------
writeIfaceFile :: DynFlags -> ModLocation -> ModIface -> IO ()
writeIfaceFile dflags location new_iface
- = do createDirectoryHierarchy (directoryOf hi_file_path)
+ = do createDirectoryHierarchy (takeDirectory hi_file_path)
writeBinIface dflags hi_file_path new_iface
where hi_file_path = ml_hi_file location
addVersionInfo ver_fn Nothing new_iface new_decls
-- No old interface, so definitely write a new one!
- = (new_iface { mi_orphan = anyNothing ifInstOrph (mi_insts new_iface)
- || anyNothing ifRuleOrph (mi_rules new_iface),
- mi_decls = [(initialVersion, decl) | decl <- new_decls],
- mi_ver_fn = mkIfaceVerCache (zip (repeat initialVersion) new_decls)},
+ = (new_iface { mi_orphan = not (null orph_insts && null orph_rules)
+ , mi_finsts = not . null $ mi_fam_insts new_iface
+ , mi_decls = [(initialVersion, decl) | decl <- new_decls]
+ , mi_ver_fn = mkIfaceVerCache (zip (repeat initialVersion)
+ new_decls)
+ },
False,
ptext SLIT("No old interface file"),
pprOrphans orph_insts orph_rules)
mi_exp_vers = bump_unless no_export_change old_exp_vers,
mi_rule_vers = bump_unless no_rule_change old_rule_vers,
mi_orphan = not (null new_orph_rules && null new_orph_insts),
+ mi_finsts = not . null $ mi_fam_insts new_iface,
mi_decls = decls_w_vers,
mi_ver_fn = mkIfaceVerCache decls_w_vers }
mkOrphMap ifInstOrph (mi_insts old_iface)
(new_non_orph_insts, new_orph_insts) =
mkOrphMap ifInstOrph (mi_insts new_iface)
+ old_fam_insts = mi_fam_insts old_iface
+ new_fam_insts = mi_fam_insts new_iface
same_insts occ = eqMaybeBy (eqListBy eqIfInst)
(lookupOccEnv old_non_orph_insts occ)
(lookupOccEnv new_non_orph_insts occ)
-- Kept sorted
no_decl_change = isEmptyOccSet changed_occs
no_rule_change = not (changedWrtNames changed_occs (eqListBy eqIfRule old_orph_rules new_orph_rules)
- || changedWrtNames changed_occs (eqListBy eqIfInst old_orph_insts new_orph_insts))
+ || changedWrtNames changed_occs (eqListBy eqIfInst old_orph_insts new_orph_insts)
+ || changedWrtNames changed_occs (eqListBy eqIfFamInst old_fam_insts new_fam_insts))
no_deprec_change = mi_deprecs new_iface == mi_deprecs old_iface
-- If the usages havn't changed either, we don't need to write the interface file
no_other_changes = mi_usages new_iface == mi_usages old_iface &&
- mi_deps new_iface == mi_deps old_iface
+ mi_deps new_iface == mi_deps old_iface &&
+ mi_hpc new_iface == mi_hpc old_iface
no_change_at_all = no_output_change && no_other_changes
pp_diffs = vcat [pp_change no_export_change "Export list"
where
occ = ifName new_decl
why = case lookupOccEnv eq_env occ of
- Just (EqBut names) -> sep [ppr occ <> colon, ptext SLIT("Free vars (only) changed:"),
+ Just (EqBut names) -> sep [ppr occ <> colon, ptext SLIT("Free vars (only) changed:") <> ppr names,
nest 2 (braces (fsep (map ppr (occSetElts
(occs `intersectOccSet` changed_occs)))))]
where occs = mkOccSet (map nameOccName (nameSetToList names))
-- return True if an external name has changed
name_changed :: Name -> Bool
name_changed nm
- | Just ents <- lookupUFM usg_modmap (moduleName mod)
- = case lookupUFM ents parent_occ of
- Nothing -> pprPanic "computeChangedOccs" (ppr nm)
- Just v -> v < new_version
+ | isWiredInName nm -- Wired-in things don't get into interface
+ = False -- files and hence don't get into the ver_fn
+ | Just ents <- lookupUFM usg_modmap (moduleName mod),
+ Just v <- lookupUFM ents parent_occ
+ = v < new_version
+ | modulePackageId mod == this_pkg
+ = WARN(True, ptext SLIT("computeChangedOccs") <+> ppr nm) True
+ -- should really be a panic, see #1959. The problem is that the usages doesn't
+ -- contain all the names that might be referred to by unfoldings. So as a
+ -- conservative workaround we just assume these names have changed.
| otherwise = False -- must be in another package
where
mod = nameModule nm
(parent_occ, new_version) = ver_fn nm
+ this_pkg = modulePackageId this_module
+
-- Turn the usages from the old ModIface into a mapping
- usg_modmap = listToUFM [ (usg_mod usg, listToUFM (usg_entities usg))
+ usg_modmap = listToUFM [ (usg_name usg, listToUFM (usg_entities usg))
| usg <- old_usages ]
- get_local_eq_info :: GenIfaceEq NameSet -> GenIfaceEq OccSet
+ get_local_eq_info :: GenIfaceEq Name -> GenIfaceEq OccName
get_local_eq_info Equal = Equal
get_local_eq_info NotEqual = NotEqual
get_local_eq_info (EqBut ns) = foldNameSet f Equal ns
where (occs, iface_eqs) = unzip pairs
add_changes so_far other = so_far
-type OccIfaceEq = GenIfaceEq OccSet
+type OccIfaceEq = GenIfaceEq OccName
changedWrt :: OccSet -> OccIfaceEq -> Bool
changedWrt so_far Equal = False
= (extendOccEnv_C (\ ds _ -> d:ds) non_orphs occ [d], orphs)
| otherwise = (non_orphs, d:orphs)
-anyNothing :: (a -> Maybe b) -> [a] -> Bool
-anyNothing p [] = False
-anyNothing p (x:xs) = isNothing (p x) || anyNothing p xs
-
-----------------------
-mkIfaceDeprec :: Deprecations -> IfaceDeprecs
-mkIfaceDeprec NoDeprecs = NoDeprecs
-mkIfaceDeprec (DeprecAll t) = DeprecAll t
-mkIfaceDeprec (DeprecSome env) = DeprecSome (sortLe (<=) (nameEnvElts env))
-
----------------------
bump_unless :: Bool -> Version -> Version
bump_unless True v = v -- True <=> no change
\begin{code}
mkUsageInfo :: HscEnv
- -> ModuleEnv (Module, Bool, SrcSpan)
+ -> ModuleEnv (Module, [(ModuleName, Bool, SrcSpan)])
-> [(ModuleName, IsBootInterface)]
-> NameSet -> IO [Usage]
mkUsageInfo hsc_env dir_imp_mods dep_mods used_names
-- don't get evaluated for a while and we can end up hanging on to
-- the entire collection of Ifaces.
+mk_usage_info :: PackageIfaceTable
+ -> HscEnv
+ -> ModuleEnv (Module, [(ModuleName, Bool, SrcSpan)])
+ -> [(ModuleName, IsBootInterface)]
+ -> NameSet
+ -> [Usage]
mk_usage_info pit hsc_env dir_imp_mods dep_mods used_names
= mapCatMaybes mkUsage dep_mods
-- ToDo: do we need to sort into canonical order?
add_item occs _ = occ:occs
depend_on_exports mod = case lookupModuleEnv dir_imp_mods mod of
- Just (_,no_imp,_) -> not no_imp
- Nothing -> True
+ Just (_, xs) -> any (\(_, no_imp, _) -> not no_imp) xs
+ Nothing -> True
-- We want to create a Usage for a home module if
-- a) we used something from; has something in used_names
-- b) we imported all of it, even if we used nothing from it
-- (need to recompile if its export list changes: export_vers)
- -- c) is a home-package orphan module (need to recompile if its
- -- instance decls change: rules_vers)
+ -- c) is a home-package orphan or family-instance module (need to
+ -- recompile if its instance decls change: rules_vers)
mkUsage :: (ModuleName, IsBootInterface) -> Maybe Usage
mkUsage (mod_name, _)
| isNothing maybe_iface -- We can't depend on it if we didn't
|| (null used_occs -- load its interface.
&& isNothing export_vers
- && not orphan_mod)
+ && not orphan_mod
+ && not finsts_mod)
= Nothing -- Record no usage info
| otherwise
Just iface = maybe_iface
orphan_mod = mi_orphan iface
+ finsts_mod = mi_finsts iface
version_env = mi_ver_fn iface
mod_vers = mi_mod_vers iface
rules_vers = mi_rule_vers iface
| (mod, avails) <- fmToList groupFM
]
where
+ -- Group by the module where the exported entities are defined
+ -- (which may not be the same for all Names in an Avail)
-- Deliberately use FiniteMap rather than UniqFM so we
-- get a canonical ordering
groupFM :: ModuleEnv (FiniteMap FastString (GenAvailInfo OccName))
groupFM = foldl add emptyModuleEnv exports
- add env avail
- = extendModuleEnv_C add_avail env mod (unitFM avail_fs avail_occ)
+ add_one :: ModuleEnv (FiniteMap FastString (GenAvailInfo OccName))
+ -> Module -> GenAvailInfo OccName
+ -> ModuleEnv (FiniteMap FastString (GenAvailInfo OccName))
+ add_one env mod avail
+ = extendModuleEnv_C plusFM env mod
+ (unitFM (occNameFS (availName avail)) avail)
+
+ -- NB: we should not get T(X) and T(Y) in the export list
+ -- else the plusFM will simply discard one! They
+ -- should have been combined by now.
+ add env (Avail n)
+ = add_one env (nameModule n) (Avail (nameOccName n))
+
+ add env (AvailTC tc ns)
+ = foldl add_for_mod env mods
where
- avail_occ = availToOccs avail
- mod = nameModule (availName avail)
- avail_fs = occNameFS (availName avail_occ)
- add_avail avail_fm _ = addToFM avail_fm avail_fs avail_occ
-
- availToOccs (Avail n) = Avail (nameOccName n)
- availToOccs (AvailTC tc ns) = AvailTC (nameOccName tc) (map nameOccName ns)
+ tc_occ = nameOccName tc
+ mods = nub (map nameModule ns)
+ -- Usually just one, but see Note [Original module]
+
+ add_for_mod env mod
+ = add_one env mod (AvailTC tc_occ (sort names_from_mod))
+ -- NB. sort the children, we need a canonical order
+ where
+ names_from_mod = [nameOccName n | n <- ns, nameModule n == mod]
\end{code}
+Note [Orignal module]
+~~~~~~~~~~~~~~~~~~~~~
+Consider this:
+ module X where { data family T }
+ module Y( T(..) ) where { import X; data instance T Int = MkT Int }
+The exported Avail from Y will look like
+ X.T{X.T, Y.MkT}
+That is, in Y,
+ - only MkT is brought into scope by the data instance;
+ - but the parent (used for grouping and naming in T(..) exports) is X.T
+ - and in this case we export X.T too
+
+In the result of MkIfaceExports, the names are grouped by defining module,
+so we may need to split up a single Avail into multiple ones.
+
%************************************************************************
%* *
check_old_iface hsc_env mod_summary source_unchanged maybe_iface
= do -- CHECK WHETHER THE SOURCE HAS CHANGED
- { ifM (not source_unchanged)
+ { when (not source_unchanged)
(traceHiDiffs (nest 4 (text "Source file changed or recompilation check turned off")))
-- If the source has changed and we're in interactive mode, avoid reading
-- an interface; just return the one we might have been supplied with.
- ; ghc_mode <- getGhcMode
- ; if (ghc_mode == Interactive || ghc_mode == JustTypecheck)
- && not source_unchanged then
+ ; let dflags = hsc_dflags hsc_env
+ ; if not (isObjectTarget (hscTarget dflags)) && not source_unchanged then
return (outOfDate, maybe_iface)
else
case maybe_iface of {
Just old_iface -> do -- Use the one we already have
{ traceIf (text "We already have the old interface for" <+> ppr (ms_mod mod_summary))
- ; recomp <- checkVersions hsc_env source_unchanged old_iface
+ ; recomp <- checkVersions hsc_env source_unchanged mod_summary old_iface
; return (recomp, Just old_iface) }
; Nothing -> do
-- We have got the old iface; check its versions
{ traceIf (text "Read the interface file" <+> text iface_path)
- ; recomp <- checkVersions hsc_env source_unchanged iface
- ; returnM (recomp, Just iface)
+ ; recomp <- checkVersions hsc_env source_unchanged mod_summary iface
+ ; return (recomp, Just iface)
}}}}}
+
\end{code}
@recompileRequired@ is called from the HscMain. It checks whether
checkVersions :: HscEnv
-> Bool -- True <=> source unchanged
+ -> ModSummary
-> ModIface -- Old interface
-> IfG RecompileRequired
-checkVersions hsc_env source_unchanged iface
+checkVersions hsc_env source_unchanged mod_summary iface
| not source_unchanged
- = returnM outOfDate
+ = return outOfDate
| otherwise
= do { traceHiDiffs (text "Considering whether compilation is required for" <+>
ppr (mi_module iface) <> colon)
+ ; recomp <- checkDependencies hsc_env mod_summary iface
+ ; if recomp then return outOfDate else do {
+
-- Source code unchanged and no errors yet... carry on
-- First put the dependent-module info, read from the old interface, into the envt,
-- 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 }
+ updateEps_ $ \eps -> eps { eps_is_boot = mod_deps }
; let this_pkg = thisPackage (hsc_dflags hsc_env)
; checkList [checkModUsage this_pkg u | u <- mi_usages iface]
- }
+ }}
where
-- This is a bit of a hack really
mod_deps :: ModuleNameEnv (ModuleName, IsBootInterface)
mod_deps = mkModDeps (dep_mods (mi_deps iface))
+
+-- If the direct imports of this module are resolved to targets that
+-- are not among the dependencies of the previous interface file,
+-- then we definitely need to recompile. This catches cases like
+-- - an exposed package has been upgraded
+-- - we are compiling with different package flags
+-- - a home module that was shadowing a package module has been removed
+-- - a new home module has been added that shadows a package module
+-- See bug #1372.
+--
+-- Returns True if recompilation is required.
+checkDependencies :: HscEnv -> ModSummary -> ModIface -> IfG RecompileRequired
+checkDependencies hsc_env summary iface
+ = orM (map dep_missing (ms_imps summary ++ ms_srcimps summary))
+ where
+ prev_dep_mods = dep_mods (mi_deps iface)
+ prev_dep_pkgs = dep_pkgs (mi_deps iface)
+
+ this_pkg = thisPackage (hsc_dflags hsc_env)
+
+ orM = foldr f (return False)
+ where f m rest = do b <- m; if b then return True else rest
+
+ dep_missing (L _ mod) = do
+ find_res <- liftIO $ findImportedModule hsc_env mod Nothing
+ case find_res of
+ Found _ mod
+ | pkg == this_pkg
+ -> if moduleName mod `notElem` map fst prev_dep_mods
+ then do traceHiDiffs $
+ text "imported module " <> quotes (ppr mod) <>
+ text " not among previous dependencies"
+ return outOfDate
+ else
+ return upToDate
+ | otherwise
+ -> if pkg `notElem` prev_dep_pkgs
+ then do traceHiDiffs $
+ text "imported module " <> quotes (ppr mod) <>
+ text " is from package " <> quotes (ppr pkg) <>
+ text ", which is not among previous dependencies"
+ return outOfDate
+ else
+ return upToDate
+ where pkg = modulePackageId mod
+ _otherwise -> return outOfDate
+
checkModUsage :: PackageId ->Usage -> IfG RecompileRequired
-- Given the usage information extracted from the old
-- M.hi file for the module being compiled, figure out
usg_rules = old_rule_vers,
usg_exports = maybe_old_export_vers,
usg_entities = old_decl_vers })
- = -- Load the imported interface is possible
- let
- doc_str = sep [ptext SLIT("need version info for"), ppr mod_name]
- in
- traceHiDiffs (text "Checking usages for module" <+> ppr mod_name) `thenM_`
+ = do -- Load the imported interface is possible
+ let doc_str = sep [ptext SLIT("need version info for"), ppr mod_name]
+ traceHiDiffs (text "Checking usages for module" <+> ppr mod_name)
- let
- mod = mkModule this_pkg mod_name
- in
- loadInterface doc_str mod ImportBySystem `thenM` \ mb_iface ->
+ let mod = mkModule this_pkg mod_name
+
+ mb_iface <- loadInterface doc_str mod ImportBySystem
-- Load the interface, but don't complain on failure;
-- Instead, get an Either back which we can test
new_rule_vers = mi_rule_vers iface
in
-- CHECK MODULE
- checkModuleVersion old_mod_vers new_mod_vers `thenM` \ recompile ->
+ checkModuleVersion old_mod_vers new_mod_vers >>= \ recompile ->
if not recompile then
- returnM upToDate
+ return upToDate
else
-- CHECK EXPORT LIST
else
-- CHECK ITEMS ONE BY ONE
- checkList [checkEntityUsage new_decl_vers u | u <- old_decl_vers] `thenM` \ recompile ->
+ checkList [checkEntityUsage new_decl_vers u | u <- old_decl_vers] >>= \ recompile ->
if recompile then
- returnM outOfDate -- This one failed, so just bail out now
+ return outOfDate -- This one failed, so just bail out now
else
up_to_date (ptext SLIT(" Great! The bits I use are up to date"))
}
out_of_date (sep [ptext SLIT("No longer exported:"), ppr name])
Just (_, new_vers) -- It's there, but is it up to date?
- | new_vers == old_vers -> traceHiDiffs (text " Up to date" <+> ppr name <+> parens (ppr new_vers)) `thenM_`
- returnM upToDate
+ | new_vers == old_vers -> do traceHiDiffs (text " Up to date" <+> ppr name <+> parens (ppr new_vers))
+ return upToDate
| otherwise -> out_of_date_vers (ptext SLIT(" Out of date:") <+> ppr name)
old_vers new_vers
-up_to_date msg = traceHiDiffs msg `thenM_` returnM upToDate
-out_of_date msg = traceHiDiffs msg `thenM_` returnM outOfDate
+up_to_date msg = traceHiDiffs msg >> return upToDate
+out_of_date msg = traceHiDiffs msg >> return outOfDate
out_of_date_vers msg old_vers new_vers
= out_of_date (hsep [msg, ppr old_vers, ptext SLIT("->"), ppr new_vers])
----------------------
checkList :: [IfG RecompileRequired] -> IfG RecompileRequired
-- This helper is used in two places
-checkList [] = returnM upToDate
-checkList (check:checks) = check `thenM` \ recompile ->
- if recompile then
- returnM outOfDate
- else
- checkList checks
+checkList [] = return upToDate
+checkList (check:checks) = do recompile <- check
+ if recompile
+ then return outOfDate
+ else checkList checks
\end{code}
%************************************************************************
tyThingToIfaceDecl (ATyCon tycon)
| isSynTyCon tycon
- = IfaceSyn { ifName = getOccName tycon,
- ifTyVars = toIfaceTvBndrs tyvars,
+ = IfaceSyn { ifName = getOccName tycon,
+ ifTyVars = toIfaceTvBndrs tyvars,
ifOpenSyn = syn_isOpen,
- ifSynRhs = toIfaceType syn_tyki }
+ ifSynRhs = toIfaceType syn_tyki,
+ ifFamInst = famInstToIface (tyConFamInst_maybe tycon)
+ }
| isAlgTyCon tycon
= IfaceData { ifName = getOccName tycon,
= IfaceForeign { ifName = getOccName tycon,
ifExtName = tyConExtName tycon }
- | isPrimTyCon tycon || isFunTyCon tycon
- -- Needed in GHCi for ':info Int#', for example
- = IfaceData { ifName = getOccName tycon,
- ifTyVars = toIfaceTvBndrs (take (tyConArity tycon) alphaTyVars),
- ifCtxt = [],
- ifCons = IfAbstractTyCon,
- ifGadtSyntax = False,
- ifGeneric = False,
- ifRec = NonRecursive,
- ifFamInst = Nothing }
-
| otherwise = pprPanic "toIfaceDecl" (ppr tycon)
where
tyvars = tyConTyVars tycon
(syn_isOpen, syn_tyki) = case synTyConRhs tycon of
- OpenSynTyCon ki -> (True , ki)
- SynonymTyCon ty -> (False, ty)
+ OpenSynTyCon ki _ -> (True , ki)
+ SynonymTyCon ty -> (False, ty)
- ifaceConDecls (NewTyCon { data_con = con }) =
+ ifaceConDecls (NewTyCon { data_con = con }) =
IfNewTyCon (ifaceConDecl con)
- ifaceConDecls (DataTyCon { data_cons = cons }) =
+ ifaceConDecls (DataTyCon { data_cons = cons }) =
IfDataTyCon (map ifaceConDecl cons)
- ifaceConDecls OpenDataTyCon = IfOpenDataTyCon
- ifaceConDecls OpenNewTyCon = IfOpenNewTyCon
- ifaceConDecls AbstractTyCon = IfAbstractTyCon
+ ifaceConDecls OpenTyCon {} = IfOpenDataTyCon
+ ifaceConDecls AbstractTyCon = IfAbstractTyCon
-- The last case happens when a TyCon has been trimmed during tidying
-- Furthermore, tyThingToIfaceDecl is also used
-- in TcRnDriver for GHCi, when browsing a module, in which case the
ifConUnivTvs = toIfaceTvBndrs (dataConUnivTyVars data_con),
ifConExTvs = toIfaceTvBndrs (dataConExTyVars data_con),
ifConEqSpec = to_eq_spec (dataConEqSpec data_con),
- ifConCtxt = toIfaceContext (dataConTheta data_con),
+ ifConCtxt = toIfaceContext (dataConEqTheta data_con ++ dataConDictTheta data_con),
ifConArgTys = map toIfaceType (dataConOrigArgTys data_con),
ifConFields = map getOccName
(dataConFieldLabels data_con),
--------------------------
instanceToIfaceInst :: Instance -> IfaceInst
instanceToIfaceInst ispec@(Instance { is_dfun = dfun_id, is_flag = oflag,
- is_cls = cls, is_tcs = mb_tcs,
- is_orph = orph })
- = IfaceInst { ifDFun = getName dfun_id,
+ is_cls = cls_name, is_tcs = mb_tcs })
+ = ASSERT( cls_name == className cls )
+ IfaceInst { ifDFun = dfun_name,
ifOFlag = oflag,
- ifInstCls = cls,
+ ifInstCls = cls_name,
ifInstTys = map do_rough mb_tcs,
ifInstOrph = orph }
where
do_rough Nothing = Nothing
do_rough (Just n) = Just (toIfaceTyCon_name n)
+ dfun_name = idName dfun_id
+ mod = nameModule dfun_name
+ is_local name = nameIsLocalOrFrom mod name
+
+ -- Compute orphanhood. See Note [Orphans] in IfaceSyn
+ (_, _, cls, tys) = tcSplitDFunTy (idType dfun_id)
+ -- Slightly awkward: we need the Class to get the fundeps
+ (tvs, fds) = classTvsFds cls
+ arg_names = [filterNameSet is_local (tyClsNamesOfType ty) | ty <- tys]
+ orph | is_local cls_name = Just (nameOccName cls_name)
+ | all isJust mb_ns = head mb_ns
+ | otherwise = Nothing
+
+ mb_ns :: [Maybe OccName] -- One for each fundep; a locally-defined name
+ -- that is not in the "determined" arguments
+ mb_ns | null fds = [choose_one arg_names]
+ | otherwise = map do_one fds
+ do_one (ltvs,rtvs) = choose_one [ns | (tv,ns) <- tvs `zip` arg_names
+ , not (tv `elem` rtvs)]
+
+ choose_one :: [NameSet] -> Maybe OccName
+ choose_one nss = case nameSetToList (unionManyNameSets nss) of
+ [] -> Nothing
+ (n:ns) -> Just (nameOccName n)
+
--------------------------
famInstToIfaceFamInst :: FamInst -> IfaceFamInst
famInstToIfaceFamInst fi@(FamInst { fi_tycon = tycon,
do_rough (Just n) = Just (toIfaceTyCon_name n)
--------------------------
+toIfaceLetBndr id = IfLetBndr (occNameFS (getOccName id))
+ (toIfaceType (idType id))
+ prag_info
+ where
+ -- Stripped-down version of tcIfaceIdInfo
+ -- Change this if you want to export more IdInfo for
+ -- non-top-level Ids. Don't forget to change
+ -- CoreTidy.tidyLetBndr too!
+ --
+ -- See Note [IdInfo on nested let-bindings] in IfaceSyn
+ id_info = idInfo id
+ inline_prag = inlinePragInfo id_info
+ prag_info | isAlwaysActive inline_prag = NoInfo
+ | otherwise = HasInfo [HsInline inline_prag]
+
+--------------------------
toIfaceIdInfo :: IdInfo -> [IfaceInfoItem]
toIfaceIdInfo id_info
= catMaybes [arity_hsinfo, caf_hsinfo, strict_hsinfo,
------------ Worker --------------
work_info = workerInfo id_info
- has_worker = case work_info of { HasWorker _ _ -> True; other -> False }
+ has_worker = workerExists work_info
wrkr_hsinfo = case work_info of
HasWorker work_id wrap_arity ->
Just (HsWorker ((idName work_id)) wrap_arity)
| otherwise = Just (HsInline inline_prag)
--------------------------
-coreRuleToIfaceRule :: CoreRule -> IfaceRule
-coreRuleToIfaceRule (BuiltinRule { ru_fn = fn})
+coreRuleToIfaceRule :: Module -> CoreRule -> IfaceRule
+coreRuleToIfaceRule mod (BuiltinRule { ru_fn = fn})
= pprTrace "toHsRule: builtin" (ppr fn) $
bogusIfaceRule fn
-coreRuleToIfaceRule (Rule { ru_name = name, ru_fn = fn,
- ru_act = act, ru_bndrs = bndrs,
- ru_args = args, ru_rhs = rhs, ru_orph = orph })
+coreRuleToIfaceRule mod (Rule { ru_name = name, ru_fn = fn,
+ ru_act = act, ru_bndrs = bndrs,
+ ru_args = args, ru_rhs = rhs })
= IfaceRule { ifRuleName = name, ifActivation = act,
ifRuleBndrs = map toIfaceBndr bndrs,
ifRuleHead = fn,
do_arg (Type ty) = IfaceType (toIfaceType (deNoteType ty))
do_arg arg = toIfaceExpr arg
+ -- Compute orphanhood. See Note [Orphans] in IfaceSyn
+ -- A rule is an orphan only if none of the variables
+ -- mentioned on its left-hand side are locally defined
+ lhs_names = fn : nameSetToList (exprsFreeNames args)
+ -- No need to delete bndrs, because
+ -- exprsFreeNames finds only External names
+
+ orph = case filter (nameIsLocalOrFrom mod) lhs_names of
+ (n:ns) -> Just (nameOccName n)
+ [] -> Nothing
+
bogusIfaceRule :: Name -> IfaceRule
bogusIfaceRule id_name
= IfaceRule { ifRuleName = FSLIT("bogus"), ifActivation = NeverActive,
toIfaceNote (CoreNote s) = IfaceCoreNote s
---------------------
-toIfaceBind (NonRec b r) = IfaceNonRec (toIfaceIdBndr b) (toIfaceExpr r)
-toIfaceBind (Rec prs) = IfaceRec [(toIfaceIdBndr b, toIfaceExpr r) | (b,r) <- prs]
+toIfaceBind (NonRec b r) = IfaceNonRec (toIfaceLetBndr b) (toIfaceExpr r)
+toIfaceBind (Rec prs) = IfaceRec [(toIfaceLetBndr b, toIfaceExpr r) | (b,r) <- prs]
---------------------
toIfaceAlt (c,bs,r) = (toIfaceCon c, map getFS bs, toIfaceExpr r)
| Just fcall <- isFCallId_maybe v = IfaceFCall fcall (toIfaceType (idType v))
-- Foreign calls have special syntax
| isExternalName name = IfaceExt name
+ | Just (TickBox m ix) <- isTickBoxOp_maybe v
+ = IfaceTick m ix
| otherwise = IfaceLcl (getFS name)
where
name = idName v