{-# LANGUAGE DeriveFunctor #-}
-- | The CompPipeline monad and associated ops
--
-- Defined in separate module so that it can safely be imported from Hooks
module GHC.Driver.Pipeline.Monad (
    CompPipeline(..), evalP
  , PhasePlus(..)
  , PipeEnv(..), PipeState(..), PipelineOutput(..)
  , getPipeEnv, getPipeState, getPipeSession
  , setDynFlags, setModLocation, setForeignOs, setIface
  , pipeStateDynFlags, pipeStateModIface, setPlugins
  ) where

import GHC.Prelude

import GHC.Utils.Monad
import GHC.Utils.Outputable
import GHC.Utils.Logger

import GHC.Driver.Session
import GHC.Driver.Phases
import GHC.Driver.Env
import GHC.Driver.Plugins

import GHC.Utils.TmpFs (TempFileLifetime)

import GHC.Types.SourceFile

import GHC.Unit.Module
import GHC.Unit.Module.ModIface
import GHC.Unit.Module.Status

import Control.Monad

newtype CompPipeline a = P { forall a.
CompPipeline a -> PipeEnv -> PipeState -> IO (PipeState, a)
unP :: PipeEnv -> PipeState -> IO (PipeState, a) }
    deriving ((forall a b. (a -> b) -> CompPipeline a -> CompPipeline b)
-> (forall a b. a -> CompPipeline b -> CompPipeline a)
-> Functor CompPipeline
forall a b. a -> CompPipeline b -> CompPipeline a
forall a b. (a -> b) -> CompPipeline a -> CompPipeline b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: forall a b. a -> CompPipeline b -> CompPipeline a
$c<$ :: forall a b. a -> CompPipeline b -> CompPipeline a
fmap :: forall a b. (a -> b) -> CompPipeline a -> CompPipeline b
$cfmap :: forall a b. (a -> b) -> CompPipeline a -> CompPipeline b
Functor)

evalP :: CompPipeline a -> PipeEnv -> PipeState -> IO (PipeState, a)
evalP :: forall a.
CompPipeline a -> PipeEnv -> PipeState -> IO (PipeState, a)
evalP (P PipeEnv -> PipeState -> IO (PipeState, a)
f) PipeEnv
env PipeState
st = PipeEnv -> PipeState -> IO (PipeState, a)
f PipeEnv
env PipeState
st

instance Applicative CompPipeline where
    pure :: forall a. a -> CompPipeline a
pure a
a = (PipeEnv -> PipeState -> IO (PipeState, a)) -> CompPipeline a
forall a.
(PipeEnv -> PipeState -> IO (PipeState, a)) -> CompPipeline a
P ((PipeEnv -> PipeState -> IO (PipeState, a)) -> CompPipeline a)
-> (PipeEnv -> PipeState -> IO (PipeState, a)) -> CompPipeline a
forall a b. (a -> b) -> a -> b
$ \PipeEnv
_env PipeState
state -> (PipeState, a) -> IO (PipeState, a)
forall (m :: * -> *) a. Monad m => a -> m a
return (PipeState
state, a
a)
    <*> :: forall a b.
CompPipeline (a -> b) -> CompPipeline a -> CompPipeline b
(<*>) = CompPipeline (a -> b) -> CompPipeline a -> CompPipeline b
forall (m :: * -> *) a b. Monad m => m (a -> b) -> m a -> m b
ap

instance Monad CompPipeline where
  P PipeEnv -> PipeState -> IO (PipeState, a)
m >>= :: forall a b.
CompPipeline a -> (a -> CompPipeline b) -> CompPipeline b
>>= a -> CompPipeline b
k = (PipeEnv -> PipeState -> IO (PipeState, b)) -> CompPipeline b
forall a.
(PipeEnv -> PipeState -> IO (PipeState, a)) -> CompPipeline a
P ((PipeEnv -> PipeState -> IO (PipeState, b)) -> CompPipeline b)
-> (PipeEnv -> PipeState -> IO (PipeState, b)) -> CompPipeline b
forall a b. (a -> b) -> a -> b
$ \PipeEnv
env PipeState
state -> do (PipeState
state',a
a) <- PipeEnv -> PipeState -> IO (PipeState, a)
m PipeEnv
env PipeState
state
                                   CompPipeline b -> PipeEnv -> PipeState -> IO (PipeState, b)
forall a.
CompPipeline a -> PipeEnv -> PipeState -> IO (PipeState, a)
unP (a -> CompPipeline b
k a
a) PipeEnv
env PipeState
state'

instance MonadIO CompPipeline where
    liftIO :: forall a. IO a -> CompPipeline a
liftIO IO a
m = (PipeEnv -> PipeState -> IO (PipeState, a)) -> CompPipeline a
forall a.
(PipeEnv -> PipeState -> IO (PipeState, a)) -> CompPipeline a
P ((PipeEnv -> PipeState -> IO (PipeState, a)) -> CompPipeline a)
-> (PipeEnv -> PipeState -> IO (PipeState, a)) -> CompPipeline a
forall a b. (a -> b) -> a -> b
$ \PipeEnv
_env PipeState
state -> do a
a <- IO a
m; (PipeState, a) -> IO (PipeState, a)
forall (m :: * -> *) a. Monad m => a -> m a
return (PipeState
state, a
a)

data PhasePlus = RealPhase Phase
               | HscOut HscSource ModuleName HscStatus

instance Outputable PhasePlus where
    ppr :: PhasePlus -> SDoc
ppr (RealPhase Phase
p) = Phase -> SDoc
forall a. Outputable a => a -> SDoc
ppr Phase
p
    ppr (HscOut {}) = String -> SDoc
text String
"HscOut"

-- -----------------------------------------------------------------------------
-- The pipeline uses a monad to carry around various bits of information

-- PipeEnv: invariant information passed down
data PipeEnv = PipeEnv {
       PipeEnv -> Phase
stop_phase   :: Phase,       -- ^ Stop just before this phase
       PipeEnv -> String
src_filename :: String,      -- ^ basename of original input source
       PipeEnv -> String
src_basename :: String,      -- ^ basename of original input source
       PipeEnv -> String
src_suffix   :: String,      -- ^ its extension
       PipeEnv -> PipelineOutput
output_spec  :: PipelineOutput -- ^ says where to put the pipeline output
  }

-- PipeState: information that might change during a pipeline run
data PipeState = PipeState {
       PipeState -> HscEnv
hsc_env   :: HscEnv,
          -- ^ only the DynFlags and the Plugins change in the HscEnv.  The
          -- DynFlags change at various points, for example when we read the
          -- OPTIONS_GHC pragmas in the Cpp phase.
       PipeState -> Maybe ModLocation
maybe_loc :: Maybe ModLocation,
          -- ^ the ModLocation.  This is discovered during compilation,
          -- in the Hsc phase where we read the module header.
       PipeState -> [String]
foreign_os :: [FilePath],
         -- ^ additional object files resulting from compiling foreign
         -- code. They come from two sources: foreign stubs, and
         -- add{C,Cxx,Objc,Objcxx}File from template haskell
       PipeState -> Maybe ModIface
iface :: Maybe ModIface
         -- ^ Interface generated by HscOut phase. Only available after the
         -- phase runs.
  }

pipeStateDynFlags :: PipeState -> DynFlags
pipeStateDynFlags :: PipeState -> DynFlags
pipeStateDynFlags = HscEnv -> DynFlags
hsc_dflags (HscEnv -> DynFlags)
-> (PipeState -> HscEnv) -> PipeState -> DynFlags
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PipeState -> HscEnv
hsc_env

pipeStateModIface :: PipeState -> Maybe ModIface
pipeStateModIface :: PipeState -> Maybe ModIface
pipeStateModIface = PipeState -> Maybe ModIface
iface

data PipelineOutput
  = Temporary TempFileLifetime
        -- ^ Output should be to a temporary file: we're going to
        -- run more compilation steps on this output later.
  | Persistent
        -- ^ We want a persistent file, i.e. a file in the current directory
        -- derived from the input filename, but with the appropriate extension.
        -- eg. in "ghc -c Foo.hs" the output goes into ./Foo.o.
  | SpecificFile
        -- ^ The output must go into the specific outputFile in DynFlags.
        -- We don't store the filename in the constructor as it changes
        -- when doing -dynamic-too.
    deriving Int -> PipelineOutput -> ShowS
[PipelineOutput] -> ShowS
PipelineOutput -> String
(Int -> PipelineOutput -> ShowS)
-> (PipelineOutput -> String)
-> ([PipelineOutput] -> ShowS)
-> Show PipelineOutput
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [PipelineOutput] -> ShowS
$cshowList :: [PipelineOutput] -> ShowS
show :: PipelineOutput -> String
$cshow :: PipelineOutput -> String
showsPrec :: Int -> PipelineOutput -> ShowS
$cshowsPrec :: Int -> PipelineOutput -> ShowS
Show

getPipeEnv :: CompPipeline PipeEnv
getPipeEnv :: CompPipeline PipeEnv
getPipeEnv = (PipeEnv -> PipeState -> IO (PipeState, PipeEnv))
-> CompPipeline PipeEnv
forall a.
(PipeEnv -> PipeState -> IO (PipeState, a)) -> CompPipeline a
P ((PipeEnv -> PipeState -> IO (PipeState, PipeEnv))
 -> CompPipeline PipeEnv)
-> (PipeEnv -> PipeState -> IO (PipeState, PipeEnv))
-> CompPipeline PipeEnv
forall a b. (a -> b) -> a -> b
$ \PipeEnv
env PipeState
state -> (PipeState, PipeEnv) -> IO (PipeState, PipeEnv)
forall (m :: * -> *) a. Monad m => a -> m a
return (PipeState
state, PipeEnv
env)

getPipeState :: CompPipeline PipeState
getPipeState :: CompPipeline PipeState
getPipeState = (PipeEnv -> PipeState -> IO (PipeState, PipeState))
-> CompPipeline PipeState
forall a.
(PipeEnv -> PipeState -> IO (PipeState, a)) -> CompPipeline a
P ((PipeEnv -> PipeState -> IO (PipeState, PipeState))
 -> CompPipeline PipeState)
-> (PipeEnv -> PipeState -> IO (PipeState, PipeState))
-> CompPipeline PipeState
forall a b. (a -> b) -> a -> b
$ \PipeEnv
_env PipeState
state -> (PipeState, PipeState) -> IO (PipeState, PipeState)
forall (m :: * -> *) a. Monad m => a -> m a
return (PipeState
state, PipeState
state)

getPipeSession :: CompPipeline HscEnv
getPipeSession :: CompPipeline HscEnv
getPipeSession = (PipeEnv -> PipeState -> IO (PipeState, HscEnv))
-> CompPipeline HscEnv
forall a.
(PipeEnv -> PipeState -> IO (PipeState, a)) -> CompPipeline a
P ((PipeEnv -> PipeState -> IO (PipeState, HscEnv))
 -> CompPipeline HscEnv)
-> (PipeEnv -> PipeState -> IO (PipeState, HscEnv))
-> CompPipeline HscEnv
forall a b. (a -> b) -> a -> b
$ \PipeEnv
_env PipeState
state -> (PipeState, HscEnv) -> IO (PipeState, HscEnv)
forall (m :: * -> *) a. Monad m => a -> m a
return (PipeState
state, PipeState -> HscEnv
hsc_env PipeState
state)

instance HasDynFlags CompPipeline where
    getDynFlags :: CompPipeline DynFlags
getDynFlags = (PipeEnv -> PipeState -> IO (PipeState, DynFlags))
-> CompPipeline DynFlags
forall a.
(PipeEnv -> PipeState -> IO (PipeState, a)) -> CompPipeline a
P ((PipeEnv -> PipeState -> IO (PipeState, DynFlags))
 -> CompPipeline DynFlags)
-> (PipeEnv -> PipeState -> IO (PipeState, DynFlags))
-> CompPipeline DynFlags
forall a b. (a -> b) -> a -> b
$ \PipeEnv
_env PipeState
state -> (PipeState, DynFlags) -> IO (PipeState, DynFlags)
forall (m :: * -> *) a. Monad m => a -> m a
return (PipeState
state, HscEnv -> DynFlags
hsc_dflags (PipeState -> HscEnv
hsc_env PipeState
state))

instance HasLogger CompPipeline where
    getLogger :: CompPipeline Logger
getLogger = (PipeEnv -> PipeState -> IO (PipeState, Logger))
-> CompPipeline Logger
forall a.
(PipeEnv -> PipeState -> IO (PipeState, a)) -> CompPipeline a
P ((PipeEnv -> PipeState -> IO (PipeState, Logger))
 -> CompPipeline Logger)
-> (PipeEnv -> PipeState -> IO (PipeState, Logger))
-> CompPipeline Logger
forall a b. (a -> b) -> a -> b
$ \PipeEnv
_env PipeState
state -> (PipeState, Logger) -> IO (PipeState, Logger)
forall (m :: * -> *) a. Monad m => a -> m a
return (PipeState
state, HscEnv -> Logger
hsc_logger (PipeState -> HscEnv
hsc_env PipeState
state))

setDynFlags :: DynFlags -> CompPipeline ()
setDynFlags :: DynFlags -> CompPipeline ()
setDynFlags DynFlags
dflags = (PipeEnv -> PipeState -> IO (PipeState, ())) -> CompPipeline ()
forall a.
(PipeEnv -> PipeState -> IO (PipeState, a)) -> CompPipeline a
P ((PipeEnv -> PipeState -> IO (PipeState, ())) -> CompPipeline ())
-> (PipeEnv -> PipeState -> IO (PipeState, ())) -> CompPipeline ()
forall a b. (a -> b) -> a -> b
$ \PipeEnv
_env PipeState
state ->
  (PipeState, ()) -> IO (PipeState, ())
forall (m :: * -> *) a. Monad m => a -> m a
return (PipeState
state{hsc_env :: HscEnv
hsc_env= (PipeState -> HscEnv
hsc_env PipeState
state){ hsc_dflags :: DynFlags
hsc_dflags = DynFlags
dflags }}, ())

setPlugins :: [LoadedPlugin] -> [StaticPlugin] -> CompPipeline ()
setPlugins :: [LoadedPlugin] -> [StaticPlugin] -> CompPipeline ()
setPlugins [LoadedPlugin]
dyn [StaticPlugin]
static = (PipeEnv -> PipeState -> IO (PipeState, ())) -> CompPipeline ()
forall a.
(PipeEnv -> PipeState -> IO (PipeState, a)) -> CompPipeline a
P ((PipeEnv -> PipeState -> IO (PipeState, ())) -> CompPipeline ())
-> (PipeEnv -> PipeState -> IO (PipeState, ())) -> CompPipeline ()
forall a b. (a -> b) -> a -> b
$ \PipeEnv
_env PipeState
state ->
  let hsc_env' :: HscEnv
hsc_env' = (PipeState -> HscEnv
hsc_env PipeState
state){ hsc_plugins :: [LoadedPlugin]
hsc_plugins = [LoadedPlugin]
dyn, hsc_static_plugins :: [StaticPlugin]
hsc_static_plugins = [StaticPlugin]
static }
  in (PipeState, ()) -> IO (PipeState, ())
