In GHCi, filter instances by what is in scope, not just by what is in scope unqualified
[ghc-hetmet.git] / compiler / main / InteractiveEval.hs
index b830db6..42b787a 100644 (file)
@@ -28,7 +28,8 @@ module InteractiveEval (
         isModuleInterpreted,
        compileExpr, dynCompileExpr,
        lookupName,
-        obtainTerm, obtainTerm1
+        obtainTerm, obtainTerm1, reconstructType,
+        skolemiseSubst, skolemiseTy
 #endif
         ) where
 
@@ -72,7 +73,6 @@ import Control.Monad
 import Foreign
 import Foreign.C
 import GHC.Exts
-import GHC.Conc         ( ThreadId(..) )
 import Data.Array
 import Control.Exception as Exception
 import Control.Concurrent
@@ -164,7 +164,7 @@ runStmt (Session ref) expr step
 
               let thing_to_run = unsafeCoerce# hval :: IO [HValue]
               status <- sandboxIO statusMVar thing_to_run
-
+              
               let ic = hsc_IC hsc_env
                   bindings = (ic_tmp_ids ic, ic_tyvars ic)
 
@@ -206,8 +206,9 @@ handleRunStatus expr ref bindings final_ids breakMVar statusMVar status
                                         final_ids emptyVarSet
                         -- the bound Ids never have any free TyVars
                     final_names = map idName final_ids
-                writeIORef ref hsc_env{hsc_IC=final_ic}
                 Linker.extendLinkEnv (zip final_names hvals)
+                hsc_env' <- rttiEnvironment hsc_env{hsc_IC=final_ic}
+                writeIORef ref hsc_env' 
                 return (RunOk final_names)
 
 
@@ -269,10 +270,26 @@ foreign import ccall "&rts_breakpoint_io_action"
 sandboxIO :: MVar Status -> IO [HValue] -> IO Status
 sandboxIO statusMVar thing = 
   withInterruptsSentTo 
-        (forkIO (do res <- Exception.try thing
+        (forkIO (do res <- Exception.try (rethrow thing)
                     putMVar statusMVar (Complete res)))
         (takeMVar statusMVar)
 
+-- We want to turn ^C into a break when -fbreak-on-exception is on,
+-- but it's an async exception and we only break for sync exceptions.
+-- Idea: if we catch and re-throw it, then the re-throw will trigger
+-- a break.  Great - but we don't want to re-throw all exceptions, because
+-- then we'll get a double break for ordinary sync exceptions (you'd have
+-- to :continue twice, which looks strange).  So if the exception is
+-- not "Interrupted", we unset the exception flag before throwing.
+--
+rethrow :: IO a -> IO a
+rethrow io = Exception.catch io $ \e -> -- NB. not catchDyn
+                case e of
+                   DynException d | Just Interrupted <- fromDynamic d
+                        -> Exception.throwIO e
+                   _ -> do poke exceptionFlag 0; Exception.throwIO e
+
+
 withInterruptsSentTo :: IO ThreadId -> IO r -> IO r
 withInterruptsSentTo io get_result = do
   ts <- takeMVar interruptTargetThread
@@ -405,7 +422,8 @@ moveHist fn (Session ref) = do
 
 -- -----------------------------------------------------------------------------
 -- After stopping at a breakpoint, add free variables to the environment
-
+result_fs = FSLIT("_result")
+       
 bindLocalsAtBreakpoint
         :: HscEnv
         -> HValue
@@ -460,7 +478,8 @@ bindLocalsAtBreakpoint hsc_env apStack (Just info) = do
    -- 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
-   let filtered_ids = [ id | (id, Just _) <- zip ids mb_hValues ]
+   let (filtered_hvs, filtered_ids) = 
+                       unzip [ (hv, id) | (id, Just hv) <- zip ids mb_hValues ]
    when (any isNothing mb_hValues) $
       debugTraceMsg (hsc_dflags hsc_env) 1 $
          text "Warning: _result has been evaluated, some bindings have been lost"
@@ -471,8 +490,7 @@ bindLocalsAtBreakpoint hsc_env apStack (Just info) = do
    -- make an Id for _result.  We use the Unique of the FastString "_result";
    -- we don't care about uniqueness here, because there will only be one
    -- _result in scope at any time.
-   let result_fs = FSLIT("_result")
-       result_name = mkInternalName (getUnique result_fs)
+   let result_name = mkInternalName (getUnique result_fs)
                           (mkVarOccFS result_fs) span
        result_id   = Id.mkGlobalId VanillaGlobal result_name result_ty 
                                    vanillaIdInfo
@@ -489,14 +507,13 @@ bindLocalsAtBreakpoint hsc_env apStack (Just info) = do
        (id_tys, tyvarss) = mapAndUnzip (skolemiseTy.idType) all_ids
        (_,tidy_tys) = tidyOpenTypes emptyTidyEnv id_tys
        new_tyvars = unionVarSets tyvarss             
-       final_ids = zipWith setIdType all_ids tidy_tys
-
-   let   ictxt0 = hsc_IC hsc_env
-         ictxt1 = extendInteractiveContext ictxt0 final_ids new_tyvars
-
+   let 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)]
-   return (hsc_env{ hsc_IC = ictxt1 }, result_name:names, span)
+   hsc_env1 <- rttiEnvironment hsc_env{ hsc_IC = ictxt1 }
+   return (hsc_env1, result_name:names, span)
   where
    mkNewId :: OccName -> Id -> IO Id
    mkNewId occ id = do
