Make record selectors into ordinary functions
[ghc-hetmet.git] / compiler / ghci / Debugger.hs
1 -----------------------------------------------------------------------------
2 --
3 -- GHCi Interactive debugging commands 
4 --
5 -- Pepe Iborra (supported by Google SoC) 2006
6 --
7 -- ToDo: lots of violation of layering here.  This module should
8 -- decide whether it is above the GHC API (import GHC and nothing
9 -- else) or below it.
10 -- 
11 -----------------------------------------------------------------------------
12
13 module Debugger (pprintClosureCommand, showTerm, pprTypeAndContents) where
14
15 import Linker
16 import RtClosureInspect
17
18 import HscTypes
19 import Id
20 import Name
21 import Var hiding ( varName )
22 import VarSet
23 import Name 
24 import UniqSupply
25 import TcType
26 import GHC
27 import DynFlags
28 import InteractiveEval
29 import Outputable
30 import SrcLoc
31 import PprTyThing
32 import MonadUtils
33
34 import Exception
35 import Control.Monad
36 import Data.List
37 import Data.Maybe
38 import Data.IORef
39
40 import System.IO
41 import GHC.Exts
42
43 -------------------------------------
44 -- | The :print & friends commands
45 -------------------------------------
46 pprintClosureCommand :: GhcMonad m => Bool -> Bool -> String -> m ()
47 pprintClosureCommand bindThings force str = do
48   tythings <- (catMaybes . concat) `liftM`
49                  mapM (\w -> GHC.parseName w >>=
50                                 mapM GHC.lookupName)
51                       (words str)
52   let ids = [id | AnId id <- tythings]
53
54   -- Obtain the terms and the recovered type information
55   (terms, substs0) <- unzip `liftM` mapM go ids
56
57   -- Apply the substitutions obtained after recovering the types
58   modifySession $ \hsc_env ->
59     let (substs, skol_vars) = unzip$ map skolemiseSubst substs0
60         hsc_ic' = foldr (flip substInteractiveContext)
61                         (extendInteractiveContext (hsc_IC hsc_env) [] (unionVarSets skol_vars))
62                         substs
63      in hsc_env{hsc_IC = hsc_ic'}
64   -- Finally, print the Terms
65   unqual  <- GHC.getPrintUnqual
66   docterms <- mapM showTerm terms
67   liftIO $ (printForUser stdout unqual . vcat)
68            (zipWith (\id docterm -> ppr id <+> char '=' <+> docterm)
69                     ids
70                     docterms)
71  where
72    -- Do the obtainTerm--bindSuspensions-computeSubstitution dance
73    go :: GhcMonad m => Id -> m (Term, TvSubst)
74    go id = do
75        term_    <- GHC.obtainTermFromId maxBound force id
76        term     <- tidyTermTyVars term_
77        term'    <- if bindThings &&
78                       False == isUnliftedTypeKind (termType term)
79                      then bindSuspensions term
80                      else return term
81      -- Before leaving, we compare the type obtained to see if it's more specific
82      --  Then, we extract a substitution,
83      --  mapping the old tyvars to the reconstructed types.
84        let reconstructed_type = termType term
85        mb_subst <- withSession $ \hsc_env ->
86                      liftIO $ improveRTTIType hsc_env (idType id) (reconstructed_type)
87        maybe (return ())
88              (\subst -> traceOptIf Opt_D_dump_rtti
89                    (fsep $ [text "RTTI Improvement for", ppr id,
90                            text "is the substitution:" , ppr subst]))
91              mb_subst
92        return (term', fromMaybe emptyTvSubst mb_subst)
93
94    tidyTermTyVars :: GhcMonad m => Term -> m Term
95    tidyTermTyVars t =
96      withSession $ \hsc_env -> do
97      let env_tvs      = ic_tyvars (hsc_IC hsc_env)
98          my_tvs       = termTyVars t
99          tvs          = env_tvs `minusVarSet` my_tvs
100          tyvarOccName = nameOccName . tyVarName
101          tidyEnv      = (initTidyOccEnv (map tyvarOccName (varSetElems tvs))
102                         , env_tvs `intersectVarSet` my_tvs)
103      return$ mapTermType (snd . tidyOpenType tidyEnv) t
104
105 -- | Give names, and bind in the interactive environment, to all the suspensions
106 --   included (inductively) in a term
107 bindSuspensions :: GhcMonad m => Term -> m Term
108 bindSuspensions t = do
109       hsc_env <- getSession
110       inScope <- GHC.getBindings
111       let ictxt        = hsc_IC hsc_env
112           prefix       = "_t"
113           alreadyUsedNames = map (occNameString . nameOccName . getName) inScope
114           availNames   = map ((prefix++) . show) [(1::Int)..] \\ alreadyUsedNames
115       availNames_var  <- liftIO $ newIORef availNames
116       (t', stuff)     <- liftIO $ foldTerm (nameSuspensionsAndGetInfos availNames_var) t
117       let (names, tys, hvals) = unzip3 stuff
118           (tys', skol_vars)   = unzip $ map skolemiseTy tys
119       let ids = [ mkVanillaGlobal name ty 
120                 | (name,ty) <- zip names tys']
121           new_ic = extendInteractiveContext ictxt ids (unionVarSets skol_vars)
122       liftIO $ extendLinkEnv (zip names hvals)
123       modifySession $ \_ -> hsc_env {hsc_IC = new_ic }
124       return t'
125      where
126
127 --    Processing suspensions. Give names and recopilate info
128         nameSuspensionsAndGetInfos :: IORef [String] ->
129                                        TermFold (IO (Term, [(Name,Type,HValue)]))
130         nameSuspensionsAndGetInfos freeNames = TermFold
131                       {
132                         fSuspension = doSuspension freeNames
133                       , fTerm = \ty dc v tt -> do
134                                     tt' <- sequence tt
135                                     let (terms,names) = unzip tt'
136                                     return (Term ty dc v terms, concat names)
137                       , fPrim    = \ty n ->return (Prim ty n,[])
138                       , fNewtypeWrap  = 
139                                 \ty dc t -> do 
140                                     (term, names) <- t
141                                     return (NewtypeWrap ty dc term, names)
142                       , fRefWrap = \ty t -> do
143                                     (term, names) <- t 
144                                     return (RefWrap ty term, names)
145                       }
146         doSuspension freeNames ct ty hval _name = do
147           name <- atomicModifyIORef freeNames (\x->(tail x, head x))
148           n <- newGrimName name
149           return (Suspension ct ty hval (Just n), [(n,ty,hval)])
150
151
152 --  A custom Term printer to enable the use of Show instances
153 showTerm :: GhcMonad m => Term -> m SDoc
154 showTerm term = do
155     dflags       <- GHC.getSessionDynFlags
156     if dopt Opt_PrintEvldWithShow dflags
157        then cPprTerm (liftM2 (++) (\_y->[cPprShowable]) cPprTermBase) term
158        else cPprTerm cPprTermBase term
159  where
160   cPprShowable prec t@Term{ty=ty, val=val} =
161     if not (isFullyEvaluatedTerm t)
162      then return Nothing
163      else do
164         hsc_env <- getSession
165         dflags  <- GHC.getSessionDynFlags
166         do
167            (new_env, bname) <- bindToFreshName hsc_env ty "showme"
168            setSession new_env
169                       -- XXX: this tries to disable logging of errors
170                       -- does this still do what it is intended to do
171                       -- with the changed error handling and logging?
172            let noop_log _ _ _ _ = return ()
173                expr = "show " ++ showSDoc (ppr bname)
174            GHC.setSessionDynFlags dflags{log_action=noop_log}
175            txt_ <- withExtendedLinkEnv [(bname, val)]
176                                          (GHC.compileExpr expr)
177            let myprec = 10 -- application precedence. TODO Infix constructors
178            let txt = unsafeCoerce# txt_
179            if not (null txt) then
180              return $ Just$ cparen (prec >= myprec &&
181                                          needsParens txt)
182                                    (text txt)
183             else return Nothing
184          `gfinally` do
185            setSession hsc_env
186            GHC.setSessionDynFlags dflags
187   cPprShowable prec NewtypeWrap{ty=new_ty,wrapped_term=t} = 
188       cPprShowable prec t{ty=new_ty}
189   cPprShowable _ _ = return Nothing
190
191   needsParens ('"':_) = False   -- some simple heuristics to see whether parens
192                                 -- are redundant in an arbitrary Show output
193   needsParens ('(':_) = False
194   needsParens txt = ' ' `elem` txt
195
196
197   bindToFreshName hsc_env ty userName = do
198     name <- newGrimName userName
199     let ictxt    = hsc_IC hsc_env
200         tmp_ids  = ic_tmp_ids ictxt
201         id       = mkVanillaGlobal name ty 
202         new_ic   = ictxt { ic_tmp_ids = id : tmp_ids }
203     return (hsc_env {hsc_IC = new_ic }, name)
204
205 --    Create new uniques and give them sequentially numbered names
206 newGrimName :: MonadIO m => String -> m Name
207 newGrimName userName  = do
208     us <- liftIO $ mkSplitUniqSupply 'b'
209     let unique  = uniqFromSupply us
210         occname = mkOccName varName userName
211         name    = mkInternalName unique occname noSrcSpan
212     return name
213
214 pprTypeAndContents :: GhcMonad m => [Id] -> m SDoc
215 pprTypeAndContents ids = do
216   dflags  <- GHC.getSessionDynFlags
217   let pefas     = dopt Opt_PrintExplicitForalls dflags
218       pcontents = dopt Opt_PrintBindContents dflags
219   if pcontents 
220     then do
221       let depthBound = 100
222       terms      <- mapM (GHC.obtainTermFromId depthBound False) ids
223       docs_terms <- mapM showTerm terms
224       return $ vcat $ zipWith (\ty cts -> ty <+> equals <+> cts)
225                              (map (pprTyThing pefas . AnId) ids)
226                              docs_terms
227     else return $  vcat $ map (pprTyThing pefas . AnId) ids
228
229 --------------------------------------------------------------
230 -- Utils 
231
232 traceOptIf :: GhcMonad m => DynFlag -> SDoc -> m ()
233 traceOptIf flag doc = do
234   dflags <- GHC.getSessionDynFlags
235   when (dopt flag dflags) $ liftIO $ printForUser stderr alwaysQualify doc