-- |
-- Module      :  Cryptol.Eval.Monad
-- Copyright   :  (c) 2013-2016 Galois, Inc.
-- License     :  BSD3
-- Maintainer  :  cryptol@galois.com
-- Stability   :  provisional
-- Portability :  portable

{-# LANGUAGE Safe #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}

module Cryptol.Backend.Monad
( -- * Evaluation monad
  Eval(..)
, runEval
, EvalOpts(..)
, PPOpts(..)
, asciiMode
, PPFloatFormat(..)
, PPFloatExp(..)
, defaultPPOpts
, io
, delayFill
, ready
, blackhole
, evalSpark
, maybeReady
  -- * Error reporting
, Unsupported(..)
, EvalError(..)
, evalPanic
, wordTooWide
, typeCannotBeDemoted
) where

import           Control.Concurrent
import           Control.Concurrent.STM

import           Control.Monad
import qualified Control.Monad.Fail as Fail
import           Control.Monad.Fix
import           Control.Monad.IO.Class
import           Data.Typeable (Typeable)
import qualified Control.Exception as X


import Cryptol.Utils.Panic
import Cryptol.Utils.PP
import Cryptol.Utils.Logger(Logger)
import Cryptol.TypeCheck.AST(Type,Name)

-- | A computation that returns an already-evaluated value.
ready :: a -> Eval a
ready :: a -> Eval a
ready a
a = a -> Eval a
forall a. a -> Eval a
Ready a
a

-- | How to pretty print things when evaluating
data PPOpts = PPOpts
  { PPOpts -> Bool
useAscii     :: Bool
  , PPOpts -> Int
useBase      :: Int
  , PPOpts -> Int
useInfLength :: Int
  , PPOpts -> Int
useFPBase    :: Int
  , PPOpts -> PPFloatFormat
useFPFormat  :: PPFloatFormat
  }

asciiMode :: PPOpts -> Integer -> Bool
asciiMode :: PPOpts -> Integer -> Bool
asciiMode PPOpts
opts Integer
width = PPOpts -> Bool
useAscii PPOpts
opts Bool -> Bool -> Bool
&& (Integer
width Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer
7 Bool -> Bool -> Bool
|| Integer
width Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer
8)

data PPFloatFormat =
    FloatFixed Int PPFloatExp -- ^ Use this many significant digis
  | FloatFrac Int             -- ^ Show this many digits after floating point
  | FloatFree PPFloatExp      -- ^ Use the correct number of digits

data PPFloatExp = ForceExponent -- ^ Always show an exponent
                | AutoExponent  -- ^ Only show exponent when needed


defaultPPOpts :: PPOpts
defaultPPOpts :: PPOpts
defaultPPOpts = PPOpts :: Bool -> Int -> Int -> Int -> PPFloatFormat -> PPOpts
PPOpts { useAscii :: Bool
useAscii = Bool
False, useBase :: Int
useBase = Int
10, useInfLength :: Int
useInfLength = Int
5
                       , useFPBase :: Int
useFPBase = Int
16
                       , useFPFormat :: PPFloatFormat
useFPFormat = PPFloatExp -> PPFloatFormat
FloatFree PPFloatExp
AutoExponent
                       }


-- | Some options for evalutaion
data EvalOpts = EvalOpts
  { EvalOpts -> Logger
evalLogger :: Logger    -- ^ Where to print stuff (e.g., for @trace@)
  , EvalOpts -> PPOpts
evalPPOpts :: PPOpts    -- ^ How to pretty print things.
  }

-- | The monad for Cryptol evaluation.
--   A computation is either "ready", which means it represents
--   only trivial computation, or is an "eval" action which must
--   be computed to get the answer, or it is a "thunk", which
--   represents a delayed, shared computation.
data Eval a
   = Ready !a
   | Eval !(IO a)
   | Thunk !(TVar (ThunkState a))

-- | This datastructure tracks the lifecycle of a thunk.
--
--   Thunks are used for basically three use cases.  First,
--   we use thunks to preserve sharing.  Basically every
--   cryptol expression that is bound to a name, and is not
--   already obviously a value (and in a few other places as
--   well) will get turned into a thunk in order to avoid
--   recomputations.  These thunks will start in the `Unforced`
--   state, and have a backup computation that just raises
--   the `LoopError` exception.
--
--   Secondly, thunks are used to cut cycles when evaluating
--   recursive definition groups.  Every named clause in a
--   recursive definition is thunked so that the value can appear
--   in its definition.  Such thunks start in the `Void` state,
--   as they must exist before we have a definition to assign them.
--   Forcing a thunk in the `Void` state is a programmer error (panic).
--   Once the body of a definition is ready, we replace the
--   thunk with the relevant computation, going to the `Unforced` state.
--
--   In the third case, we are using thunks to provide an optimistic
--   shortcut for evaluation.  In these cases we first try to run a
--   computation that is stricter than the semantics actually allows.
--   If it succeeds, all is well an we continue.  However, if it tight
--   loops, we fall back on a lazier (and generally more expensive)
--   version, which is the "backup" computation referred to above.
data ThunkState a
  = Void !String
       -- ^ This thunk has not yet been initialized
  | Unforced !(IO a) !(IO a)
       -- ^ This thunk has not yet been forced.  We keep track of the "main"
       --   computation to run and a "backup" computation to run if we
       --   detect a tight loop when evaluating the first one.
  | UnderEvaluation !ThreadId !(IO a)
       -- ^ This thunk is currently being evaluated by the thread with the given
       --   thread ID.  We track the "backup" computation to run if we detect
       --   a tight loop evaluating this thunk.  If the thunk is being evaluated
       --   by some other thread, the current thread will await its completion.
  | ForcedErr !EvalError
       -- ^ This thunk has been forced, and its evaluation results in an exception
  | Forced !a
       -- ^ This thunk has been forced to the given value


-- | Test if a value is "ready", which means that
--   it requires no computation to return.
maybeReady :: Eval a -> Eval (Maybe a)
maybeReady :: Eval a -> Eval (Maybe a)
maybeReady (Ready a
a) = Maybe a -> Eval (Maybe a)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (a -> Maybe a
forall a. a -> Maybe a
Just a
a)
maybeReady (Thunk TVar (ThunkState a)
tv) = IO (Maybe a) -> Eval (Maybe a)
forall a. IO a -> Eval a
Eval (IO (Maybe a) -> Eval (Maybe a)) -> IO (Maybe a) -> Eval (Maybe a)
forall a b. (a -> b) -> a -> b
$
  TVar (ThunkState a) -> IO (ThunkState a)
forall a. TVar a -> IO a
readTVarIO TVar (ThunkState a)
tv IO (ThunkState a) -> (ThunkState a -> IO (Maybe a)) -> IO (Maybe a)
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
     Forced a
a -> Maybe a -> IO (Maybe a)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (a -> Maybe a
forall a. a -> Maybe a
Just a
a)
     ThunkState a
_ -> Maybe a -> IO (Maybe a)
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe a
forall a. Maybe a
Nothing
maybeReady (Eval IO a
_) = Maybe a -> Eval (Maybe a)
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe a
forall a. Maybe a
Nothing


{-# INLINE delayFill #-}

-- | Delay the given evaluation computation, returning a thunk
--   which will run the computation when forced.  Run the 'retry'
--   computation instead if the resulting thunk is forced during
--   its own evaluation.
delayFill ::
  Eval a {- ^ Computation to delay -} ->
  Eval a {- ^ Backup computation to run if a tight loop is detected -} ->
  Eval (Eval a)
delayFill :: Eval a -> Eval a -> Eval (Eval a)
delayFill e :: Eval a
e@(Ready a
_) Eval a
_  = Eval a -> Eval (Eval a)
forall (m :: * -> *) a. Monad m => a -> m a
return Eval a
e
delayFill e :: Eval a
e@(Thunk TVar (ThunkState a)
_) Eval a
_  = Eval a -> Eval (Eval a)
forall (m :: * -> *) a. Monad m => a -> m a
return Eval a
e
delayFill (Eval IO a
x) Eval a
backup = IO (Eval a) -> Eval (Eval a)
forall a. IO a -> Eval a
Eval (TVar (ThunkState a) -> Eval a
forall a. TVar (ThunkState a) -> Eval a
Thunk (TVar (ThunkState a) -> Eval a)
-> IO (TVar (ThunkState a)) -> IO (Eval a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ThunkState a -> IO (TVar (ThunkState a))
forall a. a -> IO (TVar a)
newTVarIO (IO a -> IO a -> ThunkState a
forall a. IO a -> IO a -> ThunkState a
Unforced IO a
x (Eval a -> IO a
forall a. Eval a -> IO a
runEval Eval a
backup)))

-- | Begin executing the given operation in a separate thread,
--   returning a thunk which will await the completion of
--   the computation when forced.
evalSpark ::
  Eval a ->
  Eval (Eval a)

-- Ready computations need no additional evaluation.
evalSpark :: Eval a -> Eval (Eval a)
evalSpark e :: Eval a
e@(Ready a
_) = Eval a -> Eval (Eval a)
forall (m :: * -> *) a. Monad m => a -> m a
return Eval a
e

-- A thunked computation might already have
-- been forced.  If so, return the result.  Otherwise,
-- fork a thread to force this computation and return
-- the thunk.
evalSpark (Thunk TVar (ThunkState a)
tv)  = IO (Eval a) -> Eval (Eval a)
forall a. IO a -> Eval a
Eval (IO (Eval a) -> Eval (Eval a)) -> IO (Eval a) -> Eval (Eval a)
forall a b. (a -> b) -> a -> b
$
  TVar (ThunkState a) -> IO (ThunkState a)
forall a. TVar a -> IO a
readTVarIO TVar (ThunkState a)
tv IO (ThunkState a) -> (ThunkState a -> IO (Eval a)) -> IO (Eval a)
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
    Forced a
x     -> Eval a -> IO (Eval a)
forall (m :: * -> *) a. Monad m => a -> m a
return (a -> Eval a
forall a. a -> Eval a
Ready a
x)
    ForcedErr EvalError
ex -> Eval a -> IO (Eval a)
forall (m :: * -> *) a. Monad m => a -> m a
return (IO a -> Eval a
forall a. IO a -> Eval a
Eval (EvalError -> IO a
forall e a. Exception e => e -> IO a
X.throwIO EvalError
ex))
    ThunkState a
_ ->
       do ThreadId
_ <- IO () -> IO ThreadId
forkIO (TVar (ThunkState a) -> IO ()
forall a. TVar (ThunkState a) -> IO ()
sparkThunk TVar (ThunkState a)
tv)
          Eval a -> IO (Eval a)
forall (m :: * -> *) a. Monad m => a -> m a
return (TVar (ThunkState a) -> Eval a
forall a. TVar (ThunkState a) -> Eval a
Thunk TVar (ThunkState a)
tv)

-- If the computation is nontrivial but not already a thunk,
-- create a thunk and fork a thread to force it.
evalSpark (Eval IO a
x) = IO (Eval a) -> Eval (Eval a)
forall a. IO a -> Eval a
Eval (IO (Eval a) -> Eval (Eval a)) -> IO (Eval a) -> Eval (Eval a)
forall a b. (a -> b) -> a -> b
$
  do TVar (ThunkState a)
tv <- ThunkState a -> IO (TVar (ThunkState a))
forall a. a -> IO (TVar a)
newTVarIO (IO a -> IO a -> ThunkState a
forall a. IO a -> IO a -> ThunkState a
Unforced IO a
x (EvalError -> IO a
forall e a. Exception e => e -> IO a
X.throwIO (String -> EvalError
LoopError String
"")))
     ThreadId
_ <- IO () -> IO ThreadId
forkIO (TVar (ThunkState a) -> IO ()
forall a. TVar (ThunkState a) -> IO ()
sparkThunk TVar (ThunkState a)
tv)
     Eval a -> IO (Eval a)
forall (m :: * -> *) a. Monad m => a -> m a
return (TVar (ThunkState a) -> Eval a
forall a. TVar (ThunkState a) -> Eval a
Thunk TVar (ThunkState a)
tv)


-- | To the work of forcing a thunk. This is the worker computation
--   that is foked off via @evalSpark@.
sparkThunk :: TVar (ThunkState a) -> IO ()
sparkThunk :: TVar (ThunkState a) -> IO ()
sparkThunk TVar (ThunkState a)
tv =
  do ThreadId
tid <- IO ThreadId
myThreadId
     -- Try to claim the thunk.  If it is still in the @Void@ state, wait
     -- until it is in some other state.  If it is @Unforced@ claim the thunk.
     -- Otherwise, it is already evaluated or under evaluation by another thread,
     -- and we have no work to do.
     ThunkState a
st <- STM (ThunkState a) -> IO (ThunkState a)
forall a. STM a -> IO a
atomically (STM (ThunkState a) -> IO (ThunkState a))
-> STM (ThunkState a) -> IO (ThunkState a)
forall a b. (a -> b) -> a -> b
$
              do ThunkState a
st <- TVar (ThunkState a) -> STM (ThunkState a)
forall a. TVar a -> STM a
readTVar TVar (ThunkState a)
tv
                 case ThunkState a
st of
                   Void String
_ -> STM ()
forall a. STM a
retry
                   Unforced IO a
_ IO a
backup -> TVar (ThunkState a) -> ThunkState a -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar (ThunkState a)
tv (ThreadId -> IO a -> ThunkState a
forall a. ThreadId -> IO a -> ThunkState a
UnderEvaluation ThreadId
tid IO a
backup)
                   ThunkState a
_ -> () -> STM ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
                 ThunkState a -> STM (ThunkState a)
forall (m :: * -> *) a. Monad m => a -> m a
return ThunkState a
st
     -- If we successfully claimed the thunk to work on, run the computation and
     -- update the thunk state with the result.
     case ThunkState a
st of
       Unforced IO a
work IO a
_ ->
         IO a -> IO (Either EvalError a)
forall e a. Exception e => IO a -> IO (Either e a)
X.try IO a
work IO (Either EvalError a) -> (Either EvalError a -> IO ()) -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
           Left EvalError
err -> STM () -> IO ()
forall a. STM a -> IO a
atomically (TVar (ThunkState a) -> ThunkState a -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar (ThunkState a)
tv (EvalError -> ThunkState a
forall a. EvalError -> ThunkState a
ForcedErr EvalError
err))
           Right a
a  -> STM () -> IO ()
forall a. STM a -> IO a
atomically (TVar (ThunkState a) -> ThunkState a -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar (ThunkState a)
tv (a -> ThunkState a
forall a. a -> ThunkState a
Forced a
a))
       ThunkState a
_ -> () -> IO ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()


-- | Produce a thunk value which can be filled with its associated computation
--   after the fact.  A preallocated thunk is returned, along with an operation to
--   fill the thunk with the associated computation.
--   This is used to implement recursive declaration groups.
blackhole ::
  String {- ^ A name to associate with this thunk. -} ->
  Eval (Eval a, Eval a -> Eval ())
blackhole :: String -> Eval (Eval a, Eval a -> Eval ())
blackhole String
msg = IO (Eval a, Eval a -> Eval ()) -> Eval (Eval a, Eval a -> Eval ())
forall a. IO a -> Eval a
Eval (IO (Eval a, Eval a -> Eval ())
 -> Eval (Eval a, Eval a -> Eval ()))
-> IO (Eval a, Eval a -> Eval ())
-> Eval (Eval a, Eval a -> Eval ())
forall a b. (a -> b) -> a -> b
$
  do TVar (ThunkState a)
tv <- ThunkState a -> IO (TVar (ThunkState a))
forall a. a -> IO (TVar a)
newTVarIO (String -> ThunkState a
forall a. String -> ThunkState a
Void String
msg)
     let set :: Eval a -> Eval ()
set (Ready a
x)  = IO () -> Eval ()
forall a. IO a -> Eval a
io (IO () -> Eval ()) -> IO () -> Eval ()
forall a b. (a -> b) -> a -> b
$ STM () -> IO ()
forall a. STM a -> IO a
atomically (TVar (ThunkState a) -> ThunkState a -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar (ThunkState a)
tv (a -> ThunkState a
forall a. a -> ThunkState a
Forced a
x))
         set Eval a
m          = IO () -> Eval ()
forall a. IO a -> Eval a
io (IO () -> Eval ()) -> IO () -> Eval ()
forall a b. (a -> b) -> a -> b
$ STM () -> IO ()
forall a. STM a -> IO a
atomically (TVar (ThunkState a) -> ThunkState a -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar (ThunkState a)
tv (IO a -> IO a -> ThunkState a
forall a. IO a -> IO a -> ThunkState a
Unforced (Eval a -> IO a
forall a. Eval a -> IO a
runEval Eval a
m) (EvalError -> IO a
forall e a. Exception e => e -> IO a
X.throwIO (String -> EvalError
LoopError String
msg))))
     (Eval a, Eval a -> Eval ()) -> IO (Eval a, Eval a -> Eval ())
forall (m :: * -> *) a. Monad m => a -> m a
return (TVar (ThunkState a) -> Eval a
forall a. TVar (ThunkState a) -> Eval a
Thunk TVar (ThunkState a)
tv, Eval a -> Eval ()
set)

-- | Force a thunk to get the result.
unDelay :: TVar (ThunkState a) -> IO a
unDelay :: TVar (ThunkState a) -> IO a
unDelay TVar (ThunkState a)
tv =
  -- First, check if the thunk is in an evaluated state,
  -- and return the value if so.
  TVar (ThunkState a) -> IO (ThunkState a)
forall a. TVar a -> IO a
readTVarIO TVar (ThunkState a)
tv IO (ThunkState a) -> (ThunkState a -> IO a) -> IO a
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
    Forced a
x -> a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure a
x
    ForcedErr EvalError
e -> EvalError -> IO a
forall e a. Exception e => e -> IO a
X.throwIO EvalError
e
    ThunkState a
_ ->
      -- Otherwise, try to claim the thunk to work on.
      do ThreadId
tid <- IO ThreadId
myThreadId
         ThunkState a
res <- STM (ThunkState a) -> IO (ThunkState a)
forall a. STM a -> IO a
atomically (STM (ThunkState a) -> IO (ThunkState a))
-> STM (ThunkState a) -> IO (ThunkState a)
forall a b. (a -> b) -> a -> b
$ do
                  ThunkState a
res <- TVar (ThunkState a) -> STM (ThunkState a)
forall a. TVar a -> STM a
readTVar TVar (ThunkState a)
tv
                  case ThunkState a
res of
                    -- In this case, we claim the thunk.  Update the state to indicate
                    -- that we are working on it.
                    Unforced IO a
_ IO a
backup -> TVar (ThunkState a) -> ThunkState a -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar (ThunkState a)
tv (ThreadId -> IO a -> ThunkState a
forall a. ThreadId -> IO a -> ThunkState a
UnderEvaluation ThreadId
tid IO a
backup)

                    -- In this case, the thunk is already being evaluated.  If it is
                    -- under evaluation by this thread, we have to run the backup computation,
                    -- and "consume" it by updating the backup computation to one that throws
                    -- a loop error.  If some other thread is evaluating, reset the
                    -- transaction to await completion of the thunk.
                    UnderEvaluation ThreadId
t IO a
_
                      | ThreadId
tid ThreadId -> ThreadId -> Bool
forall a. Eq a => a -> a -> Bool
== ThreadId
t  -> TVar (ThunkState a) -> ThunkState a -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar (ThunkState a)
tv (ThreadId -> IO a -> ThunkState a
forall a. ThreadId -> IO a -> ThunkState a
UnderEvaluation ThreadId
tid (EvalError -> IO a
forall e a. Exception e => e -> IO a
X.throwIO (String -> EvalError
LoopError String
"")))
                      | Bool
otherwise -> STM ()
forall a. STM a
retry -- wait, if some other thread is evaualting
                    ThunkState a
_ -> () -> STM ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()

                  -- Return the original thunk state so we can decide what work to do
                  -- after the transaction completes.
                  ThunkState a -> STM (ThunkState a)
forall (m :: * -> *) a. Monad m => a -> m a
return ThunkState a
res

         -- helper for actually doing the work
         let doWork :: IO a -> IO a
doWork IO a
work =
               IO a -> IO (Either EvalError a)
forall e a. Exception e => IO a -> IO (Either e a)
X.try IO a
work IO (Either EvalError a) -> (Either EvalError a -> IO a) -> IO a
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
                 Left EvalError
ex -> do STM () -> IO ()
forall a. STM a -> IO a
atomically (TVar (ThunkState a) -> ThunkState a -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar (ThunkState a)
tv (EvalError -> ThunkState a
forall a. EvalError -> ThunkState a
ForcedErr EvalError
ex))
                               EvalError -> IO a
forall e a. Exception e => e -> IO a
X.throwIO EvalError
ex
                 Right a
a -> do STM () -> IO ()
forall a. STM a -> IO a
atomically (TVar (ThunkState a) -> ThunkState a -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar (ThunkState a)
tv (a -> ThunkState a
forall a. a -> ThunkState a
Forced a
a))
                               a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return a
a

         -- Now, examine the thunk state and decide what to do.
         case ThunkState a
res of
           Void String
msg -> String -> [String] -> IO a
forall a. HasCallStack => String -> [String] -> a
evalPanic String
"unDelay" [String
"Thunk forced before it was initialized", String
msg]
           Forced a
x -> a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure a
x
           ForcedErr EvalError
e -> EvalError -> IO a
forall e a. Exception e => e -> IO a
X.throwIO EvalError
e
           UnderEvaluation ThreadId
_ IO a
backup -> IO a -> IO a
doWork IO a
backup -- this thread was already evaluating this thunk
           Unforced IO a
work IO a
_ -> IO a -> IO a
doWork IO a
work

-- | Execute the given evaluation action.
runEval :: Eval a -> IO a
runEval :: Eval a -> IO a
runEval (Ready a
a)  = a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return a
a
runEval (Eval IO a
x)   = IO a
x
runEval (Thunk TVar (ThunkState a)
tv) = TVar (ThunkState a) -> IO a
forall a. TVar (ThunkState a) -> IO a
unDelay TVar (ThunkState a)
tv

{-# INLINE evalBind #-}
evalBind :: Eval a -> (a -> Eval b) -> Eval b
evalBind :: Eval a -> (a -> Eval b) -> Eval b
evalBind (Ready a
a) a -> Eval b
f  = a -> Eval b
f a
a
evalBind (Eval IO a
x) a -> Eval b
f   = IO b -> Eval b
forall a. IO a -> Eval a
Eval (IO a
x IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Eval b -> IO b
forall a. Eval a -> IO a
runEval (Eval b -> IO b) -> (a -> Eval b) -> a -> IO b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Eval b
f)
evalBind (Thunk TVar (ThunkState a)
x) a -> Eval b
f  = IO b -> Eval b
forall a. IO a -> Eval a
Eval (TVar (ThunkState a) -> IO a
forall a. TVar (ThunkState a) -> IO a
unDelay TVar (ThunkState a)
x IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Eval b -> IO b
forall a. Eval a -> IO a
runEval (Eval b -> IO b) -> (a -> Eval b) -> a -> IO b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Eval b
f)

instance Functor Eval where
  fmap :: (a -> b) -> Eval a -> Eval b
fmap a -> b
f (Ready a
x)   = b -> Eval b
forall a. a -> Eval a
Ready (a -> b
f a
x)
  fmap a -> b
f (Eval IO a
m)    = IO b -> Eval b
forall a. IO a -> Eval a
Eval (a -> b
f (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO a
m)
  fmap a -> b
f (Thunk TVar (ThunkState a)
tv)  = IO b -> Eval b
forall a. IO a -> Eval a
Eval (a -> b
f (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> TVar (ThunkState a) -> IO a
forall a. TVar (ThunkState a) -> IO a
unDelay TVar (ThunkState a)
tv)
  {-# INLINE fmap #-}

instance Applicative Eval where
  pure :: a -> Eval a
pure  = a -> Eval a
forall (m :: * -> *) a. Monad m => a -> m a
return
  <*> :: Eval (a -> b) -> Eval a -> Eval b
(<*>) = Eval (a -> b) -> Eval a -> Eval b
forall (m :: * -> *) a b. Monad m => m (a -> b) -> m a -> m b
ap
  {-# INLINE pure #-}
  {-# INLINE (<*>) #-}

instance Monad Eval where
  return :: a -> Eval a
return = a -> Eval a
forall a. a -> Eval a
Ready
  >>= :: Eval a -> (a -> Eval b) -> Eval b
(>>=)  = Eval a -> (a -> Eval b) -> Eval b
forall a b. Eval a -> (a -> Eval b) -> Eval b
evalBind
  {-# INLINE return #-}
  {-# INLINE (>>=) #-}

instance Fail.MonadFail Eval where
  fail :: String -> Eval a
fail String
x = IO a -> Eval a
forall a. IO a -> Eval a
Eval (String -> IO a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
x)

instance MonadIO Eval where
  liftIO :: IO a -> Eval a
liftIO = IO a -> Eval a
forall a. IO a -> Eval a
io

instance MonadFix Eval where
  mfix :: (a -> Eval a) -> Eval a
mfix a -> Eval a
f = IO a -> Eval a
forall a. IO a -> Eval a
Eval (IO a -> Eval a) -> IO a -> Eval a
forall a b. (a -> b) -> a -> b
$ (a -> IO a) -> IO a
forall (m :: * -> *) a. MonadFix m => (a -> m a) -> m a
mfix (\a
x -> Eval a -> IO a
forall a. Eval a -> IO a
runEval (a -> Eval a
f a
x))

-- | Lift an 'IO' computation into the 'Eval' monad.
io :: IO a -> Eval a
io :: IO a -> Eval a
io IO a
m = IO a -> Eval a
forall a. IO a -> Eval a
Eval IO a
m
{-# INLINE io #-}


-- Errors ----------------------------------------------------------------------

-- | Panic from an @Eval@ context.
evalPanic :: HasCallStack => String -> [String] -> a
evalPanic :: String -> [String] -> a
evalPanic String
cxt = String -> [String] -> a
forall a. HasCallStack => String -> [String] -> a
panic (String
"[Eval] " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
cxt)


-- | Data type describing errors that can occur during evaluation.
data EvalError
  = InvalidIndex (Maybe Integer)  -- ^ Out-of-bounds index
  | TypeCannotBeDemoted Type      -- ^ Non-numeric type passed to @number@ function
  | DivideByZero                  -- ^ Division or modulus by 0
  | NegativeExponent              -- ^ Exponentiation by negative integer
  | LogNegative                   -- ^ Logarithm of a negative integer
  | WordTooWide Integer           -- ^ Bitvector too large
  | UserError String              -- ^ Call to the Cryptol @error@ primitive
  | LoopError String              -- ^ Detectable nontermination
  | NoPrim Name                   -- ^ Primitive with no implementation
  | BadRoundingMode Integer       -- ^ Invalid rounding mode
  | BadValue String               -- ^ Value outside the domain of a partial function.
    deriving (Typeable,Int -> EvalError -> String -> String
[EvalError] -> String -> String
EvalError -> String
(Int -> EvalError -> String -> String)
-> (EvalError -> String)
-> ([EvalError] -> String -> String)
-> Show EvalError
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
showList :: [EvalError] -> String -> String
$cshowList :: [EvalError] -> String -> String
show :: EvalError -> String
$cshow :: EvalError -> String
showsPrec :: Int -> EvalError -> String -> String
$cshowsPrec :: Int -> EvalError -> String -> String
Show)

instance PP EvalError where
  ppPrec :: Int -> EvalError -> Doc
ppPrec Int
_ EvalError
e = case EvalError
e of
    InvalidIndex (Just Integer
i) -> String -> Doc
text String
"invalid sequence index:" Doc -> Doc -> Doc
<+> Integer -> Doc
integer Integer
i
    InvalidIndex Maybe Integer
Nothing  -> String -> Doc
text String
"invalid sequence index"
    TypeCannotBeDemoted Type
t -> String -> Doc
text String
"type cannot be demoted:" Doc -> Doc -> Doc
<+> Type -> Doc
forall a. PP a => a -> Doc
pp Type
t
    EvalError
DivideByZero -> String -> Doc
text String
"division by 0"
    EvalError
NegativeExponent -> String -> Doc
text String
"negative exponent"
    EvalError
LogNegative -> String -> Doc
text String
"logarithm of negative"
    WordTooWide Integer
w ->
      String -> Doc
text String
"word too wide for memory:" Doc -> Doc -> Doc
<+> Integer -> Doc
integer Integer
w Doc -> Doc -> Doc
<+> String -> Doc
text String
"bits"
    UserError String
x -> String -> Doc
text String
"Run-time error:" Doc -> Doc -> Doc
<+> String -> Doc
text String
x
    LoopError String
x -> String -> Doc
text String
"<<loop>>" Doc -> Doc -> Doc
<+> String -> Doc
text String
x
    BadRoundingMode Integer
r -> Doc
"invalid rounding mode" Doc -> Doc -> Doc
<+> Integer -> Doc
integer Integer
r
    BadValue String
x -> Doc
"invalid input for" Doc -> Doc -> Doc
<+> Doc -> Doc
backticks (String -> Doc
text String
x)
    NoPrim Name
x -> String -> Doc
text String
"unimplemented primitive:" Doc -> Doc -> Doc
<+> Name -> Doc
forall a. PP a => a -> Doc
pp Name
x

instance X.Exception EvalError


data Unsupported
  = UnsupportedSymbolicOp String  -- ^ Operation cannot be supported in the symbolic simulator
    deriving (Typeable,Int -> Unsupported -> String -> String
[Unsupported] -> String -> String
Unsupported -> String
(Int -> Unsupported -> String -> String)
-> (Unsupported -> String)
-> ([Unsupported] -> String -> String)
-> Show Unsupported
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
showList :: [Unsupported] -> String -> String
$cshowList :: [Unsupported] -> String -> String
show :: Unsupported -> String
$cshow :: Unsupported -> String
showsPrec :: Int -> Unsupported -> String -> String
$cshowsPrec :: Int -> Unsupported -> String -> String
Show)

instance PP Unsupported where
  ppPrec :: Int -> Unsupported -> Doc
ppPrec Int
_ Unsupported
e = case Unsupported
e of
    UnsupportedSymbolicOp String
nm -> String -> Doc
text String
"operation can not be supported on symbolic values:" Doc -> Doc -> Doc
<+> String -> Doc
text String
nm

instance X.Exception Unsupported


-- | For things like @`(inf)@ or @`(0-1)@.
typeCannotBeDemoted :: Type -> a
typeCannotBeDemoted :: Type -> a
typeCannotBeDemoted Type
t = EvalError -> a
forall a e. Exception e => e -> a
X.throw (Type -> EvalError
TypeCannotBeDemoted Type
t)

-- | For when we know that a word is too wide and will exceed gmp's
-- limits (though words approaching this size will probably cause the
-- system to crash anyway due to lack of memory).
wordTooWide :: Integer -> a
wordTooWide :: Integer -> a
wordTooWide Integer
w = EvalError -> a
forall a e. Exception e => e -> a
X.throw (Integer -> EvalError
WordTooWide Integer
w)