Commit 07c01d09 authored by simonpj@microsoft.com's avatar simonpj@microsoft.com

Fix Trac #4240: -ddump-minimal-imports

See Note [Partial export] for the details.
I also fixed one egregious bug that was just
waiting to bite: we were using loadSysInterface
instead of loadSrcInterface.
parent fc8e106e
...@@ -19,7 +19,7 @@ import TcEnv ( isBrackStage ) ...@@ -19,7 +19,7 @@ import TcEnv ( isBrackStage )
import RnEnv import RnEnv
import RnHsDoc ( rnHsDoc ) import RnHsDoc ( rnHsDoc )
import IfaceEnv ( ifaceExportNames ) import IfaceEnv ( ifaceExportNames )
import LoadIface ( loadSrcInterface, loadSysInterface ) import LoadIface ( loadSrcInterface )
import TcRnMonad import TcRnMonad
import HeaderInfo ( mkPrelImports ) import HeaderInfo ( mkPrelImports )
...@@ -92,8 +92,10 @@ rnImportDecl :: Module -> Bool ...@@ -92,8 +92,10 @@ rnImportDecl :: Module -> Bool
-> LImportDecl RdrName -> LImportDecl RdrName
-> RnM (LImportDecl Name, GlobalRdrEnv, ImportAvails,AnyHpcUsage) -> RnM (LImportDecl Name, GlobalRdrEnv, ImportAvails,AnyHpcUsage)
rnImportDecl this_mod implicit_prelude (L loc (ImportDecl loc_imp_mod_name mb_pkg want_boot rnImportDecl this_mod implicit_prelude
qual_only as_mod imp_details)) (L loc (ImportDecl { ideclName = loc_imp_mod_name, ideclPkgQual = mb_pkg
, ideclSource = want_boot, ideclQualified = qual_only
, ideclAs = as_mod, ideclHiding = imp_details }))
= setSrcSpan loc $ do = setSrcSpan loc $ do
when (isJust mb_pkg) $ do when (isJust mb_pkg) $ do
...@@ -1272,58 +1274,65 @@ findImportUsage imports rdr_env rdrs ...@@ -1272,58 +1274,65 @@ findImportUsage imports rdr_env rdrs
= map unused_decl imports = map unused_decl imports
where where
import_usage :: ImportMap import_usage :: ImportMap
import_usage = foldr add_rdr Map.empty rdrs import_usage = foldr (addUsedRdrName rdr_env) Map.empty rdrs
unused_decl decl@(L loc (ImportDecl { ideclHiding = imps })) unused_decl decl@(L loc (ImportDecl { ideclHiding = imps }))
= (decl, nubAvails used_avails, unused_imps) = (decl, nubAvails used_avails, unused_imps)
where where
used_avails = Map.lookup (srcSpanStart loc) import_usage `orElse` [] used_avails = Map.lookup (srcSpanStart loc) import_usage `orElse` []
used_names = availsToNameSet used_avails dont_report_as_unused = foldr add emptyNameSet used_avails
add (Avail n) s = s `addOneToNameSet` n
add (AvailTC n ns) s = s `addListToNameSet` (n:ns)
-- If you use 'signum' from Num, then the user may well have
-- imported Num(signum). We don't want to complain that
-- Num is not itself mentioned. Hence adding 'n' as
-- well to the list of of "don't report if unused" names
unused_imps = case imps of unused_imps = case imps of
Just (False, imp_ies) -> nameSetToList unused_imps Just (False, imp_ies) -> nameSetToList unused_imps
where where
imp_names = mkNameSet (concatMap (ieNames . unLoc) imp_ies) imp_names = mkNameSet (concatMap (ieNames . unLoc) imp_ies)
unused_imps = imp_names `minusNameSet` used_names unused_imps = imp_names `minusNameSet` dont_report_as_unused
_other -> [] -- No explicit import list => no unused-name list _other -> [] -- No explicit import list => no unused-name list
add_rdr :: RdrName -> ImportMap -> ImportMap addUsedRdrName :: GlobalRdrEnv -> RdrName -> ImportMap -> ImportMap
add_rdr rdr iu -- For a used RdrName, find all the import decls that brought
= case lookupGRE_RdrName rdr rdr_env of -- it into scope; choose one of them (bestImport), and record
[gre] | Imported imps <- gre_prov gre -- the RdrName in that import decl's entry in the ImportMap
-> add_imp gre (bestImport imps) iu addUsedRdrName rdr_env rdr imp_map
_other -> iu | [gre] <- lookupGRE_RdrName rdr rdr_env
, Imported imps <- gre_prov gre
= add_imp gre (bestImport imps) imp_map
| otherwise
= imp_map
where
add_imp :: GlobalRdrElt -> ImportSpec -> ImportMap -> ImportMap add_imp :: GlobalRdrElt -> ImportSpec -> ImportMap -> ImportMap
add_imp gre (ImpSpec { is_decl = imp_decl_spec }) iu add_imp gre (ImpSpec { is_decl = imp_decl_spec }) imp_map
= Map.insertWith add decl_loc [avail] iu = Map.insertWith add decl_loc [avail] imp_map
where where
add _ avails = avail : avails -- add is really just a specialised (++) add _ avails = avail : avails -- add is really just a specialised (++)
decl_loc = srcSpanStart (is_dloc imp_decl_spec) decl_loc = srcSpanStart (is_dloc imp_decl_spec)
name = gre_name gre name = gre_name gre
avail = case gre_par gre of avail = case gre_par gre of
ParentIs p -> AvailTC p [p,name] ParentIs p -> AvailTC p [name]
NoParent | isTyConName name -> AvailTC name [name] NoParent | isTyConName name -> AvailTC name [name]
| otherwise -> Avail name | otherwise -> Avail name
-- If you use (+) from Num, then for this purpose we want
-- to say that Num is used as well. That is why in the bestImport :: [ImportSpec] -> ImportSpec
-- ParentIs case we have [p,name] in the ParentIs case bestImport iss
= case partition isImpAll iss of
bestImport :: [ImportSpec] -> ImportSpec ([], imp_somes) -> textuallyFirst imp_somes
bestImport iss (imp_alls, _) -> textuallyFirst imp_alls
= case partition isImpAll iss of
([], imp_somes) -> textuallyFirst imp_somes textuallyFirst :: [ImportSpec] -> ImportSpec
(imp_alls, _) -> textuallyFirst imp_alls textuallyFirst iss = case sortWith (is_dloc . is_decl) iss of
[] -> pprPanic "textuallyFirst" (ppr iss)
textuallyFirst :: [ImportSpec] -> ImportSpec (is:_) -> is
textuallyFirst iss = case sortWith (is_dloc . is_decl) iss of
[] -> pprPanic "textuallyFirst" (ppr iss) isImpAll :: ImportSpec -> Bool
(is:_) -> is isImpAll (ImpSpec { is_item = ImpAll }) = True
isImpAll _other = False
isImpAll :: ImportSpec -> Bool
isImpAll (ImpSpec { is_item = ImpAll }) = True
isImpAll _other = False
\end{code} \end{code}
\begin{code} \begin{code}
...@@ -1377,32 +1386,58 @@ printMinimalImports imports_w_usage ...@@ -1377,32 +1386,58 @@ printMinimalImports imports_w_usage
, Just (False, _) <- ideclHiding decl , Just (False, _) <- ideclHiding decl
= return (L l decl) = return (L l decl)
| otherwise | otherwise
= do { ies <- initIfaceTcRn $ mapM to_ie used = do { let ImportDecl { ideclName = L _ mod_name
; return (L l (decl { ideclHiding = Just (False, map (L l) ies) })) } , ideclSource = is_boot
, ideclPkgQual = mb_pkg } = decl
; iface <- loadSrcInterface doc mod_name is_boot mb_pkg
; let lies = map (L l) (concatMap (to_ie iface) used)
; return (L l (decl { ideclHiding = Just (False, lies) })) }
where
doc = text "Compute minimal imports for" <+> ppr decl
to_ie :: AvailInfo -> IfG (IE Name) to_ie :: ModIface -> AvailInfo -> [IE Name]
-- The main trick here is that if we're importing all the constructors -- The main trick here is that if we're importing all the constructors
-- we want to say "T(..)", but if we're importing only a subset we want -- we want to say "T(..)", but if we're importing only a subset we want
-- to say "T(A,B,C)". So we have to find out what the module exports. -- to say "T(A,B,C)". So we have to find out what the module exports.
to_ie (Avail n) = return (IEVar n) to_ie _ (Avail n)
to_ie (AvailTC n [m]) = ASSERT( n==m ) = [IEVar n]
return (IEThingAbs n) to_ie _ (AvailTC n [m])
to_ie (AvailTC n ns) = do | n==m = [IEThingAbs n]
iface <- loadSysInterface doc n_mod to_ie iface (AvailTC n ns)
case [xs | (m,as) <- mi_exports iface, = case [xs | (m,as) <- mi_exports iface
m == n_mod, , m == n_mod
AvailTC x xs <- as, , AvailTC x xs <- as
x == nameOccName n] of , x == nameOccName n
[xs] | all_used xs -> return (IEThingAll n) , x `elem` xs -- Note [Partial export]
| otherwise -> return (IEThingWith n (filter (/= n) ns)) ] of
other -> pprTrace "to_ie" (ppr n <+> ppr n_mod <+> ppr other) $ [xs] | all_used xs -> [IEThingAll n]
return (IEVar n) | otherwise -> [IEThingWith n (filter (/= n) ns)]
_other -> (map IEVar ns)
where where
all_used avail_occs = all (`elem` map nameOccName ns) avail_occs all_used avail_occs = all (`elem` map nameOccName ns) avail_occs
doc = text "Compute minimal imports from" <+> ppr n
n_mod = ASSERT( isExternalName n ) nameModule n n_mod = ASSERT( isExternalName n ) nameModule n
\end{code} \end{code}
Note [Partial export]
~~~~~~~~~~~~~~~~~~~~~
Suppose we have
module A( op ) where
class C a where
op :: a -> a
module B where
import A
f = ..op...
Then the minimal import for module B is
import A( op )
not
import A( C( op ) )
which we would usually generate if C was exported from B. Hence
the (x `elem` xs) test when deciding what to generate.
%************************************************************************ %************************************************************************
%* * %* *
\subsection{Errors} \subsection{Errors}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment