{-# LANGUAGE CPP #-}
{-# LANGUAGE EmptyCase #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE Trustworthy #-}
module Clash.XException
(
XException(..), errorX, isX, hasX, maybeIsX, maybeHasX, fromJustX, undefined,
xToErrorCtx, xToError
, ShowX (..), showsX, printX, showsPrecXWith
, seqX, seqErrorX, forceX, deepseqX, rwhnfX, defaultSeqX, hwSeqX
, NFDataX (rnfX, deepErrorX, hasUndefined, ensureSpine)
)
where
import Prelude hiding (undefined)
import Clash.Annotations.Primitive (hasBlackBox, dontTranslate)
import Clash.CPP (maxTupleSize, fSuperStrict)
import Clash.XException.Internal
import Clash.XException.TH
import Control.Exception
(ErrorCall (..), Handler(..), catch, catches, evaluate, throw)
import Control.DeepSeq (NFData, rnf)
import Data.Complex (Complex)
import Data.Either (isLeft)
import Data.Foldable (toList)
import Data.Functor.Compose (Compose)
import Data.Functor.Const (Const)
import Data.Functor.Identity (Identity)
import Data.Functor.Product (Product)
import Data.Functor.Sum (Sum)
import Data.Int (Int8, Int16, Int32, Int64)
import qualified Data.List.Infinite as Inf
import Data.List.Infinite (Infinite (..))
import Data.List.NonEmpty (NonEmpty)
import Data.Ord (Down (Down))
import Data.Proxy (Proxy)
import Data.Ratio (Ratio, numerator, denominator)
import qualified Data.Semigroup as SG
import qualified Data.Monoid as M
import Data.Sequence (Seq(Empty, (:<|)))
import Data.Word (Word8, Word16, Word32, Word64)
import Foreign.C.Types (CUShort)
import GHC.Generics
import GHC.Natural (Natural)
import GHC.Stack
(HasCallStack, callStack, prettyCallStack, withFrozenCallStack)
import Numeric.Half (Half)
import System.IO.Unsafe (unsafeDupablePerformIO)
defaultSeqX :: NFDataX a => a -> b -> b
defaultSeqX = if fSuperStrict then deepseqX else seqX
{-# INLINE defaultSeqX #-}
infixr 0 `defaultSeqX`
errorX :: HasCallStack => String -> a
errorX msg = throw (XException ("X: " ++ msg ++ "\n" ++ prettyCallStack callStack))
{-# OPAQUE errorX #-}
{-# ANN errorX hasBlackBox #-}
xToErrorCtx :: String -> a -> a
xToErrorCtx ctx a = unsafeDupablePerformIO
(catch (evaluate a >> return a)
(\(XException msg) ->
throw (ErrorCall (unlines [ctx,msg]))))
{-# OPAQUE xToErrorCtx #-}
xToError :: HasCallStack => a -> a
xToError = xToErrorCtx (prettyCallStack callStack)
{-# INLINE xToError #-}
seqX :: a -> b -> b
seqX a b = unsafeDupablePerformIO
(catch (evaluate a >> return b) (\(XException _) -> return b))
{-# OPAQUE seqX #-}
{-# ANN seqX hasBlackBox #-}
infixr 0 `seqX`
seqErrorX :: a -> b -> b
seqErrorX a b = unsafeDupablePerformIO
((evaluate a >> return b) `catches`
[ Handler (\(XException _) -> return b)
, Handler (\(ErrorCall _) -> return b)
])
{-# OPAQUE seqErrorX #-}
{-# ANN seqErrorX hasBlackBox #-}
infixr 0 `seqErrorX`
hwSeqX :: a -> b -> b
hwSeqX = seqX
{-# OPAQUE hwSeqX #-}
{-# ANN hwSeqX hasBlackBox #-}
infixr 0 `hwSeqX`
maybeX :: (a -> Either String a) -> a -> Maybe a
maybeX f a = either (const Nothing) Just (f a)
maybeHasX :: (NFData a, NFDataX a) => a -> Maybe a
maybeHasX = maybeX hasX
{-# ANN maybeHasX dontTranslate #-}
maybeIsX :: a -> Maybe a
maybeIsX = maybeX isX
{-# ANN maybeIsX dontTranslate #-}
hasX :: (NFData a, NFDataX a) => a -> Either String a
hasX a =
unsafeDupablePerformIO
(catch
(evaluate (rnf a) >> return (Right a))
(\(XException msg) -> evaluate (rnfX a) >> return (Left msg)))
{-# OPAQUE hasX #-}
{-# ANN hasX dontTranslate #-}
isX :: a -> Either String a
isX a =
unsafeDupablePerformIO
(catch
(evaluate a >> return (Right a))
(\(XException msg) -> return (Left msg)))
{-# OPAQUE isX #-}
{-# ANN isX dontTranslate #-}
class ShowX a where
showsPrecX :: Int -> a -> ShowS
showX :: a -> String
showX x = showsX x ""
showListX :: [a] -> ShowS
showListX ls s = showListX__ showsX ls s
default showsPrecX :: (Generic a, GShowX (Rep a)) => Int -> a -> ShowS
showsPrecX = genericShowsPrecX
printX :: ShowX a => a -> IO ()
printX x = putStrLn $ showX x
instance ShowX ()
instance ShowX (Proxy a)
instance ShowX a => ShowX (Identity a)
instance ShowX a => ShowX (Const a b)
instance (ShowX (f a), ShowX (g a)) => ShowX (Product f g a)
instance (ShowX (f a), ShowX (g a)) => ShowX (Sum f g a)
instance (ShowX (f (g a))) => ShowX (Compose f g a)
instance {-# OVERLAPPABLE #-} ShowX a => ShowX [a] where
showsPrecX _ = showListX
instance ShowX Char where
showsPrecX = showsPrecXWith showsPrec
instance ShowX Bool
instance ShowX Double where
showsPrecX = showsPrecXWith showsPrec
instance ShowX a => ShowX (Down a) where
showsPrecX = showsPrecXWith showsPrecX
instance (ShowX a, ShowX b) => ShowX (Either a b)
instance ShowX Float where
showsPrecX = showsPrecXWith showsPrec
instance ShowX Int where
showsPrecX = showsPrecXWith showsPrec
instance ShowX Int8 where
showsPrecX = showsPrecXWith showsPrec
instance ShowX Int16 where
showsPrecX = showsPrecXWith showsPrec
instance ShowX Int32 where
showsPrecX = showsPrecXWith showsPrec
instance ShowX Int64 where
showsPrecX = showsPrecXWith showsPrec
instance ShowX Integer where
showsPrecX = showsPrecXWith showsPrec
instance ShowX Natural where
showsPrecX = showsPrecXWith showsPrec
instance ShowX Ordering
instance ShowX a => ShowX (Seq a) where
showsPrecX _ = showListX . toList
instance ShowX Word where
showsPrecX = showsPrecXWith showsPrec
instance ShowX Word8 where
showsPrecX = showsPrecXWith showsPrec
instance ShowX Word16 where
showsPrecX = showsPrecXWith showsPrec
instance ShowX Word32 where
showsPrecX = showsPrecXWith showsPrec
instance ShowX Word64 where
showsPrecX = showsPrecXWith showsPrec
instance ShowX CUShort where
showsPrecX = showsPrecXWith showsPrec
instance ShowX a => ShowX (Maybe a)
instance ShowX a => ShowX (Ratio a) where
showsPrecX = showsPrecXWith showsPrecX
instance ShowX a => ShowX (Complex a)
instance {-# OVERLAPPING #-} ShowX String where
showsPrecX = showsPrecXWith showsPrec
forceX :: NFDataX a => a -> a
forceX x = x `deepseqX` x
{-# INLINE forceX #-}
deepseqX :: NFDataX a => a -> b -> b
deepseqX a b = rnfX a `seq` b
{-# OPAQUE deepseqX #-}
{-# ANN deepseqX hasBlackBox #-}
infixr 0 `deepseqX`
rwhnfX :: a -> ()
rwhnfX = (`seqX` ())
{-# INLINE rwhnfX #-}
class NFDataX a where
deepErrorX :: HasCallStack => String -> a
default deepErrorX :: (HasCallStack, Generic a, GDeepErrorX (Rep a)) => String -> a
deepErrorX = withFrozenCallStack $ to . gDeepErrorX
hasUndefined :: a -> Bool
default hasUndefined :: (Generic a, GHasUndefined (Rep a)) => a -> Bool
hasUndefined = gHasUndefined . from
ensureSpine :: a -> a
default ensureSpine :: (Generic a, GEnsureSpine (Rep a)) => a -> a
ensureSpine = to . gEnsureSpine . from
rnfX :: a -> ()
default rnfX :: (Generic a, GNFDataX Zero (Rep a)) => a -> ()
rnfX = grnfX RnfArgs0 . from
instance NFDataX ()
instance NFDataX b => NFDataX (a -> b) where
deepErrorX = pure . deepErrorX
rnfX = rwhnfX
hasUndefined = error "hasUndefined on NFDataX (a -> b): Not Yet Implemented"
ensureSpine = id
instance NFDataX a => NFDataX (Down a) where
deepErrorX = Down . deepErrorX
rnfX d@(~(Down x)) = if isLeft (isX d) then () else rnfX x
hasUndefined d@(~(Down x))= if isLeft (isX d) then True else hasUndefined x
ensureSpine ~(Down x) = Down (ensureSpine x)
instance NFDataX a => NFDataX (Infinite a) where
deepErrorX msg = Inf.repeat (deepErrorX msg)
rnfX d@(~(x :< xs)) =
if isLeft (isX d) then
()
else
rnfX x `seq` rnfX xs
hasUndefined d@(~(x :< xs)) =
if isLeft (isX d) then
True
else
hasUndefined x || hasUndefined xs
ensureSpine ~(x :< xs) = ensureSpine x :< ensureSpine xs
instance NFDataX Bool
instance NFDataX Ordering
instance NFDataX a => NFDataX [a]
instance NFDataX a => NFDataX (NonEmpty a)
instance (NFDataX a, NFDataX b) => NFDataX (Either a b)
instance NFDataX a => NFDataX (Maybe a)
instance NFDataX (Proxy a)
instance NFDataX a => NFDataX (Identity a)
instance NFDataX a => NFDataX (Const a b)
instance (NFDataX (f a), NFDataX (g a)) => NFDataX (Product f g a)
instance (NFDataX (f a), NFDataX (g a)) => NFDataX (Sum f g a)
instance (NFDataX (f (g a))) => NFDataX (Compose f g a)
instance NFDataX Char where
deepErrorX = errorX
rnfX = rwhnfX
hasUndefined = isLeft . isX
ensureSpine = id
instance NFDataX Double where
deepErrorX = errorX
rnfX = rwhnfX
hasUndefined = isLeft . isX
ensureSpine = id
instance NFDataX Float where
deepErrorX = errorX
rnfX = rwhnfX
hasUndefined = isLeft . isX
ensureSpine = id
instance NFDataX Int where
deepErrorX = errorX
rnfX = rwhnfX
hasUndefined = isLeft . isX
ensureSpine = id
instance NFDataX Int8 where
deepErrorX = errorX
rnfX = rwhnfX
hasUndefined = isLeft . isX
ensureSpine = id
instance NFDataX Int16 where
deepErrorX = errorX
rnfX = rwhnfX
hasUndefined = isLeft . isX
ensureSpine = id
instance NFDataX Int32 where
deepErrorX = errorX
rnfX = rwhnfX
hasUndefined = isLeft . isX
ensureSpine = id
instance NFDataX Int64 where
deepErrorX = errorX
rnfX = rwhnfX
hasUndefined = isLeft . isX
ensureSpine = id
instance NFDataX Integer where
deepErrorX = errorX
rnfX = rwhnfX
hasUndefined = isLeft . isX
ensureSpine = id
instance NFDataX Natural where
deepErrorX = errorX
rnfX = rwhnfX
hasUndefined = isLeft . isX
ensureSpine = id
instance NFDataX Word where
deepErrorX = errorX
rnfX = rwhnfX
hasUndefined = isLeft . isX
ensureSpine = id
instance NFDataX Word8 where
deepErrorX :: HasCallStack => String -> a -> b
deepErrorX = errorX
rnfX :: (a -> b) -> ()
rnfX = (a -> b) -> ()
forall a. a -> ()
rwhnfX
hasUndefined :: (a -> b) -> Bool
hasUndefined = isLeft . isX
ensureSpine :: (a -> b) -> a -> b
ensureSpine = (a -> b) -> a -> b
forall a. a -> a
id
instance NFDataX Word16 where
deepErrorX :: HasCallStack => String -> Down a
deepErrorX = errorX
rnfX :: Down a -> ()
rnfX d :: Down a
= rwhnfX
hasUndefined :: Down a -> Bool
hasUndefined d :: Down a
= isLeft . isX
ensureSpine :: Down a -> Down a
ensureSpine = id
instance NFDataX Word32 where
deepErrorX :: HasCallStack => String -> Infinite a
deepErrorX = errorX
rnfX :: Infinite a -> ()
rnfX d :: Infinite a
= rwhnfX
hasUndefined = isLeft . isX
ensureSpine = id
instance NFDataX Word64 where
deepErrorX = errorX
rnfX = rwhnfX
hasUndefined = isLeft . isX
ensureSpine = id
instance NFDataX CUShort where
deepErrorX = errorX
rnfX = rwhnfX
hasUndefined = isLeft . isX
ensureSpine = id
instance NFDataX Half where
deepErrorX = errorX
rnfX = rwhnfX
hasUndefined = isLeft . isX
ensureSpine = id
instance NFDataX a => NFDataX (Seq a) where
deepErrorX = errorX
rnfX s =
if isLeft (isX s) then () else go s
where
go Empty = ()
go (x :<| xs) = rnfX x `seq` go xs
ensureSpine = id
hasUndefined s =
if isLeft (isX s) then True else go s
where
go Empty = False
go (x :<| xs) = hasUndefined x || hasUndefined xs
instance NFDataX a => NFDataX (Ratio a) where
deepErrorX = errorX
rnfX r = rnfX (numerator r) `seq` rnfX (denominator r)
hasUndefined r = isLeft (isX (numerator r)) || isLeft (isX (denominator r))
ensureSpine = id
instance NFDataX a => NFDataX (Complex a) where
deepErrorX = errorX
instance (NFDataX a, NFDataX b) => NFDataX (SG.Arg a b)
instance NFDataX (SG.All)
instance NFDataX (SG.Any)
instance NFDataX a => NFDataX (SG.Dual a)
instance NFDataX a => NFDataX (SG.Endo a)
instance NFDataX a => NFDataX (SG.First a)
instance NFDataX a => NFDataX (SG.Last a)
instance NFDataX a => NFDataX (SG.Max a)
instance NFDataX a => NFDataX (SG.Min a)
instance NFDataX a => NFDataX (SG.Product a)
instance NFDataX a => NFDataX (SG.Sum a)
instance NFDataX a => NFDataX (M.First a)
instance NFDataX a => NFDataX (M.Last a)
mkShowXTupleInstances [2..maxTupleSize]
mkNFDataXTupleInstances [2..maxTupleSize]
undefined :: HasCallStack => a
undefined = errorX "undefined"
fromJustX :: (HasCallStack, NFDataX a) => Maybe a -> a
fromJustX Nothing = deepErrorX "fromJustX: Nothing"
fromJustX (Just a) = a