X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=ghc%2Fcompiler%2Frename%2FRnNames.lhs;h=133b19daf3c6946c9926afe5955ef38ee80995ab;hb=0299e1a135c5805e09ed8e2271b3b17fc8a04869;hp=057430176323cc6ef49a42036e3b94cb46dc1684;hpb=9dd6e1c216993624a2cd74b62ca0f0569c02c26b;p=ghc-hetmet.git diff --git a/ghc/compiler/rename/RnNames.lhs b/ghc/compiler/rename/RnNames.lhs index 0574301..133b19d 100644 --- a/ghc/compiler/rename/RnNames.lhs +++ b/ghc/compiler/rename/RnNames.lhs @@ -1,42 +1,47 @@ % -% (c) The GRASP/AQUA Project, Glasgow University, 1992-1996 +% (c) The GRASP/AQUA Project, Glasgow University, 1992-1998 % \section[RnNames]{Extracting imported and top-level names in scope} \begin{code} module RnNames ( - getGlobalNames + ExportAvails, getGlobalNames, exportsFromAvail ) where #include "HsVersions.h" -import CmdLineOpts ( opt_NoImplicitPrelude, opt_WarnDuplicateExports, - opt_SourceUnchanged - ) - -import HsSyn ( HsModule(..), ImportDecl(..), HsDecl(..), - IE(..), ieName, - FixityDecl(..), - collectTopBinders - ) -import RdrHsSyn ( RdrNameHsDecl(..), RdrName(..), RdrNameIE(..), RdrNameImportDecl, - RdrNameHsModule, RdrNameFixityDecl, - rdrNameOcc, ieOcc - ) -import RnHsSyn ( RenamedHsModule(..), RenamedFixityDecl(..) ) -import RnIfaces ( getInterfaceExports, getDeclBinders, recordSlurp, checkUpToDate ) -import BasicTypes ( IfaceFlavour(..) ) +import CmdLineOpts ( DynFlag(..) ) + +import HsSyn ( HsModule(..), HsDecl(..), IE(..), ieName, ImportDecl(..), + ForeignDecl(..), + collectLocatedHsBinders + ) +import RdrHsSyn ( RdrNameIE, RdrNameImportDecl, + RdrNameHsModule, RdrNameHsDecl + ) +import RnIfaces ( recordLocalSlurps ) +import RnHiFiles ( getTyClDeclBinders, loadInterface ) import RnEnv import RnMonad import FiniteMap -import PrelMods -import UniqFM ( UniqFM, emptyUFM, addListToUFM_C, lookupUFM ) -import Bag ( Bag, bagToList ) -import Maybes ( maybeToBool, expectJust ) -import Name +import PrelNames ( pRELUDE_Name, mAIN_Name, main_RDR_Unqual, isUnboundName ) +import Module ( ModuleName, moduleName, WhereFrom(..) ) +import Name ( Name, nameSrcLoc, nameOccName ) +import NameSet +import NameEnv +import HscTypes ( Provenance(..), ImportReason(..), GlobalRdrEnv, + GenAvailInfo(..), AvailInfo, Avails, AvailEnv, + Deprecations(..), ModIface(..), emptyAvailEnv + ) +import RdrName ( rdrNameOcc, setRdrNameOcc ) +import OccName ( setOccNameSpace, dataName ) +import NameSet ( elemNameSet, emptyNameSet ) import Outputable -import Util ( removeDups ) +import Maybes ( maybeToBool, catMaybes, mapMaybe ) +import ListSetOps ( removeDups ) +import Util ( sortLt ) +import List ( partition ) \end{code} @@ -48,179 +53,210 @@ import Util ( removeDups ) %************************************************************************ \begin{code} -getGlobalNames :: RdrNameHsModule - -> RnMG (Maybe (ExportEnv, RnEnv, NameSet, Name -> PrintUnqualified)) - -- The NameSet is the set of names that are - -- either locally defined, - -- or explicitly imported - -- Nothing => no need to recompile - -getGlobalNames m@(HsModule this_mod _ exports imports _ _ mod_loc) - = fixRn (\ ~(rec_exp_fn, _) -> - - -- PROCESS LOCAL DECLS - -- Do these *first* so that the correct provenance gets - -- into the global name cache. - importsFromLocalDecls rec_exp_fn m `thenRn` \ (local_rn_env, local_mod_avails, local_avails) -> - - -- PROCESS IMPORT DECLS - mapAndUnzip3Rn importsFromImportDecl all_imports - `thenRn` \ (imp_rn_envs, imp_avails_s, explicit_imports_s) -> - - -- COMBINE RESULTS - -- We put the local env second, so that a local provenance - -- "wins", even if a module imports itself. - foldlRn plusRnEnv emptyRnEnv imp_rn_envs `thenRn` \ imp_rn_env -> - plusRnEnv imp_rn_env local_rn_env `thenRn` \ rn_env -> - - -- TRY FOR EARLY EXIT - -- We can't go for an early exit before this because we have to check - -- for name clashes. Consider: - -- - -- module A where module B where - -- import B h = True - -- f = h - -- - -- Suppose I've compiled everything up, and then I add a - -- new definition to module B, that defines "f". - -- - -- Then I must detect the name clash in A before going for an early - -- exit. The early-exit code checks what's actually needed from B - -- to compile A, and of course that doesn't include B.f. That's - -- why we wait till after the plusRnEnv stuff to do the early-exit. - checkEarlyExit this_mod `thenRn` \ up_to_date -> - if up_to_date then - returnRn (error "early exit", Nothing) - else - - - -- PROCESS EXPORT LISTS - let - export_avails :: ExportAvails - export_avails = foldr plusExportAvails local_mod_avails imp_avails_s - - explicit_names :: NameSet -- locally defined or explicitly imported - explicit_names = foldr add_on emptyNameSet (local_avails : explicit_imports_s) - add_on avails names = foldr (unionNameSets . mkNameSet . availNames) names avails - in - exportsFromAvail this_mod exports export_avails rn_env - `thenRn` \ (export_fn, export_env) -> - - -- RECORD THAT LOCALLY DEFINED THINGS ARE AVAILABLE - mapRn (recordSlurp Nothing Compulsory) local_avails `thenRn_` - - -- BUILD THE "IMPORT FN". It just tells whether a name is in - -- scope in an unqualified form. - let - print_unqual = mkImportFn imp_rn_env - in - - returnRn (export_fn, Just (export_env, rn_env, explicit_names, print_unqual)) - ) `thenRn` \ (_, result) -> - returnRn result - where - junk_exp_fn = error "RnNames:export_fn" +getGlobalNames :: Module -> RdrNameHsModule + -> RnMG (GlobalRdrEnv, -- Maps all in-scope things + GlobalRdrEnv, -- Maps just *local* things + ExportAvails) -- The exported stuff + +getGlobalNames this_mod (HsModule _ _ _ imports decls _ mod_loc) + = -- PROCESS LOCAL DECLS + -- Do these *first* so that the correct provenance gets + -- into the global name cache. + importsFromLocalDecls this_mod decls `thenRn` \ (local_gbl_env, local_mod_avails) -> + + -- PROCESS IMPORT DECLS + -- Do the non {- SOURCE -} ones first, so that we get a helpful + -- warning for {- SOURCE -} ones that are unnecessary + doptRn Opt_NoImplicitPrelude `thenRn` \ opt_no_prelude -> + let + all_imports = mk_prel_imports opt_no_prelude ++ imports + (source, ordinary) = partition is_source_import all_imports + is_source_import (ImportDecl _ ImportByUserSource _ _ _ _) = True + is_source_import other = False + + get_imports = importsFromImportDecl this_mod_name + in + mapAndUnzipRn get_imports ordinary `thenRn` \ (imp_gbl_envs1, imp_avails_s1) -> + mapAndUnzipRn get_imports source `thenRn` \ (imp_gbl_envs2, imp_avails_s2) -> + + -- COMBINE RESULTS + -- We put the local env second, so that a local provenance + -- "wins", even if a module imports itself. + let + gbl_env :: GlobalRdrEnv + imp_gbl_env = foldr plusGlobalRdrEnv emptyRdrEnv (imp_gbl_envs2 ++ imp_gbl_envs1) + gbl_env = imp_gbl_env `plusGlobalRdrEnv` local_gbl_env + + all_avails :: ExportAvails + all_avails = foldr plusExportAvails local_mod_avails (imp_avails_s2 ++ imp_avails_s1) + in - all_imports = prel_imports ++ imports + -- ALL DONE + returnRn (gbl_env, local_gbl_env, all_avails) + where + this_mod_name = moduleName this_mod -- NB: opt_NoImplicitPrelude is slightly different to import Prelude (); -- because the former doesn't even look at Prelude.hi for instance declarations, -- whereas the latter does. - prel_imports | this_mod == pRELUDE || - explicit_prelude_import || - opt_NoImplicitPrelude - = [] - - | otherwise = [ImportDecl pRELUDE - False {- Not qualified -} - HiFile {- Not source imported -} - Nothing {- No "as" -} - Nothing {- No import list -} - mod_loc] + mk_prel_imports no_prelude + | this_mod_name == pRELUDE_Name || + explicit_prelude_import || + no_prelude + = [] + + | otherwise = [ImportDecl pRELUDE_Name + ImportByUser + False {- Not qualified -} + Nothing {- No "as" -} + Nothing {- No import list -} + mod_loc] explicit_prelude_import - = not (null [ () | (ImportDecl mod qual _ _ _ _) <- imports, mod == pRELUDE ]) + = not (null [ () | (ImportDecl mod _ _ _ _ _) <- imports, mod == pRELUDE_Name ]) \end{code} \begin{code} -checkEarlyExit mod - = checkErrsRn `thenRn` \ no_errs_so_far -> - if not no_errs_so_far then - -- Found errors already, so exit now - returnRn True - else +importsFromImportDecl :: ModuleName + -> RdrNameImportDecl + -> RnMG (GlobalRdrEnv, + ExportAvails) + +importsFromImportDecl this_mod_name (ImportDecl imp_mod_name from qual_only as_mod import_spec iloc) + = pushSrcLocRn iloc $ - traceRn (text "Considering whether compilation is required...") `thenRn_` - if not opt_SourceUnchanged then - -- Source code changed and no errors yet... carry on - traceRn (nest 4 (text "source file changed or recompilation check turned off")) `thenRn_` - returnRn False + loadInterface (ppr imp_mod_name <+> ptext SLIT("is directly imported")) + imp_mod_name from `thenRn` \ iface -> + let + imp_mod = mi_module iface + avails_by_module = mi_exports iface + deprecs = mi_deprecs iface + + avails :: Avails + avails = [ avail | (mod_name, avails) <- avails_by_module, + mod_name /= this_mod_name, + avail <- avails ] + -- If the module exports anything defined in this module, just ignore it. + -- Reason: otherwise it looks as if there are two local definition sites + -- for the thing, and an error gets reported. Easiest thing is just to + -- filter them out up front. This situation only arises if a module + -- imports itself, or another module that imported it. (Necessarily, + -- this invoves a loop.) + -- + -- Tiresome consequence: if you say + -- module A where + -- import B( AType ) + -- type AType = ... + -- + -- module B( AType ) where + -- import {-# SOURCE #-} A( AType ) + -- + -- then you'll get a 'B does not export AType' message. Oh well. + + in + if null avails_by_module then + -- If there's an error in loadInterface, (e.g. interface + -- file not found) we get lots of spurious errors from 'filterImports' + returnRn (emptyRdrEnv, mkEmptyExportAvails imp_mod_name) else - -- Unchanged source, and no errors yet; see if usage info - -- up to date, and exit if so - checkUpToDate mod `thenRn` \ up_to_date -> - putDocRn (text "Compilation" <+> - text (if up_to_date then "IS NOT" else "IS") <+> - text "required") `thenRn_` - returnRn up_to_date -\end{code} - -\begin{code} -importsFromImportDecl :: RdrNameImportDecl - -> RnMG (RnEnv, ExportAvails, [AvailInfo]) + -- Complain if we import a deprecated module + ifOptRn Opt_WarnDeprecations ( + case deprecs of + DeprecAll txt -> addWarnRn (moduleDeprec imp_mod_name txt) + other -> returnRn () + ) `thenRn_` + + -- Filter the imports according to the import list + filterImports imp_mod_name from import_spec avails `thenRn` \ (filtered_avails, explicits) -> -importsFromImportDecl (ImportDecl mod qual_only as_source as_mod import_spec loc) - = pushSrcLocRn loc $ - getInterfaceExports mod as_source `thenRn` \ (avails, fixities) -> - filterImports mod import_spec avails `thenRn` \ (filtered_avails, hides, explicits) -> let - how_in_scope = FromImportDecl mod loc + unqual_imp = not qual_only -- Maybe want unqualified names + qual_mod = case as_mod of + Nothing -> imp_mod_name + Just another_name -> another_name + + mk_prov name = NonLocalDef (UserImport imp_mod iloc (name `elemNameSet` explicits)) + gbl_env = mkGlobalRdrEnv qual_mod unqual_imp mk_prov filtered_avails deprecs + exports = mkExportAvails qual_mod unqual_imp gbl_env filtered_avails in - qualifyImports mod - True -- Want qualified names - (not qual_only) -- Maybe want unqualified names - as_mod - hides - filtered_avails (\n -> how_in_scope) - [ (occ,(fixity,how_in_scope)) | (occ,fixity) <- fixities ] - `thenRn` \ (rn_env, mod_avails) -> - returnRn (rn_env, mod_avails, explicits) + returnRn (gbl_env, exports) \end{code} \begin{code} -importsFromLocalDecls rec_exp_fn (HsModule mod _ _ _ fix_decls decls _) - = foldlRn getLocalDeclBinders [] decls `thenRn` \ avails -> - mapRn fixityFromFixDecl fix_decls `thenRn` \ fixities -> - qualifyImports mod - False -- Don't want qualified names - True -- Want unqualified names - Nothing -- No "as M" part - [] -- Hide nothing - avails (\n -> FromLocalDefn (getSrcLoc n)) - fixities - `thenRn` \ (rn_env, mod_avails) -> - returnRn (rn_env, mod_avails, avails) +importsFromLocalDecls this_mod decls + = mapRn (getLocalDeclBinders this_mod) decls `thenRn` \ avails_s -> + -- The avails that are returned don't include the "system" names + let + avails = concat avails_s + + all_names :: [Name] -- All the defns; no dups eliminated + all_names = [name | avail <- avails, name <- availNames avail] + + dups :: [[Name]] + (_, dups) = removeDups compare all_names + in + -- Check for duplicate definitions + -- The complaint will come out as "Multiple declarations of Foo.f" because + -- since 'f' is in the env twice, the unQualInScope used by the error-msg + -- printer returns False. It seems awkward to fix, unfortunately. + mapRn_ (addErrRn . dupDeclErr) dups `thenRn_` + + + -- Record that locally-defined things are available + recordLocalSlurps (availsToNameSet avails) `thenRn_` + let + mod_name = moduleName this_mod + unqual_imp = True -- Want unqualified names + mk_prov n = LocalDef -- Provenance is local + + gbl_env = mkGlobalRdrEnv mod_name unqual_imp mk_prov avails NoDeprecs + -- NoDeprecs: don't complain about locally defined names + -- For a start, we may be exporting a deprecated thing + -- Also we may use a deprecated thing in the defn of another + -- deprecated things. We may even use a deprecated thing in + -- the defn of a non-deprecated thing, when changing a module's + -- interface + + exports = mkExportAvails mod_name unqual_imp gbl_env avails + in + returnRn (gbl_env, exports) + +--------------------------- +getLocalDeclBinders :: Module -> RdrNameHsDecl -> RnMG [AvailInfo] +getLocalDeclBinders mod (TyClD tycl_decl) + = -- For type and class decls, we generate Global names, with + -- no export indicator. They need to be global because they get + -- permanently bound into the TyCons and Classes. They don't need + -- an export indicator because they are all implicitly exported. + getTyClDeclBinders mod tycl_decl `thenRn` \ (avail, sys_names) -> + + -- Record that the system names are available + recordLocalSlurps (mkNameSet sys_names) `thenRn_` + returnRn [avail] + +getLocalDeclBinders mod (ValD binds) + = mapRn new (collectLocatedHsBinders binds) `thenRn` \ avails -> + returnRn avails where - newLocalName rdr_name loc - = newLocallyDefinedGlobalName mod (rdrNameOcc rdr_name) rec_exp_fn loc - - getLocalDeclBinders avails (ValD binds) - = mapRn do_one (bagToList (collectTopBinders binds)) `thenRn` \ val_avails -> - returnRn (val_avails ++ avails) - - getLocalDeclBinders avails decl - = getDeclBinders newLocalName decl `thenRn` \ avail -> - case avail of - NotAvailable -> returnRn avails -- Instance decls and suchlike - other -> returnRn (avail : avails) - - do_one (rdr_name, loc) - = newLocalName rdr_name loc `thenRn` \ name -> - returnRn (Avail name) + new (rdr_name, loc) = newTopBinder mod rdr_name loc `thenRn` \ name -> + returnRn (Avail name) + +getLocalDeclBinders mod (ForD (ForeignImport nm _ _ _ loc)) + = newTopBinder mod nm loc `thenRn` \ name -> + returnRn [Avail name] +getLocalDeclBinders mod (ForD _) + = returnRn [] + +getLocalDeclBinders mod (FixD _) = returnRn [] +getLocalDeclBinders mod (DeprecD _) = returnRn [] +getLocalDeclBinders mod (DefD _) = returnRn [] +getLocalDeclBinders mod (InstD _) = returnRn [] +getLocalDeclBinders mod (RuleD _) = returnRn [] \end{code} + %************************************************************************ %* * \subsection{Filtering imports} @@ -231,48 +267,93 @@ importsFromLocalDecls rec_exp_fn (HsModule mod _ _ _ fix_decls decls _) available, and filters it through the import spec (if any). \begin{code} -filterImports :: Module - -> Maybe (Bool, [RdrNameIE]) -- Import spec; True => hidin - -> [AvailInfo] -- What's available - -> RnMG ([AvailInfo], -- What's actually imported - [AvailInfo], -- What's to be hidden (the unqualified version, that is) - [AvailInfo]) -- What was imported explicitly +filterImports :: ModuleName -- The module being imported + -> WhereFrom -- Tells whether it's a {-# SOURCE #-} import + -> Maybe (Bool, [RdrNameIE]) -- Import spec; True => hiding + -> [AvailInfo] -- What's available + -> RnMG ([AvailInfo], -- What's imported + NameSet) -- What was imported explicitly -- Complains if import spec mentions things that the module doesn't export -- Warns/informs if import spec contains duplicates. -filterImports mod Nothing imports - = returnRn (imports, [], []) - -filterImports mod (Just (want_hiding, import_items)) avails - = mapRn check_item import_items `thenRn` \ item_avails -> - if want_hiding - then - returnRn (avails, item_avails, []) -- All imported; item_avails to be hidden - else - returnRn (item_avails, [], item_avails) -- Just item_avails imported; nothing to be hidden +filterImports mod from Nothing imports + = returnRn (imports, emptyNameSet) +filterImports mod from (Just (want_hiding, import_items)) total_avails + = flatMapRn get_item import_items `thenRn` \ avails_w_explicits -> + let + (item_avails, explicits_s) = unzip avails_w_explicits + explicits = foldl addListToNameSet emptyNameSet explicits_s + in + if want_hiding then + let -- All imported; item_avails to be hidden + hidden = availsToNameSet item_avails + keep n = not (n `elemNameSet` hidden) + in + returnRn (pruneAvails keep total_avails, emptyNameSet) + else + -- Just item_avails imported; nothing to be hidden + returnRn (item_avails, explicits) where import_fm :: FiniteMap OccName AvailInfo import_fm = listToFM [ (nameOccName name, avail) - | avail <- avails, - name <- availEntityNames avail] + | avail <- total_avails, + name <- availNames avail] + -- Even though availNames returns data constructors too, + -- they won't make any difference because naked entities like T + -- in an import list map to TcOccs, not VarOccs. + + bale_out item = addErrRn (badImportItemErr mod from item) `thenRn_` + returnRn [] + + get_item :: RdrNameIE -> RnMG [(AvailInfo, [Name])] + -- Empty list for a bad item. + -- Singleton is typical case. + -- Can have two when we are hiding, and mention C which might be + -- both a class and a data constructor. + -- The [Name] is the list of explicitly-mentioned names + get_item item@(IEModuleContents _) = bale_out item + + get_item item@(IEThingAll _) + = case check_item item of + Nothing -> bale_out item + Just avail@(AvailTC _ [n]) -> -- This occurs when you import T(..), but + -- only export T abstractly. The single [n] + -- in the AvailTC is the type or class itself + ifOptRn Opt_WarnMisc (addWarnRn (dodgyImportWarn mod item)) `thenRn_` + returnRn [(avail, [availName avail])] + Just avail -> returnRn [(avail, [availName avail])] + + get_item item@(IEThingAbs n) + | want_hiding -- hiding( C ) + -- Here the 'C' can be a data constructor *or* a type/class + = case catMaybes [check_item item, check_item (IEVar data_n)] of + [] -> bale_out item + avails -> returnRn [(a, []) | a <- avails] + -- The 'explicits' list is irrelevant when hiding + where + data_n = setRdrNameOcc n (setOccNameSpace (rdrNameOcc n) dataName) - check_item item@(IEModuleContents _) - = addErrRn (badImportItemErr mod item) `thenRn_` - returnRn NotAvailable + get_item item + = case check_item item of + Nothing -> bale_out item + Just avail -> returnRn [(avail, availNames avail)] check_item item | not (maybeToBool maybe_in_import_avails) || - (case filtered_avail of { NotAvailable -> True; other -> False }) - = addErrRn (badImportItemErr mod item) `thenRn_` - returnRn NotAvailable + not (maybeToBool maybe_filtered_avail) + = Nothing - | otherwise = returnRn filtered_avail + | otherwise + = Just filtered_avail where - maybe_in_import_avails = lookupFM import_fm (ieOcc item) + wanted_occ = rdrNameOcc (ieName item) + maybe_in_import_avails = lookupFM import_fm wanted_occ + Just avail = maybe_in_import_avails - filtered_avail = filterAvail item avail + maybe_filtered_avail = filterAvail item avail + Just filtered_avail = maybe_filtered_avail \end{code} @@ -283,291 +364,181 @@ filterImports mod (Just (want_hiding, import_items)) avails %* * %************************************************************************ -@qualifyImports@ takes the @ExportEnv@ after filtering through the import spec -of an import decl, and deals with producing an @RnEnv@ with the -right qualified names. It also turns the @Names@ in the @ExportEnv@ into -fully fledged @Names@. - \begin{code} -qualifyImports :: Module -- Imported module - -> Bool -- True <=> want qualified import - -> Bool -- True <=> want unqualified import - -> Maybe Module -- Optional "as M" part - -> [AvailInfo] -- What's to be hidden - -> Avails -> (Name -> HowInScope) -- Whats imported and how - -> [(OccName, (Fixity, HowInScope))] -- Ditto for fixities - -> RnMG (RnEnv, ExportAvails) - -qualifyImports this_mod qual_imp unqual_imp as_mod hides - avails name_to_his fixities - = - -- Make the name environment. Even though we're talking about a - -- single import module there might still be name clashes, - -- because it might be the module being compiled. - foldlRn add_avail emptyGlobalNameEnv avails `thenRn` \ name_env1 -> - let - -- Delete things that are hidden - name_env2 = foldl del_avail name_env1 hides - - -- Create the fixity env - fixity_env = foldl (add_fixity name_env2) emptyFixityEnv fixities - - -- Create the export-availability info - export_avails = mkExportAvails unqual_imp qual_mod avails - in - returnRn (RnEnv name_env2 fixity_env, export_avails) +type ExportAvails + = (FiniteMap ModuleName Avails, + -- Used to figure out "module M" export specifiers + -- Includes avails only from *unqualified* imports + -- (see 1.4 Report Section 5.1.1) + + AvailEnv) -- All the things that are available. + -- Its domain is all the "main" things; + -- i.e. *excluding* class ops and constructors + -- (which appear inside their parent AvailTC) + +mkEmptyExportAvails :: ModuleName -> ExportAvails +mkEmptyExportAvails mod_name = (unitFM mod_name [], emptyNameEnv) + +plusExportAvails :: ExportAvails -> ExportAvails -> ExportAvails +plusExportAvails (m1, e1) (m2, e2) = (plusFM_C (++) m1 m2, plusAvailEnv e1 e2) + +mkExportAvails :: ModuleName -> Bool -> GlobalRdrEnv -> [AvailInfo] -> ExportAvails +mkExportAvails mod_name unqual_imp gbl_env avails + = (mod_avail_env, entity_avail_env) where - qual_mod = case as_mod of - Nothing -> this_mod - Just another_name -> another_name - - add_avail :: GlobalNameEnv -> AvailInfo -> RnMG GlobalNameEnv - add_avail env avail = foldlRn add_name env (availNames avail) - add_name env name = add qual_imp env (Qual qual_mod occ err_hif) `thenRn` \ env1 -> - add unqual_imp env1 (Unqual occ) - where - add False env rdr_name = returnRn env - add True env rdr_name = addOneToGlobalNameEnv env rdr_name (name, name_to_his name) - occ = nameOccName name - - del_avail env avail = foldl delOneFromGlobalNameEnv env rdr_names - where - rdr_names = map (Unqual . nameOccName) (availNames avail) - - add_fixity name_env fix_env (occ_name, fixity) - = add qual $ add unqual $ fix_env - where - qual = Qual qual_mod occ_name err_hif - unqual = Unqual occ_name - - add rdr_name fix_env | maybeToBool (lookupFM name_env rdr_name) - = addOneToFixityEnv fix_env rdr_name fixity - | otherwise - = fix_env - -err_hif = error "qualifyImports: hif" -- Not needed in key to mapping + mod_avail_env = unitFM mod_name unqual_avails + + -- unqual_avails is the Avails that are visible in *unqualified* form + -- We need to know this so we know what to export when we see + -- module M ( module P ) where ... + -- Then we must export whatever came from P unqualified. + + unqual_avails | not unqual_imp = [] -- Short cut when no unqualified imports + | otherwise = pruneAvails (unQualInScope gbl_env) avails + + entity_avail_env = foldl insert emptyAvailEnv avails + insert env avail = extendNameEnv_C plusAvail env (availName avail) avail + -- 'avails' may have several items with the same availName + -- E.g import Ix( Ix(..), index ) + -- will give Ix(Ix,index,range) and Ix(index) + -- We want to combine these \end{code} -unQualify adds an Unqual binding for every existing Qual binding. - -\begin{code} -unQualify :: FiniteMap RdrName elt -> FiniteMap RdrName elt -unQualify fm = addListToFM fm [(Unqual occ, elt) | (Qual _ occ _, elt) <- fmToList fm] -\end{code} %************************************************************************ %* * -\subsection{Local declarations} +\subsection{Export list processing} %* * %************************************************************************ - -\begin{code} -fixityFromFixDecl :: RdrNameFixityDecl -> RnMG (OccName, (Fixity, HowInScope)) - -fixityFromFixDecl (FixityDecl rdr_name fixity loc) - = returnRn (rdrNameOcc rdr_name, (fixity, FromLocalDefn loc)) -\end{code} - - -%************************************************************************ -%* * -\subsection{Export list processing -%* * -%************************************************************************ - -The @AvailEnv@ type is just used internally in @exportsFromAvail@. -When exporting we need to combine the availabilities for a particular -exported thing, and we also need to check for name clashes -- that -is: two exported things must have different @OccNames@. - -\begin{code} -type AvailEnv = FiniteMap OccName (RdrNameIE, AvailInfo, Int{-no. of clashes-}) - -- The FM maps each OccName to the RdrNameIE that gave rise to it, - -- for error reporting, as well as to its AvailInfo - -emptyAvailEnv = emptyFM - -{- - Add new entry to environment. Checks for name clashes, i.e., - plain duplicates or exported entity pairs that have different OccNames. - (c.f. 5.1.1 of Haskell 1.4 report.) --} -addAvailEnv :: Bool -> RdrNameIE -> AvailEnv -> AvailInfo -> RnM s d AvailEnv -addAvailEnv warn_dups ie env NotAvailable = returnRn env -addAvailEnv warn_dups ie env (AvailTC _ []) = returnRn env -addAvailEnv warn_dups ie env avail - | warn_dups = mapMaybeRn (addErrRn . availClashErr) () conflict `thenRn_` - returnRn (addToFM_C add_avail env key elt) - | otherwise = returnRn (addToFM_C add_avail env key elt) - where - key = nameOccName (availName avail) - elt = (ie,avail,reports_on) - - reports_on - | maybeToBool dup = 1 - | otherwise = 0 - - conflict = conflictFM bad_avail env key elt - dup - | warn_dups = conflictFM dup_avail env key elt - | otherwise = Nothing - -addListToAvailEnv :: AvailEnv -> RdrNameIE -> [AvailInfo] -> RnM s d AvailEnv -addListToAvailEnv env ie items = foldlRn (addAvailEnv False ie) env items - -bad_avail (ie1,avail1,r1) (ie2,avail2,r2) - = availName avail1 /= availName avail2 -- Same OccName, different Name -dup_avail (ie1,avail1,r1) (ie2,avail2,r2) - = availName avail1 == availName avail2 -- Same OccName & avail. - -add_avail (ie1,a1,r1) (ie2,a2,r2) = (ie1, a1 `plusAvail` a2, r1 + r2) -\end{code} - Processing the export list. -You might think that we should record things that appear in the export list as -``occurrences'' (using addOccurrenceName), but you'd be wrong. We do check (here) -that they are in scope, but there is no need to slurp in their actual declaration -(which is what addOccurrenceName forces). Indeed, doing so would big trouble when -compiling PrelBase, because it re-exports GHC, which includes takeMVar#, whose type -includes ConcBase.StateAndSynchVar#, and so on... +You might think that we should record things that appear in the export list +as ``occurrences'' (using @addOccurrenceName@), but you'd be wrong. +We do check (here) that they are in scope, +but there is no need to slurp in their actual declaration +(which is what @addOccurrenceName@ forces). + +Indeed, doing so would big trouble when +compiling @PrelBase@, because it re-exports @GHC@, which includes @takeMVar#@, +whose type includes @ConcBase.StateAndSynchVar#@, and so on... \begin{code} -exportsFromAvail :: Module - -> Maybe [RdrNameIE] -- Export spec - -> ExportAvails - -> RnEnv - -> RnMG (Name -> ExportFlag, ExportEnv) +type ExportAccum -- The type of the accumulating parameter of + -- the main worker function in exportsFromAvail + = ([ModuleName], -- 'module M's seen so far + ExportOccMap, -- Tracks exported occurrence names + AvailEnv) -- The accumulated exported stuff, kept in an env + -- so we can common-up related AvailInfos + +type ExportOccMap = FiniteMap OccName (Name, RdrNameIE) + -- Tracks what a particular exported OccName + -- in an export list refers to, and which item + -- it came from. It's illegal to export two distinct things + -- that have the same occurrence name + + +exportsFromAvail :: ModuleName + -> Maybe [RdrNameIE] -- Export spec + -> FiniteMap ModuleName Avails -- Used for (module M) exports + -> NameEnv AvailInfo -- Domain is every in-scope thing + -> GlobalRdrEnv + -> RnMG Avails -- Complains if two distinct exports have same OccName -- Warns about identical exports. -- Complains about exports items not in scope -exportsFromAvail this_mod Nothing export_avails rn_env - = exportsFromAvail this_mod (Just [IEModuleContents this_mod]) export_avails rn_env +exportsFromAvail this_mod Nothing + mod_avail_env entity_avail_env global_name_env + = exportsFromAvail this_mod true_exports mod_avail_env entity_avail_env global_name_env + where + true_exports = Just $ if this_mod == mAIN_Name + then [IEVar main_RDR_Unqual] + -- export Main.main *only* unless otherwise specified, + else [IEModuleContents this_mod] + -- but for all other modules export everything. exportsFromAvail this_mod (Just export_items) - (mod_avail_env, entity_avail_env) - (RnEnv global_name_env fixity_env) - = checkForModuleExportDups export_items `thenRn` \ export_items' -> - foldlRn exports_from_item emptyAvailEnv export_items' `thenRn` \ export_avail_env -> + mod_avail_env entity_avail_env global_name_env + = doptRn Opt_WarnDuplicateExports `thenRn` \ warn_dup_exports -> + foldlRn (exports_from_item warn_dup_exports) + ([], emptyFM, emptyAvailEnv) export_items + `thenRn` \ (_, _, export_avail_map) -> let - dup_entries = fmToList (filterFM (\ _ (_,_,clashes) -> clashes > 0) export_avail_env) + export_avails :: [AvailInfo] + export_avails = nameEnvElts export_avail_map in - mapRn (addWarnRn . dupExportWarn) dup_entries `thenRn_` - let - export_avails = map (\ (_,a,_) -> a) (eltsFM export_avail_env) - export_fixities = mk_exported_fixities (availsToNameSet export_avails) - export_fn = mk_export_fn export_avails - in - returnRn (export_fn, ExportEnv export_avails export_fixities) + returnRn export_avails where - exports_from_item :: AvailEnv -> RdrNameIE -> RnMG AvailEnv - exports_from_item export_avail_env ie@(IEModuleContents mod) + exports_from_item :: Bool -> ExportAccum -> RdrNameIE -> RnMG ExportAccum + + exports_from_item warn_dups acc@(mods, occs, avails) ie@(IEModuleContents mod) + | mod `elem` mods -- Duplicate export of M + = warnCheckRn warn_dups (dupModuleExport mod) `thenRn_` + returnRn acc + + | otherwise = case lookupFM mod_avail_env mod of - Nothing -> failWithRn export_avail_env (modExportErr mod) - Just avails -> addListToAvailEnv export_avail_env ie avails - - exports_from_item export_avail_env ie - | not (maybeToBool maybe_in_scope) - = failWithRn export_avail_env (unknownNameErr (ieName ie)) - -#ifdef DEBUG - -- I can't see why this should ever happen; if the thing is in scope - -- at all it ought to have some availability - | not (maybeToBool maybe_avail) - = pprTrace "exportsFromAvail: curious Nothing:" (ppr name) - returnRn export_avail_env -#endif - - | not enough_avail - = failWithRn export_avail_env (exportItemErr ie export_avail) - - | otherwise -- Phew! It's OK! - = addAvailEnv opt_WarnDuplicateExports ie export_avail_env export_avail - where - maybe_in_scope = lookupFM global_name_env (ieName ie) - Just (name,_) = maybe_in_scope - maybe_avail = lookupUFM entity_avail_env name - Just avail = maybe_avail - export_avail = filterAvail ie avail - enough_avail = case export_avail of {NotAvailable -> False; other -> True} - - -- We export a fixity iff we export a thing with the same (qualified) RdrName - mk_exported_fixities :: NameSet -> [(OccName, Fixity)] - mk_exported_fixities exports - = fmToList (foldr (perhaps_add_fixity exports) - emptyFM - (fmToList fixity_env)) - - perhaps_add_fixity :: NameSet -> (RdrName, (Fixity, HowInScope)) - -> FiniteMap OccName Fixity - -> FiniteMap OccName Fixity - perhaps_add_fixity exports (rdr_name, (fixity, how_in_scope)) fix_env - = let - do_nothing = fix_env -- The default is to pass on the env unchanged - in - -- Step 1: check whether the rdr_name is in scope; if so find its Name - case lookupFM global_name_env rdr_name of { - Nothing -> do_nothing; - Just (fixity_name,_) -> - - -- Step 2: check whether the fixity thing is exported - if not (fixity_name `elemNameSet` exports) then - do_nothing - else - - -- Step 3: check whether we already have a fixity for the - -- Name's OccName in the fix_env we are building up. This can easily - -- happen. the original fixity_env might contain bindings for - -- M.a and N.a, if a was imported via M and N. - -- If this does happen, we expect the fixity to be the same either way. - let - occ_name = rdrNameOcc rdr_name - in - case lookupFM fix_env occ_name of { - Just fixity1 -> -- Got it already - ASSERT( fixity == fixity1 ) - do_nothing; - Nothing -> - - -- Step 3: add it to the outgoing fix_env - addToFM fix_env occ_name fixity - }} - -{- warn and weed out duplicate module entries from export list. -} -checkForModuleExportDups :: [RdrNameIE] -> RnMG [RdrNameIE] -checkForModuleExportDups ls - | opt_WarnDuplicateExports = check_modules ls - | otherwise = returnRn ls - where - -- NOTE: reorders the export list by moving all module-contents - -- exports to the end (removing duplicates in the process.) - check_modules ls = - (case dups of - [] -> returnRn () - ls -> mapRn (\ ds@(IEModuleContents x:_) -> - addWarnRn (dupModuleExport x (length ds))) ls `thenRn_` - returnRn ()) `thenRn_` - returnRn (ls_no_modules ++ no_module_dups) - where - (ls_no_modules,modules) = foldr split_mods ([],[]) ls - - split_mods i@(IEModuleContents _) ~(no_ms,ms) = (no_ms,i:ms) - split_mods i ~(no_ms,ms) = (i:no_ms,ms) - - (no_module_dups, dups) = removeDups cmp_mods modules - - cmp_mods (IEModuleContents m1) (IEModuleContents m2) = m1 `compare` m2 - -mk_export_fn :: [AvailInfo] -> (Name -> ExportFlag) -mk_export_fn avails - = \name -> if name `elemNameSet` exported_names - then Exported - else NotExported + Nothing -> failWithRn acc (modExportErr mod) + Just mod_avails -> foldlRn (check_occs ie) occs mod_avails + `thenRn` \ occs' -> + let + avails' = foldl addAvail avails mod_avails + in + returnRn (mod:mods, occs', avails') + + exports_from_item warn_dups acc@(mods, occs, avails) ie + = lookupSrcName global_name_env (ieName ie) `thenRn` \ name -> + + -- See what's available in the current environment + case lookupNameEnv entity_avail_env name of { + Nothing -> -- Presumably this happens because lookupSrcName didn't find + -- the name and returned an unboundName, which won't be in + -- the entity_avail_env, of course + WARN( not (isUnboundName name), ppr name ) + returnRn acc ; + + Just avail -> + + -- Filter out the bits we want + case filterAvail ie avail of { + Nothing -> -- Not enough availability + failWithRn acc (exportItemErr ie) ; + + Just export_avail -> + + -- Phew! It's OK! Now to check the occurrence stuff! + warnCheckRn (ok_item ie avail) (dodgyExportWarn ie) `thenRn_` + check_occs ie occs export_avail `thenRn` \ occs' -> + returnRn (mods, occs', addAvail avails export_avail) + }} + + + +ok_item (IEThingAll _) (AvailTC _ [n]) = False + -- This occurs when you import T(..), but + -- only export T abstractly. The single [n] + -- in the AvailTC is the type or class itself +ok_item _ _ = True + +check_occs :: RdrNameIE -> ExportOccMap -> AvailInfo -> RnMG ExportOccMap +check_occs ie occs avail + = doptRn Opt_WarnDuplicateExports `thenRn` \ warn_dup_exports -> + foldlRn (check warn_dup_exports) occs (availNames avail) where - exported_names :: NameSet - exported_names = availsToNameSet avails + check warn_dup occs name + = case lookupFM occs name_occ of + Nothing -> returnRn (addToFM occs name_occ (name, ie)) + Just (name', ie') + | name == name' -> -- Duplicate export + warnCheckRn warn_dup + (dupExportWarn name_occ ie ie') + `thenRn_` returnRn occs + + | otherwise -> -- Same occ name but different names: an error + failWithRn occs (exportClashErr name_occ ie ie') + where + name_occ = nameOccName name \end{code} %************************************************************************ @@ -577,33 +548,52 @@ mk_export_fn avails %************************************************************************ \begin{code} -badImportItemErr mod ie - = sep [ptext SLIT("Module"), quotes (pprModule mod), +badImportItemErr mod from ie + = sep [ptext SLIT("Module"), quotes (ppr mod), source_import, ptext SLIT("does not export"), quotes (ppr ie)] - + where + source_import = case from of + ImportByUserSource -> ptext SLIT("(hi-boot interface)") + other -> empty + +dodgyImportWarn mod item = dodgyMsg (ptext SLIT("import")) item +dodgyExportWarn item = dodgyMsg (ptext SLIT("export")) item + +dodgyMsg kind item@(IEThingAll tc) + = sep [ ptext SLIT("The") <+> kind <+> ptext SLIT("item") <+> quotes (ppr item), + ptext SLIT("suggests that") <+> quotes (ppr tc) <+> ptext SLIT("has constructor or class methods"), + ptext SLIT("but it has none; it is a type synonym or abstract type or class") ] + modExportErr mod - = hsep [ ptext SLIT("Unknown module in export list: module"), quotes (pprModule mod)] + = hsep [ ptext SLIT("Unknown module in export list: module"), quotes (ppr mod)] -exportItemErr export_item NotAvailable - = sep [ ptext SLIT("Export item not in scope:"), quotes (ppr export_item)] +exportItemErr export_item + = sep [ ptext SLIT("The export item") <+> quotes (ppr export_item), + ptext SLIT("attempts to export constructors or class methods that are not visible here") ] -exportItemErr export_item avail - = hang (ptext SLIT("Export item not fully in scope:")) - 4 (vcat [hsep [ptext SLIT("Wanted: "), ppr export_item], - hsep [ptext SLIT("Available:"), ppr (ieOcc export_item), pprAvail avail]]) +exportClashErr occ_name ie1 ie2 + = hsep [ptext SLIT("The export items"), quotes (ppr ie1) + ,ptext SLIT("and"), quotes (ppr ie2) + ,ptext SLIT("create conflicting exports for"), quotes (ppr occ_name)] -availClashErr (occ_name, ((ie1,avail1,_), (ie2,avail2,_))) - = hsep [ptext SLIT("The export items"), quotes (ppr ie1), ptext SLIT("and"), quotes (ppr ie2), - ptext SLIT("create conflicting exports for"), quotes (ppr occ_name)] +dupDeclErr (n:ns) + = vcat [ptext SLIT("Multiple declarations of") <+> quotes (ppr n), + nest 4 (vcat (map ppr sorted_locs))] + where + sorted_locs = sortLt occ'ed_before (map nameSrcLoc (n:ns)) + occ'ed_before a b = LT == compare a b -dupExportWarn (occ_name, (_,_,times)) +dupExportWarn occ_name ie1 ie2 = hsep [quotes (ppr occ_name), - ptext SLIT("mentioned"), speakNTimes (times+1), - ptext SLIT("in export list")] + ptext SLIT("is exported by"), quotes (ppr ie1), + ptext SLIT("and"), quotes (ppr ie2)] -dupModuleExport mod times - = hsep [ptext SLIT("Module"), quotes (pprModule mod), - ptext SLIT("mentioned"), speakNTimes times, +dupModuleExport mod + = hsep [ptext SLIT("Duplicate"), + quotes (ptext SLIT("Module") <+> ppr mod), ptext SLIT("in export list")] -\end{code} +moduleDeprec mod txt + = sep [ ptext SLIT("Module") <+> quotes (ppr mod) <+> ptext SLIT("is deprecated:"), + nest 4 (ppr txt) ] +\end{code}