forall (m :: * -> *) a. Monad m => a -> m a
return (PipeState
state{hsc_env :: HscEnv
hsc_env = HscEnv
hsc_env'}, ())

setModLocation :: ModLocation -> CompPipeline ()
setModLocation :: ModLocation -> CompPipeline ()
setModLocation ModLocation
loc = (PipeEnv -> PipeState -> IO (PipeState, ())) -> CompPipeline ()
forall a.
(PipeEnv -> PipeState -> IO (PipeState, a)) -> CompPipeline a
P ((PipeEnv -> PipeState -> IO (PipeState, ())) -> CompPipeline ())
-> (PipeEnv -> PipeState -> IO (PipeState, ())) -> CompPipeline ()
forall a b. (a -> b) -> a -> b
$ \PipeEnv
_env PipeState
state ->
  (PipeState, ()) -> IO (PipeState, ())
forall (m :: * -> *) a. Monad m => a -> m a
return (PipeState
state{ maybe_loc :: Maybe ModLocation
maybe_loc = ModLocation -> Maybe ModLocation
forall a. a -> Maybe a
Just ModLocation
loc }, ())

setForeignOs :: [FilePath] -> CompPipeline ()
setForeignOs :: [String] -> CompPipeline ()
setForeignOs [String]
os = (PipeEnv -> PipeState -> IO (PipeState, ())) -> CompPipeline ()
forall a.
(PipeEnv -> PipeState -> IO (PipeState, a)) -> CompPipeline a
P ((PipeEnv -> PipeState -> IO (PipeState, ())) -> CompPipeline ())
-> (PipeEnv -> PipeState -> IO (PipeState, ())) -> CompPipeline ()
forall a b. (a -> b) -> a -> b
$ \PipeEnv
_env PipeState
state ->
  (PipeState, ()) -> IO (PipeState, ())
forall (m :: * -> *) a. Monad m => a -> m a
return (PipeState
state{ foreign_os :: [String]
foreign_os = [String]
os }, ())

setIface :: ModIface -> CompPipeline ()
setIface :: ModIface -> CompPipeline ()
setIface ModIface
iface = (PipeEnv -> PipeState -> IO (PipeState, ())) -> CompPipeline ()
forall a.
(PipeEnv -> PipeState -> IO (PipeState, a)) -> CompPipeline a
P ((PipeEnv -> PipeState -> IO (PipeState, ())) -> CompPipeline ())
-> (PipeEnv -> PipeState -> IO (PipeState, ())) -> CompPipeline ()
forall a b. (a -> b) -> a -> b
$ \PipeEnv
_env PipeState
state -> (PipeState, ()) -> IO (PipeState, ())
forall (m :: * -> *) a. Monad m => a -> m a
return (PipeState
state{ iface :: Maybe ModIface
iface = ModIface -> Maybe ModIface
forall a. a -> Maybe a
Just ModIface
iface }, ())