Commit 526c3af1 authored by Simon Marlow's avatar Simon Marlow

Use MD5 checksums for recompilation checking (fixes #1372, #1959)

This is a much more robust way to do recompilation checking.  The idea
is to create a fingerprint of the ABI of an interface, and track
dependencies by recording the fingerprints of ABIs that a module
depends on.  If any of those ABIs have changed, then we need to
recompile.

In bug #1372 we weren't recording dependencies on package modules,
this patch fixes that by recording fingerprints of package modules
that we depend on.  Within a package there is still fine-grained
recompilation avoidance as before.

We currently use MD5 for fingerprints, being a good compromise between
efficiency and security.  We're not worried about attackers, but we
are worried about accidental collisions.

All the MD5 sums do make interface files a bit bigger, but compile
times on the whole are about the same as before.  Recompilation
avoidance should be a bit more accurate than in 6.8.2 due to fixing
#1959, especially when using -O.
parent 842e9d66
......@@ -572,7 +572,7 @@ SRC_MKDEPENDC_OPTS += -I$(GHC_INCLUDE_DIR)
SRC_HC_OPTS += \
-cpp -fglasgow-exts -fno-generics -Rghc-timing \
-I. -Iparser
-I. -Iparser -Iutil
# Omitted: -I$(GHC_INCLUDE_DIR)
# We should have -I$(GHC_INCLUDE_DIR) in SRC_HC_OPTS,
......
......@@ -42,6 +42,7 @@ module Module
modulePackageId, moduleName,
pprModule,
mkModule,
stableModuleCmp,
-- * The ModuleLocation type
ModLocation(..),
......@@ -71,6 +72,7 @@ import FiniteMap
import LazyUniqFM
import FastString
import Binary
import Util
import System.FilePath
\end{code}
......@@ -182,6 +184,7 @@ mkModuleNameFS s = ModuleName s
moduleNameSlashes :: ModuleName -> String
moduleNameSlashes = dots_to_slashes . moduleNameString
where dots_to_slashes = map (\c -> if c == '.' then pathSeparator else c)
\end{code}
%************************************************************************
......@@ -205,8 +208,13 @@ instance Binary Module where
put_ bh (Module p n) = put_ bh p >> put_ bh n
get bh = do p <- get bh; n <- get bh; return (Module p n)
instance Uniquable PackageId where
getUnique pid = getUnique (packageIdFS pid)
-- This gives a stable ordering, as opposed to the Ord instance which
-- gives an ordering based on the Uniques of the components, which may
-- not be stable from run to run of the compiler.
stableModuleCmp :: Module -> Module -> Ordering
stableModuleCmp (Module p1 n1) (Module p2 n2)
= (packageIdFS p1 `compare` packageIdFS p2) `thenCmp`
(moduleNameFS n1 `compare` moduleNameFS n2)
mkModule :: PackageId -> ModuleName -> Module
mkModule = Module
......@@ -235,9 +243,17 @@ pprPackagePrefix p mod = getPprStyle doc
%************************************************************************
\begin{code}
newtype PackageId = PId FastString deriving( Eq, Ord ) -- includes the version
newtype PackageId = PId FastString deriving( Eq ) -- includes the version
-- here to avoid module loops with PackageConfig
instance Uniquable PackageId where
getUnique pid = getUnique (packageIdFS pid)
-- Note: *not* a stable lexicographic ordering, a faster unique-based
-- ordering.
instance Ord PackageId where
nm1 `compare` nm2 = getUnique nm1 `compare` getUnique nm2
instance Outputable PackageId where
ppr pid = text (packageIdString pid)
......
......@@ -40,16 +40,13 @@ import {-# SOURCE #-} TypeRep( TyThing )
import OccName
import Module
import SrcLoc
import UniqFM
import Unique
import Maybes
import Binary
import FastMutInt
import FastTypes
import FastString
import Outputable
import Data.IORef
import Data.Array
\end{code}
......@@ -309,20 +306,9 @@ instance NamedThing Name where
\begin{code}
instance Binary Name where
put_ bh name = do
case getUserData bh of {
UserData { ud_symtab_map = symtab_map_ref,
ud_symtab_next = symtab_next } -> do
symtab_map <- readIORef symtab_map_ref
case lookupUFM symtab_map name of
Just (off,_) -> put_ bh off
Nothing -> do
off <- readFastMutInt symtab_next
writeFastMutInt symtab_next (off+1)
writeIORef symtab_map_ref
$! addToUFM symtab_map name (off,name)
put_ bh off
}
put_ bh name =
case getUserData bh of
UserData{ ud_put_name = put_name } -> put_name bh name
get bh = do
i <- get bh
......
......@@ -5,6 +5,7 @@
\begin{code}
module OccName (
mk_deriv,
-- * The NameSpace type; abstact
NameSpace, tcName, clsName, tcClsName, dataName, varName,
tvName, srcDataName,
......
......@@ -363,6 +363,17 @@ lintCoreExpr e@(Case scrut var alt_ty alts) =
do { scrut_ty <- lintCoreExpr scrut
; alt_ty <- lintTy alt_ty
; var_ty <- lintTy (idType var)
; let mb_tc_app = splitTyConApp_maybe (idType var)
; case mb_tc_app of
Just (tycon, _)
| debugIsOn &&
isAlgTyCon tycon &&
null (tyConDataCons tycon) ->
pprTrace "case binder's type has no constructors" (ppr e)
$ return ()
_otherwise -> return ()
-- Don't use lintIdBndr on var, because unboxed tuple is legitimate
; subst <- getTvSubst
......
......@@ -1635,7 +1635,8 @@ showPackages = do
pkg_ids <- fmap (preloadPackages . pkgState) getDynFlags
io $ putStrLn $ showSDoc $ vcat $
text "packages currently loaded:"
: map (nest 2 . text . packageIdString) (sort pkg_ids)
: map (nest 2 . text . packageIdString)
(sortBy (compare `on` packageIdFS) pkg_ids)
where showFlag (ExposePackage p) = text $ " -package " ++ p
showFlag (HidePackage p) = text $ " -hide-package " ++ p
showFlag (IgnorePackage p) = text $ " -ignore-package " ++ p
......
......@@ -32,7 +32,9 @@ import SrcLoc
import ErrUtils
import Config
import FastMutInt
import Unique
import Outputable
import FastString
import Data.List
import Data.Word
......@@ -149,7 +151,19 @@ writeBinIface dflags hi_path mod_iface = do
put_ bh symtab_p_p
-- Make some intial state
ud <- newWriteState
symtab_next <- newFastMutInt
writeFastMutInt symtab_next 0
symtab_map <- newIORef emptyUFM
let bin_symtab = BinSymbolTable {
bin_symtab_next = symtab_next,
bin_symtab_map = symtab_map }
dict_next_ref <- newFastMutInt
writeFastMutInt dict_next_ref 0
dict_map_ref <- newIORef emptyUFM
let bin_dict = BinDictionary {
bin_dict_next = dict_next_ref,
bin_dict_map = dict_map_ref }
ud <- newWriteState (putName bin_symtab) (putFastString bin_dict)
-- Put the main thing,
bh <- return $ setUserData bh ud
......@@ -161,8 +175,8 @@ writeBinIface dflags hi_path mod_iface = do
seekBin bh symtab_p -- Seek back to the end of the file
-- Write the symbol table itself
symtab_next <- readFastMutInt (ud_symtab_next ud)
symtab_map <- readIORef (ud_symtab_map ud)
symtab_next <- readFastMutInt symtab_next
symtab_map <- readIORef symtab_map
putSymbolTable bh symtab_next symtab_map
debugTraceMsg dflags 3 (text "writeBinIface:" <+> int symtab_next
<+> text "Names")
......@@ -176,8 +190,8 @@ writeBinIface dflags hi_path mod_iface = do
seekBin bh dict_p -- Seek back to the end of the file
-- Write the dictionary itself
dict_next <- readFastMutInt (ud_dict_next ud)
dict_map <- readIORef (ud_dict_map ud)
dict_next <- readFastMutInt dict_next_ref
dict_map <- readIORef dict_map_ref
putDictionary bh dict_next dict_map
debugTraceMsg dflags 3 (text "writeBinIface:" <+> int dict_next
<+> text "dict entries")
......@@ -248,6 +262,51 @@ serialiseName bh name _ = do
let mod = nameModule name
put_ bh (modulePackageId mod, moduleName mod, nameOccName name)
putName :: BinSymbolTable -> BinHandle -> Name -> IO ()
putName BinSymbolTable{
bin_symtab_map = symtab_map_ref,
bin_symtab_next = symtab_next } bh name
= do
symtab_map <- readIORef symtab_map_ref
case lookupUFM symtab_map name of
Just (off,_) -> put_ bh off
Nothing -> do
off <- readFastMutInt symtab_next
writeFastMutInt symtab_next (off+1)
writeIORef symtab_map_ref
$! addToUFM symtab_map name (off,name)
put_ bh off
data BinSymbolTable = BinSymbolTable {
bin_symtab_next :: !FastMutInt, -- The next index to use
bin_symtab_map :: !(IORef (UniqFM (Int,Name)))
-- indexed by Name
}
putFastString :: BinDictionary -> BinHandle -> FastString -> IO ()
putFastString BinDictionary { bin_dict_next = j_r,
bin_dict_map = out_r} bh f
= do
out <- readIORef out_r
let uniq = getUnique f
case lookupUFM out uniq of
Just (j, _) -> put_ bh j
Nothing -> do
j <- readFastMutInt j_r
put_ bh j
writeFastMutInt j_r (j + 1)
writeIORef out_r $! addToUFM out uniq (j, f)
data BinDictionary = BinDictionary {
bin_dict_next :: !FastMutInt, -- The next index to use
bin_dict_map :: !(IORef (UniqFM (Int,FastString)))
-- indexed by FastString
}
-- -----------------------------------------------------------------------------
-- All the binary instances
......@@ -300,70 +359,74 @@ instance Binary ModIface where
put_ bh (ModIface {
mi_module = mod,
mi_boot = is_boot,
mi_mod_vers = mod_vers,
mi_iface_hash= iface_hash,
mi_mod_hash = mod_hash,
mi_orphan = orphan,
mi_finsts = hasFamInsts,
mi_deps = deps,
mi_usages = usages,
mi_exports = exports,
mi_exp_vers = exp_vers,
mi_exp_hash = exp_hash,
mi_fixities = fixities,
mi_deprecs = deprecs,
mi_decls = decls,
mi_insts = insts,
mi_fam_insts = fam_insts,
mi_rules = rules,
mi_rule_vers = rule_vers,
mi_orphan_hash = orphan_hash,
mi_vect_info = vect_info,
mi_hpc = hpc_info }) = do
put_ bh mod
put_ bh is_boot
put_ bh mod_vers
put_ bh iface_hash
put_ bh mod_hash
put_ bh orphan
put_ bh hasFamInsts
lazyPut bh deps
lazyPut bh usages
put_ bh exports
put_ bh exp_vers
put_ bh exp_hash
put_ bh fixities
lazyPut bh deprecs
put_ bh decls
put_ bh insts
put_ bh fam_insts
lazyPut bh rules
put_ bh rule_vers
put_ bh orphan_hash
put_ bh vect_info
put_ bh hpc_info
get bh = do
mod_name <- get bh
is_boot <- get bh
mod_vers <- get bh
iface_hash <- get bh
mod_hash <- get bh
orphan <- get bh
hasFamInsts <- get bh
deps <- lazyGet bh
usages <- {-# SCC "bin_usages" #-} lazyGet bh
exports <- {-# SCC "bin_exports" #-} get bh
exp_vers <- get bh
exp_hash <- get bh
fixities <- {-# SCC "bin_fixities" #-} get bh
deprecs <- {-# SCC "bin_deprecs" #-} lazyGet bh
decls <- {-# SCC "bin_tycldecls" #-} get bh
insts <- {-# SCC "bin_insts" #-} get bh
fam_insts <- {-# SCC "bin_fam_insts" #-} get bh
rules <- {-# SCC "bin_rules" #-} lazyGet bh
rule_vers <- get bh
orphan_hash <- get bh
vect_info <- get bh
hpc_info <- get bh
return (ModIface {
mi_module = mod_name,
mi_boot = is_boot,
mi_mod_vers = mod_vers,
mi_iface_hash = iface_hash,
mi_mod_hash = mod_hash,
mi_orphan = orphan,
mi_finsts = hasFamInsts,
mi_deps = deps,
mi_usages = usages,
mi_exports = exports,
mi_exp_vers = exp_vers,
mi_exp_hash = exp_hash,
mi_fixities = fixities,
mi_deprecs = deprecs,
mi_decls = decls,
......@@ -371,13 +434,13 @@ instance Binary ModIface where
mi_insts = insts,
mi_fam_insts = fam_insts,
mi_rules = rules,
mi_rule_vers = rule_vers,
mi_orphan_hash = orphan_hash,
mi_vect_info = vect_info,
mi_hpc = hpc_info,
-- And build the cached values
mi_dep_fn = mkIfaceDepCache deprecs,
mi_fix_fn = mkIfaceFixCache fixities,
mi_ver_fn = mkIfaceVerCache decls })
mi_hash_fn = mkIfaceHashCache decls })
getWayDescr :: IO String
getWayDescr = do
......@@ -421,22 +484,31 @@ instance (Binary name) => Binary (GenAvailInfo name) where
return (AvailTC ab ac)
instance Binary Usage where
put_ bh usg = do
put_ bh (usg_name usg)
put_ bh (usg_mod usg)
put_ bh usg@UsagePackageModule{} = do
putByte bh 0
put_ bh (usg_mod usg)
put_ bh (usg_mod_hash usg)
put_ bh usg@UsageHomeModule{} = do
putByte bh 1
put_ bh (usg_mod_name usg)
put_ bh (usg_mod_hash usg)
put_ bh (usg_exports usg)
put_ bh (usg_entities usg)
put_ bh (usg_rules usg)
get bh = do
nm <- get bh
mod <- get bh
exps <- get bh
ents <- get bh
rules <- get bh
return (Usage { usg_name = nm, usg_mod = mod,
usg_exports = exps, usg_entities = ents,
usg_rules = rules })
h <- getByte bh
case h of
0 -> do
nm <- get bh
mod <- get bh
return UsagePackageModule { usg_mod = nm, usg_mod_hash = mod }
_ -> do
nm <- get bh
mod <- get bh
exps <- get bh
ents <- get bh
return UsageHomeModule { usg_mod_name = nm, usg_mod_hash = mod,
usg_exports = exps, usg_entities = ents }
instance Binary Deprecations where
put_ bh NoDeprecs = putByte bh 0
......
......@@ -15,10 +15,9 @@ module IfaceSyn (
-- Misc
ifaceDeclSubBndrs, visibleIfConDecls,
-- Equality
GenIfaceEq(..), IfaceEq, (&&&), bool, eqListBy, eqMaybeBy,
eqIfDecl, eqIfInst, eqIfFamInst, eqIfRule, checkBootDecl,
-- Free Names
freeNamesIfDecl, freeNamesIfRule,
-- Pretty printing
pprIfaceExpr, pprIfaceDeclHead
) where
......@@ -30,8 +29,6 @@ import IfaceType
import NewDemand
import Class
import UniqFM
import UniqSet
import NameSet
import Name
import CostCentre
......@@ -46,7 +43,6 @@ import Data.List
import Data.Maybe
infixl 3 &&&
infix 4 `eqIfExt`, `eqIfIdInfo`, `eqIfType`
\end{code}
......@@ -648,385 +644,128 @@ instance Outputable IfaceInfoItem where
ppr (HsStrictness str) = ptext (sLit "Strictness:") <+> pprIfaceStrictSig str
ppr HsNoCafRefs = ptext (sLit "HasNoCafRefs")
ppr (HsWorker w a) = ptext (sLit "Worker:") <+> ppr w <+> int a
\end{code}
%************************************************************************
%* *
Equality, for interface file version generaion only
%* *
%************************************************************************
Equality over IfaceSyn returns an IfaceEq, not a Bool. The new
constructor is EqBut, which gives the set of things whose version must
be equal for the whole thing to be equal. So the key function is
eqIfExt, which compares Names.
Of course, equality is also done modulo alpha conversion.
-- -----------------------------------------------------------------------------
-- Finding the Names in IfaceSyn
-- This is used for dependency analysis in MkIface, so that we
-- fingerprint a declaration before the things that depend on it. It
-- is specific to interface-file fingerprinting in the sense that we
-- don't collect *all* Names: for example, the DFun of an instance is
-- recorded textually rather than by its fingerprint when
-- fingerprinting the instance, so DFuns are not dependencies.
freeNamesIfDecl :: IfaceDecl -> NameSet
freeNamesIfDecl (IfaceId _s t i) =
freeNamesIfType t &&&
freeNamesIfIdInfo i
freeNamesIfDecl IfaceForeign{} =
emptyNameSet
freeNamesIfDecl d@IfaceData{} =
freeNamesIfTcFam (ifFamInst d) &&&
freeNamesIfContext (ifCtxt d) &&&
freeNamesIfConDecls (ifCons d)
freeNamesIfDecl d@IfaceSyn{} =
freeNamesIfType (ifSynRhs d) &&&
freeNamesIfTcFam (ifFamInst d)
freeNamesIfDecl d@IfaceClass{} =
freeNamesIfContext (ifCtxt d) &&&
freeNamesIfDecls (ifATs d) &&&
fnList freeNamesIfClsSig (ifSigs d)
\begin{code}
data GenIfaceEq a
= Equal -- Definitely exactly the same
| NotEqual -- Definitely different
| EqBut (UniqSet a) -- The same provided these things have not changed
type IfaceEq = GenIfaceEq Name
instance Outputable a => Outputable (GenIfaceEq a) where
ppr Equal = ptext (sLit "Equal")
ppr NotEqual = ptext (sLit "NotEqual")
ppr (EqBut occset) = ptext (sLit "EqBut") <+> ppr (uniqSetToList occset)
bool :: Bool -> IfaceEq
bool True = Equal
bool False = NotEqual
toBool :: IfaceEq -> Bool
toBool Equal = True
toBool (EqBut _) = True
toBool NotEqual = False
zapEq :: IfaceEq -> IfaceEq -- Used to forget EqBut information
zapEq (EqBut _) = Equal
zapEq other = other
(&&&) :: IfaceEq -> IfaceEq -> IfaceEq
Equal &&& x = x
NotEqual &&& _ = NotEqual
EqBut nms &&& Equal = EqBut nms
EqBut _ &&& NotEqual = NotEqual
EqBut nms1 &&& EqBut nms2 = EqBut (nms1 `unionNameSets` nms2)
-- This function is the core of the EqBut stuff
-- ASSUMPTION: The left-hand argument is the NEW CODE, and hence
-- any Names in the left-hand arg have the correct parent in them.
eqIfExt :: Name -> Name -> IfaceEq
eqIfExt name1 name2
| name1 == name2 = EqBut (unitNameSet name1)
| otherwise = NotEqual
---------------------
checkBootDecl :: IfaceDecl -- The boot decl
-> IfaceDecl -- The real decl
-> Bool -- True <=> compatible
checkBootDecl (IfaceId s1 t1 _) (IfaceId s2 t2 _)
= ASSERT( s1==s2 ) toBool (t1 `eqIfType` t2)
checkBootDecl d1@(IfaceForeign {}) d2@(IfaceForeign {})
= ASSERT (ifName d1 == ifName d2 ) ifExtName d1 == ifExtName d2
checkBootDecl d1@(IfaceSyn {}) d2@(IfaceSyn {})
= ASSERT( ifName d1 == ifName d2 )
toBool $ eqWith (ifTyVars d1) (ifTyVars d2) $ \ env ->
eq_ifType env (ifSynRhs d1) (ifSynRhs d2)
checkBootDecl d1@(IfaceData {}) d2@(IfaceData {})
-- We don't check the recursion flags because the boot-one is
-- recursive, to be conservative, but the real one may not be.
-- I'm not happy with the way recursive flags are dealt with.
= ASSERT( ifName d1 == ifName d2 )
toBool $ eqWith (ifTyVars d1) (ifTyVars d2) $ \ env ->
eq_ifContext env (ifCtxt d1) (ifCtxt d2) &&&
case ifCons d1 of
IfAbstractTyCon -> Equal
cons1 -> eq_hsCD env cons1 (ifCons d2)
checkBootDecl d1@(IfaceClass {}) d2@(IfaceClass {})
= ASSERT( ifName d1 == ifName d2 )
toBool $ eqWith (ifTyVars d1) (ifTyVars d2) $ \ env ->
eqListBy (eq_hsFD env) (ifFDs d1) (ifFDs d2) &&&
case (ifCtxt d1, ifSigs d1) of
([], []) -> Equal
(cxt1, sigs1) -> eq_ifContext env cxt1 (ifCtxt d2) &&&
eqListBy (eq_cls_sig env) sigs1 (ifSigs d2)
checkBootDecl _ _ = False -- default case
---------------------
eqIfDecl :: IfaceDecl -> IfaceDecl -> IfaceEq
eqIfDecl (IfaceId s1 t1 i1) (IfaceId s2 t2 i2)
= bool (s1 == s2) &&& (t1 `eqIfType` t2) &&& (i1 `eqIfIdInfo` i2)
eqIfDecl d1@(IfaceForeign {}) d2@(IfaceForeign {})
= bool (ifName d1 == ifName d2 && ifExtName d1 == ifExtName d2)
eqIfDecl d1@(IfaceData {}) d2@(IfaceData {})
= bool (ifName d1 == ifName d2 &&
ifRec d1 == ifRec d2 &&
ifGadtSyntax d1 == ifGadtSyntax d2 &&
ifGeneric d1 == ifGeneric d2) &&&
ifFamInst d1 `eqIfTc_fam` ifFamInst d2 &&&
eqWith (ifTyVars d1) (ifTyVars d2) (\ env ->
eq_ifContext env (ifCtxt d1) (ifCtxt d2) &&&
eq_hsCD env (ifCons d1) (ifCons d2)
)
-- The type variables of the data type do not scope
-- over the constructors (any more), but they do scope
-- over the stupid context in the IfaceConDecls
eqIfDecl d1@(IfaceSyn {}) d2@(IfaceSyn {})
= bool (ifName d1 == ifName d2) &&&
ifFamInst d1 `eqIfTc_fam` ifFamInst d2 &&&
eqWith (ifTyVars d1) (ifTyVars d2) (\ env ->
eq_ifType env (ifSynRhs d1) (ifSynRhs d2)
)
eqIfDecl d1@(IfaceClass {}) d2@(IfaceClass {})
= bool (ifName d1 == ifName d2 &&
ifRec d1 == ifRec d2) &&&
eqWith (ifTyVars d1) (ifTyVars d2) (\ env ->
eq_ifContext env (ifCtxt d1) (ifCtxt d2) &&&
eqListBy (eq_hsFD env) (ifFDs d1) (ifFDs d2) &&&
eqListBy eqIfDecl (ifATs d1) (ifATs d2) &&&
eqListBy (eq_cls_sig env) (ifSigs d1) (ifSigs d2)
)
eqIfDecl _ _ = NotEqual -- default case
-- Helper
eqWith :: [IfaceTvBndr] -> [IfaceTvBndr] -> (EqEnv -> IfaceEq) -> IfaceEq
eqWith = eq_ifTvBndrs emptyEqEnv
eqIfTc_fam :: Maybe (IfaceTyCon, [IfaceType])
-> Maybe (IfaceTyCon, [IfaceType])
-> IfaceEq
Nothing `eqIfTc_fam` Nothing = Equal
(Just (fam1, tys1)) `eqIfTc_fam` (Just (fam2, tys2)) =
fam1 `eqIfTc` fam2 &&& eqListBy eqIfType tys1 tys2
_ `eqIfTc_fam` _ = NotEqual
-----------------------
eqIfInst :: IfaceInst -> IfaceInst -> IfaceEq
eqIfInst d1 d2 = bool (ifDFun d1 == ifDFun d2 && ifOFlag d1 == ifOFlag d2)
-- All other changes are handled via the version info on the dfun
eqIfFamInst :: IfaceFamInst -> IfaceFamInst -> IfaceEq
eqIfFamInst d1 d2 = bool (ifFamInstTyCon d1 == ifFamInstTyCon d2)
-- All other changes are handled via the version info on the tycon
freeNamesIfTcFam :: Maybe (IfaceTyCon, [IfaceType]) -> NameSet
freeNamesIfTcFam (Just (tc,tys)) =
freeNamesIfTc tc &&& fnList freeNamesIfType tys
freeNamesIfTcFam Nothing =
emptyNameSet
freeNamesIfContext :: IfaceContext -> NameSet
freeNamesIfContext = fnList freeNamesIfPredType
freeNamesIfDecls :: [IfaceDecl] -> NameSet
freeNamesIfDecls = fnList freeNamesIfDecl
freeNamesIfClsSig :: IfaceClassOp -> NameSet
freeNamesIfClsSig (IfaceClassOp _n _dm ty) = freeNamesIfType ty
freeNamesIfConDecls :: IfaceConDecls -> NameSet