Commit eb732195 authored by Simon Marlow's avatar Simon Marlow

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
parent bdb0d24b
......@@ -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
......
......@@ -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
......
{-# 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
......
......@@ -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
......
......@@ -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
......
{-# 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(..)
......
......@@ -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
......
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment