[project @ 2002-01-03 17:09:13 by simonmar]
[ghc-hetmet.git] / ghc / compiler / compMan / CompManager.lhs
index 48145ee..2958f35 100644 (file)
@@ -1,34 +1,43 @@
 %
-% (c) The University of Glasgow, 2000
+% (c) The University of Glasgow, 2002
+%
+% The Compilation Manager
 %
-\section[CompManager]{The Compilation Manager}
-
 \begin{code}
+{-# OPTIONS -fvia-C #-}
 module CompManager ( 
-    cmInit,      -- :: GhciMode -> IO CmState
+    ModuleGraph, 
+    CmRunResult(..),
+
+    CmState, emptyCmState,  -- abstract
+
+    cmInit,       -- :: GhciMode -> IO CmState
 
-    cmLoadModule, -- :: CmState -> FilePath -> IO (CmState, [String])
+    cmDepAnal,    -- :: CmState -> DynFlags -> [FilePath] -> IO ModuleGraph
 
-    cmUnload,    -- :: CmState -> DynFlags -> IO CmState
+    cmLoadModules, -- :: CmState -> DynFlags -> ModuleGraph
+                  --    -> IO (CmState, [String])
 
-    cmSetContext, -- :: CmState -> String -> IO CmState
+    cmUnload,     -- :: CmState -> DynFlags -> IO CmState
 
-    cmGetContext, -- :: CmState -> IO String
+    cmSetContext,  -- :: CmState -> String -> IO CmState
+
+    cmGetContext,  -- :: CmState -> IO String
 
 #ifdef GHCI
-    cmInfoThing,  -- :: CmState -> DynFlags -> String -> IO (Maybe TyThing)
+    cmInfoThing,   -- :: CmState -> DynFlags -> String -> IO (Maybe TyThing)
 
-    cmRunStmt,   -- :: CmState -> DynFlags -> String -> IO (CmState, [Name])
+    cmRunStmt,    -- :: CmState -> DynFlags -> String
+                  --    -> IO (CmState, CmRunResult)
 
-    cmTypeOfExpr, -- :: CmState -> DynFlags -> String
-                 -- -> IO (CmState, Maybe String)
+    cmTypeOfExpr,  -- :: CmState -> DynFlags -> String
+                  --   -> IO (CmState, Maybe String)
 
-    cmTypeOfName, -- :: CmState -> Name -> IO (Maybe String)
+    cmTypeOfName,  -- :: CmState -> Name -> IO (Maybe String)
 
-    cmCompileExpr,-- :: CmState -> DynFlags -> String 
-                 -- -> IO (CmState, Maybe HValue)#endif
+    cmCompileExpr, -- :: CmState -> DynFlags -> String 
+                  --   -> IO (CmState, Maybe HValue)#endif
 #endif
-    CmState, emptyCmState  -- abstract
   )
 where
 
@@ -38,6 +47,7 @@ import CmLink
 import CmTypes
 import DriverPipeline
 import DriverFlags     ( getDynFlags )
+import DriverState     ( v_Output_file )
 import DriverPhases
 import DriverUtil
 import Finder
@@ -47,33 +57,37 @@ import HscMain              ( initPersistentCompilerState, hscThing )
 import HscMain         ( initPersistentCompilerState )
 #endif
 import HscTypes
-import RnEnv           ( unQualInScope )
-import Id              ( idType, idName )
 import Name            ( Name, NamedThing(..), nameRdrName, nameModule,
                          isHomePackageName )
-import NameEnv
 import RdrName         ( lookupRdrEnv, emptyRdrEnv )
 import Module
 import GetImports
-import Type            ( tidyType )
-import VarEnv          ( emptyTidyEnv )
 import UniqFM
 import Unique          ( Uniquable )
-import Digraph         ( SCC(..), stronglyConnComp, flattenSCC )
+import Digraph         ( SCC(..), stronglyConnComp, flattenSCC, flattenSCCs )
 import ErrUtils                ( showPass )
 import SysTools                ( cleanTempFilesExcept )
 import Util
 import Outputable
-import BasicTypes      ( Fixity, defaultFixity )
 import Panic
 import CmdLineOpts     ( DynFlags(..) )
 
 import IOExts
 
 #ifdef GHCI
+import Id              ( idType, idName )
+import NameEnv
+import Type            ( tidyType )
+import VarEnv          ( emptyTidyEnv )
+import RnEnv           ( unQualInScope, mkIfaceGlobalRdrEnv )
+import BasicTypes      ( Fixity, defaultFixity )
 import Interpreter     ( HValue )
 import HscMain         ( hscStmt )
 import PrelGHC         ( unsafeCoerce# )
+
+import Foreign
+import CForeign
+import Exception       ( Exception, try )
 #endif
 
 -- lang
@@ -207,21 +221,25 @@ cmInfoThing cmstate dflags id
 -- 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
+data CmRunResult
+  = CmRunOk [Name]             -- names bound by this evaluation
+  | CmRunFailed 
+  | CmRunDeadlocked            -- statement deadlocked
+  | CmRunException Exception   -- statement raised an exception
+
+cmRunStmt :: CmState -> DynFlags -> String -> IO (CmState, CmRunResult)                
+cmRunStmt cmstate@CmState{ hst=hst, hit=hit, pcs=pcs, pls=pls, ic=icontext }
+          dflags expr
    = do 
        let InteractiveContext { 
                ic_rn_env = rn_env, 
-               ic_type_env = type_env,
-               ic_module   = this_mod } = icontext
+               ic_type_env = type_env } = icontext
 
         (new_pcs, maybe_stuff) 
            <- hscStmt dflags hst hit pcs icontext expr False{-stmt-}
 
         case maybe_stuff of
-          Nothing -> return (cmstate{ pcs=new_pcs }, [])
+          Nothing -> return (cmstate{ pcs=new_pcs }, CmRunFailed)
           Just (ids, _, bcos) -> do
 
                -- update the interactive context
@@ -248,17 +266,56 @@ cmRunStmt cmstate dflags expr
 
                -- 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
+               either_hvals <- sandboxIO thing_to_run
+               case either_hvals of
+                  Left err
+                       | err == dEADLOCKED
+                       -> return ( cmstate{ pcs=new_pcs, ic=new_ic }, 
+                                   CmRunDeadlocked )
+                       | otherwise
+                       -> do hPutStrLn stderr ("unknown failure, code " ++ show err)
+                             return ( cmstate{ pcs=new_pcs, ic=new_ic }, CmRunFailed )
+
+                  Right maybe_hvals ->
+                    case maybe_hvals of
+                       Left e -> 
+                           return ( cmstate{ pcs=new_pcs, ic=new_ic }, 
+                                    CmRunException e )
+                       Right hvals -> do
+                            -- 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 }, 
+                                    CmRunOk names)
+
+-- We run the statement in a "sandbox", which amounts to calling into
+-- the RTS to request a new main thread.  The main benefit is that we
+-- get to detect a deadlock this way, but also there's no danger that
+-- exceptions raised by the expression can affect the interpreter.
+
+sandboxIO :: IO a -> IO (Either Int (Either Exception a))
+sandboxIO thing = do
+  st_thing <- newStablePtr (Exception.try thing)
+  alloca $ \ p_st_result -> do
+    stat <- rts_evalStableIO st_thing p_st_result
+    freeStablePtr st_thing
+    if stat == 1
+       then do st_result <- peek p_st_result
+               result <- deRefStablePtr st_result
+               freeStablePtr st_result
+               return (Right result)
+       else do
+               return (Left (fromIntegral stat))
+
+-- ToDo: slurp this in from ghc/includes/RtsAPI.h somehow
+dEADLOCKED = 4 :: Int
+
+foreign import "rts_evalStableIO"  {- safe -}
+  rts_evalStableIO :: StablePtr (IO a) -> Ptr (StablePtr a) -> IO CInt
+  -- more informative than the C type!
 #endif
 
 -----------------------------------------------------------------------------
@@ -281,15 +338,18 @@ cmTypeOfExpr cmstate dflags expr
                tidy_ty = tidyType emptyTidyEnv ty
    where
        CmState{ hst=hst, hit=hit, pcs=pcs, ic=ic } = cmstate
-#endif
 
 getUnqual pcs hit ic
    = case lookupIfaceByModName hit pit modname of
        Nothing    -> alwaysQualify
-       Just iface -> unQualInScope (mi_globals iface)
- where
+       Just iface -> 
+          case mi_globals iface of
+             Just env -> unQualInScope env
+             Nothing  -> unQualInScope (mkIfaceGlobalRdrEnv (mi_exports iface))
+  where
     pit = pcs_PIT pcs
     modname = moduleName (ic_module ic)
+#endif
 
 -----------------------------------------------------------------------------
 -- cmTypeOfName: returns a string representing the type of a name.
@@ -344,21 +404,6 @@ cmCompileExpr cmstate dflags expr
 #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.
 
@@ -374,18 +419,35 @@ cmUnload state@CmState{ gmode=mode, pls=pls, pcs=pcs } dflags
       new_state <- cmInit mode
       return new_state{ pcs=pcs, pls=new_pls }
 
+
+-----------------------------------------------------------------------------
+-- Trace dependency graph
+
+-- This is a seperate pass so that the caller can back off and keep
+-- the current state if the downsweep fails.
+
+cmDepAnal :: CmState -> DynFlags -> [FilePath] -> IO ModuleGraph
+cmDepAnal cmstate dflags rootnames
+  = do showPass dflags "Chasing dependencies"
+       when (verbosity dflags >= 1 && gmode cmstate == Batch) $
+           hPutStrLn stderr (showSDoc (hcat [
+            text progName, text ": chasing modules from: ",
+            hcat (punctuate comma (map text rootnames))]))
+       downsweep rootnames (mg cmstate)
+
 -----------------------------------------------------------------------------
 -- 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.
 
-cmLoadModule :: CmState 
-             -> [FilePath]
+cmLoadModules :: CmState 
+            -> DynFlags
+             -> ModuleGraph
              -> IO (CmState,           -- new state
                    Bool,               -- was successful
                    [String])           -- list of modules loaded
 
-cmLoadModule cmstate1 rootnames
+cmLoadModules cmstate1 dflags mg2unsorted
    = do -- version 1's are the original, before downsweep
         let pls1      = pls    cmstate1
         let pcs1      = pcs    cmstate1
@@ -394,22 +456,17 @@ cmLoadModule cmstate1 rootnames
        -- similarly, ui1 is the (complete) set of linkables from
        -- 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
-       dflags <- getDynFlags
         let verb = verbosity dflags
 
-       showPass dflags "Chasing dependencies"
-        when (verb >= 1 && ghci_mode == Batch) $
-           hPutStrLn stderr (showSDoc (hcat [
-            text progName, text ": chasing modules from: ",
-            hcat (punctuate comma (map text rootnames))]))
+       -- Find out if we have a Main module
+        let a_root_is_Main 
+               = any ((=="Main").moduleNameUserString.name_of_summary) 
+                     mg2unsorted
 
-        (mg2unsorted, a_root_is_Main) <- downsweep rootnames mg1
         let mg2unsorted_names = map name_of_summary mg2unsorted
 
         -- reachable_from follows source as well as normal imports
@@ -482,9 +539,13 @@ cmLoadModule cmstate1 rootnames
 
         let threaded2 = CmThreaded pcs1 hst1 hit1
 
+       -- clean up between compilations
+       let cleanup = cleanTempFilesExcept verb 
+                         (ppFilesFromSummaries (flattenSCCs upsweep_these))
+
         (upsweep_complete_success, threaded3, modsUpswept, newLis)
            <- upsweep_mods ghci_mode dflags valid_linkables reachable_from 
-                           threaded2 upsweep_these
+                           threaded2 cleanup upsweep_these
 
         let ui3 = add_to_ui valid_linkables newLis
         let (CmThreaded pcs3 hst3 hit3) = threaded3
@@ -514,6 +575,13 @@ cmLoadModule cmstate1 rootnames
              -- clean up after ourselves
              cleanTempFilesExcept verb (ppFilesFromSummaries modsDone)
 
+             -- issue a warning for the confusing case where the user said '-o foo'
+             -- but we're not going to do any linking.
+             ofile <- readIORef v_Output_file
+             when (ghci_mode == Batch && isJust ofile && not a_root_is_Main
+                    && verb > 0) $
+                hPutStrLn stderr "Warning: output was redirected with -o, but no output will be generated\nbecause there is no Main module."
+
              -- link everything together
               linkresult <- link ghci_mode dflags a_root_is_Main ui3 pls2
 
@@ -816,6 +884,7 @@ upsweep_mods :: GhciMode
              -> UnlinkedImage         -- valid linkables
              -> (ModuleName -> [ModuleName])  -- to construct downward closures
              -> CmThreaded            -- PCS & HST & HIT
+            -> IO ()                 -- how to clean up unwanted tmp files
              -> [SCC ModSummary]      -- mods to do (the worklist)
                                       -- ...... RETURNING ......
              -> IO (Bool{-complete success?-},
@@ -823,17 +892,17 @@ upsweep_mods :: GhciMode
                     [ModSummary],     -- mods which succeeded
                     [Linkable])       -- new linkables
 
