summaryrefslogtreecommitdiff
path: root/libraries/ghci
diff options
context:
space:
mode:
authorSimon Marlow <marlowsd@gmail.com>2016-06-23 09:22:32 +0100
committerSimon Marlow <marlowsd@gmail.com>2016-06-24 11:29:33 +0100
commiteb732195f6c005c769232a79e5d17e3d768603d1 (patch)
treeaf50965f6f74f524d0552c66140cb5982a6dcfd6 /libraries/ghci
parentbdb0d24be9c83b08fd3f4b870a17f6be31a24b1b (diff)
downloadhaskell-eb732195f6c005c769232a79e5d17e3d768603d1.tar.gz
Remote GHCi: comments only
Summary: Add more Notes and signposts across the codebase to help navigation. Test Plan: validate Reviewers: goldfire, simonpj, austin, ezyang, hvr, bgamari, erikd Subscribers: thomie Differential Revision: https://phabricator.haskell.org/D2358
Diffstat (limited to 'libraries/ghci')
-rw-r--r--libraries/ghci/GHCi/CreateBCO.hs1
-rw-r--r--libraries/ghci/GHCi/Message.hs17
-rw-r--r--libraries/ghci/GHCi/RemoteTypes.hs8
-rw-r--r--libraries/ghci/GHCi/Run.hs5
-rw-r--r--libraries/ghci/GHCi/TH.hs98
5 files changed, 125 insertions, 4 deletions
diff --git a/libraries/ghci/GHCi/CreateBCO.hs b/libraries/ghci/GHCi/CreateBCO.hs
index 9501b5f0a7..f42c975cd7 100644
--- a/libraries/ghci/GHCi/CreateBCO.hs
+++ b/libraries/ghci/GHCi/CreateBCO.hs
@@ -10,6 +10,7 @@
-- (c) The University of Glasgow 2002-2006
--
+-- | Create real byte-code objects from 'ResolvedBCO's.
module GHCi.CreateBCO (createBCOs) where
import GHCi.ResolvedBCO
diff --git a/libraries/ghci/GHCi/Message.hs b/libraries/ghci/GHCi/Message.hs
index b46030f7ea..b14fca4f2d 100644
--- a/libraries/ghci/GHCi/Message.hs
+++ b/libraries/ghci/GHCi/Message.hs
@@ -2,6 +2,12 @@
GeneralizedNewtypeDeriving, ExistentialQuantification, RecordWildCards #-}
{-# OPTIONS_GHC -fno-warn-name-shadowing -fno-warn-orphans #-}
+-- |
+-- Remote GHCi message types and serialization.
+--
+-- For details on Remote GHCi, see Note [Remote GHCi] in
+-- compiler/ghci/GHCi.hs.
+--
module GHCi.Message
( Message(..), Msg(..)
, THMessage(..), THMsg(..)
@@ -44,7 +50,8 @@ import System.IO.Error
-- -----------------------------------------------------------------------------
-- The RPC protocol between GHC and the interactive server
--- | A @Message a@ is a message that returns a value of type @a@
+-- | A @Message a@ is a message that returns a value of type @a@.
+-- These are requests sent from GHC to the server.
data Message a where
-- | Exit the iserv process
Shutdown :: Message ()
@@ -159,6 +166,8 @@ data Message a where
-> Message (Maybe HValueRef)
-- Template Haskell -------------------------------------------
+ -- For more details on how TH works with Remote GHCi, see
+ -- Note [Remote Template Haskell] in libraries/ghci/GHCi/TH.hs.
-- | Start a new TH module, return a state token that should be
StartTH :: Message (RemoteRef (IORef QState))
@@ -198,7 +207,8 @@ instance Binary a => Binary (QResult a)
-- | Messages sent back to GHC from GHCi.TH, to implement the methods
--- of 'Quasi'.
+-- of 'Quasi'. For an overview of how TH works with Remote GHCi, see
+-- Note [Remote Template Haskell] in GHCi.TH.
data THMessage a where
NewName :: String -> THMessage (THResult TH.Name)
Report :: Bool -> String -> THMessage (THResult ())
@@ -352,6 +362,9 @@ data THResultType = THExp | THPat | THType | THDec | THAnnWrapper
instance Binary THResultType
+-- | The server-side Template Haskell state. This is created by the
+-- StartTH message. A new one is created per module that GHC
+-- typechecks.
data QState = QState
{ qsMap :: Map TypeRep Dynamic
-- ^ persistent data between splices in a module
diff --git a/libraries/ghci/GHCi/RemoteTypes.hs b/libraries/ghci/GHCi/RemoteTypes.hs
index ea91f19a2b..5bc0136113 100644
--- a/libraries/ghci/GHCi/RemoteTypes.hs
+++ b/libraries/ghci/GHCi/RemoteTypes.hs
@@ -1,4 +1,12 @@
{-# LANGUAGE CPP, StandaloneDeriving, GeneralizedNewtypeDeriving #-}
+
+-- |
+-- Types for referring to remote objects in Remote GHCi. For more
+-- details, see Note [External GHCi pointers] in compiler/ghci/GHCi.hs
+--
+-- For details on Remote GHCi, see Note [Remote GHCi] in
+-- compiler/ghci/GHCi.hs.
+--
module GHCi.RemoteTypes
( RemotePtr(..), toRemotePtr, fromRemotePtr, castRemotePtr
, HValue(..)
diff --git a/libraries/ghci/GHCi/Run.hs b/libraries/ghci/GHCi/Run.hs
index a2ea4e203c..542fe551cd 100644
--- a/libraries/ghci/GHCi/Run.hs
+++ b/libraries/ghci/GHCi/Run.hs
@@ -3,7 +3,10 @@
{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
-- |
--- Execute GHCi messages
+-- Execute GHCi messages.
+--
+-- For details on Remote GHCi, see Note [Remote GHCi] in
+-- compiler/ghci/GHCi.hs.
--
module GHCi.Run
( run, redirectInterrupts
diff --git a/libraries/ghci/GHCi/TH.hs b/libraries/ghci/GHCi/TH.hs
index 6d6158ffdb..3495162a12 100644
--- a/libraries/ghci/GHCi/TH.hs
+++ b/libraries/ghci/GHCi/TH.hs
@@ -7,6 +7,85 @@
--
module GHCi.TH (startTH, finishTH, runTH, GHCiQException(..)) where
+{- Note [Remote Template Haskell]
+
+Here is an overview of how TH works with -fexternal-interpreter.
+
+Initialisation
+~~~~~~~~~~~~~~
+
+GHC sends a StartTH message to the server (see TcSplice.getTHState):
+
+ StartTH :: Message (RemoteRef (IORef QState))
+
+The server creates an initial QState object, makes an IORef to it, and
+returns a RemoteRef to this to GHC. (see GHCi.TH.startTH below).
+
+This happens once per module, the first time we need to run a TH
+splice. The reference that GHC gets back is kept in
+tcg_th_remote_state in the TcGblEnv, and passed to each RunTH call
+that follows.
+
+
+For each splice
+~~~~~~~~~~~~~~~
+
+1. GHC compiles a splice to byte code, and sends it to the server: in
+ a CreateBCOs message:
+
+ CreateBCOs :: [LB.ByteString] -> Message [HValueRef]
+
+2. The server creates the real byte-code objects in its heap, and
+ returns HValueRefs to GHC. HValueRef is the same as RemoteRef
+ HValue.
+
+3. GHC sends a RunTH message to the server:
+
+ RunTH
+ :: RemoteRef (IORef QState)
+ -- The state returned by StartTH in step1
+ -> HValueRef
+ -- The HValueRef we got in step 4, points to the code for the splice
+ -> THResultType
+ -- Tells us what kind of splice this is (decl, expr, type, etc.)
+ -> Maybe TH.Loc
+ -- Source location
+ -> Message (QResult ByteString)
+ -- Eventually it will return a QResult back to GHC. The
+ -- ByteString here is the (encoded) result of the splice.
+
+4. The server runs the splice code.
+
+5. Each time the splice code calls a method of the Quasi class, such
+ as qReify, a message is sent from the server to GHC. These
+ messages are defined by the THMessage type. GHC responds with the
+ result of the request, e.g. in the case of qReify it would be the
+ TH.Info for the requested entity.
+
+6. When the splice has been fully evaluated, the server sends
+ RunTHDone back to GHC. This tells GHC that the server has finished
+ sending THMessages and will send the QResult next.
+
+8. The server then sends a QResult back to GHC, which is notionally
+ the response to the original RunTH message. The QResult indicates
+ whether the splice succeeded, failed, or threw an exception.
+
+
+After typechecking
+~~~~~~~~~~~~~~~~~~
+
+GHC sends a FinishTH message to the server (see TcSplice.finishTH).
+The server runs any finalizers that were added by addModuleFinalizer.
+
+
+Other Notes on TH / Remote GHCi
+
+ * Note [Remote GHCi] in compiler/ghci/GHCi.hs
+ * Note [External GHCi pointers] in compiler/ghci/GHCi.hs
+ * Note [TH recover with -fexternal-interpreter] in
+ compiler/typecheck/TcSplice.hs
+-}
+
import GHCi.Message
import GHCi.RemoteTypes
import GHC.Serialized
@@ -29,6 +108,7 @@ import qualified Language.Haskell.TH as TH
import qualified Language.Haskell.TH.Syntax as TH
import Unsafe.Coerce
+-- | Create a new instance of 'QState'
initQState :: Pipe -> QState
initQState p = QState M.empty [] Nothing p
@@ -39,8 +119,10 @@ runModFinalizers = go =<< getState
putState (s { qsFinalizers = ff}) >> TH.runQ f >> getState >>= go
go _ = return ()
+-- | The monad in which we run TH computations on the server
newtype GHCiQ a = GHCiQ { runGHCiQ :: QState -> IO (a, QState) }
+-- | The exception thrown by "fail" in the GHCiQ monad
data GHCiQException = GHCiQException QState String
deriving Show
@@ -75,6 +157,7 @@ putState s = GHCiQ $ \_ -> return ((),s)
noLoc :: TH.Loc
noLoc = TH.Loc "<no file>" "<no package>" "<no module>" (0,0) (0,0)
+-- | Send a 'THMessage' to GHC and return the result.
ghcCmd :: Binary a => THMessage (THResult a) -> GHCiQ a
ghcCmd m = GHCiQ $ \s -> do
r <- remoteTHCall (qsPipe s) m
@@ -126,11 +209,14 @@ instance TH.Quasi GHCiQ where
qIsExtEnabled x = ghcCmd (IsExtEnabled x)
qExtsEnabled = ghcCmd ExtsEnabled
+-- | The implementation of the 'StartTH' message: create
+-- a new IORef QState, and return a RemoteRef to it.
startTH :: IO (RemoteRef (IORef QState))
startTH = do
r <- newIORef (initQState (error "startTH: no pipe"))
mkRemoteRef r
+-- | The implementation of the 'FinishTH' message.
finishTH :: Pipe -> RemoteRef (IORef QState) -> IO ()
finishTH pipe rstate = do
qstateref <- localRef rstate
@@ -138,11 +224,20 @@ finishTH pipe rstate = do
_ <- runGHCiQ runModFinalizers qstate { qsPipe = pipe }
return ()
+-- | The implementation of the 'RunTH' message
runTH
- :: Pipe -> RemoteRef (IORef QState) -> HValueRef
+ :: Pipe
+ -> RemoteRef (IORef QState)
+ -- ^ The TH state, created by 'startTH'
+ -> HValueRef
+ -- ^ The splice to run
-> THResultType
+ -- ^ What kind of splice it is
-> Maybe TH.Loc
+ -- ^ The source location
-> IO ByteString
+ -- ^ Returns an (encoded) result that depends on the THResultType
+
runTH pipe rstate rhv ty mb_loc = do
hv <- localRef rhv
case ty of
@@ -156,6 +251,7 @@ runTH pipe rstate rhv ty mb_loc = do
AnnotationWrapper thing -> return $!
LB.toStrict (runPut (put (toSerialized serializeWithData thing)))
+-- | Run a Q computation.
runTHQ
:: Binary a => Pipe -> RemoteRef (IORef QState) -> Maybe TH.Loc -> TH.Q a
-> IO ByteString