sdl3-bindgen-sys-0.0.0.1: runtime-cexpr/C/Type.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE PartialTypeSignatures #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
module C.Type (
-- * C types
Type (..),
ArithmeticType (..),
IntegralType (..),
CharLikeType (..),
IntLikeType (..),
Sign (..),
FloatingType (..),
IntegerConversionRank (..),
intLikeTypeSign,
charLikeTypeSizeInBits,
intLikeTypeSizeInBits,
intLikeTypeConversionRank,
intLikeTypeFitsInInt,
showTypeAsCType,
-- * Platform
Platform (..),
WordWidth (..),
OS (..),
hostPlatform,
-- * Singletons for C types
SType (..),
SArithmeticType (..),
SIntegralType (..),
SCharLikeType (..),
SIntLikeType (..),
SFloatingType (..),
-- ** Promotion
promoteType,
promoteArithmeticType,
promoteIntegralType,
promoteCharLikeType,
promoteIntLikeType,
promoteFloatingType,
-- ** Demotion
demoteType,
demoteArithmeticType,
demoteIntegralType,
demoteCharLikeType,
demoteIntLikeType,
demoteFloatingType,
-- ** Utilities
witnessType,
witnessArithmeticType,
witnessFloatingType,
witnessIntegralType,
witnessCharLike,
witnessIntLike,
) where
import Data.GADT.Compare
import Data.Kind qualified as Hs
import Data.Semigroup (Arg (..))
import Data.Type.Equality
import Foreign.C.Types
import Foreign.Ptr qualified as Foreign (Ptr)
import Foreign.Storable (sizeOf)
import GHC.Generics (Generic)
import System.Info qualified (os)
--------------------------------------------------------------------------------
data Type a
= Void
| Arithmetic !ArithmeticType
| Ptr !a
deriving stock (Eq, Foldable, Functor, Generic, Ord, Show, Traversable)
data ArithmeticType
= Integral !IntegralType
| FloatLike !FloatingType
deriving stock (Eq, Generic, Ord, Show)
data FloatingType = FloatType | DoubleType
deriving stock (Eq, Generic, Ord, Show)
data IntegralType
= Bool
| CharLike !CharLikeType
| IntLike !IntLikeType
deriving stock (Eq, Generic, Ord, Show)
data CharLikeType = Char | SChar | UChar
deriving stock (Eq, Generic, Ord, Show)
data Sign = Signed | Unsigned
deriving stock (Eq, Generic, Ord, Show)
data IntLikeType
= Short !Sign
| Int !Sign
| Long !Sign
| LongLong !Sign
| PtrDiff
deriving stock (Eq, Generic, Ord, Show)
--------------------------------------------------------------------------------
data WordWidth = WordWidth32 | WordWidth64
deriving stock (Eq, Generic, Ord, Show)
wordWidthInBits :: WordWidth -> Word
wordWidthInBits = \case
WordWidth32 -> 32
WordWidth64 -> 64
data OS = Windows | Posix
deriving stock (Eq, Generic, Ord, Show)
data Platform = Platform
{ platformWordWidth :: !WordWidth
, platformOS :: !OS
}
deriving stock (Eq, Generic, Show)
hostPlatform :: Platform
hostPlatform =
Platform
{ platformWordWidth =
case sizeOf @(Foreign.Ptr ()) undefined of
4 -> WordWidth32
8 -> WordWidth64
w -> error $ "hostPlatform: unsupported word width (" ++ show (8 * w) ++ " bits)"
, platformOS =
case System.Info.os of
"mingw32" -> Windows
_ -> Posix
}
newtype IntegerConversionRank = IntegerConversionRank Rational
deriving stock (Eq, Generic, Ord, Show)
intLikeTypeSign :: IntLikeType -> Sign
intLikeTypeSign = \case
Short s -> s
Int s -> s
Long s -> s
LongLong s -> s
PtrDiff -> Signed
charLikeTypeSizeInBits :: Platform -> CharLikeType -> Word
charLikeTypeSizeInBits _ = \case
-- NB: this would need to change if we wanted to support
-- platforms on which char is not 8 bits wide.
Char -> 8
SChar -> 8
UChar -> 8
intLikeTypeSizeInBits :: Platform -> IntLikeType -> Word
intLikeTypeSizeInBits plat i =
case platformWordWidth plat of
WordWidth32 ->
case i of
Short{} -> 16
Int{} -> 32
Long{} -> 32
LongLong{} -> 64
PtrDiff -> 32
WordWidth64 ->
case i of
Short{} -> 16
Int{} -> 32
Long{} ->
case platformOS plat of
Windows -> 32
Posix -> 64
LongLong{} -> 64
PtrDiff -> 64
intLikeTypeConversionRank :: Platform -> IntLikeType -> IntegerConversionRank
intLikeTypeConversionRank plat =
IntegerConversionRank . \case
-- Rules for integer conversion ranks:
--
-- 1. No two signed integer types other than char and signed char (if char is signed)
-- have the same rank, even if they have the same representation.
-- 2. The rank of a signed integer type is greater than the rank of any
-- signed integer type with a smaller width.
-- 3. The ranks of char/short/int/long/long long increase in order.
-- 4. The rank of any unsigned integer type equals the rank of the
-- corresponding signed integer type.
-- 5. The rank of any standard integer type is greater than the rank of
-- any extended integer type with the same width.
-- 6. The rank of bool is less than the rank of all standard integer types.
-- 7. The rank of any extended signed integer type relative to another extended
-- signed integer type with the same width is implementation-defined.
-- Standard integer types.
-- Implement (3), ignoring sign as per (4).
Short{} -> 3
Int{} -> 4
Long{} -> 5
LongLong{} -> 6
-- Extended types.
_extended_ty ->
-- The following logic comes from (1) and (5), which dictate that the
-- integer conversion rank of ptrdiff_t and size_t must be:
--
-- (a) strictly greater than the integer conversion rank of any
-- standard integer type whose size is less than the word width,
-- (b) strictly less than the integer conversion rank of any standard
-- integer type whose size is greater than or equal to the word width.
case minimum
[ Arg rk ty
| ty <- [Short Signed, Int Signed, Long Signed, LongLong Signed]
, let sz = intLikeTypeSizeInBits plat ty
rk = intLikeTypeConversionRank plat ty
, sz >= wordWidthInBits (platformWordWidth plat)
] of
Arg (IntegerConversionRank rk) _ ->
rk - 0.5
-- 0.5 is an arbitrary value in the open interval ]0,1[
--
-- This assumes that the standard integer types are given
-- integral integer conversion ranks.
-- | Does the given 'IntLikeType' fit inside the (signed) @int@ type
-- on this platform?
intLikeTypeFitsInInt :: Platform -> IntLikeType -> Bool
intLikeTypeFitsInInt plat ty =
-- TODO: this logic is questionable, as in theory I think we could have
-- an 'IntLike' type of a small size but of an entirely distinct range,
-- e.g. an 8-bit unsigned integer type that can store values in the range
-- [2^32, 2^32+2^8-1].
case intLikeTypeSign ty of
Signed ->
sz <= intSz
Unsigned ->
sz < intSz
where
sz, intSz :: Word
sz = intLikeTypeSizeInBits plat ty
intSz = intLikeTypeSizeInBits plat (Int Signed)
--------------------------------------------------------------------------------
showTypeAsCType :: (Show a) => Type a -> String -> String
showTypeAsCType ty s =
case ty of
Void -> "void" +++ s
Arithmetic a -> showArithmeticTypeAsCType a +++ s
Ptr a -> addStar (show a) ++ s
where
x +++ "" = x
x +++ y = x ++ " " ++ y
addStar x@(_ : _)
| last x == '*' =
x ++ "*"
addStar x =
x ++ " *"
showArithmeticTypeAsCType :: ArithmeticType -> String
showArithmeticTypeAsCType = \case
Integral i ->
showIntegralTypeAsCType i
FloatLike f ->
case f of
FloatType -> "float"
DoubleType -> "double"
showIntegralTypeAsCType :: IntegralType -> String
showIntegralTypeAsCType = \case
Bool -> "bool"
CharLike c ->
case c of
Char -> "char"
SChar -> "signed char"
UChar -> "unsigned char"
IntLike i ->
showIntLikeTypeAsCType i
showIntLikeTypeAsCType :: IntLikeType -> String
showIntLikeTypeAsCType = \case
Short s -> withSign s "short"
Int s -> withSign s "int"
Long s -> withSign s "long"
LongLong s -> withSign s "long long"
PtrDiff -> "ptrdiff_t"
where
withSign s = case s of
Signed -> id
Unsigned -> ("unsigned " ++)
--------------------------------------------------------------------------------
-- Singletons
type SType :: (Hs.Type -> Hs.Type) -> Hs.Type -> Hs.Type
data SType rec a where
SVoid :: SType rec ()
SArithmetic :: !(SArithmeticType ty) -> SType rec ty
SPtr :: rec ty -> SType rec (Foreign.Ptr ty)
deriving stock instance (forall x. Show (rec x)) => Show (SType rec a)
data SArithmeticType ty where
SIntegral :: !(SIntegralType ty) -> SArithmeticType ty
SFloatLike :: !(SFloatingType ty) -> SArithmeticType ty
deriving stock instance Show (SArithmeticType ty)
data SFloatingType ty where
SFloatType :: SFloatingType CFloat
SDoubleType :: SFloatingType CDouble
deriving stock instance Show (SFloatingType ty)
data SIntegralType ty where
SBool :: SIntegralType CBool
SCharLike :: !(SCharLikeType ty) -> SIntegralType ty
SIntLike :: !(SIntLikeType ty) -> SIntegralType ty
deriving stock instance Show (SIntegralType ty)
data SCharLikeType ty where
S_Char :: SCharLikeType CChar
S_SChar :: SCharLikeType CSChar
S_UChar :: SCharLikeType CUChar
deriving stock instance Show (SCharLikeType ty)
data SIntLikeType ty where
SShort :: SIntLikeType CShort
SUShort :: SIntLikeType CUShort
SInt :: SIntLikeType CInt
SUInt :: SIntLikeType CUInt
SLong :: SIntLikeType CLong
SULong :: SIntLikeType CULong
SLongLong :: SIntLikeType CLLong
SULongLong :: SIntLikeType CULLong
SPtrDiff :: SIntLikeType CPtrdiff
-- NB: make sure to update 'GEq SIntLikeType' when updating this datatype
deriving stock instance Show (SIntLikeType ty)
instance (GEq rec) => GEq (SType rec) where
geq SVoid SVoid = Just Refl
geq (SArithmetic a) (SArithmetic b) = geq a b
geq (SPtr a) (SPtr b) =
case geq a b of
Just Refl -> Just Refl
Nothing -> Nothing
geq _ _ = Nothing
instance GEq SArithmeticType where
geq (SIntegral a) (SIntegral b) = geq a b
geq (SFloatLike a) (SFloatLike b) = geq a b
geq _ _ = Nothing
instance GEq SFloatingType where
geq SFloatType SFloatType = Just Refl
geq SDoubleType SDoubleType = Just Refl
geq _ _ = Nothing
instance GEq SCharLikeType where
geq S_Char S_Char = Just Refl
geq S_SChar S_SChar = Just Refl
geq S_UChar S_UChar = Just Refl
geq _ _ = Nothing
instance GEq SIntegralType where
geq SBool SBool = Just Refl
geq (SCharLike a) (SCharLike b) = geq a b
geq (SIntLike a) (SIntLike b) = geq a b
geq _ _ = Nothing
instance GEq SIntLikeType where
geq SShort SShort = Just Refl
geq SUShort SUShort = Just Refl
geq SInt SInt = Just Refl
geq SUInt SUInt = Just Refl
geq SLong SLong = Just Refl
geq SULong SULong = Just Refl
geq SLongLong SLongLong = Just Refl
geq SULongLong SULongLong = Just Refl
geq SPtrDiff SPtrDiff = Just Refl
geq _ _ = Nothing
promoteType
:: (a -> (forall ty. rec ty -> r) -> r)
-> Type a
-> (forall ty. (Ord ty, Show ty) => SType rec ty -> r)
-> r
promoteType recur ty f = case ty of
Void -> f SVoid
Arithmetic i -> promoteArithmeticType i (f . SArithmetic)
Ptr p -> recur p (f . SPtr)
promoteArithmeticType
:: ArithmeticType -> (forall ty. (Ord ty, Show ty) => SArithmeticType ty -> r) -> r
promoteArithmeticType ty f = case ty of
Integral t -> promoteIntegralType t (f . SIntegral)
FloatLike t -> promoteFloatingType t (f . SFloatLike)
promoteFloatingType
:: FloatingType -> (forall ty. (Ord ty, Show ty) => SFloatingType ty -> r) -> r
promoteFloatingType ty f = case ty of
FloatType -> f SFloatType
DoubleType -> f SDoubleType
promoteIntegralType
:: IntegralType -> (forall ty. (Show ty, Integral ty) => SIntegralType ty -> r) -> r
promoteIntegralType ty f = case ty of
Bool -> f SBool
CharLike c -> promoteCharLikeType c (f . SCharLike)
IntLike i -> promoteIntLikeType i (f . SIntLike)
promoteCharLikeType
:: CharLikeType -> (forall ty. (Show ty, Integral ty) => SCharLikeType ty -> r) -> r
promoteCharLikeType ty f = case ty of
Char -> f S_Char
UChar -> f S_UChar
SChar -> f S_SChar
promoteIntLikeType
:: IntLikeType -> (forall ty. (Show ty, Integral ty) => SIntLikeType ty -> r) -> r
promoteIntLikeType ty f = case ty of
Short s ->
case s of
Signed -> f SShort
Unsigned -> f SUShort
Int s ->
case s of
Signed -> f SInt
Unsigned -> f SUInt
Long s ->
case s of
Signed -> f SLong
Unsigned -> f SULong
LongLong s ->
case s of
Signed -> f SLongLong
Unsigned -> f SULongLong
PtrDiff -> f SPtrDiff
demoteType :: (forall ty'. rec ty' -> a) -> SType rec ty -> Type a
demoteType recur = \case
SVoid -> Void
SArithmetic a -> Arithmetic $ demoteArithmeticType a
SPtr a -> Ptr $ recur a
demoteArithmeticType :: SArithmeticType ty -> ArithmeticType
demoteArithmeticType = \case
SIntegral i -> Integral $ demoteIntegralType i
SFloatLike f -> FloatLike $ demoteFloatingType f
demoteIntegralType :: SIntegralType ty -> IntegralType
demoteIntegralType = \case
SBool -> Bool
SCharLike c -> CharLike $ demoteCharLikeType c
SIntLike i -> IntLike $ demoteIntLikeType i
demoteFloatingType :: SFloatingType ty -> FloatingType
demoteFloatingType = \case
SFloatType -> FloatType
SDoubleType -> DoubleType
demoteCharLikeType :: SCharLikeType ty -> CharLikeType
demoteCharLikeType = \case
S_Char -> Char
S_UChar -> UChar
S_SChar -> SChar
demoteIntLikeType :: SIntLikeType ty -> IntLikeType
demoteIntLikeType = \case
SShort -> Short Signed
SUShort -> Short Unsigned
SInt -> Int Signed
SUInt -> Int Unsigned
SLong -> Long Signed
SULong -> Long Unsigned
SLongLong -> LongLong Signed
SULongLong -> LongLong Unsigned
SPtrDiff -> PtrDiff
witnessType
:: forall c ty rec r
. ( forall x. c (Foreign.Ptr x)
, c CChar
, c CSChar
, c CUChar
, c CShort
, c CUShort
, c CInt
, c CUInt
, c CLong
, c CULong
, c CLLong
, c CULLong
, c CPtrdiff
, c CSize
, c CBool
, c CFloat
, c CDouble
, c ()
)
=> (forall ty'. rec ty' -> ((c ty') => r) -> r)
-> SType rec ty
-> ((c ty) => r)
-> r
witnessType recur ty f =
case ty of
SVoid -> f
SArithmetic i -> witnessArithmeticType @c i f
SPtr p -> recur p f
witnessArithmeticType
:: forall c ty r
. ( c CChar
, c CSChar
, c CUChar
, c CShort
, c CUShort
, c CInt
, c CUInt
, c CLong
, c CULong
, c CLLong
, c CULLong
, c CPtrdiff
, c CSize
, c CBool
, c CFloat
, c CDouble
)
=> SArithmeticType ty -> ((c ty) => r) -> r
witnessArithmeticType ty f =
case ty of
SIntegral i -> witnessIntegralType @c i f
SFloatLike k -> witnessFloatingType @c k f
witnessFloatingType
:: forall c ty r
. (c CFloat, c CDouble)
=> SFloatingType ty -> ((c ty) => r) -> r
witnessFloatingType ty f =
case ty of
SFloatType -> f
SDoubleType -> f
witnessIntegralType
:: forall c ty r
. ( c CChar
, c CSChar
, c CUChar
, c CShort
, c CUShort
, c CInt
, c CUInt
, c CLong
, c CULong
, c CLLong
, c CULLong
, c CPtrdiff
, c CSize
, c CBool
)
=> SIntegralType ty -> ((c ty) => r) -> r
witnessIntegralType ty f =
case ty of
SBool -> f
SCharLike c -> witnessCharLike @c c f
SIntLike i -> witnessIntLike @c i f
witnessCharLike
:: forall c ty r
. (c CChar, c CSChar, c CUChar)
=> SCharLikeType ty -> ((c ty) => r) -> r
witnessCharLike ty f =
case ty of
S_Char -> f
S_SChar -> f
S_UChar -> f
witnessIntLike
:: forall c ty r
. ( c CShort
, c CUShort
, c CInt
, c CUInt
, c CLong
, c CULong
, c CLLong
, c CULLong
, c CPtrdiff
, c CSize
)
=> SIntLikeType ty -> ((c ty) => r) -> r
witnessIntLike ty f =
case ty of
SShort -> f
SUShort -> f
SInt -> f
SUInt -> f
SLong -> f
SULong -> f
SLongLong -> f
SULongLong -> f
SPtrDiff -> f