[project @ 2001-03-19 16:22:00 by simonmar]
[ghc-hetmet.git] / ghc / compiler / compMan / CompManager.lhs
index f734401..e38b206 100644 (file)
@@ -4,12 +4,30 @@
 \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
+
+    cmSetContext, -- :: CmState -> String -> IO CmState
+
+    cmGetContext, -- :: CmState -> IO String
+
 #ifdef GHCI
-                     cmGetExpr, cmRunExpr,
+    cmRunStmt,   --  :: CmState -> DynFlags -> String -> IO (CmState, [Name])
+
+    cmTypeOfExpr, --  :: CmState -> DynFlags -> String
+                 --  -> IO (CmState, Maybe String)
+
+    cmTypeOfName, -- :: CmState -> Name -> IO (Maybe String)
+
+    cmCompileExpr,-- :: CmState -> DynFlags -> String 
+                 -- -> IO (CmState, Maybe HValue)#endif
 #endif
-                     CmState, emptyCmState  -- abstract
-                   )
+    CmState, emptyCmState  -- abstract
+  )
 where
 
 #include "HsVersions.h"
@@ -17,17 +35,22 @@ where
 import CmLink
 import CmTypes
 import HscTypes
+import RnEnv           ( unQualInScope )
+import Id              ( idType, idName )
+import Name            ( Name, NamedThing(..), nameRdrName )
+import NameEnv
+import RdrName         ( lookupRdrEnv, 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,
-                         UniqFM, listToUFM )
+import UniqFM
 import Unique          ( Uniquable )
 import Digraph         ( SCC(..), stronglyConnComp, flattenSCC )
 import DriverFlags     ( getDynFlags )
@@ -36,6 +59,7 @@ import DriverUtil     ( splitFilename3 )
 import ErrUtils                ( showPass )
 import Util
 import DriverUtil
+import TmpFiles
 import Outputable
 import Panic
 import CmdLineOpts     ( DynFlags(..) )
@@ -43,8 +67,7 @@ import IOExts
 
 #ifdef GHCI
 import Interpreter     ( HValue )