-upsweep_mods ghci_mode dflags oldUI reachable_from threaded 
+upsweep_mods ghci_mode dflags oldUI reachable_from threaded cleanup
      []
    = return (True, threaded, [], [])
 
-upsweep_mods ghci_mode dflags oldUI reachable_from threaded 
+upsweep_mods ghci_mode dflags oldUI reachable_from threaded cleanup
      ((CyclicSCC ms):_)
    = do hPutStrLn stderr ("Module imports form a cycle for modules:\n\t" ++
                           unwords (map (moduleNameUserString.name_of_summary) ms))
         return (False, threaded, [], [])
 
-upsweep_mods ghci_mode dflags oldUI reachable_from threaded 
+upsweep_mods ghci_mode dflags oldUI reachable_from threaded cleanup
      ((AcyclicSCC mod):mods)
    = do --case threaded of
         --   CmThreaded pcsz hstz hitz
@@ -842,12 +911,16 @@ upsweep_mods ghci_mode dflags oldUI reachable_from threaded
         (threaded1, maybe_linkable) 
            <- upsweep_mod ghci_mode dflags oldUI threaded mod 
                           (reachable_from (name_of_summary mod))
+
+       -- remove unwanted tmp files between compilations
+       cleanup
+
         case maybe_linkable of
            Just linkable 
               -> -- No errors; do the rest
                  do (restOK, threaded2, modOKs, linkables) 
                        <- upsweep_mods ghci_mode dflags oldUI reachable_from 
-                                       threaded1 mods
+                                       threaded1 cleanup mods
                     return (restOK, threaded2, mod:modOKs, linkable:linkables)
            Nothing -- we got a compilation error; give up now
               -> return (False, threaded1, [], [])
@@ -866,7 +939,6 @@ upsweep_mod :: GhciMode
 upsweep_mod ghci_mode dflags oldUI threaded1 summary1 reachable_inc_me
    = do 
         let mod_name = name_of_summary summary1
-       let verb = verbosity dflags
 
         let (CmThreaded pcs1 hst1 hit1) = threaded1
         let old_iface = lookupUFM hit1 mod_name
@@ -985,22 +1057,26 @@ topological_sort include_source_imports summaries
          sccs
 
 
+-----------------------------------------------------------------------------
+-- Downsweep (dependency analysis)
+
 -- Chase downwards from the specified root set, returning summaries
 -- for all home modules encountered.  Only follow source-import
--- links.  Also returns a Bool to indicate whether any of the roots
--- are module Main.
-downsweep :: [FilePath] -> [ModSummary] -> IO ([ModSummary], Bool)
-downsweep rootNm old_summaries
-   = do rootSummaries <- mapM getRootSummary rootNm
-        let a_root_is_Main 
-               = any ((=="Main").moduleNameUserString.name_of_summary) 
-                     rootSummaries
+-- links.
+
+-- We pass in the previous collection of summaries, which is used as a
+-- cache to avoid recalculating a module summary if the source is
+-- unchanged.
+
+downsweep :: [FilePath] -> [ModSummary] -> IO [ModSummary]
+downsweep roots old_summaries
+   = do rootSummaries <- mapM getRootSummary roots
         all_summaries
            <- loop (concat (map ms_imps rootSummaries))
                (mkModuleEnv [ (mod, s) | s <- rootSummaries, 
                                          let mod = ms_mod s, isHomeModule mod 
                             ])
-        return (all_summaries, a_root_is_Main)
+        return all_summaries
      where
        getRootSummary :: FilePath -> IO ModSummary
        getRootSummary file
@@ -1057,8 +1133,8 @@ downsweep rootNm old_summaries
 
 -- We have two types of summarisation:
 --
---    * Summarise a file.  This is used for the root module passed to
---     cmLoadModule.  The file is read, and used to determine the root
+--    * Summarise a file.  This is used for the root module(s) passed to
+--     cmLoadModules.  The file is read, and used to determine the root
 --     module name.  The module name may differ from the filename.
 --
 --    * Summarise a module.  We are given a module name, and must provide
@@ -1070,7 +1146,7 @@ summariseFile file
    = do hspp_fn <- preprocess file
         (srcimps,imps,mod_name) <- getImportsFromFile hspp_fn
 
-        let (path, basename, ext) = splitFilename3 file
+        let (path, basename, _ext) = splitFilename3 file
 
        (mod, location)
           <- mkHomeModuleLocn mod_name (path ++ '/':basename) file