grisette-0.10.0.0: src/Grisette/Internal/SymPrim/SomeBV.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveLift #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskellQuotes #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE ViewPatterns #-}
-- |
-- Module : Grisette.Internal.SymPrim.SomeBV
-- Copyright : (c) Sirui Lu 2024
-- License : BSD-3-Clause (see the LICENSE file)
--
-- Maintainer : siruilu@cs.washington.edu
-- Stability : Experimental
-- Portability : GHC only
module Grisette.Internal.SymPrim.SomeBV
( SomeBV (..),
SomeBVException (..),
-- * Constructing and pattern matching on SomeBV
unsafeSomeBV,
conBV,
conBVView,
pattern ConBV,
symBV,
ssymBV,
isymBV,
arbitraryBV,
-- * Synonyms
pattern SomeIntN,
type SomeIntN,
pattern SomeWordN,
type SomeWordN,
pattern SomeSymIntN,
type SomeSymIntN,
pattern SomeSymWordN,
type SomeSymWordN,
-- * Helpers for manipulating SomeBV
unarySomeBV,
unarySomeBVR1,
binSomeBV,
binSomeBVR1,
binSomeBVR2,
binSomeBVSafe,
binSomeBVSafeR1,
binSomeBVSafeR2,
)
where
import Control.DeepSeq (NFData (rnf))
import Control.Exception (Exception, throw)
import Control.Monad (when)
import Control.Monad.Except (ExceptT, MonadError (throwError), runExceptT)
import Data.Bifunctor (Bifunctor (bimap))
import qualified Data.Binary as Binary
import Data.Bits
( Bits
( bit,
bitSize,
bitSizeMaybe,
clearBit,
complement,
complementBit,
isSigned,
popCount,
rotate,
rotateL,
rotateR,
setBit,
shift,
shiftL,
shiftR,
testBit,
unsafeShiftL,
unsafeShiftR,
xor,
zeroBits,
(.&.),
(.|.)
),
FiniteBits (countLeadingZeros, countTrailingZeros, finiteBitSize),
)
import Data.Bytes.Get (MonadGet (getWord8))
import Data.Bytes.Put (MonadPut (putWord8))
import Data.Bytes.Serial (Serial (deserialize, serialize))
import Data.Data (Proxy (Proxy))
import Data.Hashable (Hashable (hashWithSalt))
import Data.Maybe (catMaybes, fromJust, isJust)
import qualified Data.Serialize as Cereal
import qualified Data.Text as T
import Data.Type.Equality (type (:~:) (Refl))
import GHC.Exception (Exception (displayException))
import GHC.Generics (Generic)
import GHC.Natural (Natural)
import GHC.TypeNats
( KnownNat,
Nat,
natVal,
sameNat,
type (+),
type (<=),
)
import Generics.Deriving (Default (Default))
import Grisette.Internal.Core.Control.Monad.Union (Union)
import Grisette.Internal.Core.Data.Class.BitVector
( BV (bv, bvConcat, bvExt, bvSelect, bvSext, bvZext),
SizedBV
( sizedBVConcat,
sizedBVExt,
sizedBVFromIntegral,
sizedBVSelect,
sizedBVSext,
sizedBVZext
),
)
import Grisette.Internal.Core.Data.Class.EvalSym
( EvalSym (evalSym),
)
import Grisette.Internal.Core.Data.Class.ExtractSym
( ExtractSym (extractSymMaybe),
)
import Grisette.Internal.Core.Data.Class.GenSym
( GenSym (fresh),
GenSymSimple (simpleFresh),
)
import Grisette.Internal.Core.Data.Class.ITEOp (ITEOp (symIte))
import Grisette.Internal.Core.Data.Class.Mergeable
( Mergeable (rootStrategy),
MergingStrategy (SimpleStrategy, SortedStrategy),
wrapStrategy,
)
import Grisette.Internal.Core.Data.Class.PPrint
( PPrint (pformat),
)
import Grisette.Internal.Core.Data.Class.PlainUnion (simpleMerge)
import Grisette.Internal.Core.Data.Class.SafeDiv
( DivOr (divModOr, divOr, modOr, quotOr, quotRemOr, remOr),
SafeDiv (safeDiv, safeDivMod, safeMod, safeQuot, safeQuotRem, safeRem),
)
import Grisette.Internal.Core.Data.Class.SafeLinearArith
( SafeLinearArith (safeAdd, safeNeg, safeSub),
)
import Grisette.Internal.Core.Data.Class.SafeSymRotate
( SafeSymRotate (safeSymRotateL, safeSymRotateR),
)
import Grisette.Internal.Core.Data.Class.SafeSymShift
( SafeSymShift
( safeSymShiftL,
safeSymShiftR,
safeSymStrictShiftL,
safeSymStrictShiftR
),
)
import Grisette.Internal.Core.Data.Class.SignConversion
( SignConversion (toSigned, toUnsigned),
)
import Grisette.Internal.Core.Data.Class.SimpleMergeable (mrgIf)
import Grisette.Internal.Core.Data.Class.Solvable
( Solvable (con, conView, isym, ssym, sym),
)
import Grisette.Internal.Core.Data.Class.SubstSym
( SubstSym (substSym),
)
import Grisette.Internal.Core.Data.Class.SymEq
( SymEq (symDistinct, (./=), (.==)),
)
import Grisette.Internal.Core.Data.Class.SymOrd
( SymOrd (symCompare, (.<), (.<=), (.>), (.>=)),
)
import Grisette.Internal.Core.Data.Class.SymRotate
( SymRotate (symRotate, symRotateNegated),
)
import Grisette.Internal.Core.Data.Class.SymShift
( SymShift (symShift, symShiftNegated),
)
import Grisette.Internal.Core.Data.Class.ToCon (ToCon (toCon))
import Grisette.Internal.Core.Data.Class.ToSym (ToSym (toSym))
import Grisette.Internal.Core.Data.Class.TryMerge (TryMerge, mrgSingle, tryMerge)
import Grisette.Internal.Core.Data.Symbol (Identifier, Symbol)
import Grisette.Internal.SymPrim.AllSyms (AllSyms (allSyms, allSymsS))
import Grisette.Internal.SymPrim.BV
( IntN,
WordN,
)
import Grisette.Internal.SymPrim.SymBV
( SymIntN,
SymWordN,
)
import Grisette.Internal.Utils.Parameterized
( KnownProof (KnownProof),
LeqProof (LeqProof),
NatRepr,
SomePositiveNatRepr (SomePositiveNatRepr),
knownAdd,
leqAddPos,
mkPositiveNatRepr,
unsafeKnownProof,
unsafeLeqProof,
)
import Grisette.Lib.Data.Functor (mrgFmap)
import Language.Haskell.TH.Syntax (Lift (liftTyped))
import Test.QuickCheck (Arbitrary (arbitrary), Gen)
import Unsafe.Coerce (unsafeCoerce)
-- | An exception that would be thrown when operations are performed on
-- incompatible bit widths.
data SomeBVException = BitwidthMismatch | UndeterminedBitwidth T.Text
deriving (Show, Eq, Ord, Generic)
deriving anyclass (Hashable, NFData)
deriving
( Mergeable,
ExtractSym,
PPrint,
SubstSym,
EvalSym,
SymEq,
SymOrd,
ToCon SomeBVException,
ToSym SomeBVException
)
via (Default (SomeBVException))
instance Exception SomeBVException where
displayException BitwidthMismatch = "Bit width does not match"
displayException (UndeterminedBitwidth msg) =
"Cannot determine bit-width for literals: " <> T.unpack msg
class MaySomeBV bv where
assignLitBitWidth :: (KnownNat n, 1 <= n) => SomeBVLit -> bv n
instance MaySomeBV IntN where
assignLitBitWidth = \case
SomeBVIntLit i -> fromInteger i
SomeBVCondLit _ -> error "Should not happen"
instance MaySomeBV WordN where
assignLitBitWidth = \case
SomeBVIntLit i -> fromInteger i
SomeBVCondLit _ -> error "Should not happen"
instance MaySomeBV SymIntN where
assignLitBitWidth = \case
SomeBVIntLit i -> fromInteger i
SomeBVCondLit u -> simpleMerge $ do
i <- u
mrgSingle $ fromInteger i
instance MaySomeBV SymWordN where
assignLitBitWidth = \case
SomeBVIntLit i -> fromInteger i
SomeBVCondLit u -> simpleMerge $ do
i <- u
mrgSingle $ fromInteger i
assignBitWidthList ::
forall bv.
(forall n. (KnownNat n, 1 <= n) => Num (bv n), MaySomeBV bv) =>
T.Text ->
[SomeBV bv] ->
Either SomeBVException [SomeBV bv]
assignBitWidthList msg bvs = case allNonMaybeBitWidth of
[] -> Left $ UndeterminedBitwidth msg
(x : xs) ->
if all (== x) xs
then case allHasBitWidth of
(SomeBV (i :: bv i) : _) -> Right $ fmap (assignSingleBitWidth i) bvs
_ -> error "Should not happen"
else Left BitwidthMismatch
where
maybeBitWidth :: SomeBV bv -> Maybe Int
maybeBitWidth (SomeBV (_ :: bv n)) = Just $ fromIntegral $ natVal (Proxy @n)
maybeBitWidth (SomeBVLit _) = Nothing
allMaybeBitWidth = map maybeBitWidth bvs
allNonMaybeBitWidth = catMaybes allMaybeBitWidth
allHasBitWidth = filter (isJust . maybeBitWidth) bvs
assignSingleBitWidth ::
forall i. (KnownNat i, 1 <= i) => bv i -> SomeBV bv -> SomeBV bv
assignSingleBitWidth _ s@(SomeBV _) = s
assignSingleBitWidth _ (SomeBVLit i) = SomeBV (assignLitBitWidth i :: bv i)
class AssignBitWidth a where
assignBitWidth :: T.Text -> a -> Either SomeBVException a
instance
(forall n. (KnownNat n, 1 <= n) => Num (bv n), MaySomeBV bv) =>
AssignBitWidth (SomeBV bv, SomeBV bv)
where
assignBitWidth msg (a, b) = do
l <- assignBitWidthList msg [a, b]
case l of
[a', b'] -> Right (a', b')
_ -> error "Should not happen"
instance
(forall n. (KnownNat n, 1 <= n) => Num (bv n), MaySomeBV bv) =>
AssignBitWidth (SomeBV bv, SomeBV bv, SomeBV bv)
where
assignBitWidth msg (a, b, c) = do
l <- assignBitWidthList msg [a, b, c]
case l of
[a', b', c'] -> Right (a', b', c')
_ -> error "Should not happen"
instance
(forall n. (KnownNat n, 1 <= n) => Num (bv n), MaySomeBV bv) =>
AssignBitWidth (SomeBV bv, SomeBV bv, SomeBV bv, SomeBV bv)
where
assignBitWidth msg (a, b, c, d) = do
l <- assignBitWidthList msg [a, b, c, d]
case l of
[a', b', c', d'] -> Right (a', b', c', d')
_ -> error "Should not happen"
-- $setup
-- >>> import Grisette.Core
-- >>> import Grisette.SymPrim
-- | Non-indexed bitvectors.
--
-- The creation of t'SomeBV' can be done with the `bv` function with a positive
-- bit width and a value:
--
-- >>> bv 4 0xf :: SomeBV IntN
-- 0xf
--
-- Operations on two t'SomeBV' values require the bitwidths to be the same. So
-- you should check for the bit width (via `finiteBitSize`) before performing
-- operations:
--
-- >>> bv 4 0x3 + bv 4 0x3 :: SomeBV IntN
-- 0x6
-- >>> bv 4 0x3 + bv 8 0x3 :: SomeBV IntN
-- *** Exception: BitwidthMismatch
--
-- One exception is that the equality testing (both concrete and symbolic via
-- 'SymEq') does not require the bitwidths to be the same. Different bitwidths
-- means the values are not equal:
--
-- >>> (bv 4 0x3 :: SomeBV IntN) == (bv 8 0x3)
-- False
--
-- __Note__: t'SomeBV' can be constructed out of integer literals without the
-- bit width provided. Further binary operations will usually require at least
-- one operand has the bit-width, and will use that as the bit-width for the
-- result.
--
-- For example:
--
-- 3 :: SomeBV IntN
-- bvlit(3)
-- >>> bv 4 0x1 + 3 :: SomeBV IntN
-- 0x4
-- >>> 3 * bv 4 0x1 :: SomeBV IntN
-- 0x3
-- >>> 3 * 3 :: SomeBV IntN
-- *** Exception: UndeterminedBitwidth "(*)"
--
-- Some operations allows the literals to be used without the bit-width, such as
-- '(+)', '(-)', 'negate', 'toUnsigned', 'toSigned', '.&.', '.|.', 'xor',
-- 'complement', 'setBit', 'clearBit', 'complementBit', 'shiftL', and
-- 'unsafeShiftL'.
--
-- >>> 3 + 3 :: SomeBV IntN
-- bvlit(6)
data SomeBV bv where
SomeBV :: (KnownNat n, 1 <= n) => bv n -> SomeBV bv
SomeBVLit :: SomeBVLit -> SomeBV bv
data SomeBVLit where
SomeBVIntLit :: Integer -> SomeBVLit
SomeBVCondLit :: Union Integer -> SomeBVLit
deriving (Eq, Generic, Lift)
deriving anyclass (Hashable, NFData)
deriving (Mergeable, ExtractSym, AllSyms) via (Default SomeBVLit)
instance PPrint SomeBVLit where
pformat (SomeBVIntLit i) = pformat i
pformat (SomeBVCondLit u) = pformat u
toUnionInteger :: SomeBVLit -> Union Integer
toUnionInteger (SomeBVIntLit i) = mrgSingle i
toUnionInteger (SomeBVCondLit u) = u
instance Num SomeBVLit where
SomeBVIntLit a + SomeBVIntLit b = SomeBVIntLit $ a + b
l + r = SomeBVCondLit $ toUnionInteger l + toUnionInteger r
SomeBVIntLit a - SomeBVIntLit b = SomeBVIntLit $ a - b
l - r = SomeBVCondLit $ toUnionInteger l - toUnionInteger r
SomeBVIntLit a * SomeBVIntLit b = SomeBVIntLit $ a * b
l * r = SomeBVCondLit $ toUnionInteger l * toUnionInteger r
negate (SomeBVIntLit a) = SomeBVIntLit $ negate a
negate l = SomeBVCondLit $ negate $ toUnionInteger l
abs (SomeBVIntLit a) = SomeBVIntLit $ abs a
abs l = SomeBVCondLit $ abs $ toUnionInteger l
signum (SomeBVIntLit a) = SomeBVIntLit $ signum a
signum l = SomeBVCondLit $ signum $ toUnionInteger l
fromInteger = SomeBVIntLit
instance Bits SomeBVLit where
SomeBVIntLit l .&. SomeBVIntLit r = SomeBVIntLit $ l .&. r
l .&. r = SomeBVCondLit $ do
l <- toUnionInteger l
r <- toUnionInteger r
mrgSingle $ l .&. r
SomeBVIntLit l .|. SomeBVIntLit r = SomeBVIntLit $ l .|. r
l .|. r = SomeBVCondLit $ do
l <- toUnionInteger l
r <- toUnionInteger r
mrgSingle $ l .|. r
SomeBVIntLit l `xor` SomeBVIntLit r = SomeBVIntLit $ l `xor` r
l `xor` r = SomeBVCondLit $ do
l <- toUnionInteger l
r <- toUnionInteger r
mrgSingle $ l `xor` r
complement (SomeBVIntLit l) = SomeBVIntLit $ complement l
complement l = SomeBVCondLit $ do
l <- toUnionInteger l
mrgSingle $ complement l
setBit (SomeBVIntLit l) i = SomeBVIntLit $ setBit l i
setBit l i = SomeBVCondLit $ do
l <- toUnionInteger l
mrgSingle $ setBit l i
clearBit (SomeBVIntLit l) i = SomeBVIntLit $ clearBit l i
clearBit l i = SomeBVCondLit $ do
l <- toUnionInteger l
mrgSingle $ clearBit l i
complementBit (SomeBVIntLit l) i = SomeBVIntLit $ complementBit l i
complementBit l i = SomeBVCondLit $ do
l <- toUnionInteger l
mrgSingle $ complementBit l i
shiftL (SomeBVIntLit a) i = SomeBVIntLit $ shiftL a i
shiftL l i = SomeBVCondLit $ do
l <- toUnionInteger l
mrgSingle $ shiftL l i
unsafeShiftL (SomeBVIntLit a) i = SomeBVIntLit $ unsafeShiftL a i
unsafeShiftL l i = SomeBVCondLit $ do
l <- toUnionInteger l
mrgSingle $ unsafeShiftL l i
shift = throw $ UndeterminedBitwidth "shift"
rotate = throw $ UndeterminedBitwidth "rotate"
bitSize = throw $ UndeterminedBitwidth "bitSize"
bitSizeMaybe = throw $ UndeterminedBitwidth "bitSizeMaybe"
isSigned = error "isSigned is not defined for SomeBVLit"
testBit = throw $ UndeterminedBitwidth "testBit"
bit = throw $ UndeterminedBitwidth "bit"
popCount = throw $ UndeterminedBitwidth "popCount"
instance Show SomeBVLit where
show (SomeBVIntLit i) = show i
show (SomeBVCondLit u) = show u
instance Serial SomeBVLit where
serialize (SomeBVIntLit i) = putWord8 0 >> serialize i
serialize (SomeBVCondLit u) =
putWord8 1 >> serialize u
deserialize = do
tag <- getWord8
case tag of
0 -> SomeBVIntLit <$> deserialize
1 -> SomeBVCondLit <$> deserialize
_ -> fail "Invalid tag"
instance Cereal.Serialize SomeBVLit where
put = serialize
get = deserialize
instance Binary.Binary SomeBVLit where
put = serialize
get = deserialize
instance
(forall n. (KnownNat n, 1 <= n) => Serial (bv n)) =>
Serial (SomeBV bv)
where
serialize (SomeBV (bv :: bv n)) =
putWord8 0 >> serialize (natVal (Proxy @n)) >> serialize bv
serialize (SomeBVLit i) = putWord8 1 >> serialize i
deserialize = do
tag <- getWord8
case tag of
0 -> do
n :: Natural <- deserialize
when (n == 0) $ fail "Invalid bit width"
case mkPositiveNatRepr n of
SomePositiveNatRepr (_ :: NatRepr x) -> do
x <- deserialize @(bv x)
return $ SomeBV x
1 -> SomeBVLit <$> deserialize
_ -> fail "Invalid tag"
instance
(forall n. (KnownNat n, 1 <= n) => Serial (bv n)) =>
Cereal.Serialize (SomeBV bv)
where
put = serialize
get = deserialize
instance
(forall n. (KnownNat n, 1 <= n) => Serial (bv n)) =>
Binary.Binary (SomeBV bv)
where
put = serialize
get = deserialize
instance
( forall n. (KnownNat n, 1 <= n) => Hashable (bv n),
forall n. (KnownNat n, 1 <= n) => Num (bv n),
MaySomeBV bv
) =>
Hashable (SomeBV bv)
where
hashWithSalt s (SomeBV (bv :: bv n)) =
s `hashWithSalt` (natVal (Proxy @n)) `hashWithSalt` bv
hashWithSalt s (SomeBVLit i) = s `hashWithSalt` i
{-# INLINE hashWithSalt #-}
instance
(forall n. (KnownNat n, 1 <= n) => Lift (bv n)) =>
Lift (SomeBV bv)
where
liftTyped (SomeBV bv) = [||SomeBV bv||]
liftTyped (SomeBVLit i) = [||SomeBVLit i||]
{-# INLINE liftTyped #-}
instance
(forall n. (KnownNat n, 1 <= n) => Show (bv n)) =>
Show (SomeBV bv)
where
show (SomeBV bv) = show bv
show (SomeBVLit i) = "bvlit(" <> show i <> ")"
{-# INLINE show #-}
-- , MaySomeBV bv
instance
(forall n. (KnownNat n, 1 <= n) => NFData (bv n)) =>
NFData (SomeBV bv)
where
rnf (SomeBV bv) = rnf bv
rnf (SomeBVLit i) = rnf i
{-# INLINE rnf #-}
instance
( forall n. (KnownNat n, 1 <= n) => Eq (bv n),
forall n. (KnownNat n, 1 <= n) => Num (bv n),
MaySomeBV bv
) =>
Eq (SomeBV bv)
where
SomeBV (l :: bv l) == SomeBV (r :: bv r) =
case sameNat (Proxy @l) (Proxy @r) of
Just Refl -> l == r
Nothing -> False
SomeBV (l :: bv l) == SomeBVLit r = l == assignLitBitWidth r
l == r@SomeBV {} = r == l
_ == _ = throw $ UndeterminedBitwidth "=="
{-# INLINE (==) #-}
SomeBV (l :: bv l) /= SomeBV (r :: bv r) =
case sameNat (Proxy @l) (Proxy @r) of
Just Refl -> l /= r
Nothing -> True
SomeBV (l :: bv l) /= SomeBVLit r = l /= assignLitBitWidth r
l /= r@SomeBV {} = r /= l
_ /= _ = throw $ UndeterminedBitwidth "/="
{-# INLINE (/=) #-}
instance
( forall n. (KnownNat n, 1 <= n) => Ord (bv n),
forall n. (KnownNat n, 1 <= n) => Num (bv n),
MaySomeBV bv
) =>
Ord (SomeBV bv)
where
(<) = binSomeBV (<) (const $ const $ throw $ UndeterminedBitwidth "<")
{-# INLINE (<) #-}
(<=) = binSomeBV (<=) (const $ const $ throw $ UndeterminedBitwidth "(<=)")
{-# INLINE (<=) #-}
(>) = binSomeBV (>) (const $ const $ throw $ UndeterminedBitwidth ">")
{-# INLINE (>) #-}
(>=) = binSomeBV (>=) (const $ const $ throw $ UndeterminedBitwidth "(>=)")
{-# INLINE (>=) #-}
max = binSomeBVR1 max (const $ const $ throw $ UndeterminedBitwidth "max")
{-# INLINE max #-}
min = binSomeBVR1 min (const $ const $ throw $ UndeterminedBitwidth "min")
{-# INLINE min #-}
compare =
binSomeBV compare (const $ const $ throw $ UndeterminedBitwidth "compare")
{-# INLINE compare #-}
instance (forall n. (KnownNat n, 1 <= n) => Num (bv n), MaySomeBV bv) => Num (SomeBV bv) where
(+) = binSomeBVR1 (+) (+)
{-# INLINE (+) #-}
(-) = binSomeBVR1 (-) (-)
{-# INLINE (-) #-}
(*) = binSomeBVR1 (*) (const $ const $ throw $ UndeterminedBitwidth "(*)")
{-# INLINE (*) #-}
negate = unarySomeBVR1 negate negate
{-# INLINE negate #-}
abs = unarySomeBVR1 abs (const $ throw $ UndeterminedBitwidth "abs")
{-# INLINE abs #-}
signum = unarySomeBVR1 signum (const $ throw $ UndeterminedBitwidth "signum")
{-# INLINE signum #-}
fromInteger = SomeBVLit . SomeBVIntLit
{-# INLINE fromInteger #-}
instance
( forall n. (KnownNat n, 1 <= n) => Bits (bv n),
forall n. (KnownNat n, 1 <= n) => Num (bv n),
MaySomeBV bv
) =>
Bits (SomeBV bv)
where
(.&.) = binSomeBVR1 (.&.) (.&.)
(.|.) = binSomeBVR1 (.|.) (.|.)
xor = binSomeBVR1 xor xor
complement = unarySomeBVR1 complement complement
shift s i =
unarySomeBVR1 (`shift` i) (const $ throw $ UndeterminedBitwidth "shift") s
rotate s i =
unarySomeBVR1 (`rotate` i) (const $ throw $ UndeterminedBitwidth "rotate") s
zeroBits =
error $
"zeroBits is not defined for SomeBV as no bitwidth is known, use "
<> "(bv <bitwidth> 0) or (SomeBV (zeroBits :: bv <bitwidth>)) instead"
bit =
error $
"bit is not defined for SomeBV as no bitwidth is known, use "
<> "(SomeBV (bit <bit> :: bv <bitwidth>)) instead"
setBit s i = unarySomeBVR1 (`setBit` i) (`setBit` i) s
clearBit s i = unarySomeBVR1 (`clearBit` i) (`clearBit` i) s
complementBit s i = unarySomeBVR1 (`complementBit` i) (`complementBit` i) s
testBit s i =
unarySomeBV (`testBit` i) (const $ throw $ UndeterminedBitwidth "testBit") s
bitSizeMaybe =
unarySomeBV
bitSizeMaybe
(const $ throw $ UndeterminedBitwidth "bitSizeMaybe")
bitSize =
fromJust
. unarySomeBV
bitSizeMaybe
(const $ throw $ UndeterminedBitwidth "bitSize")
isSigned _ = isSigned (undefined :: bv 1)
shiftL s i = unarySomeBVR1 (`shiftL` i) (`shiftL` i) s
unsafeShiftL s i = unarySomeBVR1 (`unsafeShiftL` i) (`unsafeShiftL` i) s
shiftR s i =
unarySomeBVR1 (`shiftR` i) (const $ throw $ UndeterminedBitwidth "shiftR") s
unsafeShiftR s i =
unarySomeBVR1
(`unsafeShiftR` i)
(const $ throw $ UndeterminedBitwidth "unsafeShiftR")
s
rotateL s i =
unarySomeBVR1
(`rotateL` i)
(const $ throw $ UndeterminedBitwidth "rotateL")
s
rotateR s i =
unarySomeBVR1
(`rotateR` i)
(const $ throw $ UndeterminedBitwidth "rotateR")
s
popCount =
unarySomeBV popCount (const $ throw $ UndeterminedBitwidth "popCount")
instance
( forall n. (KnownNat n, 1 <= n) => FiniteBits (bv n),
forall n. (KnownNat n, 1 <= n) => Num (bv n),
MaySomeBV bv
) =>
FiniteBits (SomeBV bv)
where
finiteBitSize =
unarySomeBV
finiteBitSize
(const $ throw $ UndeterminedBitwidth "finiteBitSize")
{-# INLINE finiteBitSize #-}
countLeadingZeros =
unarySomeBV
countLeadingZeros
(const $ throw $ UndeterminedBitwidth "countLeadingZeros")
{-# INLINE countLeadingZeros #-}
countTrailingZeros =
unarySomeBV
countTrailingZeros
(const $ throw $ UndeterminedBitwidth "countTrailingZeros")
{-# INLINE countTrailingZeros #-}
instance
(forall n. (KnownNat n, 1 <= n) => Enum (bv n)) =>
Enum (SomeBV bv)
where
toEnum =
error $
"toEnum is not defined for SomeBV, use "
<> "(SomeBV (toEnum <value> :: bv <bitwidth>)) instead"
{-# INLINE toEnum #-}
fromEnum =
unarySomeBV fromEnum (const $ throw $ UndeterminedBitwidth "fromEnum")
{-# INLINE fromEnum #-}
instance
(forall n. (KnownNat n, 1 <= n) => Real (bv n), MaySomeBV bv) =>
Real (SomeBV bv)
where
toRational =
unarySomeBV toRational (const $ throw $ UndeterminedBitwidth "toRational")
{-# INLINE toRational #-}
instance
(forall n. (KnownNat n, 1 <= n) => Integral (bv n), MaySomeBV bv) =>
Integral (SomeBV bv)
where
toInteger =
unarySomeBV
toInteger
(const $ throw $ UndeterminedBitwidth "toInteger")
{-# INLINE toInteger #-}
quot = binSomeBVR1 quot (const $ throw $ UndeterminedBitwidth "quot")
{-# INLINE quot #-}
rem = binSomeBVR1 rem (const $ throw $ UndeterminedBitwidth "rem")
{-# INLINE rem #-}
div = binSomeBVR1 div (const $ throw $ UndeterminedBitwidth "div")
{-# INLINE div #-}
mod = binSomeBVR1 mod (const $ throw $ UndeterminedBitwidth "mod")
{-# INLINE mod #-}
quotRem = binSomeBVR2 quotRem (const $ throw $ UndeterminedBitwidth "quotRem")
{-# INLINE quotRem #-}
divMod = binSomeBVR2 divMod (const $ throw $ UndeterminedBitwidth "divMod")
{-# INLINE divMod #-}
instance (SizedBV bv) => BV (SomeBV bv) where
bvConcat (SomeBV (a :: bv l)) (SomeBV (b :: bv r)) =
case ( leqAddPos (Proxy @l) (Proxy @r),
knownAdd @l @r KnownProof KnownProof
) of
(LeqProof, KnownProof) ->
SomeBV $ sizedBVConcat a b
bvConcat _ _ = throw $ UndeterminedBitwidth "bvConcat"
{-# INLINE bvConcat #-}
bvZext l (SomeBV (a :: bv n))
| l < n = error "bvZext: trying to zero extend a value to a smaller size"
| otherwise = res (Proxy @n)
where
n = fromIntegral $ natVal (Proxy @n)
res :: forall (l :: Nat). Proxy l -> SomeBV bv
res p =
case ( unsafeKnownProof @l (fromIntegral l),
unsafeLeqProof @1 @l,
unsafeLeqProof @n @l
) of
(KnownProof, LeqProof, LeqProof) -> SomeBV $ sizedBVZext p a
bvZext _ _ = throw $ UndeterminedBitwidth "bvZext"
{-# INLINE bvZext #-}
bvSext l (SomeBV (a :: bv n))
| l < n = error "bvSext: trying to zero extend a value to a smaller size"
| otherwise = res (Proxy @n)
where
n = fromIntegral $ natVal (Proxy @n)
res :: forall (l :: Nat). Proxy l -> SomeBV bv
res p =
case ( unsafeKnownProof @l (fromIntegral l),
unsafeLeqProof @1 @l,
unsafeLeqProof @n @l
) of
(KnownProof, LeqProof, LeqProof) -> SomeBV $ sizedBVSext p a
bvSext _ _ = throw $ UndeterminedBitwidth "bvSext"
{-# INLINE bvSext #-}
bvExt l (SomeBV (a :: bv n))
| l < n = error "bvExt: trying to zero extend a value to a smaller size"
| otherwise = res (Proxy @n)
where
n = fromIntegral $ natVal (Proxy @n)
res :: forall (l :: Nat). Proxy l -> SomeBV bv
res p =
case ( unsafeKnownProof @l (fromIntegral l),
unsafeLeqProof @1 @l,
unsafeLeqProof @n @l
) of
(KnownProof, LeqProof, LeqProof) -> SomeBV $ sizedBVExt p a
bvExt _ _ = throw $ UndeterminedBitwidth "bvExt"
{-# INLINE bvExt #-}
bvSelect ix w (SomeBV (a :: bv n))
| ix + w > n =
error $
"bvSelect: trying to select a bitvector outside the bounds of the "
<> "inserialize"
| w == 0 = error "bvSelect: trying to select a bitvector of size 0"
| otherwise = res (Proxy @n) (Proxy @n)
where
n = fromIntegral $ natVal (Proxy @n)
res :: forall (w :: Nat) (ix :: Nat). Proxy w -> Proxy ix -> SomeBV bv
res _ _ =
case ( unsafeKnownProof @ix (fromIntegral ix),
unsafeKnownProof @w (fromIntegral w),
unsafeLeqProof @1 @w,
unsafeLeqProof @(ix + w) @n
) of
(KnownProof, KnownProof, LeqProof, LeqProof) ->
SomeBV $ sizedBVSelect (Proxy @ix) (Proxy @w) a
bvSelect _ _ _ = throw $ UndeterminedBitwidth "bvSelect"
bv n i = unsafeSomeBV n $ \_ -> sizedBVFromIntegral i
{-# INLINE bv #-}
instance
(forall n. (KnownNat n, 1 <= n) => EvalSym (bv n)) =>
EvalSym (SomeBV bv)
where
evalSym fillDefault model = unarySomeBVR1 (evalSym fillDefault model) id
{-# INLINE evalSym #-}
instance
(forall n. (KnownNat n, 1 <= n) => ExtractSym (bv n)) =>
ExtractSym (SomeBV bv)
where
extractSymMaybe = unarySomeBV extractSymMaybe extractSymMaybe
{-# INLINE extractSymMaybe #-}
instance
(forall n. (KnownNat n, 1 <= n) => PPrint (bv n)) =>
PPrint (SomeBV bv)
where
pformat (SomeBV bv) = pformat bv
pformat (SomeBVLit i) = "bvlit(" <> pformat i <> ")"
{-# INLINE pformat #-}
data CompileTimeNat where
CompileTimeNat :: (KnownNat n, 1 <= n) => Proxy n -> CompileTimeNat
instance Show CompileTimeNat where
show (CompileTimeNat (Proxy :: Proxy n)) = show (natVal (Proxy @n))
{-# INLINE show #-}
instance Eq CompileTimeNat where
CompileTimeNat (Proxy :: Proxy n) == CompileTimeNat (Proxy :: Proxy m) =
case sameNat (Proxy @n) (Proxy @m) of
Just Refl -> True
Nothing -> False
{-# INLINE (==) #-}
instance Ord CompileTimeNat where
compare
(CompileTimeNat (Proxy :: Proxy n))
(CompileTimeNat (Proxy :: Proxy m)) =
compare (natVal (Proxy @n)) (natVal (Proxy @m))
{-# INLINE compare #-}
instance
(forall n. (KnownNat n, 1 <= n) => Mergeable (bv n)) =>
Mergeable (SomeBV bv)
where
rootStrategy =
SortedStrategy @(Maybe CompileTimeNat)
( \case
(SomeBVLit _) -> Nothing
(SomeBV (_ :: bv n)) -> Just (CompileTimeNat (Proxy @n))
)
( \case
Nothing -> SimpleStrategy $
\c (SomeBVLit l) (SomeBVLit r) ->
SomeBVLit $
SomeBVCondLit $
mrgIf c (toUnionInteger l) (toUnionInteger r)
Just (CompileTimeNat (_ :: proxy n)) ->
wrapStrategy
(rootStrategy @(bv n))
SomeBV
(\(SomeBV x) -> unsafeCoerce x)
)
-- | The 'symDistinct' instance for t'SomeBV' will have the following behavior:
--
-- * If the list is empty or has only one element, it will return 'True'.
-- * If none of the elements have a bit-width, it will throw
-- 'UndeterminedBitwidth' exception.
-- * If the elements have different bit-widths, it will throw a
-- 'BitwidthMismatch' exception.
-- * If there are at least one element have a bit-width, and all elements with
-- known bit-width have the same bit-width, it will generate a single symbolic
-- formula using @distinct@.
instance
( forall n. (KnownNat n, 1 <= n) => SymEq (bv n),
forall n. (KnownNat n, 1 <= n) => Num (bv n),
MaySomeBV bv
) =>
SymEq (SomeBV bv)
where
SomeBV (l :: bv l) .== SomeBV (r :: bv r) =
case sameNat (Proxy @l) (Proxy @r) of
Just Refl -> l .== r
Nothing -> con False
SomeBV (l :: bv l) .== SomeBVLit r = l .== assignLitBitWidth r
SomeBVLit l .== SomeBV (r :: bv r) = assignLitBitWidth l .== r
SomeBVLit _ .== SomeBVLit _ = throw $ UndeterminedBitwidth ".=="
{-# INLINE (.==) #-}
SomeBV (l :: bv l) ./= SomeBV (r :: bv r) =
case sameNat (Proxy @l) (Proxy @r) of
Just Refl -> l ./= r
Nothing -> con True
SomeBV (l :: bv l) ./= SomeBVLit r = l ./= assignLitBitWidth r
SomeBVLit l ./= SomeBV (r :: bv r) = assignLitBitWidth l ./= r
SomeBVLit _ ./= SomeBVLit _ = throw $ UndeterminedBitwidth "./="
symDistinct l = case l of
[] -> con True
[_] -> con True
_ -> case assignBitWidthList "symDistinct" l of
Right (SomeBV (a :: bv a) : l) -> symDistinct $ a : go l
where
go :: [SomeBV bv] -> [bv a]
go [] = []
go (SomeBV (x :: bv x) : xs) = case sameNat (Proxy @x) (Proxy @a) of
Just Refl -> x : go xs
Nothing -> error "Should not happen"
go (SomeBVLit _ : _) = error "Should not happen"
Right _ -> error "Should not happen"
Left UndeterminedBitwidth {} -> throw $ UndeterminedBitwidth "symDistinct"
Left BitwidthMismatch -> throw BitwidthMismatch
{-# INLINE (./=) #-}
instance
( forall n. (KnownNat n, 1 <= n) => SymOrd (bv n),
forall n. (KnownNat n, 1 <= n) => Num (bv n),
MaySomeBV bv
) =>
SymOrd (SomeBV bv)
where
(.<) = binSomeBV (.<) (const $ const $ throw $ UndeterminedBitwidth "(.<)")
{-# INLINE (.<) #-}
(.<=) = binSomeBV (.<=) (const $ const $ throw $ UndeterminedBitwidth "(.<=)")
{-# INLINE (.<=) #-}
(.>) = binSomeBV (.>) (const $ const $ throw $ UndeterminedBitwidth "(.>)")
{-# INLINE (.>) #-}
(.>=) = binSomeBV (.>=) (const $ const $ throw $ UndeterminedBitwidth "(.>=)")
{-# INLINE (.>=) #-}
symCompare =
binSomeBV
symCompare
(const $ const $ throw $ UndeterminedBitwidth "symCompare")
{-# INLINE symCompare #-}
instance
(forall n. (KnownNat n, 1 <= n) => SubstSym (bv n)) =>
SubstSym (SomeBV bv)
where
substSym c s = unarySomeBVR1 (substSym c s) id
{-# INLINE substSym #-}
instance
( KnownNat n,
1 <= n,
forall m. (KnownNat m, 1 <= m) => GenSym () (bv m),
Mergeable (SomeBV bv)
) =>
GenSym (Proxy n) (SomeBV bv)
where
fresh _ =
(\(i :: Union (bv n)) -> mrgFmap SomeBV i) <$> fresh ()
{-# INLINE fresh #-}
instance
( KnownNat n,
1 <= n,
forall m. (KnownNat m, 1 <= m) => GenSymSimple () (bv m),
Mergeable (SomeBV bv)
) =>
GenSymSimple (Proxy n) (SomeBV bv)
where
simpleFresh _ = (\(i :: bv n) -> SomeBV i) <$> simpleFresh ()
{-# INLINE simpleFresh #-}
instance
( forall m. (KnownNat m, 1 <= m) => GenSym () (bv m),
Mergeable (SomeBV bv)
) =>
GenSym (SomeBV bv) (SomeBV bv)
where
fresh (SomeBV (_ :: bv x)) = fresh (Proxy @x)
fresh (SomeBVLit _) = throw $ UndeterminedBitwidth "fresh"
{-# INLINE fresh #-}
instance
( forall m. (KnownNat m, 1 <= m) => GenSymSimple () (bv m),
Mergeable (SomeBV bv)
) =>
GenSymSimple (SomeBV bv) (SomeBV bv)
where
simpleFresh (SomeBV (_ :: bv x)) = simpleFresh (Proxy @x)
simpleFresh (SomeBVLit _) = throw $ UndeterminedBitwidth "simpleFresh"
{-# INLINE simpleFresh #-}
instance
( forall n. (KnownNat n, 1 <= n) => GenSym () (bv n),
Mergeable (SomeBV bv)
) =>
GenSym Int (SomeBV bv)
where
fresh n
| n <= 0 = error "fresh: cannot generate a bitvector of non-positive size"
| otherwise = case mkPositiveNatRepr (fromIntegral n) of
SomePositiveNatRepr (_ :: NatRepr x) -> fresh (Proxy @x)
{-# INLINE fresh #-}
instance
( forall n. (KnownNat n, 1 <= n) => GenSymSimple () (bv n),
Mergeable (SomeBV bv)
) =>
GenSymSimple Int (SomeBV bv)
where
simpleFresh n
| n <= 0 = error "fresh: cannot generate a bitvector of non-positive size"
| otherwise = case mkPositiveNatRepr (fromIntegral n) of
SomePositiveNatRepr (_ :: NatRepr x) -> simpleFresh (Proxy @x)
{-# INLINE simpleFresh #-}
instance
( forall n. (KnownNat n, 1 <= n) => SignConversion (ubv n) (sbv n),
-- Add this to help the type checker resolve the functional dependency
SignConversion (ubv 1) (sbv 1)
) =>
SignConversion (SomeBV ubv) (SomeBV sbv)
where
toSigned (SomeBV (n :: ubv n)) = SomeBV (toSigned n :: sbv n)
toSigned (SomeBVLit i) = SomeBVLit i
{-# INLINE toSigned #-}
toUnsigned (SomeBV (n :: sbv n)) = SomeBV (toUnsigned n :: ubv n)
toUnsigned (SomeBVLit i) = SomeBVLit i
{-# INLINE toUnsigned #-}
instance
(forall n. (KnownNat n, 1 <= n) => ToCon (sbv n) (cbv n)) =>
ToCon (SomeBV sbv) (SomeBV cbv)
where
toCon (SomeBV (n :: sbv n)) = SomeBV <$> (toCon n :: Maybe (cbv n))
toCon (SomeBVLit i) = Just $ SomeBVLit i
{-# INLINE toCon #-}
instance
(forall n. (KnownNat n, 1 <= n) => ToSym (cbv n) (sbv n)) =>
ToSym (SomeBV cbv) (SomeBV sbv)
where
toSym (SomeBV (n :: cbv n)) = SomeBV (toSym n :: sbv n)
toSym (SomeBVLit i) = SomeBVLit i
{-# INLINE toSym #-}
divRemOrBase0 ::
( forall n.
(KnownNat n, 1 <= n) =>
(bv n, bv n) ->
bv n ->
bv n ->
(bv n, bv n)
) ->
(SomeBV bv, SomeBV bv) ->
SomeBV bv ->
SomeBV bv ->
(SomeBV bv, SomeBV bv)
divRemOrBase0
f
(SomeBV (dd :: bv dd), SomeBV (dm :: bv dm))
(SomeBV (a :: bv a))
(SomeBV (b :: bv b)) =
case ( sameNat (Proxy @a) (Proxy @b),
sameNat (Proxy @a) (Proxy @dd),
sameNat (Proxy @a) (Proxy @dm)
) of
(Just Refl, Just Refl, Just Refl) -> bimap SomeBV SomeBV $ f (dd, dm) a b
_ -> error "Should not happen"
divRemOrBase0 _ _ _ _ = error "Should not happen"
{-# INLINE divRemOrBase0 #-}
divRemOrBase ::
(forall n. (KnownNat n, 1 <= n) => Num (bv n), MaySomeBV bv) =>
( forall n.
(KnownNat n, 1 <= n) =>
(bv n, bv n) ->
bv n ->
bv n ->
(bv n, bv n)
) ->
(SomeBV bv, SomeBV bv) ->
SomeBV bv ->
SomeBV bv ->
(SomeBV bv, SomeBV bv)
divRemOrBase f (a, b) c d =
case assignBitWidth "divRemOrBase" (a, b, c, d) of
Right (a', b', c', d') -> divRemOrBase0 f (a', b') c' d'
Left e -> throw e
instance
( forall n. (KnownNat n, 1 <= n) => DivOr (bv n),
forall n. (KnownNat n, 1 <= n) => Num (bv n),
MaySomeBV bv
) =>
DivOr (SomeBV bv)
where
divOr = ternSomeBVR1 divOr
{-# INLINE divOr #-}
modOr = ternSomeBVR1 modOr
{-# INLINE modOr #-}
quotOr = ternSomeBVR1 quotOr
{-# INLINE quotOr #-}
remOr = ternSomeBVR1 remOr
{-# INLINE remOr #-}
divModOr = divRemOrBase divModOr
{-# INLINE divModOr #-}
quotRemOr = divRemOrBase quotRemOr
{-# INLINE quotRemOr #-}
instance
( forall n.
(KnownNat n, 1 <= n) =>
SafeDiv e (bv n) (ExceptT e m),
MonadError (Either SomeBVException e) m,
TryMerge m,
Mergeable e,
forall n. (KnownNat n, 1 <= n) => Num (bv n),
MaySomeBV bv
) =>
SafeDiv (Either SomeBVException e) (SomeBV bv) m
where
safeDiv =
binSomeBVSafeR1
(safeDiv @e)
(const $ const $ throwError $ Left $ UndeterminedBitwidth "safeDiv")
{-# INLINE safeDiv #-}
safeMod =
binSomeBVSafeR1
(safeMod @e)
(const $ const $ throwError $ Left $ UndeterminedBitwidth "safeMod")
{-# INLINE safeMod #-}
safeQuot =
binSomeBVSafeR1
(safeQuot @e)
(const $ const $ throwError $ Left $ UndeterminedBitwidth "safeQuot")
{-# INLINE safeQuot #-}
safeRem =
binSomeBVSafeR1
(safeRem @e)
(const $ const $ throwError $ Left $ UndeterminedBitwidth "safeRem")
{-# INLINE safeRem #-}
safeDivMod =
binSomeBVSafeR2
(safeDivMod @e)
(const $ const $ throwError $ Left $ UndeterminedBitwidth "safeDivMod")
{-# INLINE safeDivMod #-}
safeQuotRem =
binSomeBVSafeR2
(safeQuotRem @e)
(const $ const $ throwError $ Left $ UndeterminedBitwidth "safeQuotRem")
{-# INLINE safeQuotRem #-}
instance
( forall n.
(KnownNat n, 1 <= n) =>
SafeLinearArith e (bv n) (ExceptT e m),
MonadError (Either SomeBVException e) m,
TryMerge m,
Mergeable e,
forall n. (KnownNat n, 1 <= n) => Num (bv n),
MaySomeBV bv
) =>
SafeLinearArith (Either SomeBVException e) (SomeBV bv) m
where
safeAdd =
binSomeBVSafeR1
(safeAdd @e)
(const $ const $ throwError $ Left $ UndeterminedBitwidth "safeAdd")
{-# INLINE safeAdd #-}
safeSub =
binSomeBVSafeR1
(safeSub @e)
(const $ const $ throwError $ Left $ UndeterminedBitwidth "safeSub")
{-# INLINE safeSub #-}
safeNeg =
unarySomeBV
( \v ->
mrgFmap SomeBV $
runExceptT (safeNeg @e v) >>= either (throwError . Right) pure
)
(const $ throwError $ Left $ UndeterminedBitwidth "safeNeg")
{-# INLINE safeNeg #-}
instance
( forall n. (KnownNat n, 1 <= n) => SymShift (bv n),
forall n. (KnownNat n, 1 <= n) => Num (bv n),
MaySomeBV bv
) =>
SymShift (SomeBV bv)
where
symShift =
binSomeBVR1
symShift
(const $ const $ throw $ UndeterminedBitwidth "safeShift")
{-# INLINE symShift #-}
symShiftNegated =
binSomeBVR1
symShiftNegated
(const $ const $ throw $ UndeterminedBitwidth "safeShiftNegated")
{-# INLINE symShiftNegated #-}
instance
( forall n. (KnownNat n, 1 <= n) => SymRotate (bv n),
forall n. (KnownNat n, 1 <= n) => Num (bv n),
MaySomeBV bv
) =>
SymRotate (SomeBV bv)
where
symRotate =
binSomeBVR1
symRotate
(const $ const $ throw $ UndeterminedBitwidth "safeRotate")
{-# INLINE symRotate #-}
symRotateNegated =
binSomeBVR1
symRotateNegated
(const $ const $ throw $ UndeterminedBitwidth "safeRotateNegated")
{-# INLINE symRotateNegated #-}
instance
( forall n.
(KnownNat n, 1 <= n) =>
SafeSymShift e (bv n) (ExceptT e m),
MonadError (Either SomeBVException e) m,
TryMerge m,
Mergeable e,
forall n. (KnownNat n, 1 <= n) => Num (bv n),
MaySomeBV bv
) =>
SafeSymShift (Either SomeBVException e) (SomeBV bv) m
where
safeSymShiftL =
binSomeBVSafeR1
(safeSymShiftL @e)
(const $ const $ throwError $ Left $ UndeterminedBitwidth "safeSymShiftL")
{-# INLINE safeSymShiftL #-}
safeSymShiftR =
binSomeBVSafeR1
(safeSymShiftR @e)
(const $ const $ throwError $ Left $ UndeterminedBitwidth "safeSymShiftR")
{-# INLINE safeSymShiftR #-}
safeSymStrictShiftL =
binSomeBVSafeR1
(safeSymStrictShiftL @e)
(const $ const $ throwError $ Left $ UndeterminedBitwidth "safeSymStrictShiftL")
{-# INLINE safeSymStrictShiftL #-}
safeSymStrictShiftR =
binSomeBVSafeR1
(safeSymStrictShiftR @e)
(const $ const $ throwError $ Left $ UndeterminedBitwidth "safeSymStrictShiftR")
{-# INLINE safeSymStrictShiftR #-}
instance
( forall n.
(KnownNat n, 1 <= n) =>
SafeSymRotate e (bv n) (ExceptT e m),
MonadError (Either SomeBVException e) m,
TryMerge m,
Mergeable e,
forall n. (KnownNat n, 1 <= n) => Num (bv n),
MaySomeBV bv
) =>
SafeSymRotate (Either SomeBVException e) (SomeBV bv) m
where
safeSymRotateL =
binSomeBVSafeR1
(safeSymRotateL @e)
(const $ const $ throwError $ Left $ UndeterminedBitwidth "safeSymRotateL")
{-# INLINE safeSymRotateL #-}
safeSymRotateR =
binSomeBVSafeR1
(safeSymRotateR @e)
(const $ const $ throwError $ Left $ UndeterminedBitwidth "safeSymRotateR")
{-# INLINE safeSymRotateR #-}
instance
( forall n. (KnownNat n, 1 <= n) => ITEOp (bv n),
forall n. (KnownNat n, 1 <= n) => Num (bv n),
MaySomeBV bv
) =>
ITEOp (SomeBV bv)
where
symIte cond =
binSomeBVR1
(symIte cond)
(\l r -> SomeBVCondLit $ mrgIf cond (toUnionInteger l) (toUnionInteger r))
instance
( forall n. (KnownNat n, 1 <= n) => AllSyms (bv n),
MaySomeBV bv
) =>
AllSyms (SomeBV bv)
where
allSyms = unarySomeBV allSyms allSyms
{-# INLINE allSyms #-}
allSymsS = unarySomeBV allSymsS allSymsS
{-# INLINE allSymsS #-}
-- Synonyms
-- | Type synonym for t'SomeBV' for concrete signed bitvectors.
type SomeIntN = SomeBV IntN
-- | Pattern synonym for t'SomeBV' for concrete signed bitvectors.
pattern SomeIntN :: () => (KnownNat n, 1 <= n) => IntN n -> SomeIntN
pattern SomeIntN a = SomeBV a
-- | Type synonym for t'SomeBV' for concrete unsigned bitvectors.
type SomeWordN = SomeBV WordN
-- | Pattern synonym for t'SomeBV' for concrete unsigned bitvectors.
pattern SomeWordN :: () => (KnownNat n, 1 <= n) => WordN n -> SomeWordN
pattern SomeWordN a = SomeBV a
-- | Type synonym for t'SomeBV' for symbolic signed bitvectors.
type SomeSymIntN = SomeBV SymIntN
-- | Pattern synonym for t'SomeBV' for symbolic signed bitvectors.
pattern SomeSymIntN :: () => (KnownNat n, 1 <= n) => SymIntN n -> SomeSymIntN
pattern SomeSymIntN a = SomeBV a
-- | Type synonym for t'SomeBV' for symbolic unsigned bitvectors.
type SomeSymWordN = SomeBV SymWordN
-- | Pattern synonym for t'SomeBV' for symbolic unsigned bitvectors.
pattern SomeSymWordN :: () => (KnownNat n, 1 <= n) => SymWordN n -> SomeSymWordN
pattern SomeSymWordN a = SomeBV a
-- Construction
-- | Construct a t'SomeBV' with a given run-time bitwidth and a polymorphic
-- value for the underlying bitvector.
unsafeSomeBV ::
forall bv.
Int ->
(forall proxy n. (KnownNat n, 1 <= n) => proxy n -> bv n) ->
SomeBV bv
unsafeSomeBV n i
| n <= 0 = error "unsafeBV: trying to create a bitvector of non-positive size"
| otherwise = case mkPositiveNatRepr (fromIntegral n) of
SomePositiveNatRepr (_ :: NatRepr x) -> SomeBV (i (Proxy @x))
{-# INLINE unsafeSomeBV #-}
-- | Construct a symbolic t'SomeBV' with a given concrete t'SomeBV'. Similar to
-- 'con' but for t'SomeBV'.
--
-- >>> a = bv 8 0x12 :: SomeIntN
-- >>> conBV a :: SomeSymIntN
-- 0x12
conBV ::
forall cbv bv.
( forall n. (KnownNat n, 1 <= n) => Solvable (cbv n) (bv n),
Solvable (cbv 1) (bv 1)
) =>
SomeBV cbv ->
SomeBV bv
conBV (SomeBV (v :: cbv n)) = SomeBV $ con @(cbv n) @(bv n) v
conBV (SomeBVLit i) = SomeBVLit i
-- | View pattern for symbolic t'SomeBV' to see if it contains a concrete value
-- and extract it. Similar to 'conView' but for t'SomeBV'.
--
-- >>> conBVView (bv 8 0x12 :: SomeSymIntN)
-- Just 0x12
-- >>> conBVView (ssymBV 4 "a" :: SomeSymIntN)
-- Nothing
conBVView ::
forall cbv bv.
( forall n. (KnownNat n, 1 <= n) => Solvable (cbv n) (bv n),
Solvable (cbv 1) (bv 1)
) =>
SomeBV bv ->
Maybe (SomeBV cbv)
conBVView (SomeBV (bv :: bv n)) = case conView @(cbv n) bv of
Just c -> Just $ SomeBV c
Nothing -> Nothing
conBVView (SomeBVLit i) = Just $ SomeBVLit i
-- | Pattern synonym for symbolic t'SomeBV' to see if it contains a concrete
-- value and extract it. Similar to 'Grisette.Core.Con' but for t'SomeBV'.
--
-- >>> case (bv 8 0x12 :: SomeSymIntN) of { ConBV c -> c; _ -> error "impossible" }
-- 0x12
pattern ConBV ::
forall cbv bv.
( forall n. (KnownNat n, 1 <= n) => Solvable (cbv n) (bv n),
Solvable (cbv 1) (bv 1)
) =>
SomeBV cbv ->
SomeBV bv
pattern ConBV c <- (conBVView -> Just c)
where
ConBV c = conBV c
-- | Construct a symbolic t'SomeBV' with a given run-time bitwidth and a symbol.
-- Similar to 'sym' but for t'SomeBV'.
--
-- >>> symBV 8 "a" :: SomeSymIntN
-- a
symBV ::
forall cbv bv.
( forall n. (KnownNat n, 1 <= n) => Solvable (cbv n) (bv n),
Solvable (cbv 1) (bv 1)
) =>
Int ->
Symbol ->
SomeBV bv
symBV n s = unsafeSomeBV n $ \(_ :: proxy n) -> sym @(cbv n) s
-- | Construct a symbolic t'SomeBV' with a given run-time bitwidth and an
-- identifier. Similar to 'ssym' but for t'SomeBV'.
--
-- >>> ssymBV 8 "a" :: SomeSymIntN
-- a
ssymBV ::
forall cbv bv.
( forall n. (KnownNat n, 1 <= n) => Solvable (cbv n) (bv n),
Solvable (cbv 1) (bv 1)
) =>
Int ->
Identifier ->
SomeBV bv
ssymBV n s = unsafeSomeBV n $ \(_ :: proxy n) -> ssym @(cbv n) s
-- | Construct a symbolic t'SomeBV' with a given run-time bitwidth, an identifier
-- and an index. Similar to 'isym' but for t'SomeBV'.
--
-- >>> isymBV 8 "a" 1 :: SomeSymIntN
-- a@1
isymBV ::
forall cbv bv.
( forall n. (KnownNat n, 1 <= n) => Solvable (cbv n) (bv n),
Solvable (cbv 1) (bv 1)
) =>
Int ->
Identifier ->
Int ->
SomeBV bv
isymBV n s i = unsafeSomeBV n $ \(_ :: proxy n) -> isym @(cbv n) s i
-- | Generate an arbitrary t'SomeBV' with a given run-time bitwidth.
arbitraryBV ::
forall bv.
(forall n. (KnownNat n, 1 <= n) => Arbitrary (bv n)) =>
Int ->
Gen (SomeBV bv)
arbitraryBV n
| n <= 0 =
error "arbitraryBV: trying to create a bitvector of non-positive size"
| otherwise = case mkPositiveNatRepr (fromIntegral n) of
SomePositiveNatRepr (_ :: NatRepr x) -> do
v <- arbitrary :: Gen (bv x)
return $ SomeBV v
-- Helpers
-- | Lift a unary operation on sized bitvectors that returns anything to
-- t'SomeBV'.
unarySomeBV ::
forall bv r.
(forall n. (KnownNat n, 1 <= n) => bv n -> r) ->
(SomeBVLit -> r) ->
SomeBV bv ->
r
unarySomeBV f _ (SomeBV bv) = f bv
unarySomeBV _ g (SomeBVLit i) = g i
{-# INLINE unarySomeBV #-}
-- | Lift a unary operation on sized bitvectors that returns a bitvector to
-- t'SomeBV'. The result will also be wrapped with t'SomeBV'.
unarySomeBVR1 ::
(forall n. (KnownNat n, 1 <= n) => bv n -> bv n) ->
(SomeBVLit -> SomeBVLit) ->
SomeBV bv ->
SomeBV bv
unarySomeBVR1 f g = unarySomeBV (SomeBV . f) (SomeBVLit . g)
{-# INLINE unarySomeBVR1 #-}
-- | Lift a binary operation on sized bitvectors that returns anything to
-- t'SomeBV'. Crash if the bitwidths do not match.
binSomeBV ::
(forall n. (KnownNat n, 1 <= n) => Num (bv n), MaySomeBV bv) =>
(forall n. (KnownNat n, 1 <= n) => bv n -> bv n -> r) ->
(SomeBVLit -> SomeBVLit -> r) ->
SomeBV bv ->
SomeBV bv ->
r
binSomeBV f _ (SomeBV (l :: bv l)) (SomeBV (r :: bv r)) =
case sameNat (Proxy @l) (Proxy @r) of
Just Refl -> f l r
Nothing -> throw BitwidthMismatch
binSomeBV f _ (SomeBV (l :: bv l)) (SomeBVLit r) = f l $ assignLitBitWidth r
binSomeBV f _ (SomeBVLit l) (SomeBV (r :: bv r)) = f (assignLitBitWidth l) r
binSomeBV _ g (SomeBVLit l) (SomeBVLit r) = g l r
{-# INLINE binSomeBV #-}
-- | Lift a ternary operation on sized bitvectors that returns anything to
-- t'SomeBV'. Crash if the bitwidths do not match.
ternSomeBV ::
(forall n. (KnownNat n, 1 <= n) => Num (bv n), MaySomeBV bv) =>
(forall n. (KnownNat n, 1 <= n) => bv n -> bv n -> bv n -> r) ->
SomeBV bv ->
SomeBV bv ->
SomeBV bv ->
r
ternSomeBV f (SomeBV (a :: bv a)) (SomeBV (b :: bv b)) (SomeBV (c :: bv c)) =
case (sameNat (Proxy @a) (Proxy @b), sameNat (Proxy @a) (Proxy @c)) of
(Just Refl, Just Refl) -> f a b c
_ -> throw BitwidthMismatch
ternSomeBV f a b c =
case assignBitWidth "ternSomeBV" (a, b, c) of
Right (a', b', c') -> ternSomeBV f a' b' c'
Left e -> throw e
{-# INLINE ternSomeBV #-}
-- | Lift a binary operation on sized bitvectors that returns a bitvector to
-- t'SomeBV'. The result will also be wrapped with t'SomeBV'. Crash if the
-- bitwidths do not match.
binSomeBVR1 ::
(forall n. (KnownNat n, 1 <= n) => Num (bv n), MaySomeBV bv) =>
(forall n. (KnownNat n, 1 <= n) => bv n -> bv n -> bv n) ->
(SomeBVLit -> SomeBVLit -> SomeBVLit) ->
SomeBV bv ->
SomeBV bv ->
SomeBV bv
binSomeBVR1 f g = binSomeBV (\a b -> SomeBV $ f a b) (\a b -> SomeBVLit $ g a b)
{-# INLINE binSomeBVR1 #-}
-- | Lift a binary operation on sized bitvectors that returns two bitvectors to
-- t'SomeBV'. The results will also be wrapped with t'SomeBV'. Crash if the
-- bitwidths do not match.
binSomeBVR2 ::
(forall n. (KnownNat n, 1 <= n) => Num (bv n), MaySomeBV bv) =>
(forall n. (KnownNat n, 1 <= n) => bv n -> bv n -> (bv n, bv n)) ->
(SomeBVLit -> SomeBVLit -> (SomeBVLit, SomeBVLit)) ->
SomeBV bv ->
SomeBV bv ->
(SomeBV bv, SomeBV bv)
binSomeBVR2 f g =
binSomeBV
(\a b -> let (x, y) = f a b in (SomeBV x, SomeBV y))
(\a b -> let (x, y) = g a b in (SomeBVLit x, SomeBVLit y))
{-# INLINE binSomeBVR2 #-}
-- | Lift a ternary operation on sized bitvectors that returns a bitvector to
-- t'SomeBV'. The result will also be wrapped with t'SomeBV'. Crash if the
-- bitwidths do not match.
ternSomeBVR1 ::
(forall n. (KnownNat n, 1 <= n) => Num (bv n), MaySomeBV bv) =>
(forall n. (KnownNat n, 1 <= n) => bv n -> bv n -> bv n -> bv n) ->
SomeBV bv ->
SomeBV bv ->
SomeBV bv ->
SomeBV bv
ternSomeBVR1 f = ternSomeBV (\a b c -> SomeBV $ f a b c)
{-# INLINE ternSomeBVR1 #-}
-- | Lift a binary operation on sized bitvectors that returns anything wrapped
-- with 'ExceptT' to t'SomeBV'. If the bitwidths do not match, throw an
-- 'BitwidthMismatch' error to the monadic context.
binSomeBVSafe ::
( MonadError (Either SomeBVException e) m,
TryMerge m,
Mergeable e,
Mergeable r,
forall n. (KnownNat n, 1 <= n) => Num (bv n),
MaySomeBV bv
) =>
(forall n. (KnownNat n, 1 <= n) => bv n -> bv n -> ExceptT e m r) ->
(SomeBVLit -> SomeBVLit -> ExceptT (Either SomeBVException e) m r) ->
SomeBV bv ->
SomeBV bv ->
m r
binSomeBVSafe f _ (SomeBV (l :: bv l)) (SomeBV (r :: bv r)) =
case sameNat (Proxy @l) (Proxy @r) of
Just Refl ->
tryMerge $ runExceptT (f l r) >>= either (throwError . Right) pure
Nothing -> tryMerge $ throwError $ Left BitwidthMismatch
binSomeBVSafe _ g (SomeBVLit l) (SomeBVLit r) =
tryMerge $ runExceptT (g l r) >>= either throwError pure
binSomeBVSafe f g l r =
case assignBitWidth "binSomeBVSafe" (l, r) of
Right (l', r') -> binSomeBVSafe f g l' r'
Left e -> tryMerge $ throwError $ Left e
{-# INLINE binSomeBVSafe #-}
-- | Lift a binary operation on sized bitvectors that returns a bitvector
-- wrapped with 'ExceptT' to t'SomeBV'. The result will also be wrapped with
-- t'SomeBV'.
--
-- If the bitwidths do not match, throw an 'BitwidthMismatch' error to the
-- monadic context.
binSomeBVSafeR1 ::
( MonadError (Either SomeBVException e) m,
TryMerge m,
Mergeable e,
forall n. (KnownNat n, 1 <= n) => Mergeable (bv n),
forall n. (KnownNat n, 1 <= n) => Num (bv n),
MaySomeBV bv
) =>
(forall n. (KnownNat n, 1 <= n) => bv n -> bv n -> ExceptT e m (bv n)) ->
(SomeBVLit -> SomeBVLit -> ExceptT (Either SomeBVException e) m SomeBVLit) ->
SomeBV bv ->
SomeBV bv ->
m (SomeBV bv)
binSomeBVSafeR1 f g =
binSomeBVSafe
(\l r -> mrgFmap SomeBV $ f l r)
(\l r -> mrgFmap SomeBVLit $ g l r)
{-# INLINE binSomeBVSafeR1 #-}
-- | Lift a binary operation on sized bitvectors that returns two bitvectors
-- wrapped with 'ExceptT' to t'SomeBV'. The results will also be wrapped with
-- t'SomeBV'.
--
-- If the bitwidths do not match, throw an 'BitwidthMismatch' error to the
-- monadic context.
binSomeBVSafeR2 ::
( MonadError (Either SomeBVException e) m,
TryMerge m,
Mergeable e,
forall n. (KnownNat n, 1 <= n) => Mergeable (bv n),
forall n. (KnownNat n, 1 <= n) => Num (bv n),
MaySomeBV bv
) =>
( forall n.
(KnownNat n, 1 <= n) =>
bv n ->
bv n ->
ExceptT e m (bv n, bv n)
) ->
( SomeBVLit ->
SomeBVLit ->
ExceptT (Either SomeBVException e) m (SomeBVLit, SomeBVLit)
) ->
SomeBV bv ->
SomeBV bv ->
m (SomeBV bv, SomeBV bv)
binSomeBVSafeR2 f g =
binSomeBVSafe
(\l r -> mrgFmap (bimap SomeBV SomeBV) $ f l r)
(\l r -> mrgFmap (bimap SomeBVLit SomeBVLit) $ g l r)
{-# INLINE binSomeBVSafeR2 #-}