summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/ghci/GHCi.hs7
-rw-r--r--compiler/typecheck/TcSplice.hs16
-rw-r--r--iserv/src/Main.hs11
-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
8 files changed, 156 insertions, 7 deletions
diff --git a/compiler/ghci/GHCi.hs b/compiler/ghci/GHCi.hs
index 7097e665ce..b4777a3c21 100644
--- a/compiler/ghci/GHCi.hs
+++ b/compiler/ghci/GHCi.hs
@@ -137,6 +137,13 @@ Things that do not work with -fexternal-interpreter
dynCompileExpr cannot work, because we have no way to run code of an
unknown type in the remote process. This API fails with an error
message if it is used with -fexternal-interpreter.
+
+Other Notes on Remote GHCi
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * This wiki page has an implementation overview:
+ https://ghc.haskell.org/trac/ghc/wiki/Commentary/Compiler/ExternalInterpreter
+ * Note [External GHCi pointers] in compiler/ghci/GHCi.hs
+ * Note [Remote Template Haskell] in libraries/ghci/GHCi/TH.hs
-}
-- | Run a command in the interpreter's context. With
diff --git a/compiler/typecheck/TcSplice.hs b/compiler/typecheck/TcSplice.hs
index 69cacd58e1..44bc299487 100644
--- a/compiler/typecheck/TcSplice.hs
+++ b/compiler/typecheck/TcSplice.hs
@@ -947,12 +947,14 @@ runTH ty fhv = do
dflags <- getDynFlags
if not (gopt Opt_ExternalInterpreter dflags)
then do
- -- just run it in the local TcM
+ -- Run it in the local TcM
hv <- liftIO $ wormhole dflags fhv
r <- runQuasi (unsafeCoerce# hv :: TH.Q a)
return r
else
- -- run it on the server
+ -- Run it on the server. For an overview of how TH works with
+ -- Remote GHCi, see Note [Remote Template Haskell] in
+ -- libraries/ghci/GHCi/TH.hs.
withIServ hsc_env $ \i -> do
rstate <- getTHState i
loc <- TH.qLocation
@@ -965,7 +967,8 @@ runTH ty fhv = do
return $! runGet get (LB.fromStrict bs)
--- | communicate with a remotely-running TH computation until it finishes
+-- | communicate with a remotely-running TH computation until it finishes.
+-- See Note [Remote Template Haskell] in libraries/ghci/GHCi/TH.hs.
runRemoteTH
:: IServ
-> [Messages] -- saved from nested calls to qRecover
@@ -1030,6 +1033,13 @@ Back in GHC, when we receive:
and merge in the new messages if caught_error is false.
-}
+-- | Retrieve (or create, if it hasn't been created already), the
+-- remote TH state. The TH state is a remote reference to an IORef
+-- QState living on the server, and we have to pass this to each RunTH
+-- call we make.
+--
+-- The TH state is stored in tcg_th_remote_state in the TcGblEnv.
+--
getTHState :: IServ -> TcM (ForeignRef (IORef QState))
getTHState i = do
tcg <- getGblEnv
diff --git a/iserv/src/Main.hs b/iserv/src/Main.hs
index 2e4555b017..3fcd49f5c7 100644
--- a/iserv/src/Main.hs
+++ b/iserv/src/Main.hs
@@ -1,4 +1,11 @@
{-# LANGUAGE RecordWildCards, GADTs, ScopedTypeVariables, RankNTypes #-}
+
+-- |
+-- The Remote GHCi server.
+--
+-- For details on Remote GHCi, see Note [Remote GHCi] in
+-- compiler/ghci/GHCi.hs.
+--
module Main (main) where
import GHCi.Run
@@ -55,6 +62,10 @@ serv verbose pipe@Pipe{..} restore = loop
writePipe pipe (put r)
loop
+ -- Run some TH code, which may interact with GHC by sending
+ -- THMessage requests, and then finally send RunTHDone followed by a
+ -- QResult. For an overview of how TH works with Remote GHCi, see
+ -- Note [Remote Template Haskell] in libraries/ghci/GHCi/TH.hs.
wrapRunTH :: forall a. (Binary a, Show a) => IO a -> IO ()
wrapRunTH io = do
r <- try io
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