\section[CompManager]{The Compilation Manager}
\begin{code}
-module CompManager ( cmInit, cmLoadModule, cmUnload,
+module CompManager (
+ cmInit, -- :: GhciMode -> IO CmState
+ cmLoadModule, -- :: CmState -> FilePath -> IO (CmState, [String])
+ cmUnload, -- :: CmState -> IO CmState
+ cmTypeOfName, -- :: CmState -> Name -> IO (Maybe String)
+
+ cmSetContext, -- :: CmState -> String -> IO CmState
+ cmGetContext, -- :: CmState -> IO String
#ifdef GHCI
- cmGetExpr, cmRunExpr,
+ cmRunStmt, -- :: CmState -> DynFlags -> String -> IO (CmState, [Name])
#endif
- CmState, emptyCmState -- abstract
- )
+ CmState, emptyCmState -- abstract
+ )
where
#include "HsVersions.h"
import CmLink
import CmTypes
import HscTypes
+import RnEnv ( unQualInScope )
+import Id ( idType, idName )
+import Name ( Name, lookupNameEnv, extendNameEnvList,
+ NamedThing(..) )
+import RdrName ( emptyRdrEnv )
import Module ( Module, ModuleName, moduleName, isHomeModule,
- mkModuleName, moduleNameUserString )
+ mkModuleName, moduleNameUserString, moduleUserString )
import CmStaticInfo ( GhciMode(..) )
import DriverPipeline
import GetImports
-import HscTypes ( HomeSymbolTable, HomeIfaceTable,
- PersistentCompilerState, ModDetails(..) )
+import Type ( tidyType )
+import VarEnv ( emptyTidyEnv )
+import HscTypes
import HscMain ( initPersistentCompilerState )
import Finder
-import UniqFM ( emptyUFM, lookupUFM, addToUFM, delListFromUFM,
+import UniqFM ( lookupUFM, addToUFM, delListFromUFM,
UniqFM, listToUFM )
import Unique ( Uniquable )
import Digraph ( SCC(..), stronglyConnComp, flattenSCC )
import ErrUtils ( showPass )
import Util
import DriverUtil
+import TmpFiles
import Outputable
import Panic
import CmdLineOpts ( DynFlags(..) )
#ifdef GHCI
import Interpreter ( HValue )
-import HscMain ( hscExpr )
-import Type ( Type )
+import HscMain ( hscStmt )
import PrelGHC ( unsafeCoerce# )
#endif
\begin{code}
-cmInit :: GhciMode -> IO CmState
-cmInit gmode
- = emptyCmState gmode
-
-#ifdef GHCI
-cmGetExpr :: CmState
- -> DynFlags
- -> Bool -- True <=> wrap in 'print' to get an IO-typed result
- -> Module
- -> String
- -> IO (CmState, Maybe (HValue, PrintUnqualified, Type))
-cmGetExpr cmstate dflags wrap_io mod expr
- = do (new_pcs, maybe_stuff) <-
- hscExpr dflags wrap_io hst hit pcs mod expr
- case maybe_stuff of
- Nothing -> return (cmstate{ pcs=new_pcs }, Nothing)
- Just (bcos, print_unqual, ty) -> do
- hValue <- linkExpr pls bcos
- return (cmstate{ pcs=new_pcs },
- Just (hValue, print_unqual, ty))
-
- -- ToDo: check that the module we passed in is sane/exists?
- where
- CmState{ hst=hst, hit=hit, pcs=pcs, pls=pls } = cmstate
-
--- The HValue should represent a value of type IO () (Perhaps IO a?)
-cmRunExpr :: HValue -> IO ()
-cmRunExpr hval
- = do unsafeCoerce# hval :: IO ()
- -- putStrLn "done."
-#endif
-
-emptyHIT :: HomeIfaceTable
-emptyHIT = emptyUFM
-emptyHST :: HomeSymbolTable
-emptyHST = emptyUFM
-
-- Persistent state for the entire system
data CmState
= CmState {
ui :: UnlinkedImage, -- the unlinked images
mg :: ModuleGraph, -- the module graph
gmode :: GhciMode, -- NEVER CHANGES
+ ic :: InteractiveContext, -- command-line binding info
pcs :: PersistentCompilerState, -- compile's persistent state
pls :: PersistentLinkerState -- link's persistent state
}
-emptyCmState :: GhciMode -> IO CmState
-emptyCmState gmode
+emptyCmState :: GhciMode -> Module -> IO CmState
+emptyCmState gmode mod
= do pcs <- initPersistentCompilerState
pls <- emptyPLS
- return (CmState { hst = emptyHST,
- hit = emptyHIT,
- ui = emptyUI,
- mg = emptyMG,
- gmode = gmode,
+ return (CmState { hst = emptySymbolTable,
+ hit = emptyIfaceTable,
+ ui = emptyUI,
+ mg = emptyMG,
+ gmode = gmode,
+ ic = emptyInteractiveContext mod,
pcs = pcs,
pls = pls })
+emptyInteractiveContext mod
+ = InteractiveContext { ic_module = mod,
+ ic_rn_env = emptyRdrEnv,
+ ic_type_env = emptyTypeEnv }
+
+defaultCurrentModuleName = mkModuleName "Prelude"
+GLOBAL_VAR(defaultCurrentModule, error "no defaultCurrentModule", Module)
+
-- CM internal types
type UnlinkedImage = [Linkable] -- the unlinked images (should be a set, really)
emptyUI :: UnlinkedImage
emptyMG :: ModuleGraph
emptyMG = []
-\end{code}
+-----------------------------------------------------------------------------
+-- Produce an initial CmState.
-Unload the compilation manager's state: everything it knows about the
-current collection of modules in the Home package.
+cmInit :: GhciMode -> IO CmState
+cmInit mode = do
+ prel <- moduleNameToModule defaultCurrentModuleName
+ writeIORef defaultCurrentModule prel
+ emptyCmState mode prel
+
+-----------------------------------------------------------------------------
+-- Setting the context doesn't throw away any bindings; the bindings
+-- we've built up in the InteractiveContext simply move to the new
+-- module. They always shadow anything in scope in the current context.
+
+cmSetContext :: CmState -> String -> IO CmState
+cmSetContext cmstate str
+ = do let mn = mkModuleName str
+ modules_loaded = [ (name_of_summary s, ms_mod s) | s <- mg cmstate ]
+
+ m <- case lookup mn modules_loaded of
+ Just m -> return m
+ Nothing -> do
+ mod <- moduleNameToModule mn
+ if isHomeModule mod
+ then throwDyn (OtherError (showSDoc
+ (quotes (ppr (moduleName mod))
+ <+> text "is not currently loaded")))
+ else return mod
+
+ return cmstate{ ic = (ic cmstate){ic_module=m} }
+
+cmGetContext :: CmState -> IO String
+cmGetContext cmstate = return (moduleUserString (ic_module (ic cmstate)))
+
+moduleNameToModule :: ModuleName -> IO Module
+moduleNameToModule mn
+ = do maybe_stuff <- findModule mn
+ case maybe_stuff of
+ Nothing -> throwDyn (OtherError ("can't find module `"
+ ++ moduleNameUserString mn ++ "'"))
+ Just (m,_) -> return m
+
+-----------------------------------------------------------------------------
+-- cmRunStmt: Run a statement/expr.
+
+#ifdef GHCI
+cmRunStmt :: CmState -> DynFlags -> String -> IO (CmState, [Name])
+cmRunStmt cmstate dflags expr
+ = do
+ let icontext = ic cmstate
+ InteractiveContext {
+ ic_rn_env = rn_env,
+ ic_type_env = type_env,
+ ic_module = this_mod } = icontext
+
+ (new_pcs, maybe_stuff) <- hscStmt dflags hst hit pcs icontext expr
+ case maybe_stuff of
+ Nothing -> return (cmstate{ pcs=new_pcs }, [])
+ Just (ids, bcos) -> do
+ let
+ new_rn_env = extendLocalRdrEnv rn_env (map idName ids)
+
+ -- Extend the renamer-env from bound_ids, not
+ -- bound_names, because the latter may contain
+ -- [it] when the former is empty
+ new_type_env = extendNameEnvList type_env
+ [ (getName id, AnId id) | id <- ids]
+
+ new_ic = icontext { ic_rn_env = new_rn_env,
+ ic_type_env = new_type_env }
+
+ hval <- linkExpr pls bcos
+ hvals <- unsafeCoerce# hval :: IO [HValue]
+ let names = map idName ids
+ new_pls <- updateClosureEnv pls (zip names hvals)
+ return (cmstate{ pcs=new_pcs, pls=new_pls, ic=new_ic }, names)
+
+ -- ToDo: check that the module we passed in is sane/exists?
+ where
+ CmState{ hst=hst, hit=hit, pcs=pcs, pls=pls } = cmstate
+#endif
+
+-----------------------------------------------------------------------------
+-- cmTypeOf: returns a string representing the type of a name.
+
+cmTypeOfName :: CmState -> Name -> IO (Maybe String)
+cmTypeOfName CmState{ hit=hit, pcs=pcs, ic=ic } name
+ = case lookupNameEnv (ic_type_env ic) name of
+ Nothing -> return Nothing
+ Just (AnId id) ->
+ let pit = pcs_PIT pcs
+ modname = moduleName (ic_module ic)
+ ty = tidyType emptyTidyEnv (idType id)
+ str = case lookupIfaceByModName hit pit modname of
+ Nothing -> showSDoc (ppr ty)
+ Just iface -> showSDocForUser unqual (ppr ty)
+ where unqual = unQualInScope (mi_globals iface)
+ in return (Just str)
+
+ _ -> panic "cmTypeOfName"
+
+-----------------------------------------------------------------------------
+-- cmInfo: return "info" about an expression. The info might be:
+--
+-- * its type, for an expression,
+-- * the class definition, for a class
+-- * the datatype definition, for a tycon (or synonym)
+-- * the export list, for a module
+--
+-- Can be used to find the type of the last expression compiled, by looking
+-- for "it".
+
+cmInfo :: CmState -> String -> IO (Maybe String)
+cmInfo cmstate str
+ = do error "cmInfo not implemented yet"
+
+-----------------------------------------------------------------------------
+-- Unload the compilation manager's state: everything it knows about the
+-- current collection of modules in the Home package.
-\begin{code}
cmUnload :: CmState -> IO CmState
cmUnload state
= do -- Throw away the old home dir cache
where
CmState{ hst=hst, hit=hit } = state
(new_hst, new_hit) = retainInTopLevelEnvs [] (hst,hit)
-\end{code}
-The real business of the compilation manager: given a system state and
-a module name, try and bring the module up to date, probably changing
-the system state at the same time.
+-----------------------------------------------------------------------------
+-- The real business of the compilation manager: given a system state and
+-- a module name, try and bring the module up to date, probably changing
+-- the system state at the same time.
-\begin{code}
cmLoadModule :: CmState
-> FilePath
-> IO (CmState, -- new state
Bool, -- was successful
- [Module]) -- list of modules loaded
+ [String]) -- list of modules loaded
cmLoadModule cmstate1 rootname
= do -- version 1's are the original, before downsweep
-- the previous pass, if any.
let ui1 = ui cmstate1
let mg1 = mg cmstate1
+ let ic1 = ic cmstate1
let ghci_mode = gmode cmstate1 -- this never changes
let mg2_with_srcimps = topological_sort True mg2unsorted
-- Sort out which linkables we wish to keep in the unlinked image.
- -- For each module, we take:
- --
- -- - the old in-core linkable, if available
- -- - an on-disk linkable, if available
- --
- -- and we take the youngest of these, provided it is younger than the
- -- source file.
- --
- -- If a module has a valid linkable, then it may be STABLE (see below),
- -- and it is classified as SOURCE UNCHANGED for the purposes of calling
- -- compile.
- valid_linkables <- getValidLinkables ui1 mg2unsorted
+ -- See getValidLinkables below for details.
+ valid_linkables <- getValidLinkables ui1 mg2unsorted_names
+ mg2_with_srcimps
-- Figure out a stable set of modules which can be retained
-- the top level envs, to avoid upsweeping them. Goes to a
valid_linkables
when (verb >= 2) $
- putStrLn (showSDoc (text "STABLE MODULES:"
+ putStrLn (showSDoc (text "Stable modules:"
<+> sep (map (text.moduleNameUserString) stable_mods)))
-- unload any modules which aren't going to be re-linked this
-- Easy; just relink it all.
do when (verb >= 2) $
hPutStrLn stderr "Upsweep completely successful."
- linkresult
- <- link ghci_mode dflags a_root_is_Main ui3 pls2
- case linkresult of
- LinkErrs _ _
- -> panic "cmLoadModule: link failed (1)"
- LinkOK pls3
- -> do let cmstate3
- = CmState { hst=hst3, hit=hit3,
- ui=ui3, mg=modsDone,
- gmode=ghci_mode,
- pcs=pcs3, pls=pls3 }
- return (cmstate3, True,
- map ms_mod modsDone)
+
+ -- clean up after ourselves
+ cleanTempFilesExcept verb (ppFilesFromSummaries modsDone)
+
+ -- link everything together
+ linkresult <- link ghci_mode dflags a_root_is_Main ui3 pls2
+
+ cmLoadFinish True linkresult
+ hst3 hit3 ui3 modsDone ghci_mode pcs3
else
-- Tricky. We need to back out the effects of compiling any
let modsDone_names
= map name_of_summary modsDone
let mods_to_zap_names
- = findPartiallyCompletedCycles modsDone_names mg2_with_srcimps
- let (hst4, hit4, ui4)
+ = findPartiallyCompletedCycles modsDone_names
+ mg2_with_srcimps
+ let (hst4, hit4, ui4)
= removeFromTopLevelEnvs mods_to_zap_names (hst3,hit3,ui3)
+
let mods_to_keep
- = filter ((`notElem` mods_to_zap_names).name_of_summary) modsDone
- let mods_to_keep_names
- = map name_of_summary mods_to_keep
- -- we could get the relevant linkables by filtering newLis, but
- -- it seems easier to drag them out of the updated, cleaned-up UI
- let linkables_to_link
- = map (unJust "linkables_to_link" . findModuleLinkable_maybe ui4)
- mods_to_keep_names
-
- linkresult <- link ghci_mode dflags False linkables_to_link pls2
- case linkresult of
- LinkErrs _ _
- -> panic "cmLoadModule: link failed (2)"
- LinkOK pls3
- -> do let cmstate4
- = CmState { hst=hst4, hit=hit4,
- ui=ui4, mg=mods_to_keep,
- gmode=ghci_mode, pcs=pcs3, pls=pls3 }
- return (cmstate4, False,
- map ms_mod mods_to_keep)
+ = filter ((`notElem` mods_to_zap_names).name_of_summary)
+ modsDone
+
+ -- clean up after ourselves
+ cleanTempFilesExcept verb (ppFilesFromSummaries mods_to_keep)
+
+ -- link everything together
+ linkresult <- link ghci_mode dflags False ui4 pls2
+
+ cmLoadFinish False linkresult
+ hst4 hit4 ui4 mods_to_keep ghci_mode pcs3
+
+
+-- Finish up after a cmLoad.
+--
+-- Empty the interactive context and set the module context to the topmost
+-- newly loaded module, or the Prelude if none were loaded.
+cmLoadFinish ok linkresult hst hit ui mods ghci_mode pcs
+ = do case linkresult of {
+ LinkErrs _ _ -> panic "cmLoadModule: link failed (2)";
+ LinkOK pls -> do
+
+ def_mod <- readIORef defaultCurrentModule
+ let current_mod = case mods of
+ [] -> def_mod
+ (x:_) -> ms_mod x
+
+ new_ic = emptyInteractiveContext current_mod
+
+ new_cmstate = CmState{ hst=hst, hit=hit,
+ ui=ui, mg=mods,
+ gmode=ghci_mode, pcs=pcs,
+ pls=pls,
+ ic = new_ic }
+ mods_loaded = map (moduleNameUserString.name_of_summary) mods
+ return (new_cmstate, ok, mods_loaded)
+ }
+
+ppFilesFromSummaries summaries
+ = [ fn | Just fn <- map (ml_hspp_file . ms_location) summaries ]
-----------------------------------------------------------------------------
-- getValidLinkables
+-- For each module (or SCC of modules), we take:
+--
+-- - the old in-core linkable, if available
+-- - an on-disk linkable, if available
+--
+-- and we take the youngest of these, provided it is younger than the
+-- source file. We ignore the on-disk linkables unless all of the
+-- dependents of this SCC also have on-disk linkables.
+--
+-- If a module has a valid linkable, then it may be STABLE (see below),
+-- and it is classified as SOURCE UNCHANGED for the purposes of calling
+-- compile.
+--
+-- ToDo: this pass could be merged with the preUpsweep.
+
getValidLinkables
- :: [Linkable] -- old linkables
- -> [ModSummary] -- all modules in the program
- -> IO [Linkable] -- still-valid linkables
-
-getValidLinkables old_linkables summaries
- = do lis <- mapM (getValidLinkable old_linkables) summaries
- return (concat lis)
-
-getValidLinkable old_linkables summary
- = do let mod_name = moduleName (ms_mod summary)
- maybe_disk_linkable
- <- case ml_obj_file (ms_location summary) of
- Nothing -> return Nothing
- Just obj_fn -> maybe_getFileLinkable mod_name obj_fn
-
- -- find an old in-core linkable if we have one. (forget about
- -- on-disk linkables for now, we'll check again whether there's
- -- one here below, just in case a new one has popped up recently).
- let old_linkable = findModuleLinkable_maybe old_linkables mod_name
- maybe_old_linkable =
+ :: [Linkable] -- old linkables
+ -> [ModuleName] -- all home modules
+ -> [SCC ModSummary] -- all modules in the program, dependency order
+ -> IO [Linkable] -- still-valid linkables
+
+getValidLinkables old_linkables all_home_mods module_graph
+ = foldM (getValidLinkablesSCC old_linkables all_home_mods) [] module_graph
+
+getValidLinkablesSCC old_linkables all_home_mods new_linkables scc0
+ = let
+ scc = flattenSCC scc0
+ scc_names = map name_of_summary scc
+ home_module m = m `elem` all_home_mods && m `notElem` scc_names
+ scc_allhomeimps = nub (filter home_module (concatMap ms_allimps scc))
+
+ has_object m = case findModuleLinkable_maybe new_linkables m of
+ Nothing -> False
+ Just l -> isObjectLinkable l
+
+ objects_allowed = all has_object scc_allhomeimps
+ in do
+
+ these_linkables
+ <- foldM (getValidLinkable old_linkables objects_allowed) [] scc
+
+ -- since an scc can contain only all objects or no objects at all,
+ -- we have to check whether we got all objects or not, and re-do
+ -- the linkable check if not.
+ adjusted_linkables
+ <- if objects_allowed && not (all isObjectLinkable these_linkables)
+ then foldM (getValidLinkable old_linkables False) [] scc
+ else return these_linkables
+
+ return (adjusted_linkables ++ new_linkables)
+
+
+getValidLinkable :: [Linkable] -> Bool -> [Linkable] -> ModSummary
+ -> IO [Linkable]
+getValidLinkable old_linkables objects_allowed new_linkables summary
+ = do
+ let mod_name = name_of_summary summary
+
+ maybe_disk_linkable
+ <- if (not objects_allowed)
+ then return Nothing
+ else case ml_obj_file (ms_location summary) of
+ Just obj_fn -> maybe_getFileLinkable mod_name obj_fn
+ Nothing -> return Nothing
+
+ -- find an old in-core linkable if we have one. (forget about
+ -- on-disk linkables for now, we'll check again whether there's
+ -- one here below, just in case a new one has popped up recently).
+ let old_linkable = findModuleLinkable_maybe old_linkables mod_name
+ maybe_old_linkable =
case old_linkable of
Just (LM _ _ ls) | all isInterpretable ls -> old_linkable
- _ -> Nothing
+ _ -> Nothing
- -- The most recent of the old UI linkable or whatever we could
- -- find on disk is returned as the linkable if compile
- -- doesn't think we need to recompile.
- let linkable_list
+ -- The most recent of the old UI linkable or whatever we could
+ -- find on disk is returned as the linkable if compile
+ -- doesn't think we need to recompile.
+ let linkable_list
= case (maybe_old_linkable, maybe_disk_linkable) of
(Nothing, Nothing) -> []
(Nothing, Just di) -> [di]
| linkableTime ui >= linkableTime di -> [ui]
| otherwise -> [di]
- -- only linkables newer than the source code are valid
- let maybe_src_date = ms_hs_date summary
+ -- only linkables newer than the source code are valid
+ let maybe_src_date = ms_hs_date summary
- valid_linkable_list
+ valid_linkable_list
= case maybe_src_date of
Nothing -> panic "valid_linkable_list"
Just src_date
- -> filter (\li -> linkableTime li > src_date) linkable_list
-
- return valid_linkable_list
+ -> filter (\li -> linkableTime li > src_date) linkable_list
+ return (valid_linkable_list ++ new_linkables)
maybe_getFileLinkable :: ModuleName -> FilePath -> IO (Maybe Linkable)
scc_names
= map name_of_summary scc
in_stable_or_scc m
- = --trace (showSDoc (text "ISOS" <+> ppr m <+> ppr scc_names <+> ppr stable)) (
- m `elem` scc_names || m `elem` stable
- --)
+ = m `elem` scc_names || m `elem` stable
-- now we check for valid linkables: each module in the SCC must
-- have a valid linkable (see getValidLinkables above), and the
let mod_name = name_of_summary summary1
let verb = verbosity dflags
- when (verb == 1) $
- if (ghci_mode == Batch)
- then hPutStr stderr (progName ++ ": module "
- ++ moduleNameUserString mod_name
- ++ ": ")
- else hPutStr stderr ("Compiling "
- ++ moduleNameUserString mod_name
- ++ " ... ")
-
let (CmThreaded pcs1 hst1 hit1) = threaded1
let old_iface = lookupUFM hit1 mod_name
getSummary :: ModuleName -> IO ModSummary
getSummary nm
- -- | trace ("getSummary: "++ showSDoc (ppr nm)) True
= do found <- findModule nm
case found of
- -- Be sure not to use the mod and location passed in to
- -- summarise for any other purpose -- summarise may change
- -- the module names in them if name of module /= name of file,
- -- and put the changed versions in the returned summary.
- -- These will then conflict with the passed-in versions.
Just (mod, location) -> do
let old_summary = findModInSummaries old_summaries mod
new_summary <- summarise mod location old_summary
let all_home = map (moduleName.ms_mod) home_summaries
-- imports for modules we don't already have
- let needed_imps = filter (`notElem` all_home) imps
+ let needed_imps = nub (filter (`notElem` all_home) imps)
-- summarise them
needed_summaries <- mapM getSummary needed_imps
Nothing -> return Nothing
Just src_fn -> maybe_getModificationTime src_fn
- if mod_name == moduleName mod
- then return ()
- else throwDyn (OtherError
- (showSDoc (text "file name does not match module name: "
- <+> ppr (moduleName mod) <+> text "vs"
- <+> ppr mod_name)))
+ when (mod_name /= moduleName mod) $
+ throwDyn (OtherError
+ (showSDoc (text "file name does not match module name: "
+ <+> ppr (moduleName mod) <+> text "vs"
+ <+> ppr mod_name)))
return (Just (ModSummary mod location{ml_hspp_file=Just hspp_fn}
srcimps imps