{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE PatternSynonyms #-}
--
-- (c) The University of Glasgow 2002-2006
--

-- | The IO Monad with an environment
--
-- The environment is passed around as a Reader monad but
-- as its in the IO monad, mutable references can be used
-- for updating state.
--
module GHC.Data.IOEnv (
        IOEnv, -- Instance of Monad

        -- Monad utilities
        module GHC.Utils.Monad,

        -- Errors
        failM, failWithM,
        IOEnvFailure(..),

        -- Getting at the environment
        getEnv, setEnv, updEnv,

        runIOEnv, unsafeInterleaveM, uninterruptibleMaskM_,
        tryM, tryAllM, tryMostM, fixM,

        -- I/O operations
        IORef, newMutVar, readMutVar, writeMutVar, updMutVar, updMutVarM,
        atomicUpdMutVar, atomicUpdMutVar'
  ) where

import GHC.Prelude

import GHC.Driver.Session
import {-# SOURCE #-} GHC.Driver.Hooks
import GHC.IO (catchException)
import GHC.Utils.Exception
import GHC.Unit.Module
import GHC.Utils.Panic

import Data.IORef       ( IORef, newIORef, readIORef, writeIORef, modifyIORef,
                          atomicModifyIORef, atomicModifyIORef' )
import System.IO.Unsafe ( unsafeInterleaveIO )
import System.IO        ( fixIO )
import Control.Monad
import Control.Monad.Trans.Reader
import Control.Monad.Catch (MonadCatch, MonadMask, MonadThrow)
import GHC.Utils.Monad
import GHC.Utils.Logger
import Control.Applicative (Alternative(..))
import GHC.Exts( oneShot )
import Control.Concurrent.MVar (newEmptyMVar, readMVar, putMVar)
import Control.Concurrent (forkIO, killThread)

----------------------------------------------------------------------
-- Defining the monad type
----------------------------------------------------------------------


newtype IOEnv env a = IOEnv' (env -> IO a)
  deriving (forall {env}. Monad (IOEnv env)
forall e a. Exception e => e -> IOEnv env a
forall env e a. Exception e => e -> IOEnv env a
forall (m :: * -> *).
Monad m -> (forall e a. Exception e => e -> m a) -> MonadThrow m
throwM :: forall e a. Exception e => e -> IOEnv env a
$cthrowM :: forall env e a. Exception e => e -> IOEnv env a
MonadThrow, forall env. MonadThrow (IOEnv env)
forall e a.
Exception e =>
IOEnv env a -> (e -> IOEnv env a) -> IOEnv env a
forall env e a.
Exception e =>
IOEnv env a -> (e -> IOEnv env a) -> IOEnv env a
forall (m :: * -> *).
MonadThrow m
-> (forall e a. Exception e => m a -> (e -> m a) -> m a)
-> MonadCatch m
catch :: forall e a.
Exception e =>
IOEnv env a -> (e -> IOEnv env a) -> IOEnv env a
$ccatch :: forall env e a.
Exception e =>
IOEnv env a -> (e -> IOEnv env a) -> IOEnv env a
MonadCatch, forall env. MonadCatch (IOEnv env)
forall b.
((forall a. IOEnv env a -> IOEnv env a) -> IOEnv env b)
-> IOEnv env b
forall env b.
((forall a. IOEnv env a -> IOEnv env a) -> IOEnv env b)
-> IOEnv env b
forall a b c.
IOEnv env a
-> (a -> ExitCase b -> IOEnv env c)
-> (a -> IOEnv env b)
-> IOEnv env (b, c)
forall env a b c.
IOEnv env a
-> (a -> ExitCase b -> IOEnv env c)
-> (a -> IOEnv env b)
-> IOEnv env (b, c)
forall (m :: * -> *).
MonadCatch m
-> (forall b. ((forall a. m a -> m a) -> m b) -> m b)
-> (forall b. ((forall a. m a -> m a) -> m b) -> m b)
-> (forall a b c.
    m a -> (a -> ExitCase b -> m c) -> (a -> m b) -> m (b, c))
-> MonadMask m
generalBracket :: forall a b c.
IOEnv env a
-> (a -> ExitCase b -> IOEnv env c)
-> (a -> IOEnv env b)
-> IOEnv env (b, c)
$cgeneralBracket :: forall env a b c.
IOEnv env a
-> (a -> ExitCase b -> IOEnv env c)
-> (a -> IOEnv env b)
-> IOEnv env (b, c)
uninterruptibleMask :: forall b.
((forall a. IOEnv env a -> IOEnv env a) -> IOEnv env b)
-> IOEnv env b
$cuninterruptibleMask :: forall env b.
((forall a. IOEnv env a -> IOEnv env a) -> IOEnv env b)
-> IOEnv env b
mask :: forall b.
((forall a. IOEnv env a -> IOEnv env a) -> IOEnv env b)
-> IOEnv env b
$cmask :: forall env b.
((forall a. IOEnv env a -> IOEnv env a) -> IOEnv env b)
-> IOEnv env b
MonadMask, forall {env}. Monad (IOEnv env)
forall a. (a -> IOEnv env a) -> IOEnv env a
forall env a. (a -> IOEnv env a) -> IOEnv env a
forall (m :: * -> *).
Monad m -> (forall a. (a -> m a) -> m a) -> MonadFix m
mfix :: forall a. (a -> IOEnv env a) -> IOEnv env a
$cmfix :: forall env a. (a -> IOEnv env a) -> IOEnv env a
MonadFix) via (ReaderT env IO)


-- See Note [The one-shot state monad trick] in GHC.Utils.Monad
instance Functor (IOEnv env) where
   fmap :: forall a b. (a -> b) -> IOEnv env a -> IOEnv env b
fmap a -> b
f (IOEnv env -> IO a
g) = forall env a. (env -> IO a) -> IOEnv env a
IOEnv forall a b. (a -> b) -> a -> b
$ \env
env -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> b
f (env -> IO a
g env
env)
   a
a <$ :: forall a b. a -> IOEnv env b -> IOEnv env a
<$ IOEnv env -> IO b
g     = forall env a. (env -> IO a) -> IOEnv env a
IOEnv forall a b. (a -> b) -> a -> b
$ \env
env -> env -> IO b
g env
env forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall (f :: * -> *) a. Applicative f => a -> f a
pure a
a

instance MonadIO (IOEnv env) where
   liftIO :: forall a. IO a -> IOEnv env a
liftIO IO a
f = forall env a. (env -> IO a) -> IOEnv env a
IOEnv (\env
_ -> IO a
f)

pattern IOEnv :: forall env a. (env -> IO a) -> IOEnv env a
pattern $bIOEnv :: forall env a. (env -> IO a) -> IOEnv env a
$mIOEnv :: forall {r} {env} {a}.
IOEnv env a -> ((env -> IO a) -> r) -> ((# #) -> r) -> r
IOEnv m <- IOEnv' m
  where
    IOEnv env -> IO a
m = forall env a. (env -> IO a) -> IOEnv env a
IOEnv' (oneShot :: forall a b. (a -> b) -> a -> b
oneShot env -> IO a
m)

{-# COMPLETE IOEnv #-}

unIOEnv :: IOEnv env a -> (env -> IO a)
unIOEnv :: forall env a. IOEnv env a -> env -> IO a
unIOEnv (IOEnv env -> IO a
m) = env -> IO a
m

instance Monad (IOEnv m) where
    >>= :: forall a b. IOEnv m a -> (a -> IOEnv m b) -> IOEnv m b
(>>=)  = forall m a b. IOEnv m a -> (a -> IOEnv m b) -> IOEnv m b
thenM
    >> :: forall a b. IOEnv m a -> IOEnv m b -> IOEnv m b
(>>)   = forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
(*>)

instance MonadFail (IOEnv m) where
    fail :: forall a. String -> IOEnv m a
fail String
_ = forall env a. IOEnv env a
failM -- Ignore the string

instance Applicative (IOEnv m) where
    pure :: forall a. a -> IOEnv m a
pure = forall a env. a -> IOEnv env a
returnM
    IOEnv m -> IO (a -> b)
f <*> :: forall a b. IOEnv m (a -> b) -> IOEnv m a -> IOEnv m b
<*> IOEnv m -> IO a
x = forall env a. (env -> IO a) -> IOEnv env a
IOEnv (\ m
env -> m -> IO (a -> b)
f m
env forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> m -> IO a
x m
env )
    *> :: forall a b. IOEnv m a -> IOEnv m b -> IOEnv m b
(*>) = forall m a b. IOEnv m a -> IOEnv m b -> IOEnv m b
thenM_

returnM :: a -> IOEnv env a
returnM :: forall a env. a -> IOEnv env a
returnM a
a = forall env a. (env -> IO a) -> IOEnv env a
IOEnv (\ env
_ -> forall (m :: * -> *) a. Monad m => a -> m a
return a
a)

thenM :: IOEnv env a -> (a -> IOEnv env b) -> IOEnv env b
thenM :: forall m a b. IOEnv m a -> (a -> IOEnv m b) -> IOEnv m b
thenM (IOEnv env -> IO a
m) a -> IOEnv env b
f = forall env a. (env -> IO a) -> IOEnv env a
IOEnv (\ env
env -> do { a
r <- env -> IO a
m env
env ;
                                         forall env a. IOEnv env a -> env -> IO a
unIOEnv (a -> IOEnv env b
f a
r) env
env })

thenM_ :: IOEnv env a -> IOEnv env b -> IOEnv env b
thenM_ :: forall m a b. IOEnv m a -> IOEnv m b -> IOEnv m b
thenM_ (IOEnv env -> IO a
m) IOEnv env b
f = forall env a. (env -> IO a) -> IOEnv env a
IOEnv (\ env
env -> do { a
_ <- env -> IO a
m env
env ; forall env a. IOEnv env a -> env -> IO a
unIOEnv IOEnv env b
f env
env })

failM :: IOEnv env a
failM :: forall env a. IOEnv env a
failM = forall env a. (env -> IO a) -> IOEnv env a
IOEnv (\ env
_ -> forall e a. Exception e => e -> IO a
throwIO IOEnvFailure
IOEnvFailure)

failWithM :: String -> IOEnv env a
failWithM :: forall m a. String -> IOEnv m a
failWithM String
s = forall env a. (env -> IO a) -> IOEnv env a
IOEnv (\ env
_ -> forall a. IOError -> IO a
ioError (String -> IOError
userError String
s))

data IOEnvFailure = IOEnvFailure

instance Show IOEnvFailure where
    show :: IOEnvFailure -> String
show IOEnvFailure
IOEnvFailure = String
"IOEnv failure"

instance Exception IOEnvFailure

instance ContainsDynFlags env => HasDynFlags (IOEnv env) where
    getDynFlags :: IOEnv env DynFlags
getDynFlags = do env
env <- forall env. IOEnv env env
getEnv
                     forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$! forall t. ContainsDynFlags t => t -> DynFlags
extractDynFlags env
env

instance ContainsHooks env => HasHooks (IOEnv env) where
    getHooks :: IOEnv env Hooks
getHooks = do env
env <- forall env. IOEnv env env
getEnv
                  forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$! forall a. ContainsHooks a => a -> Hooks
extractHooks env
env

instance ContainsLogger env => HasLogger (IOEnv env) where
    getLogger :: IOEnv env Logger
getLogger = do env
env <- forall env. IOEnv env env
getEnv
                   forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$! forall t. ContainsLogger t => t -> Logger
extractLogger env
env


instance ContainsModule env => HasModule (IOEnv env) where
    getModule :: IOEnv env Module
getModule = do env
env <- forall env. IOEnv env env
getEnv
                   forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall t. ContainsModule t => t -> Module
extractModule env
env

----------------------------------------------------------------------
-- Fundamental combinators specific to the monad
----------------------------------------------------------------------


---------------------------
runIOEnv :: env -> IOEnv env a -> IO a
runIOEnv :: forall env a. env -> IOEnv env a -> IO a
runIOEnv env
env (IOEnv env -> IO a
m) = env -> IO a
m env
env


---------------------------
{-# NOINLINE fixM #-}
  -- Aargh!  Not inlining fixM alleviates a space leak problem.
  -- Normally fixM is used with a lazy tuple match: if the optimiser is
  -- shown the definition of fixM, it occasionally transforms the code
  -- in such a way that the code generator doesn't spot the selector
  -- thunks.  Sigh.

fixM :: (a -> IOEnv env a) -> IOEnv env a
fixM :: forall a env. (a -> IOEnv env a) -> IOEnv env a
fixM a -> IOEnv env a
f = forall env a. (env -> IO a) -> IOEnv env a
IOEnv (\ env
env -> forall a. (a -> IO a) -> IO a
fixIO (\ a
r -> forall env a. IOEnv env a -> env -> IO a
unIOEnv (a -> IOEnv env a
f a
r) env
env))


---------------------------
tryM :: IOEnv env r -> IOEnv env (Either IOEnvFailure r)
-- Reflect UserError exceptions (only) into IOEnv monad
-- Other exceptions are not caught; they are simply propagated as exns
--
-- The idea is that errors in the program being compiled will give rise
-- to UserErrors.  But, say, pattern-match failures in GHC itself should
-- not be caught here, else they'll be reported as errors in the program
-- begin compiled!
tryM :: forall env r. IOEnv env r -> IOEnv env (Either IOEnvFailure r)
tryM (IOEnv env -> IO r
thing) = forall env a. (env -> IO a) -> IOEnv env a
IOEnv (\ env
env -> forall a. IO a -> IO (Either IOEnvFailure a)
tryIOEnvFailure (env -> IO r
thing env
env))

tryIOEnvFailure :: IO a -> IO (Either IOEnvFailure a)
tryIOEnvFailure :: forall a. IO a -> IO (Either IOEnvFailure a)
tryIOEnvFailure = forall e a. Exception e => IO a -> IO (Either e a)
try

tryAllM :: IOEnv env r -> IOEnv env (Either SomeException r)
-- Catch *all* synchronous exceptions
-- This is used when running a Template-Haskell splice, when
-- even a pattern-match failure is a programmer error
tryAllM :: forall env r. IOEnv env r -> IOEnv env (Either SomeException r)
tryAllM (IOEnv env -> IO r
thing) = forall env a. (env -> IO a) -> IOEnv env a
IOEnv (\ env
env -> forall a. IO a -> IO (Either SomeException a)
safeTry (env -> IO r
thing env
env))

-- | Like 'try', but doesn't catch asynchronous exceptions
safeTry :: IO a -> IO (Either SomeException a)
safeTry :: forall a. IO a -> IO (Either SomeException a)
safeTry IO a
act = do
  MVar (Either SomeException a)
var <- forall a. IO (MVar a)
newEmptyMVar
  -- uninterruptible because we want to mask around 'killThread', which is interruptible.
  forall b. ((forall a. IO a -> IO a) -> IO b) -> IO b
uninterruptibleMask forall a b. (a -> b) -> a -> b
$ \forall a. IO a -> IO a
restore -> do
    -- Fork, so that 'act' is safe from all asynchronous exceptions other than the ones we send it
    ThreadId
t <- IO () -> IO ThreadId
forkIO forall a b. (a -> b) -> a -> b
$ forall e a. Exception e => IO a -> IO (Either e a)
try (forall a. IO a -> IO a
restore IO a
act) forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall a. MVar a -> a -> IO ()
putMVar MVar (Either SomeException a)
var
    forall a. IO a -> IO a
restore (forall a. MVar a -> IO a
readMVar MVar (Either SomeException a)
var)
      forall e a. Exception e => IO a -> (e -> IO a) -> IO a
`catchException` \(SomeException
e :: SomeException) -> do
        -- Control reaches this point only if the parent thread was sent an async exception
        -- In that case, kill the 'act' thread and re-raise the exception
        ThreadId -> IO ()
killThread ThreadId
t
        forall e a. Exception e => e -> IO a
throwIO SomeException
e

tryMostM :: IOEnv env r -> IOEnv env (Either SomeException r)
tryMostM :: forall env r. IOEnv env r -> IOEnv env (Either SomeException r)
tryMostM (IOEnv env -> IO r
thing) = forall env a. (env -> IO a) -> IOEnv env a
IOEnv (\ env
env -> forall a. IO a -> IO (Either SomeException a)
tryMost (env -> IO r
thing env
env))

---------------------------
unsafeInterleaveM :: IOEnv env a -> IOEnv env a
unsafeInterleaveM :: forall env a. IOEnv env a -> IOEnv env a
unsafeInterleaveM (IOEnv env -> IO a
m) = forall env a. (env -> IO a) -> IOEnv env a
IOEnv (\ env
env -> forall a. IO a -> IO a
unsafeInterleaveIO (env -> IO a
m env
env))

uninterruptibleMaskM_ :: IOEnv env a -> IOEnv env a
uninterruptibleMaskM_ :: forall env a. IOEnv env a -> IOEnv env a
uninterruptibleMaskM_ (IOEnv env -> IO a
m) = forall env a. (env -> IO a) -> IOEnv env a
IOEnv (\ env
env -> forall a. IO a -> IO a
uninterruptibleMask_ (env -> IO a
m env
env))

----------------------------------------------------------------------
-- Alternative/MonadPlus
----------------------------------------------------------------------

instance Alternative (IOEnv env) where
    empty :: forall a. IOEnv env a
empty   = forall env a. (env -> IO a) -> IOEnv env a
IOEnv (forall a b. a -> b -> a
const forall (f :: * -> *) a. Alternative f => f a
empty)
    IOEnv env a
m <|> :: forall a. IOEnv env a -> IOEnv env a -> IOEnv env a
<|> IOEnv env a
n = forall env a. (env -> IO a) -> IOEnv env a
IOEnv (\env
env -> forall env a. IOEnv env a -> env -> IO a
unIOEnv IOEnv env a
m env
env forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> forall env a. IOEnv env a -> env -> IO a
unIOEnv IOEnv env a
n env
env)

instance MonadPlus (IOEnv env)

----------------------------------------------------------------------
-- Accessing input/output
----------------------------------------------------------------------

newMutVar :: a -> IOEnv env (IORef a)
newMutVar :: forall a env. a -> IOEnv env (IORef a)
newMutVar a
val = forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (forall a. a -> IO (IORef a)
newIORef a
val)

writeMutVar :: IORef a -> a -> IOEnv env ()
writeMutVar :: forall a env. IORef a -> a -> IOEnv env ()
writeMutVar IORef a
var a
val = forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (forall a. IORef a -> a -> IO ()
writeIORef IORef a
var a
val)

readMutVar :: IORef a -> IOEnv env a
readMutVar :: forall a env. IORef a -> IOEnv env a
readMutVar IORef a
var = forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (forall a. IORef a -> IO a
readIORef IORef a
var)

updMutVar :: IORef a -> (a -> a) -> IOEnv env ()
updMutVar :: forall a env. IORef a -> (a -> a) -> IOEnv env ()
updMutVar IORef a
var a -> a
upd = forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (forall a. IORef a -> (a -> a) -> IO ()
modifyIORef IORef a
var a -> a
upd)

updMutVarM :: IORef a -> (a -> IOEnv env a) -> IOEnv env ()
updMutVarM :: forall a env. IORef a -> (a -> IOEnv env a) -> IOEnv env ()
updMutVarM IORef a
ref a -> IOEnv env a
upd
  = do { a
contents     <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. IORef a -> IO a
readIORef IORef a
ref
       ; a
new_contents <- a -> IOEnv env a
upd a
contents
       ; forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. IORef a -> a -> IO ()
writeIORef IORef a
ref a
new_contents }

-- | Atomically update the reference.  Does not force the evaluation of the
-- new variable contents.  For strict update, use 'atomicUpdMutVar''.
atomicUpdMutVar :: IORef a -> (a -> (a, b)) -> IOEnv env b
atomicUpdMutVar :: forall a b env. IORef a -> (a -> (a, b)) -> IOEnv env b
atomicUpdMutVar IORef a
var a -> (a, b)
upd = forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef IORef a
var a -> (a, b)
upd)

-- | Strict variant of 'atomicUpdMutVar'.
atomicUpdMutVar' :: IORef a -> (a -> (a, b)) -> IOEnv env b
atomicUpdMutVar' :: forall a b env. IORef a -> (a -> (a, b)) -> IOEnv env b
atomicUpdMutVar' IORef a
var a -> (a, b)
upd = forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' IORef a
var a -> (a, b)
upd)

----------------------------------------------------------------------
-- Accessing the environment
----------------------------------------------------------------------

getEnv :: IOEnv env env
{-# INLINE getEnv #-}
getEnv :: forall env. IOEnv env env
getEnv = forall env a. (env -> IO a) -> IOEnv env a
IOEnv (\ env
env -> forall (m :: * -> *) a. Monad m => a -> m a
return env
env)

-- | Perform a computation with a different environment
setEnv :: env' -> IOEnv env' a -> IOEnv env a
{-# INLINE setEnv #-}
setEnv :: forall env' a env. env' -> IOEnv env' a -> IOEnv env a
setEnv env'
new_env (IOEnv env' -> IO a
m) = forall env a. (env -> IO a) -> IOEnv env a
IOEnv (\ env
_ -> env' -> IO a
m env'
new_env)

-- | Perform a computation with an altered environment
updEnv :: (env -> env') -> IOEnv env' a -> IOEnv env a
{-# INLINE updEnv #-}
updEnv :: forall env env' a. (env -> env') -> IOEnv env' a -> IOEnv env a
updEnv env -> env'
upd (IOEnv env' -> IO a
m) = forall env a. (env -> IO a) -> IOEnv env a
IOEnv (\ env
env -> env' -> IO a
m (env -> env'
upd env
env))