@@ -507,6 +524,26 @@ bindLocalsAtBreakpoint hsc_env apStack (Just info) = do
          new_id = Id.mkGlobalId VanillaGlobal name ty (idInfo id)
      return new_id
 
+rttiEnvironment :: HscEnv -> IO HscEnv 
+rttiEnvironment hsc_env@HscEnv{hsc_IC=ic} = do
+   let InteractiveContext{ic_tmp_ids=tmp_ids, ic_tyvars = tyvars} = ic
+       incompletelyTypedIds = 
+           [id | id <- tmp_ids
+               , not $ null [v | v <- varSetElems$ tyVarsOfType (idType id)
+                              , isSkolemTyVar v]
+               , (occNameFS.nameOccName.idName) id /= result_fs]
+   tys <- reconstructType hsc_env False `mapM` incompletelyTypedIds
+          -- map termType `fmap` (obtainTerm hsc_env False `mapM` incompletelyTypedIds)
+   
+   let substs = [computeRTTIsubst ty ty' 
+                 | (ty, Just ty') <- zip (map idType incompletelyTypedIds) tys]
+       ic'    = foldr (flip substInteractiveContext) ic 
+                           (map skolemiseSubst $ catMaybes substs)
+   return hsc_env{hsc_IC=ic'}
+
+skolemiseSubst subst = subst `setTvSubstEnv` 
+                        mapVarEnv (fst.skolemiseTy) (getTvSubstEnv subst)
+
 skolemiseTy :: Type -> (Type, TyVarSet)
 skolemiseTy ty = (substTy subst ty, mkVarSet new_tyvars)
   where env           = mkVarEnv (zip tyvars new_tyvar_tys)
@@ -632,7 +669,7 @@ mkExportEnv hsc_env mods = do
 
 nameSetToGlobalRdrEnv :: NameSet -> ModuleName -> GlobalRdrEnv
 nameSetToGlobalRdrEnv names mod =
-  mkGlobalRdrEnv [ GRE  { gre_name = name, gre_prov = vanillaProv mod }
+  mkGlobalRdrEnv [ GRE  { gre_name = name, gre_par = NoParent, gre_prov = vanillaProv mod }
                 | name <- nameSetToList names ]
 
 vanillaProv :: ModuleName -> Provenance
@@ -674,8 +711,29 @@ moduleIsInterpreted s modl = withSession s $ \h ->
                 _not_a_home_module -> return False
 
 -- | Looks up an identifier in the current interactive context (for :info)
+-- Filter the instances by the ones whose tycons (or clases resp) 
+-- are in scope (qualified or otherwise).  Otherwise we list a whole lot too many!
+-- The exact choice of which ones to show, and which to hide, is a judgement call.
+--     (see Trac #1581)
 getInfo :: Session -> Name -> IO (Maybe (TyThing,Fixity,[Instance]))
-getInfo s name = withSession s $ \hsc_env -> tcRnGetInfo hsc_env name
+getInfo s name 
+  = withSession s $ \hsc_env -> 
+    do { mb_stuff <- tcRnGetInfo hsc_env name
+       ; case mb_stuff of
+           Nothing -> return Nothing
+           Just (thing, fixity, ispecs) -> do
+       { let rdr_env = ic_rn_gbl_env (hsc_IC hsc_env)
+       ; return (Just (thing, fixity, filter (plausible rdr_env) ispecs)) } }
+  where
+    plausible rdr_env ispec    -- Dfun involving only names that are in ic_rn_glb_env
+       = all ok $ nameSetToList $ tyClsNamesOfType $ idType $ instanceDFunId ispec
+       where   -- A name is ok if it's in the rdr_env, 
+               -- whether qualified or not
+         ok n | n == name         = True       -- The one we looked for in the first place!
+              | isBuiltInSyntax n = True
+              | isExternalName n  = any ((== n) . gre_name)
+                                        (lookupGRE_Name rdr_env n)
+              | otherwise         = True
 
 -- | Returns all names in scope in the current interactive context
 getNamesInScope :: Session -> IO [Name]
@@ -804,12 +862,21 @@ isModuleInterpreted s mod_summary = withSession s $ \hsc_env ->
                      where
                         obj_linkable = isObjectLinkable (expectJust "showModule" (hm_linkable mod_info))
 
-obtainTerm1 :: Session -> Bool -> Maybe Type -> a -> IO Term
-obtainTerm1 sess force mb_ty x = withSession sess $ \hsc_env -> cvObtainTerm hsc_env force mb_ty (unsafeCoerce# x)
+----------------------------------------------------------------------------
+-- RTTI primitives
+
+obtainTerm1 :: HscEnv -> Bool -> Maybe Type -> a -> IO Term
+obtainTerm1 hsc_env force mb_ty x = 
+              cvObtainTerm hsc_env force mb_ty (unsafeCoerce# x)
 
-obtainTerm :: Session -> Bool -> Id -> IO Term
-obtainTerm sess force id = withSession sess $ \hsc_env -> do
+obtainTerm :: HscEnv -> Bool -> Id -> IO Term
+obtainTerm hsc_env force id =  do
               hv <- Linker.getHValue hsc_env (varName id) 
               cvObtainTerm hsc_env force (Just$ idType id) hv
 
+-- Uses RTTI to reconstruct the type of an Id, making it less polymorphic
+reconstructType :: HscEnv -> Bool -> Id -> IO (Maybe Type)
+reconstructType hsc_env force id = do
+              hv <- Linker.getHValue hsc_env (varName id) 
+              cvReconstructType hsc_env force (Just$ idType id) hv
 #endif /* GHCI */