-import HscMain         ( hscExpr )
-import Type            ( Type )
+import HscMain         ( hscStmt )
 import PrelGHC         ( unsafeCoerce# )
 #endif
 
@@ -57,48 +80,11 @@ import Directory        ( getModificationTime, doesFileExist )
 import IO
 import Monad
 import List            ( nub )
-import Maybe           ( catMaybes, fromMaybe, isJust, fromJust )
+import Maybe
 \end{code}
 
 
 \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 {
@@ -107,23 +93,33 @@ data 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
@@ -133,12 +129,207 @@ type ModuleGraph = [ModSummary]  -- the module graph, topologically sorted
 emptyMG :: ModuleGraph
 emptyMG = []
 
-\end{code}
+-----------------------------------------------------------------------------
+-- Produce an initial CmState.
+
+cmInit :: GhciMode -> IO CmState
+cmInit mode = do
+   prel <- moduleNameToModule defaultCurrentModuleName
+   writeIORef defaultCurrentModule prel
+   emptyCmState mode prel
 
-Unload the compilation manager's state: everything it knows about the
-current collection of modules in the Home package.
+-----------------------------------------------------------------------------
+-- 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,                 -- new state
+              [Name])                  -- names bound by this evaluation
+cmRunStmt cmstate dflags expr
+   = do 
+       let 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 False{-stmt-}
+
+        case maybe_stuff of
+          Nothing -> return (cmstate{ pcs=new_pcs }, [])
+          Just (ids, _, bcos) -> do
+
+               -- update the interactive context
+               let 
+                   names = map idName ids
+
+                   -- these names have just been shadowed
+                   shadowed = [ n | r <- map nameRdrName names,
+                                    Just n <- [lookupRdrEnv rn_env r] ]
+                   
+                   new_rn_env   = extendLocalRdrEnv rn_env names
+
+                   -- remove any shadowed bindings from the type_env
+                   filtered_type_env = delListFromNameEnv type_env shadowed
+
+                   new_type_env = extendNameEnvList filtered_type_env  
+                                       [ (getName id, AnId id) | id <- ids]
+
+                   new_ic = icontext { ic_rn_env   = new_rn_env, 
+                                       ic_type_env = new_type_env }
+
+               -- link it
+               hval <- linkExpr pls bcos
+
+               -- run it!
+               let thing_to_run = unsafeCoerce# hval :: IO [HValue]
+               hvals <- thing_to_run
+
+               -- Get the newly bound things, and bind them.  Don't forget
+               -- to delete any shadowed bindings from the closure_env, lest
+               -- we end up with a space leak.
+               pls <- delListFromClosureEnv pls shadowed
+               new_pls <- addListToClosureEnv pls (zip names hvals)
+
+               return (cmstate{ pcs=new_pcs, pls=new_pls, ic=new_ic }, names)
+   where
+       CmState{ hst=hst, hit=hit, pcs=pcs, pls=pls, ic=icontext } = cmstate
+#endif
+
+-----------------------------------------------------------------------------
+-- cmTypeOfExpr: returns a string representing the type of an expression
+
+#ifdef GHCI
+cmTypeOfExpr :: CmState -> DynFlags -> String -> IO (CmState, Maybe String)
+cmTypeOfExpr cmstate dflags expr
+   = do (new_pcs, maybe_stuff) 
+         <- hscStmt dflags hst hit pcs ic expr True{-just an expr-}
+
+       let new_cmstate = cmstate{pcs = new_pcs}
+
+       case maybe_stuff of
+          Nothing -> return (new_cmstate, Nothing)
+          Just (_, ty, _) ->
+            let pit = pcs_PIT pcs
+                modname = moduleName (ic_module ic)
+                tidy_ty = tidyType emptyTidyEnv ty
+                str = case lookupIfaceByModName hit pit modname of
+                         Nothing    -> showSDoc (ppr tidy_ty)
+                         Just iface -> showSDocForUser unqual (ppr tidy_ty)
+                            where unqual = unQualInScope (mi_globals iface)
+            in return (new_cmstate, Just str)
+   where
+       CmState{ hst=hst, hit=hit, pcs=pcs, ic=ic } = cmstate
+#endif
+
+-----------------------------------------------------------------------------
+-- cmTypeOfName: returns a string representing the type of a name.
+
+#ifdef GHCI
+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"
+#endif
+
+-----------------------------------------------------------------------------
+-- cmCompileExpr: compile an expression and deliver an HValue
+
+#ifdef GHCI
+cmCompileExpr :: CmState -> DynFlags -> String -> IO (CmState, Maybe HValue)
+cmCompileExpr cmstate dflags expr
+   = do 
+       let 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 
+                 ("let __cmCompileExpr = "++expr) False{-stmt-}
+
+        case maybe_stuff of
+          Nothing -> return (cmstate{ pcs=new_pcs }, Nothing)
+          Just (ids, _, bcos) -> do
+
+               -- link it
+               hval <- linkExpr pls bcos
+
+               -- run it!
+               let thing_to_run = unsafeCoerce# hval :: IO [HValue]
+               hvals <- thing_to_run
+
+               case (ids,hvals) of
+                 ([id],[hv]) -> return (cmstate{ pcs=new_pcs }, Just hv)
+                 _ -> panic "cmCompileExpr"
+
+   where
+       CmState{ hst=hst, hit=hit, pcs=pcs, pls=pls, ic=icontext } = cmstate
+#endif
+
+-----------------------------------------------------------------------------
+-- 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
@@ -148,18 +339,17 @@ cmUnload state
    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
@@ -171,13 +361,11 @@ cmLoadModule cmstate1 rootname
        -- 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
 
         -- Do the downsweep to reestablish the module graph
-        -- then generate version 2's by retaining in HIT,HST,UI a
-        -- stable set S of modules, as defined below.
-
        dflags <- getDynFlags
         let verb = verbosity dflags
 
@@ -204,7 +392,6 @@ cmLoadModule cmstate1 rootname
        -- See getValidLinkables below for details.
        valid_linkables <- getValidLinkables ui1 mg2unsorted_names 
                                mg2_with_srcimps
-       pprTrace "valid_linkables" (ppr valid_linkables) $ do
 
         -- Figure out a stable set of modules which can be retained
         -- the top level envs, to avoid upsweeping them.  Goes to a
@@ -216,9 +403,8 @@ cmLoadModule cmstate1 rootname
         -- 1.  All home imports of ms are either in ms or S
         -- 2.  A valid linkable exists for each module in ms
 
-        stable_mods
-           <- preUpsweep valid_linkables ui1 mg2unsorted_names
-                [] mg2_with_srcimps
+        stable_mods <- preUpsweep valid_linkables hit1 
+                                 mg2unsorted_names [] mg2_with_srcimps
 
         let stable_summaries
                = concatMap (findInSummaries mg2unsorted) stable_mods
@@ -228,7 +414,7 @@ cmLoadModule cmstate1 rootname
                    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
@@ -285,19 +471,15 @@ cmLoadModule cmstate1 rootname
            -- 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
@@ -309,43 +491,67 @@ cmLoadModule cmstate1 rootname
               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
+--     - an on-disk linkable, if this is the first time around and one
+--       is 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.
+--     - the old linkable, otherwise (and if one is available).
+--
+-- and we throw away the linkable if it is older 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
@@ -393,47 +599,49 @@ getValidLinkablesSCC old_linkables all_home_mods new_linkables scc0
 getValidLinkable :: [Linkable] -> Bool -> [Linkable] -> ModSummary 
        -> IO [Linkable]
 getValidLinkable old_linkables objects_allowed new_linkables summary 
-   = do 
-       let mod_name = name_of_summary summary
+  = do let mod_name = name_of_summary summary
 
-       maybe_disk_linkable
-           <- if (not objects_allowed)
+       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
-
-        -- 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]
-                    (Just ui, Nothing) -> [ui]
-                    (Just ui, Just 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
-
-           valid_linkable_list
+       let old_linkable = findModuleLinkable_maybe old_linkables mod_name
+          maybe_old_linkable =
+               case old_linkable of
+                   Just l | not (isObjectLinkable l) || stillThere l 
+                               -> old_linkable
+                               -- ToDo: emit a warning if not (stillThere l)
+                          | otherwise
+                               -> Nothing
+
+          -- make sure that if we had an old disk linkable around, that it's
+          -- still there on the disk (in case we need to re-link it).
+          stillThere l = 
+               case maybe_disk_linkable of
+                  Nothing    -> False
+                  Just l_disk -> linkableTime l == linkableTime l_disk
+
+          -- we only look for objects on disk the first time around;
+          -- if the user compiles a module on the side during a GHCi session,
+          -- it won't be picked up until the next ":load".  This is what the
+          -- "null old_linkables" test below is.
+           linkable | null old_linkables = maybeToList maybe_disk_linkable
+                   | otherwise          = maybeToList maybe_old_linkable
+
+           -- only linkables newer than the source code are valid
+           maybe_src_date = ms_hs_date summary
+
+          valid_linkable
              = case maybe_src_date of
                  Nothing -> panic "valid_linkable_list"
                  Just src_date 
-                    -> filter (\li -> linkableTime li > src_date) linkable_list
+                    -> filter (\l -> linkableTime l > src_date) linkable
+
+       return (valid_linkable ++ new_linkables)
 
-        return (valid_linkable_list ++ new_linkables)
 
 
 maybe_getFileLinkable :: ModuleName -> FilePath -> IO (Maybe Linkable)
@@ -455,16 +663,20 @@ maybe_getFileLinkable mod_name obj_fn
 -- Do a pre-upsweep without use of "compile", to establish a 
 -- (downward-closed) set of stable modules for which we won't call compile.
 
+-- a stable module:
+--     * has a valid linkable (see getValidLinkables above)
+--     * depends only on stable modules
+--     * has an interface in the HIT (interactive mode only)
+
 preUpsweep :: [Linkable]       -- new valid linkables
-          -> [Linkable]        -- old linkables
+          -> HomeIfaceTable
            -> [ModuleName]      -- names of all mods encountered in downsweep
            -> [ModuleName]      -- accumulating stable modules
            -> [SCC ModSummary]  -- scc-ified mod graph, including src imps
            -> IO [ModuleName]  -- stable modules
 
-preUpsweep valid_lis old_lis all_home_mods stable [] 
-   = return stable
-preUpsweep valid_lis old_lis all_home_mods stable (scc0:sccs)
+preUpsweep valid_lis hit all_home_mods stable []  = return stable
+preUpsweep valid_lis hit all_home_mods stable (scc0:sccs)
    = do let scc = flattenSCC scc0
             scc_allhomeimps :: [ModuleName]
             scc_allhomeimps 
@@ -477,27 +689,20 @@ preUpsweep valid_lis old_lis all_home_mods stable (scc0:sccs)
                = 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
-           -- newest linkable must be the same as the previous linkable for
-           -- this module (if one exists).
+           -- have a valid linkable (see getValidLinkables above).
            has_valid_linkable new_summary
-             = case findModuleLinkable_maybe valid_lis modname of
-                  Nothing -> False
-                  Just l  -> case findModuleLinkable_maybe old_lis modname of
-                               Nothing -> True
-                               Just m  -> linkableTime l == linkableTime m
+             = isJust (findModuleLinkable_maybe valid_lis modname)
               where modname = name_of_summary new_summary
 
+           has_interface summary = ms_mod summary `elemUFM` hit
+
            scc_is_stable = all_imports_in_scc_or_stable
                          && all has_valid_linkable scc
+                         && all has_interface scc
 
         if scc_is_stable
-         then preUpsweep valid_lis old_lis all_home_mods 
-               (scc_names++stable) sccs
-         else preUpsweep valid_lis old_lis all_home_mods 
-               stable sccs
-
-   where 
+         then preUpsweep valid_lis hit all_home_mods (scc_names++stable) sccs
+         else preUpsweep valid_lis hit all_home_mods stable sccs
 
 
 -- Helper for preUpsweep.  Assuming that new_summary's imports are all
@@ -604,20 +809,11 @@ upsweep_mod :: GhciMode
             -> [ModuleName]
             -> IO (CmThreaded, Maybe Linkable)
 
-upsweep_mod ghci_mode dflags oldUI threaded1 summary1 reachable_from_here
+upsweep_mod ghci_mode dflags oldUI threaded1 summary1 reachable_inc_me
    = do 
         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
 
@@ -625,49 +821,40 @@ upsweep_mod ghci_mode dflags oldUI threaded1 summary1 reachable_from_here
 
             source_unchanged = isJust maybe_old_linkable
 
+           reachable_only = filter (/= (name_of_summary summary1)) 
+                               reachable_inc_me
+
+          -- in interactive mode, all home modules below us *must* have an
+          -- interface in the HIT.  We never demand-load home interfaces in
+          -- interactive mode.
             (hst1_strictDC, hit1_strictDC)
-               = retainInTopLevelEnvs 
-                    (filter (/= (name_of_summary summary1)) reachable_from_here)
-                    (hst1,hit1)
+               = ASSERT(ghci_mode == Batch || 
+                       all (`elemUFM` hit1) reachable_only)
+                retainInTopLevelEnvs reachable_only (hst1,hit1)
 
             old_linkable 
                = unJust "upsweep_mod:old_linkable" maybe_old_linkable
 
+           have_object 
+              | Just l <- maybe_old_linkable, isObjectLinkable l = True
+              | otherwise = False
+
         compresult <- compile ghci_mode summary1 source_unchanged
-                         old_iface hst1_strictDC hit1_strictDC pcs1
+                        have_object old_iface hst1_strictDC hit1_strictDC pcs1
 
         case compresult of
 
-           -- Compilation "succeeded", but didn't return a new
-           -- linkable, meaning that compilation wasn't needed, and the
-           -- new details were manufactured from the old iface.
-           CompOK pcs2 new_details new_iface Nothing
-              -> do let hst2         = addToUFM hst1 mod_name new_details
-                        hit2         = addToUFM hit1 mod_name new_iface
-                        threaded2    = CmThreaded pcs2 hst2 hit2
-
-                   if ghci_mode == Interactive && verb >= 1 then
-                     -- if we're using an object file, tell the user
-                     case old_linkable of
-                       (LM _ _ objs@(DotO _:_))
-                          -> do hPutStrLn stderr (showSDoc (space <> 
-                                  parens (hsep (text "using": 
-                                       punctuate comma 
-                                         [ text o | DotO o <- objs ]))))
-                       _ -> return ()
-                     else
-                       return ()
-
-                    return (threaded2, Just old_linkable)
-
-           -- Compilation really did happen, and succeeded.  A new
-           -- details, iface and linkable are returned.
-           CompOK pcs2 new_details new_iface (Just new_linkable)
+           -- Compilation "succeeded", and may or may not have returned a new
+           -- linkable (depending on whether compilation was actually performed
+          -- or not).
+           CompOK pcs2 new_details new_iface maybe_new_linkable
               -> do let hst2      = addToUFM hst1 mod_name new_details
                         hit2      = addToUFM hit1 mod_name new_iface
                         threaded2 = CmThreaded pcs2 hst2 hit2
 
-                   return (threaded2, Just new_linkable)
+                    return (threaded2, if isJust maybe_new_linkable
+                                         then maybe_new_linkable
+                                         else Just old_linkable)
 
            -- Compilation failed.  compile may still have updated
            -- the PCS, tho.