adapt to the new async exceptions API
[ghc-hetmet.git] / compiler / main / InteractiveEval.hs
index 77594f8..9afd1ac 100644 (file)
@@ -9,7 +9,7 @@
 module InteractiveEval (
 #ifdef GHCI
         RunResult(..), Status(..), Resume(..), History(..),
-       runStmt, SingleStep(..),
+       runStmt, parseImportDecl, SingleStep(..),
         resume,
         abandon, abandonAll,
         getResumeContext,
@@ -18,7 +18,7 @@ module InteractiveEval (
         getHistoryModule,
         back, forward,
        setContext, getContext, 
-        nameSetToGlobalRdrEnv,
+        availsToGlobalRdrEnv,
        getNamesInScope,
        getRdrNamesInScope,
        moduleIsInterpreted,
@@ -30,7 +30,7 @@ module InteractiveEval (
         isModuleInterpreted,
        compileExpr, dynCompileExpr,
        lookupName,
-        Term(..), obtainTerm, obtainTerm1, obtainTermB, reconstructType,
+        Term(..), obtainTermFromId, obtainTermFromVal, reconstructType,
         skolemiseSubst, skolemiseTy
 #endif
         ) where
@@ -40,17 +40,20 @@ module InteractiveEval (
 #include "HsVersions.h"
 
 import HscMain          hiding (compileExpr)
+import HsSyn (ImportDecl)
 import HscTypes
 import TcRnDriver
-import Type             hiding (typeKind)
-import TcType           hiding (typeKind)
+import TcRnMonad (initTc)
+import RnNames         (gresFromAvails, rnImports)
 import InstEnv
+import Type
+import TcType          hiding( typeKind )
 import Var
 import Id
-import IdInfo
 import Name             hiding ( varName )
 import NameSet
 import RdrName
+import PrelNames (pRELUDE)
 import VarSet
 import VarEnv
 import ByteCodeInstr
@@ -60,7 +63,7 @@ import Unique
 import UniqSupply
 import Module
 import Panic
-import LazyUniqFM
+import UniqFM
 import Maybes
 import ErrUtils
 import Util
@@ -72,8 +75,9 @@ import Outputable
 import FastString
 import MonadUtils
 
+import System.Directory
 import Data.Dynamic
-import Data.List (find)
+import Data.List (find, partition)
 import Control.Monad
 import Foreign
 import Foreign.C
@@ -82,7 +86,8 @@ import Data.Array
 import Exception
 import Control.Concurrent
 import Data.List (sortBy)
-import Foreign.StablePtr
+-- import Foreign.StablePtr
+import System.IO
 
 -- -----------------------------------------------------------------------------
 -- running a statement interactively
@@ -212,6 +217,7 @@ runStmt expr step =
         clearWarnings
 
         status <-
+          withVirtualCWD $
             withBreakAction (isStep step) dflags' breakMVar statusMVar $ do
                 let thing_to_run = unsafeCoerce# hval :: IO [HValue]
                 liftIO $ sandboxIO dflags' statusMVar thing_to_run
@@ -227,6 +233,30 @@ runStmt expr step =
               handleRunStatus expr bindings ids
                                breakMVar statusMVar status emptyHistory
 
+withVirtualCWD :: GhcMonad m => m a -> m a
+withVirtualCWD m = do
+  hsc_env <- getSession
+  let ic = hsc_IC hsc_env
+
+  let set_cwd = do
+        dir <- liftIO $ getCurrentDirectory
+        case ic_cwd ic of 
+           Just dir -> liftIO $ setCurrentDirectory dir
+           Nothing  -> return ()
+        return dir
+
+      reset_cwd orig_dir = do
+        virt_dir <- liftIO $ getCurrentDirectory
+        hsc_env <- getSession
+        let old_IC = hsc_IC hsc_env
+        setSession hsc_env{  hsc_IC = old_IC{ ic_cwd = Just virt_dir } }
+        liftIO $ setCurrentDirectory orig_dir
+
+  gbracket set_cwd reset_cwd $ \_ -> m
+
+parseImportDecl :: GhcMonad m => String -> m (ImportDecl RdrName)
+parseImportDecl expr = withSession $ \hsc_env -> hscImport hsc_env expr
+
 emptyHistory :: BoundedList History
 emptyHistory = nilBL 50 -- keep a log of length 50
 
@@ -284,7 +314,7 @@ traceRunStatus expr bindings final_ids
              let history' = mkHistory hsc_env apStack info `consBL` history
                 -- probably better make history strict here, otherwise
                 -- our BoundedList will be pointless.
-             liftIO $ evaluate history'
+             _ <- liftIO $ evaluate history'
              status <-
                  withBreakAction True (hsc_dflags hsc_env)
                                       breakMVar statusMVar $ do
@@ -329,13 +359,13 @@ foreign import ccall "&rts_breakpoint_io_action"
 -- is not responding".
 -- 
 -- Careful here: there may be ^C exceptions flying around, so we start the new
--- thread blocked (forkIO inherits block from the parent, #1048), and unblock
+-- thread blocked (forkIO inherits mask from the parent, #1048), and unblock
 -- only while we execute the user's code.  We can't afford to lose the final
 -- putMVar, otherwise deadlock ensues. (#1583, #1922, #1946)
 sandboxIO :: DynFlags -> MVar Status -> IO [HValue] -> IO Status
 sandboxIO dflags statusMVar thing =
-   block $ do  -- fork starts blocked
-     id <- forkIO $ do res <- Exception.try (unblock $ rethrow dflags thing)
+   mask $ \restore -> do  -- fork starts blocked
+     id <- forkIO $ do res <- Exception.try (restore $ rethrow dflags thing)
                        putMVar statusMVar (Complete res) -- empty: can't block
      withInterruptsSentTo id $ takeMVar statusMVar
 
@@ -349,40 +379,20 @@ sandboxIO dflags statusMVar thing =
 -- not "Interrupted", we unset the exception flag before throwing.
 --
 rethrow :: DynFlags -> IO a -> IO a
-#if __GLASGOW_HASKELL__ < 609
-rethrow dflags io = Exception.catch io $ \e -> do -- NB. not catchDyn
-                case e of
-                   -- If -fbreak-on-error, we break unconditionally,
-                   --  but with care of not breaking twice 
-                   _ | dopt Opt_BreakOnError dflags && 
-                       not(dopt Opt_BreakOnException dflags)
-                        -> poke exceptionFlag 1
-
-                   -- If it is an "Interrupted" exception, we allow
-                   --  a possible break by way of -fbreak-on-exception
-                   DynException d | Just Interrupted <- fromDynamic d
-                        -> return ()
-
-                   -- In any other case, we don't want to break
-                   _    -> poke exceptionFlag 0
-
-                Exception.throwIO e
-#else
-rethrow dflags io = Exception.catch io $ \se@(SomeException e) -> do
+rethrow dflags io = Exception.catch io $ \se -> do
                    -- If -fbreak-on-error, we break unconditionally,
                    --  but with care of not breaking twice 
                 if dopt Opt_BreakOnError dflags &&
                    not (dopt Opt_BreakOnException dflags)
                     then poke exceptionFlag 1
-                    else case cast e of
-                         -- If it is an "Interrupted" exception, we allow
+                    else case fromException se of
+                         -- If it is a "UserInterrupt" exception, we allow
                          --  a possible break by way of -fbreak-on-exception
-                         Just Interrupted -> return ()
+                         Just UserInterrupt -> return ()
                          -- In any other case, we don't want to break
                          _ -> poke exceptionFlag 0
 
                 Exception.throwIO se
-#endif
 
 withInterruptsSentTo :: ThreadId -> IO r -> IO r
 withInterruptsSentTo thread get_result = do
@@ -427,8 +437,8 @@ noBreakAction :: Bool -> BreakInfo -> HValue -> IO ()
 noBreakAction False _ _ = putStrLn "*** Ignoring breakpoint"
 noBreakAction True  _ _ = return () -- exception: just continue
 
-resume :: GhcMonad m => SingleStep -> m RunResult
-resume step
+resume :: GhcMonad m => (SrcSpan->Bool) -> SingleStep -> m RunResult
+resume canLogSpan step
  = do
    hsc_env <- getSession
    let ic = hsc_IC hsc_env
@@ -455,7 +465,8 @@ resume step
         when (isStep step) $ liftIO setStepFlag
         case r of 
           Resume expr tid breakMVar statusMVar bindings 
-              final_ids apStack info _ hist _ -> do
+              final_ids apStack info span hist _ -> do
+               withVirtualCWD $ do
                 withBreakAction (isStep step) (hsc_dflags hsc_env) 
                                         breakMVar statusMVar $ do
                 status <- liftIO $ withInterruptsSentTo tid $ do
@@ -463,10 +474,12 @@ resume step
                                       -- this awakens the stopped thread...
                              takeMVar statusMVar
                                       -- and wait for the result 
-                let hist' = 
-                     case info of 
-                       Nothing -> fromListBL 50 hist
-                       Just i -> mkHistory hsc_env apStack i `consBL` 
+                let prevHistoryLst = fromListBL 50 hist
+                    hist' = case info of
+                       Nothing -> prevHistoryLst
+                       Just i
+                         | not $canLogSpan span -> prevHistoryLst
+                         | otherwise -> mkHistory hsc_env apStack i `consBL`
                                                         fromListBL 50 hist
                 case step of
                   RunAndLogSteps -> 
@@ -542,8 +555,7 @@ bindLocalsAtBreakpoint hsc_env apStack Nothing = do
        e_fs      = fsLit "e"
        e_name    = mkInternalName (getUnique e_fs) (mkTyVarOccFS e_fs) span
        e_tyvar   = mkTcTyVar e_name liftedTypeKind (SkolemTv RuntimeUnkSkol)
-       exn_id    = Id.mkGlobalId VanillaGlobal exn_name (mkTyVarTy e_tyvar)
-                                vanillaIdInfo
+       exn_id    = Id.mkVanillaGlobal exn_name (mkTyVarTy e_tyvar)
        new_tyvars = unitVarSet e_tyvar
 
        ictxt0 = hsc_IC hsc_env
@@ -580,7 +592,7 @@ bindLocalsAtBreakpoint hsc_env apStack (Just info) = do
    -- has been accidentally evaluated, or something else has gone wrong.
    -- So that we don't fall over in a heap when this happens, just don't
    -- bind any free variables instead, and we emit a warning.
-   mb_hValues <- mapM (getIdValFromApStack apStack) offsets
+   mb_hValues <- mapM (getIdValFromApStack apStack) (map fromIntegral offsets)
    let filtered_ids = [ id | (id, Just _hv) <- zip ids mb_hValues ]
    when (any isNothing mb_hValues) $
       debugTraceMsg (hsc_dflags hsc_env) 1 $
@@ -594,8 +606,7 @@ bindLocalsAtBreakpoint hsc_env apStack (Just info) = do
    -- _result in scope at any time.
    let result_name = mkInternalName (getUnique result_fs)
                           (mkVarOccFS result_fs) span
-       result_id   = Id.mkGlobalId VanillaGlobal result_name result_ty 
-                                   vanillaIdInfo
+       result_id   = Id.mkVanillaGlobal result_name result_ty 
 
    -- for each Id we're about to bind in the local envt:
    --    - skolemise the type variables in its type, so they can't
@@ -604,18 +615,22 @@ bindLocalsAtBreakpoint hsc_env apStack (Just info) = do
    --    - tidy the type variables
    --    - globalise the Id (Ids are supposed to be Global, apparently).
    --
-   let all_ids | isPointer result_id = result_id : new_ids
-               | otherwise           = new_ids
+   let result_ok = isPointer result_id
+                    && not (isUnboxedTupleType (idType result_id))
+
+       all_ids | result_ok = result_id : new_ids
+               | otherwise = new_ids
        (id_tys, tyvarss) = mapAndUnzip (skolemiseTy.idType) all_ids
        (_,tidy_tys) = tidyOpenTypes emptyTidyEnv id_tys
        new_tyvars = unionVarSets tyvarss             
-   let final_ids = zipWith setIdType all_ids tidy_tys
+       final_ids = zipWith setIdType all_ids tidy_tys
        ictxt0 = hsc_IC hsc_env
        ictxt1 = extendInteractiveContext ictxt0 final_ids new_tyvars
+
    Linker.extendLinkEnv [ (name,hval) | (name, Just hval) <- zip names mb_hValues ]
-   Linker.extendLinkEnv [(result_name, unsafeCoerce# apStack)]
+   when result_ok $ Linker.extendLinkEnv [(result_name, unsafeCoerce# apStack)]
    hsc_env1 <- rttiEnvironment hsc_env{ hsc_IC = ictxt1 }
-   return (hsc_env1, result_name:names, span)
+   return (hsc_env1, if result_ok then result_name:names else names, span)
   where
    mkNewId :: OccName -> Id -> IO Id
    mkNewId occ id = do
@@ -629,7 +644,7 @@ bindLocalsAtBreakpoint hsc_env apStack (Just info) = do
          loc = nameSrcSpan (idName id)
          name = mkInternalName uniq occ loc
          ty = idType id
-         new_id = Id.mkGlobalId VanillaGlobal name ty (idInfo id)
+         new_id = Id.mkVanillaGlobalWithInfo name ty (idInfo id)
      return new_id
 
 rttiEnvironment :: HscEnv -> IO HscEnv 
@@ -637,26 +652,46 @@ rttiEnvironment hsc_env@HscEnv{hsc_IC=ic} = do
    let InteractiveContext{ic_tmp_ids=tmp_ids} = ic
        incompletelyTypedIds = 
            [id | id <- tmp_ids
-               , not $ null [v | v <- varSetElems$ tyVarsOfType (idType id)
-                              , isSkolemTyVar v]
+               , not $ noSkolems id
                , (occNameFS.nameOccName.idName) id /= result_fs]
-   tys <- reconstructType hsc_env 10 `mapM` incompletelyTypedIds
-          -- map termType `fmap` (obtainTerm hsc_env False `mapM` incompletelyTypedIds)
-   
-   improvs <- sequence [improveRTTIType hsc_env ty ty'
-                 | (ty, Just ty') <- zip (map idType incompletelyTypedIds) tys]
-   let ic' = foldr (\mb_subst ic' ->
-                        maybe (WARN(True, text ("RTTI failed to calculate the "
-                                           ++  "improvement for a type")) ic')
-                              (substInteractiveContext ic' . skolemiseSubst)
-                              mb_subst)
-                   ic
-                   improvs
-   return hsc_env{hsc_IC=ic'}
-
-skolemiseSubst :: TvSubst -> TvSubst
-skolemiseSubst subst = subst `setTvSubstEnv` 
-                        mapVarEnv (fst.skolemiseTy) (getTvSubstEnv subst)
+   hsc_env' <- foldM improveTypes hsc_env (map idName incompletelyTypedIds)
+   return hsc_env'
+    where
+     noSkolems = null . filter isSkolemTyVar . varSetElems . tyVarsOfType . idType
+     improveTypes hsc_env@HscEnv{hsc_IC=ic} name = do
+      let InteractiveContext{ic_tmp_ids=tmp_ids} = ic
+          Just id = find (\i -> idName i == name) tmp_ids
+      if noSkolems id
+         then return hsc_env
+         else do
+           mb_new_ty <- reconstructType hsc_env 10 id
+           let old_ty = idType id
+           case mb_new_ty of
+             Nothing -> return hsc_env
+             Just new_ty -> do
+              mb_subst <- improveRTTIType hsc_env old_ty new_ty
+              case mb_subst of
+               Nothing -> return $
+                        WARN(True, text (":print failed to calculate the "
+                                           ++ "improvement for a type")) hsc_env
+               Just subst -> do
+                 when (dopt Opt_D_dump_rtti (hsc_dflags hsc_env)) $
+                      printForUser stderr alwaysQualify $
+                      fsep [text "RTTI Improvement for", ppr id, equals, ppr subst]
+
+                 let (subst', skols) = skolemiseSubst subst
+                     ic' = extendInteractiveContext
+                               (substInteractiveContext ic subst') [] skols
+                 return hsc_env{hsc_IC=ic'}
+
+skolemiseSubst :: TvSubst -> (TvSubst, TyVarSet)
+skolemiseSubst subst = let
+    varenv               = getTvSubstEnv subst
+    all_together         = mapVarEnv skolemiseTy varenv
+    (varenv', skol_vars) = ( mapVarEnv fst all_together
+                           , map snd (varEnvElts all_together))
+    in (subst `setTvSubstEnv` varenv', unionVarSets skol_vars)
+                        
 
 skolemiseTy :: Type -> (Type, TyVarSet)
 skolemiseTy ty = (substTy subst ty, mkVarSet new_tyvars)
@@ -760,43 +795,48 @@ fromListBL bound l = BL (length l) bound l []
 -- we've built up in the InteractiveContext simply move to the new
 -- module.  They always shadow anything in scope in the current context.
 setContext :: GhcMonad m =>
-              [Module] -- ^ entire top level scope of these modules
-          -> [Module]  -- ^ exports only of these modules
-          -> m ()
-setContext toplev_mods export_mods = do
-  hsc_env <- getSession
-  let old_ic  = hsc_IC     hsc_env
-      hpt     = hsc_HPT    hsc_env
-  --
-  export_env  <- liftIO $ mkExportEnv hsc_env export_mods
-  toplev_envs <- liftIO $ mapM (mkTopLevEnv hpt) toplev_mods
-  let all_env = foldr plusGlobalRdrEnv export_env toplev_envs
-  modifySession $ \_ ->
-      hsc_env{ hsc_IC = old_ic { ic_toplev_scope = toplev_mods,
-                                ic_exports      = export_mods,
-                                ic_rn_gbl_env   = all_env }}
+        [Module]       -- ^ entire top level scope of these modules
+        -> [(Module, Maybe (ImportDecl RdrName))]      -- ^ exports of these modules
+        -> m ()
+setContext toplev_mods other_mods = do
+    hsc_env <- getSession
+    let old_ic  = hsc_IC     hsc_env
+        hpt     = hsc_HPT    hsc_env
+        (decls,mods)   = partition (isJust . snd) other_mods -- time for tracing
+        export_mods = map fst mods
+        imprt_decls = map noLoc (catMaybes (map snd decls))
+    --
+    export_env  <- liftIO $ mkExportEnv hsc_env export_mods
+    import_env  <-
+        if null imprt_decls then return emptyGlobalRdrEnv else do
+            let imports = rnImports imprt_decls
+                this_mod = if null toplev_mods then pRELUDE else head toplev_mods
+            (_, env, _,_) <-
+                ioMsgMaybe $ liftIO $ initTc hsc_env HsSrcFile False this_mod imports
+            return env
+    toplev_envs <- liftIO $ mapM (mkTopLevEnv hpt) toplev_mods
+    let all_env = foldr plusGlobalRdrEnv (plusGlobalRdrEnv export_env import_env) toplev_envs
+    modifySession $ \_ ->
+        hsc_env{ hsc_IC = old_ic { ic_toplev_scope = toplev_mods,
+                        ic_exports      = other_mods,
+                        ic_rn_gbl_env   = all_env }}
 
 -- Make a GlobalRdrEnv based on the exports of the modules only.
 mkExportEnv :: HscEnv -> [Module] -> IO GlobalRdrEnv
-mkExportEnv hsc_env mods = do
-  stuff <- mapM (getModuleExports hsc_env) mods
-  let 
-       (_msgs, mb_name_sets) = unzip stuff
-       gres = [ nameSetToGlobalRdrEnv (availsToNameSet avails) (moduleName mod)
-              | (Just avails, mod) <- zip mb_name_sets mods ]
-  --
-  return $! foldr plusGlobalRdrEnv emptyGlobalRdrEnv gres
-
-nameSetToGlobalRdrEnv :: NameSet -> ModuleName -> GlobalRdrEnv
-nameSetToGlobalRdrEnv names mod =
-  mkGlobalRdrEnv [ GRE  { gre_name = name, gre_par = NoParent, gre_prov = vanillaProv mod }
-                | name <- nameSetToList names ]
-
-vanillaProv :: ModuleName -> Provenance
--- We're building a GlobalRdrEnv as if the user imported
--- all the specified modules into the global interactive module
-vanillaProv mod_name = Imported [ImpSpec { is_decl = decl, is_item = ImpAll}]
+mkExportEnv hsc_env mods
+  = do { stuff <- mapM (getModuleExports hsc_env) mods
+       ; let (_msgs, mb_name_sets) = unzip stuff
+            envs = [ availsToGlobalRdrEnv (moduleName mod) avails
+                    | (Just avails, mod) <- zip mb_name_sets mods ]
+       ; return $! foldr plusGlobalRdrEnv emptyGlobalRdrEnv envs }
+
+availsToGlobalRdrEnv :: ModuleName -> [AvailInfo] -> GlobalRdrEnv
+availsToGlobalRdrEnv mod_name avails
+  = mkGlobalRdrEnv (gresFromAvails imp_prov avails)
   where
+      -- We're building a GlobalRdrEnv as if the user imported
+      -- all the specified modules into the global interactive module
+    imp_prov = Imported [ImpSpec { is_decl = decl, is_item = ImpAll}]
     decl = ImpDeclSpec { is_mod = mod_name, is_as = mod_name, 
                         is_qual = False, 
                         is_dloc = srcLocSpan interactiveSrcLoc }
@@ -816,7 +856,7 @@ mkTopLevEnv hpt modl
 -- | Get the interactive evaluation context, consisting of a pair of the
 -- set of modules from which we take the full top-level scope, and the set
 -- of modules from which we take just the exports respectively.
-getContext :: GhcMonad m => m ([Module],[Module])
+getContext :: GhcMonad m => m ([Module],[(Module, Maybe (ImportDecl RdrName))])
 getContext = withSession $ \HscEnv{ hsc_IC=ic } ->
               return (ic_toplev_scope ic, ic_exports ic)
 
@@ -940,7 +980,7 @@ dynCompileExpr expr = do
     setContext full $
         (mkModule
             (stringToPackageId "base") (mkModuleName "Data.Dynamic")
-        ):exports
+        ,Nothing):exports
     let stmt = "let __dynCompileExpr = Data.Dynamic.toDyn (" ++ expr ++ ")"
     Just (ids, hvals) <- withSession (flip hscStmt stmt)
     setContext full exports
@@ -969,23 +1009,20 @@ isModuleInterpreted mod_summary = withSession $ \hsc_env ->
 ----------------------------------------------------------------------------
 -- RTTI primitives
 
-obtainTerm1 :: HscEnv -> Bool -> Maybe Type -> a -> IO Term
-obtainTerm1 hsc_env force mb_ty x = 
-              cvObtainTerm hsc_env maxBound force mb_ty (unsafeCoerce# x)
-
-obtainTermB :: HscEnv -> Int -> Bool -> Id -> IO Term
-obtainTermB hsc_env bound force id =  do
-              hv <- Linker.getHValue hsc_env (varName id) 
-              cvObtainTerm hsc_env bound force (Just$ idType id) hv
+obtainTermFromVal :: HscEnv -> Int -> Bool -> Type -> a -> IO Term
+obtainTermFromVal hsc_env bound force ty x =
+              cvObtainTerm hsc_env bound force ty (unsafeCoerce# x)
 
-obtainTerm :: HscEnv -> Bool -> Id -> IO Term
-obtainTerm hsc_env force id =  do
-              hv <- Linker.getHValue hsc_env (varName id) 
-              cvObtainTerm hsc_env maxBound force (Just$ idType id) hv
+obtainTermFromId :: HscEnv -> Int -> Bool -> Id -> IO Term
+obtainTermFromId hsc_env bound force id =  do
+              hv <- Linker.getHValue hsc_env (varName id)
+              cvObtainTerm hsc_env bound force (idType id) hv
 
 -- Uses RTTI to reconstruct the type of an Id, making it less polymorphic
 reconstructType :: HscEnv -> Int -> Id -> IO (Maybe Type)
 reconstructType hsc_env bound id = do
               hv <- Linker.getHValue hsc_env (varName id) 
-              cvReconstructType hsc_env bound (Just$ idType id) hv
+              cvReconstructType hsc_env bound (idType id) hv
+
 #endif /* GHCI */
+