Newer
Older
% (c) The GRASP/AQUA Project, Glasgow University, 1993-1998
%
\begin{code}
-- 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
mkIface, -- Build a ModIface from a ModGuts,
-- including computing version information
writeIfaceFile, -- Write the interface file
checkOldIface, -- See if recompilation is required, by
-- comparing version information
tyThingToIfaceDecl -- Converting things to their Iface equivalents
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
) where
\end{code}
-----------------------------------------------
MkIface.lhs deals with versioning
-----------------------------------------------
Here's the version-related info in an interface file
module Foo 8 -- module-version
3 -- export-list-version
2 -- rule-version
Usages: -- Version info for what this compilation of Foo imported
Baz 3 -- Module version
[4] -- The export-list version if Foo depended on it
(g,2) -- Function and its version
(T,1) -- Type and its version
<version> f :: Int -> Int {- Unfolding: \x -> Wib.t[2] x -}
-- The [2] says that f's unfolding
-- mentions verison 2 of Wib.t
-----------------------------------------------
Basic idea
-----------------------------------------------
Basic idea:
* In the mi_usages information in an interface, we record the
version number of each free variable of the module
* In mkIface, we compute the version number of each exported thing A.f
by comparing its A.f's info with its new info, and bumping its
version number if it differs. If A.f mentions B.g, and B.g's version
number has changed, then we count A.f as having changed too.
* In checkOldIface we compare the mi_usages for the module with
the actual version info for all each thing recorded in mi_usages
Fixities
~~~~~~~~
We count A.f as changing if its fixity changes
Rules
~~~~~
If a rule changes, we want to recompile any module that might be
affected by that rule. For non-orphan rules, this is relatively easy.
If module M defines f, and a rule for f, just arrange that the version
number for M.f changes if any of the rules for M.f change. Any module
that does not depend on M.f can't be affected by the rule-change
either.
Orphan rules (ones whose 'head function' is not defined in M) are
harder. Here's what we do.
* We have a per-module orphan-rule version number which changes if
any orphan rule changes. (It's unaffected by non-orphan rules.)
* We record usage info for any orphan module 'below' this one,
giving the orphan-rule version number. We recompile if this
changes.
The net effect is that if an orphan rule changes, we recompile every
module above it. That's very conservative, but it's devilishly hard
to know what it might affect, so we just have to be conservative.
Instance decls
~~~~~~~~~~~~~~
In an iface file we have
module A where
instance Eq a => Eq [a] = dfun29
dfun29 :: ...
We have a version number for dfun29, covering its unfolding
etc. Suppose we are compiling a module M that imports A only
indirectly. If typechecking M uses this instance decl, we record the
dependency on A.dfun29 as if it were a free variable of the module
(via the tcg_inst_usages accumulator). That means that A will appear
in M's usage list. If the shape of the instance declaration changes,
then so will dfun29's version, triggering a recompilation.
Adding an instance declaration, or changing an instance decl that is
not currently used, is more tricky. (This really only makes a
difference when we have overlapping instance decls, because then the
new instance decl might kick in to override the old one.) We handle
this in a very similar way that we handle rules above.
* For non-orphan instance decls, identify one locally-defined tycon/class
mentioned in the decl. Treat the instance decl as part of the defn of that
tycon/class, so that if the shape of the instance decl changes, so does the
tycon/class; that in turn will force recompilation of anything that uses
that tycon/class.
* For orphan instance decls, act the same way as for orphan rules.
Indeed, we use the same global orphan-rule version number.
mkUsageInfo
~~~~~~~~~~~
mkUsageInfo figures out what the ``usage information'' for this
moudule is; that is, what it must record in its interface file as the
things it uses.
We produce a line for every module B below the module, A, currently being
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
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.
The usage information records:
\begin{itemize}
\item (a) anything reachable from its body code
\item (b) any module exported with a @module Foo@
\item (c) anything reachable from an exported item
\end{itemize}
Why (b)? Because if @Foo@ changes then this module's export list
will change, so we must recompile this module at least as far as
making a new interface file --- but in practice that means complete
recompilation.
Why (c)? Consider this:
\begin{verbatim}
module A( f, g ) where | module B( f ) where
import B( f ) | f = h 3
g = ... | h = ...
\end{verbatim}
Here, @B.f@ isn't used in A. Should we nevertheless record @B.f@ in
@A@'s usages? Our idea is that we aren't going to touch A.hi if it is
*identical* to what it was before. If anything about @B.f@ changes
than anyone who imports @A@ should be recompiled in case they use
@B.f@ (they'll get an early exit if they don't). So, if anything
about @B.f@ changes we'd better make sure that something in A.hi
changes, and the convenient way to do that is to record the version
number @B.f@ in A.hi in the usage list. If B.f changes that'll force a
complete recompiation of A, which is overkill but it's the only way to
write a new, slightly different, A.hi.
But the example is tricker. Even if @B.f@ doesn't change at all,
@B.h@ may do so, and this change may not be reflected in @f@'s version
number. But with -O, a module that imports A must be recompiled if
@B.h@ changes! So A must record a dependency on @B.h@. So we treat
the occurrence of @B.f@ in the export list *just as if* it were in the
code of A, and thereby haul in all the stuff reachable from it.
*** Conclusion: if A mentions B.f in its export list,
behave just as if A mentioned B.f in its source code,
and slurp in B.f and all its transitive closure ***
[NB: If B was compiled with -O, but A isn't, we should really *still*
haul in all the unfoldings for B, in case the module that imports A *is*
compiled with -O. I think this is the case.]
\begin{code}
#include "HsVersions.h"
import LoadIface
import Id
import IdInfo
import NewDemand
import CoreSyn
import Class
import TyCon
import DataCon
import Type
import TcType
import InstEnv
import FamInstEnv
import Finder
import NameEnv
import NameSet
import BinIface
import Unique
import ErrUtils
import Digraph
import SrcLoc
import PackageConfig hiding ( Version )
import Outputable
import BasicTypes hiding ( SuccessFlag(..) )
import UniqFM
import Util hiding ( eqListBy )
import FiniteMap
import FastString
\end{code}
%************************************************************************
%* *
\subsection{Completing an interface}
%* *
%************************************************************************
\begin{code}
mkIface :: HscEnv
-> Maybe ModIface -- The old interface, if we have it
-> ModDetails -- The trimmed, tidied interface
-> 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 mod_details
ModGuts{ mg_module = this_mod,
mg_rdr_env = rdr_env,
mg_fix_env = fix_env,
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
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,
-- 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
; usages <- mkUsageInfo hsc_env dir_imp_mods (dep_mods deps) used_names
; let { entities = typeEnvElts type_env ;
decls = [ tyThingToIfaceDecl entity
| entity <- entities,
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_fixities = fixities,
mi_deprecs = deprecs,
mi_globals = Just rdr_env,
-- Left out deliberately: filled in by addVersionInfo
mi_mod_vers = initialVersion,
mi_exp_vers = initialVersion,
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,
mi_fix_fn = mkIfaceFixCache fixities }
-- Add version information
; ext_ver_fn = mkParentVerFun hsc_env eps
; (new_iface, no_change_at_all, pp_diffs, pp_orphs)
= {-# SCC "versioninfo" #-}
addVersionInfo ext_ver_fn maybe_old_iface
intermediate_iface decls
}
-- Debug printing
; when (isJust pp_orphs && dopt Opt_WarnOrphans dflags)
(printDump (expectJust "mkIface" pp_orphs))
; when (dopt Opt_D_dump_hi_diffs dflags) (printDump pp_diffs)
; dumpIfSet_dyn dflags Opt_D_dump_hi "FINAL INTERFACE"
(pprModIface new_iface)
-- 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) }
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)
ifFamInstTcName = ifaceTyConName . ifFamInstTyCon
flattenVectInfo (VectInfo { vectInfoVar = vVar
, vectInfoTyCon = vTyCon
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)
writeBinIface dflags hi_file_path new_iface
where hi_file_path = ml_hi_file location
-- -----------------------------------------------------------------------------
-- Look up parents and versions of Names
-- This is like a global version of the mi_ver_fn field in each ModIface.
-- Given a Name, it finds the ModIface, and then uses mi_ver_fn to get
-- the parent and version info.
mkParentVerFun
:: HscEnv -- needed to look up versions
-> ExternalPackageState -- ditto
-> (Name -> (OccName,Version))
mkParentVerFun hsc_env eps
= \name ->
let
mod = nameModule name
occ = nameOccName name
iface = lookupIfaceByModule (hsc_dflags hsc_env) hpt pit mod `orElse`
pprPanic "lookupVers2" (ppr mod <+> ppr occ)
in
mi_ver_fn iface occ `orElse`
pprPanic "lookupVers1" (ppr mod <+> ppr occ)
hpt = hsc_HPT hsc_env
pit = eps_PIT eps
-----------------------------------------------------------------------------
-- Compute version numbers for local decls
addVersionInfo
:: (Name -> (OccName,Version)) -- lookup parents and versions of names
-> Maybe ModIface -- The old interface, read from M.hi
-> ModIface -- The new interface (lacking decls)
-> [IfaceDecl] -- The new decls
-> (ModIface, -- Updated interface
Bool, -- True <=> no changes at all; no need to write Iface
SDoc, -- Differences
Maybe SDoc) -- Warnings about orphans
addVersionInfo ver_fn Nothing new_iface new_decls
-- No old interface, so definitely write a new one!
= (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)
orph_insts = filter (isNothing . ifInstOrph) (mi_insts new_iface)
orph_rules = filter (isNothing . ifRuleOrph) (mi_rules new_iface)
addVersionInfo ver_fn (Just old_iface@(ModIface {
mi_mod_vers = old_mod_vers,
mi_exp_vers = old_exp_vers,
mi_rule_vers = old_rule_vers,
mi_decls = old_decls,
mi_ver_fn = old_decl_vers,
mi_fix_fn = old_fixities }))
new_iface@(ModIface { mi_fix_fn = new_fixities })
new_decls
| no_change_at_all
= (old_iface, True, ptext SLIT("Interface file unchanged"), pp_orphs)
| otherwise
= (final_iface, False, vcat [ptext SLIT("Interface file has changed"),
nest 2 pp_diffs], pp_orphs)
where
final_iface = new_iface {
mi_mod_vers = bump_unless no_output_change old_mod_vers,
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 }
decls_w_vers = [(add_vers decl, decl) | decl <- new_decls]
-------------------
(old_non_orph_insts, old_orph_insts) =
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)
(old_non_orph_rules, old_orph_rules) =
mkOrphMap ifRuleOrph (mi_rules old_iface)
(new_non_orph_rules, new_orph_rules) =
mkOrphMap ifRuleOrph (mi_rules new_iface)
same_rules occ = eqMaybeBy (eqListBy eqIfRule)
(lookupOccEnv old_non_orph_rules occ)
(lookupOccEnv new_non_orph_rules occ)
-------------------
-- Computing what changed
no_output_change = no_decl_change && no_rule_change &&
no_export_change && no_deprec_change
no_export_change = mi_exports new_iface == mi_exports old_iface
-- 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 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_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"
(ppr old_exp_vers <+> arrow <+> ppr (mi_exp_vers final_iface)),
pp_change no_rule_change "Rules"
(ppr old_rule_vers <+> arrow <+> ppr (mi_rule_vers final_iface)),
pp_change no_deprec_change "Deprecations" empty,
pp_change no_other_changes "Usages" empty,
pp_decl_diffs]
pp_change True what info = empty
pp_change False what info = text what <+> ptext SLIT("changed") <+> info
-------------------
old_decl_env = mkOccEnv [(ifName decl, decl) | (_,decl) <- old_decls]
same_fixity n = bool (old_fixities n == new_fixities n)
-------------------
-- Adding version info
new_version = bumpVersion old_mod_vers
-- Start from the old module version, not from
-- zero so that if you remove f, and then add
-- it again, you don't thereby reduce f's
-- version number
add_vers decl | occ `elemOccSet` changed_occs = new_version
| otherwise = snd (expectJust "add_vers" (old_decl_vers occ))
-- If it's unchanged, there jolly well
where -- should be an old version number
occ = ifName decl
-------------------
-- Deciding which declarations have changed
-- For each local decl, the IfaceEq gives the list of things that
-- must be unchanged for the declaration as a whole to be unchanged.
eq_info :: [(OccName, IfaceEq)]
eq_info = map check_eq new_decls
check_eq new_decl
| Just old_decl <- lookupOccEnv old_decl_env occ
= (occ, new_decl `eqIfDecl` old_decl &&& eq_indirects new_decl)
| otherwise {- No corresponding old decl -}
= (occ, NotEqual)
where
occ = ifName new_decl
eq_indirects :: IfaceDecl -> IfaceEq
-- When seeing if two decls are the same, remember to
-- check whether any relevant fixity or rules have changed
eq_indirects (IfaceId {ifName = occ}) = eq_ind_occ occ
eq_indirects (IfaceClass {ifName = cls_occ, ifSigs = sigs})
= same_insts cls_occ &&&
eq_ind_occs [op | IfaceClassOp op _ _ <- sigs]
eq_indirects (IfaceData {ifName = tc_occ, ifCons = cons})
= same_insts tc_occ &&& same_fixity tc_occ &&& -- The TyCon can have a fixity too
eq_ind_occs (map ifConOcc (visibleIfConDecls cons))
eq_indirects other = Equal -- Synonyms and foreign declarations
eq_ind_occ :: OccName -> IfaceEq -- For class ops and Ids; check fixity and rules
eq_ind_occ occ = same_fixity occ &&& same_rules occ
eq_ind_occs = foldr ((&&&) . eq_ind_occ) Equal
-- The Occs of declarations that changed.
changed_occs :: OccSet
changed_occs = computeChangedOccs ver_fn (mi_module new_iface)
(mi_usages old_iface) eq_info
-------------------
-- Diffs
pp_decl_diffs :: SDoc -- Nothing => no changes
pp_decl_diffs
| isEmptyOccSet changed_occs = empty
| otherwise
= vcat [ptext SLIT("Changed occs:") <+> ppr (occSetElts changed_occs),
ptext SLIT("Version change for these decls:"),
nest 2 (vcat (map show_change new_decls))]
eq_env = mkOccEnv eq_info
show_change new_decl
| not (occ `elemOccSet` changed_occs) = empty
| otherwise
= vcat [ppr occ <+> ppr (old_decl_vers occ) <+> arrow <+> ppr new_version,
nest 2 why]
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:") <> ppr names,
nest 2 (braces (fsep (map ppr (occSetElts
(occs `intersectOccSet` changed_occs)))))]
where occs = mkOccSet (map nameOccName (nameSetToList names))
Just NotEqual
| Just old_decl <- lookupOccEnv old_decl_env occ
-> vcat [ptext SLIT("Old:") <+> ppr old_decl,
ptext SLIT("New:") <+> ppr new_decl]
| otherwise
-> ppr occ <+> ptext SLIT("only in new interface")
other -> pprPanic "MkIface.show_change" (ppr occ)
pp_orphs = pprOrphans new_orph_insts new_orph_rules
| null insts && null rules = Nothing
| otherwise
= Just $ vcat [
if null insts then empty else
hang (ptext SLIT("Warning: orphan instances:"))
2 (vcat (map ppr insts)),
if null rules then empty else
hang (ptext SLIT("Warning: orphan rules:"))
2 (vcat (map ppr rules))
]
computeChangedOccs
:: (Name -> (OccName,Version)) -- get parents and versions
-> Module -- This module
-> [Usage] -- Usages from old iface
-> [(OccName, IfaceEq)] -- decl names, equality conditions
-> OccSet -- set of things that have changed
computeChangedOccs ver_fn this_module old_usages eq_info
= foldl add_changes emptyOccSet (stronglyConnComp edges)
where
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
-- 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
| otherwise = False -- must be in another package
where
mod = nameModule nm
(parent_occ, new_version) = ver_fn nm
-- Turn the usages from the old ModIface into a mapping
usg_modmap = listToUFM [ (usg_mod usg, listToUFM (usg_entities usg))
| usg <- old_usages ]
get_local_eq_info :: GenIfaceEq NameSet -> GenIfaceEq OccSet
get_local_eq_info Equal = Equal
get_local_eq_info NotEqual = NotEqual
get_local_eq_info (EqBut ns) = foldNameSet f Equal ns
where f name eq | nameModule name == this_module =
EqBut (unitOccSet (nameOccName name)) `and_occifeq` eq
| name_changed name = NotEqual
| otherwise = eq
local_eq_infos = mapSnd get_local_eq_info eq_info
edges :: [((OccName, OccIfaceEq), Unique, [Unique])]
edges = [ (node, getUnique occ, map getUnique occs)
| node@(occ, iface_eq) <- local_eq_infos
, let occs = case iface_eq of
EqBut occ_set -> occSetElts occ_set
other -> [] ]
-- Changes in declarations
add_changes :: OccSet -> SCC (OccName, OccIfaceEq) -> OccSet
add_changes so_far (AcyclicSCC (occ, iface_eq))
| changedWrt so_far iface_eq -- This one has changed
= extendOccSet so_far occ
add_changes so_far (CyclicSCC pairs)
| changedWrt so_far (foldr1 and_occifeq iface_eqs)
-- One of this group has changed
= extendOccSetList so_far occs
where (occs, iface_eqs) = unzip pairs
add_changes so_far other = so_far
type OccIfaceEq = GenIfaceEq OccSet
changedWrt :: OccSet -> OccIfaceEq -> Bool
changedWrt so_far Equal = False
changedWrt so_far NotEqual = True
changedWrt so_far (EqBut kids) = so_far `intersectsOccSet` kids
changedWrtNames :: OccSet -> IfaceEq -> Bool
changedWrtNames so_far Equal = False
changedWrtNames so_far NotEqual = True
changedWrtNames so_far (EqBut kids) =
so_far `intersectsOccSet` mkOccSet (map nameOccName (nameSetToList kids))
and_occifeq :: OccIfaceEq -> OccIfaceEq -> OccIfaceEq
Equal `and_occifeq` x = x
NotEqual `and_occifeq` x = NotEqual
EqBut nms `and_occifeq` Equal = EqBut nms
EqBut nms `and_occifeq` NotEqual = NotEqual
EqBut nms1 `and_occifeq` EqBut nms2 = EqBut (nms1 `unionOccSets` nms2)
-- mkOrphMap partitions instance decls or rules into
-- (a) an OccEnv for ones that are not orphans,
-- mapping the local OccName to a list of its decls
-- (b) a list of orphan decls
mkOrphMap :: (decl -> Maybe OccName) -- (Just occ) for a non-orphan decl, keyed by occ
-- Nothing for an orphan decl
-> [decl] -- Sorted into canonical order
-> (OccEnv [decl], -- Non-orphan decls associated with their key;
-- each sublist in canonical order
[decl]) -- Orphan decls; in canonical order
= foldl go (emptyOccEnv, []) decls
where
go (non_orphs, orphs) d
| Just occ <- get_key d
= (extendOccEnv_C (\ ds _ -> d:ds) non_orphs occ [d], orphs)
| otherwise = (non_orphs, d:orphs)
----------------------
bump_unless :: Bool -> Version -> Version
bump_unless True v = v -- True <=> no change
bump_unless False v = bumpVersion v
\end{code}
%*********************************************************
%* *
\subsection{Keeping track of what we've slurped, and version numbers}
%* *
%*********************************************************
\begin{code}
-> ModuleEnv (Module, [(ModuleName, Bool, SrcSpan)])
-> NameSet -> IO [Usage]
mkUsageInfo hsc_env dir_imp_mods dep_mods used_names
= do { eps <- hscEPS hsc_env
; let usages = mk_usage_info (eps_PIT eps) hsc_env
dir_imp_mods dep_mods used_names
; usages `seqList` return usages }
-- seq the list of Usages returned: occasionally these
-- 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?
-- ent_map groups together all the things imported and used
-- from a particular module in this package
ent_map :: ModuleEnv [OccName]
ent_map = foldNameSet add_mv emptyModuleEnv used_names
add_mv name mv_map
| isWiredInName name = mv_map -- ignore wired-in names
| otherwise
= case nameModule_maybe name of
Nothing -> mv_map -- ignore internal names
Just mod -> extendModuleEnv_C add_item mv_map mod [occ]
where
occ = nameOccName name
add_item occs _ = occ:occs
depend_on_exports mod = case lookupModuleEnv dir_imp_mods mod of
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 or family-instance module (need to
-- recompile if its instance decls change: rules_vers)
mkUsage :: (ModuleName, IsBootInterface) -> Maybe Usage
| 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 finsts_mod)
= Nothing -- Record no usage info
| otherwise
usg_mod = mod_vers,
usg_exports = export_vers,
usg_entities = fmToList ent_vers,
usg_rules = rules_vers })
where
maybe_iface = lookupIfaceByModule dflags hpt pit mod
-- In one-shot mode, the interfaces for home-package
-- modules accumulate in the PIT not HPT. Sigh.
mod = mkModule (thisPackage dflags) mod_name
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
export_vers | depend_on_exports mod = Just (mi_exp_vers iface)
| otherwise = Nothing
used_occs = lookupModuleEnv ent_map mod `orElse` []
-- Making a FiniteMap here ensures that (a) we remove duplicates
-- when we have usages on several subordinates of a single parent,
-- and (b) that the usages emerge in a canonical order, which
-- is why we use FiniteMap rather than OccEnv: FiniteMap works
-- using Ord on the OccNames, which is a lexicographic ordering.
ent_vers :: FiniteMap OccName Version
ent_vers = listToFM (map lookup_occ used_occs)
lookup_occ occ =
case version_env occ of
Nothing -> pprTrace "hmm, strange" (ppr mod <+> ppr occ) $
(occ, initialVersion) -- does this ever happen?
Just (parent, version) -> (parent, version)
\end{code}
\begin{code}
mkIfaceExports :: [AvailInfo]
-> [(Module, [GenAvailInfo OccName])]
-- Group by module and sort by occurrence
-- This keeps the list in canonical order
mkIfaceExports exports
= [ (mod, eltsFM avails)
Simon Peyton Jones
committed
-- 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
Simon Peyton Jones
committed
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
Simon Peyton Jones
committed
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
Simon Peyton Jones
committed
where
names_from_mod = [nameOccName n | n <- ns, nameModule n == mod]
Simon Peyton Jones
committed
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.
%************************************************************************
%* *
Load the old interface file for this module (unless
we have it aleady), and check whether it is up to date
%* *
%************************************************************************
\begin{code}
checkOldIface :: HscEnv
-> Bool -- Source unchanged
-> Maybe ModIface -- Old interface from compilation manager, if any
-> IO (RecompileRequired, Maybe ModIface)
checkOldIface hsc_env mod_summary source_unchanged maybe_iface
= do { showPass (hsc_dflags hsc_env)
("Checking old interface for " ++
showSDoc (ppr (ms_mod mod_summary))) ;
; initIfaceCheck hsc_env $
check_old_iface hsc_env mod_summary source_unchanged maybe_iface
check_old_iface hsc_env mod_summary source_unchanged maybe_iface
= do -- CHECK WHETHER THE SOURCE HAS CHANGED
{ ifM (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.
; 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 mod_summary old_iface