hs-bindgen-1.0.0.0: src-internal/HsBindgen/IR/C/Type.hs
-- | C types (use sites)
--
-- This module should only be used within the @HsBindgen.IR@ hierarchy. From
-- outside the @HsBindgen.IR@ hierarchy, "HsBindgen.IR.C" should be used.
--
-- Within @HsBindgen.IR@, all modules aside from "HsBindgen.IR.C" should import
-- this module qualified for consistency.
--
-- > import HsBindgen.IR.C.Type qualified as C
module HsBindgen.IR.C.Type (
-- * Definition
Type
, TypeF(
TypePrim
, TypeComplex
, TypeRef
, TypeEnum
, TypeMacro
, TypeTypedef
, TypePointers
, TypeConstArray
, TypeIncompleteArray
, TypeFun
, TypeVoid
, TypeBlock
, TypeQual
, TypeExtBinding
)
, TypeFunArg
, TypeFunArgF(..)
-- ** Qualifiers
, TypeQual(..)
-- ** References
, Ref(..)
, EnumRef
, MacroRef(..)
, TypedefRef
, ExtBindingRef
, AnonRef (..)
, anonRefType
-- * Normal forms
, Normalize(..)
, getCanonicalType
-- * Queries
, depsOfType
, depsOfTypeFunArg
, hasUnsupportedType
, getAllFunTypeIndirections
, getAllFunTypes
, getFirstFunTypeIndirection
-- * Classification
, isVoid
, isCanonicalTypeComplex
, isCanonicalTypeFunction
, isCanonicalTypeStruct
, isCanonicalTypeUnion
, isCanonicalTypeArray
, isErasedTypeConstQualified
-- * CoercePass
, CoercePassExtBindingRef(..)
) where
import Data.Foldable qualified as Foldable
import Data.Set qualified as Set
import HsBindgen.Errors (panicPure)
import HsBindgen.Frontend.Analysis
import HsBindgen.Imports
import HsBindgen.IR.C.Naming qualified as C
import HsBindgen.IR.Pass.Ann
import HsBindgen.IR.Pass.Definition
import HsBindgen.IR.Pass.ExtBinding
import HsBindgen.IR.Pass.Id
import HsBindgen.IR.Pass.Macro
import HsBindgen.Language.C qualified as C
{-------------------------------------------------------------------------------
Definition
-------------------------------------------------------------------------------}
type Type = FullType
-- | C type (use site)
data TypeF (tag :: TypeTag) (p :: Pass) =
-- | Primitive type
TypePrim C.PrimType
-- | Complex type (such as @float complex@)
| TypeComplex C.PrimType
-- | Reference to named type other than an @enum@, macro type or @typedef@
| TypeRef (Id p)
-- | Reference to an @enum@
--
-- NOTE: strictness annotations help GHC infer redundant pattern matches
| TypeEnum !(TypeEnumRefF tag p)
-- | Reference to a macro type
--
-- NOTE: strictness annotations help GHC infer redundant pattern matches
| TypeMacro !(TypeMacroRefF tag p)
-- | Reference to a @typedef@
--
-- NOTE: strictness annotations help GHC infer redundant pattern matches
| TypeTypedef !(TypedefRefF tag p)
-- | Pointer
--
-- This is /one/ layer of indirection. See also 'TypePointers'.
| TypeUnsafePointer (TypeF tag p)
-- | Array of constant size
| TypeConstArray Natural (TypeF tag p)
-- | Array of unknown size
--
-- Arrays normally have a known size, but not always:
--
-- * Arrays of unknown size are allowed as function arguments; such arrays
-- are interpreted as pointers.
-- * Arrays of unknown size may be declared for externs; this is considered
-- an incomplete type.
-- * Structs may contain an array of undefined size as their last field,
-- known as a "flexible array member" (FLAM).
--
-- We treat the FLAM case separately.
--
-- See <https://en.cppreference.com/w/c/language/array#Arrays_of_unknown_size>
| TypeIncompleteArray (TypeF tag p)
-- | Functions
| TypeFun [TypeFunArgF tag p] (TypeF tag p)
-- | Void
--
-- NOTE: @void@ has different meanings in C, depending on context.
| TypeVoid
-- | Blocks
--
-- Blocks are a clang-specific C extension.
--
-- See <https://clang.llvm.org/docs/BlockLanguageSpec.html>
| TypeBlock (TypeF tag p)
-- | Qualified type (such as @const@)
--
-- NOTE: strictness annotations help GHC infer redundant pattern matches
| TypeQual !(TypeQualifierF tag p) (TypeF tag p)
-- | Type with an external binding
--
-- NOTE: strictness annotations help GHC infer redundant pattern matches
| TypeExtBinding !(TypeExtBindingRefF tag p)
deriving stock Generic
deriving stock instance ValidTypeTag tag p => Eq (TypeF tag p)
deriving stock instance ValidTypeTag tag p => Ord (TypeF tag p)
deriving stock instance ValidTypeTag tag p => Show (TypeF tag p)
type TypeFunArg = TypeFunArgF Full
-- | C types in function argument positions
--
-- Separate types are used to represent function arguments in declarations
-- ('HsBindgen.IR.C.Decl.FunctionArg') and function arguments in types.
--
-- * An argument in a declaration may have a name, while type arguments do not
-- have names.
-- * We translate declaration arguments to Haskell, while recursively
-- translating type arguments is not necessary.
--
-- Both of these types use the @TypeFunArg@ annotation, however.
data TypeFunArgF (tag :: TypeTag) (p :: Pass) = TypeFunArgF {
typ :: TypeF tag p
, ann :: Ann "TypeFunArg" p
}
deriving stock (Generic)
deriving stock instance ValidTypeTag tag p => Eq (TypeFunArgF tag p)
deriving stock instance ValidTypeTag tag p => Show (TypeFunArgF tag p)
deriving stock instance ValidTypeTag tag p => Ord (TypeFunArgF tag p)
-- | Map 'TypeF's from one tag to another
mapTypeF :: forall tag tag' p.
-- | What to do when encountering a typedef reference.
(TypedefRefF tag p -> TypeF tag' p)
-- | What to do when encountering a type qualifier.
-> (TypeQualifierF tag p -> TypeF tag p -> TypeF tag' p)
-- | What to do when encountering an external binding reference.
-> (TypeExtBindingRefF tag p -> TypeF tag' p)
-- | What to do when encountering a macro type reference.
-> (TypeMacroRefF tag p -> TypeF tag' p)
-- | What to do when encountering an enum reference.
-> (TypeEnumRefF tag p -> TypeF tag' p)
-> TypeF tag p
-> TypeF tag' p
mapTypeF fTypedefRef fQual fExtBindingRef fMacroRef fEnumRef = go
where
go :: TypeF tag p -> TypeF tag' p
go ty = case ty of
TypePrim pt -> TypePrim pt
TypeComplex pt -> TypeComplex pt
TypeRef declId -> TypeRef declId
TypeEnum ref -> fEnumRef ref
TypeMacro ref -> fMacroRef ref
TypeTypedef ref -> fTypedefRef ref
TypePointers n t -> TypePointers n $ go t
TypeConstArray n t -> TypeConstArray n $ go t
TypeIncompleteArray t -> TypeIncompleteArray (go t)
TypeFun args res -> TypeFun (fmap goTypeFunArg args) (go res)
TypeVoid -> TypeVoid
TypeBlock t -> TypeBlock (go t)
TypeQual q t -> fQual q t
TypeExtBinding ref -> fExtBindingRef ref
goTypeFunArg :: TypeFunArgF tag p -> TypeFunArgF tag' p
goTypeFunArg arg = TypeFunArgF {
typ = go arg.typ
, ann = arg.ann
}
{-------------------------------------------------------------------------------
Qualifiers
-------------------------------------------------------------------------------}
data TypeQual =
QualConst
deriving stock (Eq, Generic, Ord, Show)
{-------------------------------------------------------------------------------
References
-------------------------------------------------------------------------------}
-- | A reference (by name) to another type, annotated with an underlying type
--
-- Reference types include:
--
-- * struct reference
-- * union reference
-- * enum reference
-- * typedef reference
-- * macro typedef reference
-- * external binding reference
--
-- See 'TypedefRef' and 'ExtBindingRef' for examples.
data Ref a (p :: Pass) = Ref {
-- | The reference type: a name
--
-- NOTE: strictness annotations help GHC infer redundant pattern matches
name :: !a
-- | The underlying type.
--
-- NOTE: the underlying type can arbitrarily reference other types,
-- including references that we have not parsed, mangled, modified, resolved
-- (binding specs), etc. Use the underlying type with care!
, underlying :: Type p
}
deriving stock (Eq, Generic, Ord, Show)
-- | @enum@ reference
--
-- > enum E : u_int { e };
-- > extern enum E x;
--
-- The type of the global variable @x@ is roughly:
--
-- > Ref { ref = "E", underlying = TypePrim u_int }
type EnumRef p = Ref (Id p) p
-- | A reference to a macro use site.
--
-- Structurally similar to 'Ref' but the 'underlying' field is driven by the
-- 'MacroUnderlying' associated type family, so it can be a placeholder (@()@)
-- during the
-- 'HsBindgen.Frontend.Pass.ReparseMacroExpansions.IsPass.ReparseMacroExpansions'
-- pass and the real @'Type' p@ after the pass is finished.
--
-- For example, if we have this C code:
--
-- > #define T int
-- > extern T x;
--
-- The type of the global variable @x@ is roughly:
--
-- > MacroRef { name = macroIdOfT, underlying = TypePrim int }
data MacroRef p = MacroRef {
name :: !(MacroId p)
, underlying :: !(MacroUnderlying p)
}
deriving stock (Generic)
deriving stock instance
(Eq (MacroId p), Eq (MacroUnderlying p))
=> Eq (MacroRef p)
deriving stock instance
(Ord (MacroId p), Ord (MacroUnderlying p))
=> Ord (MacroRef p)
deriving stock instance
(Show (MacroId p), Show (MacroUnderlying p))
=> Show (MacroRef p)
-- | @typedef@ reference
--
-- For example, if we have this C code:
--
-- > typedef int T;
-- > extern T x;
--
-- The type of the global variable @x@ is roughly:
--
-- > Ref { name = "T", underlying = TypePrim int }
type TypedefRef p = Ref (Id p) p
-- | External binding reference
--
-- For example, if we have this C code:
--
-- > struct S {};
-- > extern S x;
--
-- The type of the global variable @x@ is roughly:
--
-- > Ref { name = ResolvedBinding "S", underlying = TypeRef ("S", StructKind) }
type ExtBindingRef p = Ref (ExtBinding p) p
-- | A reference to an anonymous struct or union
data AnonRef p =
AnonRef (Id p)
-- NOTE: strictness annotations help GHC infer redundant pattern matches
| AnonExtBinding !(ExtBindingRef p)
anonRefType :: AnonRef p -> Type p
anonRefType = \case
AnonRef ref -> TypeRef ref
AnonExtBinding ext -> TypeExtBinding ext
deriving instance (Eq (Id p), Eq (ExtBindingRef p)) => Eq (AnonRef p)
deriving instance (Show (Id p), Show (ExtBindingRef p)) => Show (AnonRef p)
{-------------------------------------------------------------------------------
Normal forms
Trees That Grow, but used as Trees That Shrink. Setting these type families to
'Void' makes it impossible to construct or match on some of the constructors
in the 'TypeF' datatype.
-------------------------------------------------------------------------------}
data TypeTag =
-- | Full C type, includes all C type constructs
Full
-- | No @typedef@s, external binding specification references, macro-defined
-- types, or @enum@s
| Erased
-- | All sugar (typedefs and qualifiers like @const@) removed
| Canonical
type FullType = TypeF Full
type ErasedType = TypeF Erased
type CanonicalType = TypeF Canonical
-- | Normal forms of types
class ( PassAnn p
, PassExtBinding p
, PassId p
, PassMacro p
, Show (TypeEnumRefF tag p)
, Eq (TypeEnumRefF tag p)
, Ord (TypeEnumRefF tag p)
, Show (TypeMacroRefF tag p)
, Eq (TypeMacroRefF tag p)
, Ord (TypeMacroRefF tag p)
, Show (TypedefRefF tag p)
, Eq (TypedefRefF tag p)
, Ord (TypedefRefF tag p)
, Show (TypeQualifierF tag p)
, Eq (TypeQualifierF tag p)
, Ord (TypeQualifierF tag p)
, Show (TypeExtBindingRefF tag p)
, Eq (TypeExtBindingRefF tag p)
, Ord (TypeExtBindingRefF tag p)
) => ValidTypeTag (tag :: TypeTag) (p :: Pass) where
type family TypeEnumRefF tag p :: Star
type family TypeMacroRefF tag p :: Star
type family TypedefRefF tag p :: Star
type family TypeQualifierF tag p :: Star
type family TypeExtBindingRefF tag p :: Star
instance (
PassAnn p
, PassExtBinding p
, PassId p
, PassMacro p
) => ValidTypeTag Full p where
type instance TypeEnumRefF Full p = EnumRef p
type instance TypeMacroRefF Full p = MacroRef p
type instance TypedefRefF Full p = TypedefRef p
type instance TypeQualifierF Full p = TypeQual
type instance TypeExtBindingRefF Full p = ExtBindingRef p
instance (
PassAnn p
, PassExtBinding p
, PassId p
, PassMacro p
) => ValidTypeTag Erased p where
type instance TypeEnumRefF Erased p = Void
type instance TypeMacroRefF Erased p = Void
type instance TypedefRefF Erased p = Void
type instance TypeQualifierF Erased p = TypeQual
type instance TypeExtBindingRefF Erased p = Void
instance (
PassAnn p
, PassExtBinding p
, PassId p
, PassMacro p
) => ValidTypeTag Canonical p where
type instance TypeEnumRefF Canonical p = Void
type instance TypeMacroRefF Canonical p = Void
type instance TypedefRefF Canonical p = Void
type instance TypeQualifierF Canonical p = Void
type instance TypeExtBindingRefF Canonical p = Void
{-------------------------------------------------------------------------------
Pattern synonyms for safe pointer handling
-------------------------------------------------------------------------------}
-- | Bidirectional pattern synonym for N layers of pointer indirection
--
-- This pattern can be used both for matching and construction.
--
-- Examples (matching):
-- * @TypePointers 1 inner@ matches @TypeUnsafePointer inner@
-- * @TypePointers 2 inner@ matches @TypeUnsafePointer (TypeUnsafePointer inner)@
-- * @TypePointers 3 inner@ matches @TypeUnsafePointer (TypeUnsafePointer (TypeUnsafePointer inner))@
--
-- Examples (construction):
-- * @TypePointers 1 someType@ creates @TypeUnsafePointer someType@
-- * @TypePointers 2 someType@ creates @TypeUnsafePointer (TypeUnsafePointer someType)@
--
-- The inner type can be anything (TypeFun, TypeRef, TypePrim, etc.).
pattern TypePointers :: Int -> TypeF tag p -> TypeF tag p
pattern TypePointers n inner <- (stripPointersF -> Just (n, inner))
where
TypePointers n inner = buildPointersF n inner
-- | Helper for 'TypePointers' pattern synonym (matching direction)
--
-- Strips all pointer layers and returns the count and inner type. Returns
-- 'Nothing' if there are no pointer layers.
stripPointersF :: TypeF tag p -> Maybe (Int, TypeF tag p)
stripPointersF = go 0
where
go :: Int -> TypeF tag p -> Maybe (Int, TypeF tag p)
go !n (TypeUnsafePointer inner) = go (n + 1) inner
go !n inner
| n > 0 = Just (n, inner)
| otherwise = Nothing
-- | Helper for 'TypePointers' pattern synonym (construction direction)
--
-- Builds N layers of pointers around an inner type.
buildPointersF :: Int -> TypeF tag p -> TypeF tag p
buildPointersF n inner
| n <= 0 = inner
| otherwise = TypeUnsafePointer (buildPointersF (n - 1) inner)
-- | COMPLETE pragma to ensure exhaustiveness checking works
--
-- This tells GHC that pattern matching on these patterns (instead of the raw
-- @TypeUnsafePointer@) is complete and exhaustive.
{-# COMPLETE
TypePrim
, TypeComplex
, TypeRef
, TypeEnum
, TypeMacro
, TypeTypedef
, TypePointers
, TypeConstArray
, TypeIncompleteArray
, TypeFun
, TypeVoid
, TypeBlock
, TypeQual
, TypeExtBinding
#-}
_completePragmaCoversAllCases :: TypeF tag p -> ()
_completePragmaCoversAllCases = \case
TypePrim{} -> ()
TypeComplex{} -> ()
TypeRef{} -> ()
TypeEnum{} -> ()
TypeMacro{} -> ()
TypeTypedef{} -> ()
TypeUnsafePointer{} -> ()
TypeConstArray{} -> ()
TypeIncompleteArray{} -> ()
TypeFun{} -> ()
TypeVoid{} -> ()
TypeBlock{} -> ()
TypeQual{} -> ()
TypeExtBinding{} -> ()
{-------------------------------------------------------------------------------
Computing normal forms
-------------------------------------------------------------------------------}
-- | Normal-form computation.
--
-- Normalization recurses into 'MacroRef.underlying', which only makes sense if
-- 'MacroUnderlying p ~ Type p'. During the
-- 'HsBindgen.Frontend.Pass.ReparseMacroExpansions.IsPass.ReparseMacroExpansions'
-- pass, the 'MacroUnderlying' is temporarily set to @()@.
class Normalize tag tag' where
normalize :: (
MacroUnderlying p ~ Type p
) => TypeF tag p -> TypeF tag' p
instance Normalize tag tag where
normalize = id
-- | Erase @typedef@s
--
-- The algorithm to erase @typedef@s is simple. Replace any references to
-- @typedef@s we find by the definitions of these @typedef@s. The @typedef@
-- definitions that we inline this way can contain references to other
-- @typedef@s, but they can not construct infinitely long types, so this
-- algorithm will terminate sooner or later. In practice, @typedef@ \"chains\"
-- are probably not that long, so we do not expect this algorithm to have
-- problematic performance.
instance Normalize Full Erased where
normalize :: forall p.
MacroUnderlying p ~ Type p
=> TypeF Full p
-> TypeF Erased p
normalize = mapTypeF fTypedefRef fQual fExtBindingRef fMacroRef fEnumRef
where
fTypedefRef :: TypedefRef p -> TypeF Erased p
fTypedefRef ref = normalize ref.underlying
fQual :: TypeQual -> TypeF Full p -> TypeF Erased p
fQual qual typ = TypeQual qual $ normalize typ
fExtBindingRef :: ExtBindingRef p -> TypeF Erased p
fExtBindingRef ref = normalize ref.underlying
fMacroRef :: MacroRef p -> TypeF Erased p
fMacroRef ref = normalize ref.underlying
fEnumRef :: EnumRef p -> TypeF Erased p
fEnumRef ref = normalize ref.underlying
instance Normalize Erased Canonical where
normalize = mapTypeF absurd fQual absurd absurd absurd
where
fQual ::
MacroUnderlying p ~ Type p
=> TypeQual
-> TypeF Erased p
-> TypeF Canonical p
fQual _qual typ = normalize typ
instance Normalize Full Canonical where
normalize = getCanonicalType . getErasedType
getCanonicalType ::
(Normalize tag Canonical, MacroUnderlying p ~ Type p)
=> TypeF tag p
-> CanonicalType p
getCanonicalType = normalize
getErasedType ::
(Normalize tag Erased, MacroUnderlying p ~ Type p)
=> TypeF tag p
-> ErasedType p
getErasedType = normalize
{-------------------------------------------------------------------------------
Queries
-------------------------------------------------------------------------------}
-- | The declarations this type depends on (direct dependencies only)
--
-- We also report what the dependent needs to know about each dependency (full
-- shape vs. name only).
depsOfType :: forall p.
PassMacro p
=> Type p
-> [(Id p, Dependency)]
depsOfType = \case
-- Primitive types
TypePrim _ -> []
TypeComplex _ -> []
TypeVoid -> []
-- Interesting cases
TypeRef ref -> [(ref , NeedsShape)]
TypeEnum ref -> [(ref.name , NeedsShape)]
TypeMacro ref -> [(macroIdId (Proxy @p) ref.name , NeedsShape)]
TypeTypedef ref -> [(ref.name , NeedsShape)]
TypeUnsafePointer t -> second (const NeedsNameOnly) <$> depsOfType t
-- TODO <https://github.com/well-typed/hs-bindgen/issues/1467>
-- We could in /principle/ use extBindingId here to implement this case
-- properly. However, one of the use cases of 'depsOfType' is in a check
-- whether a type is defined in the current module; 'extBindingId' currently
-- omits the module, so this could result in potentially incorrect
-- conclusions (if there happens to be a /another/ type of the same name).
-- TypeExtBinding extBinding -> [(NeedsShape, extBindingId (Proxy @p) extBinding)]
TypeExtBinding _extBinding -> []
-- Recurse
TypeConstArray _ t -> depsOfType t
TypeIncompleteArray t -> depsOfType t
TypeBlock t -> depsOfType t
TypeQual _ t -> depsOfType t
TypeFun args res -> concatMap depsOfTypeFunArg args <> depsOfType res
depsOfTypeFunArg ::
PassMacro p
=> TypeFunArgF Full p
-> [(Id p, Dependency)]
depsOfTypeFunArg arg = depsOfType arg.typ
-- | Checks if a type is unsupported by Haskell's FFI
hasUnsupportedType :: forall tag p.
(Normalize tag Canonical, PassId p, MacroUnderlying p ~ Type p)
=> TypeF tag p
-> Bool
hasUnsupportedType = aux . getCanonicalType
where
aux :: CanonicalType p -> Bool
aux (TypeRef declId) = auxRef (idNameKind (Proxy @p) declId)
aux TypeComplex{} = True
aux TypeConstArray{} = True
aux TypeIncompleteArray{} = True
aux TypePrim{} = False
aux TypePointers{} = False
aux TypeFun{} = False
aux TypeVoid = False
aux TypeBlock{} = False
-- 'Normalize Full Erased' erases @typedef@s, @enum@s, and type macros.
auxRef :: C.NameKind -> Bool
auxRef = \case
C.NameKindOrdinary -> panicPure "Unexpected NameKindOrdinary"
C.NameKindTagged C.TagKindStruct -> True
C.NameKindTagged C.TagKindUnion -> True
C.NameKindTagged C.TagKindEnum -> panicPure "Unexpected TagKindEnum"
C.NameKindMacro -> panicPure "Unexpected NameKindMacro"
-- | Recursively accumulate all function types
getAllFunTypes ::
forall p. (PassMacro p, PassId p, PassExtBinding p, PassAnn p)
=> Type p
-> Set ([TypeFunArg p], Type p)
getAllFunTypes =
Set.map (\(_, args, res) -> (args, res))
. getFunTypes
-- | Recursively accumulate all function type indirections
getAllFunTypeIndirections ::
forall p. (PassMacro p, PassId p, PassExtBinding p, PassAnn p)
=> Type p
-> Set ([TypeFunArg p], Type p)
getAllFunTypeIndirections =
Set.map (\(_, args, res) -> (args, res))
. Set.filter (\(numIndirections, _, _) -> numIndirections > 0)
. getFunTypes
-- | Internal function: accumulate function types recursively together with the
-- number of indirections to get to those function types.
getFunTypes ::
forall p. (PassMacro p, PassId p, PassExtBinding p, PassAnn p)
=> Type p
-> Set (Int, [TypeFunArg p], Type p)
getFunTypes = go 0 Set.empty
where
go ::
Int
-> Set (Int, [TypeFunArg p], Type p)
-> Type p
-> Set (Int, [TypeFunArg p], Type p)
go numIndirections acc = \case
-- interesting case
TypeFun args res ->
let acc' = Set.insert (numIndirections, args, res) acc
in Foldable.foldl' (go (numIndirections + 1)) acc' (fmap (.typ) args ++ [res])
-- recursive cases
TypePointers n t -> go (numIndirections + n) acc t
TypeConstArray _n t -> go (numIndirections + 1) acc t
TypeIncompleteArray t -> go (numIndirections + 1) acc t
TypeBlock t -> go (numIndirections + 1) acc t
TypeQual _q t -> go (numIndirections + 1) acc t
-- primitive cases
TypePrim{} -> acc
TypeComplex{} -> acc
TypeVoid{} -> acc
-- sugar cases
TypeRef{} -> acc
TypeEnum{} -> acc
TypeMacro{} -> acc
TypeTypedef{} -> acc
TypeExtBinding{} -> acc
-- | Get the first function type indirection
getFirstFunTypeIndirection ::
forall p.
Type p
-> Maybe ( [TypeFunArg p]
, Type p
, Type p -> Type p -- ^ zipper: how to reconstruct the original type
)
getFirstFunTypeIndirection = go 0 id
where
go ::
Int
-> (Type p -> Type p)
-> Type p
-> Maybe ( [TypeFunArg p]
, Type p
, Type p -> Type p
)
go numIndirections reconstruct = \case
-- interesting case
TypeFun args res
| numIndirections <= 0 -> Nothing
| otherwise -> Just (args, res, reconstruct)
-- recursive cases
TypePointers n t -> go (numIndirections + n) (reconstruct . TypePointers n) t
TypeConstArray n t -> go (numIndirections + 1) (reconstruct . TypeConstArray n) t
TypeIncompleteArray t -> go (numIndirections + 1) (reconstruct . TypeIncompleteArray) t
TypeBlock t -> go (numIndirections + 1) (reconstruct . TypeBlock) t
TypeQual q t -> go (numIndirections + 1) (reconstruct . TypeQual q) t
-- primitive cases
TypePrim{} -> Nothing
TypeComplex{} -> Nothing
TypeVoid{} -> Nothing
-- sugar cases
TypeRef{} -> Nothing
TypeEnum{} -> Nothing
TypeMacro{} -> Nothing
TypeTypedef{} -> Nothing
TypeExtBinding{} -> Nothing
{-------------------------------------------------------------------------------
Classification: simple classifiers
-------------------------------------------------------------------------------}
isVoid :: Type p -> Bool
isVoid TypeVoid = True
isVoid _ = False
-- | Is the canonical type a complex type?
isCanonicalTypeComplex ::
(Normalize tag Canonical, MacroUnderlying p ~ Type p)
=> TypeF tag p
-> Bool
isCanonicalTypeComplex ty =
case getCanonicalType ty of
TypeComplex{} -> True
_otherwise -> False
-- | Is the canonical type a function type?
isCanonicalTypeFunction ::
(Normalize tag Canonical, MacroUnderlying p ~ Type p)
=> TypeF tag p
-> Bool
isCanonicalTypeFunction ty =
case getCanonicalType ty of
TypeFun{} -> True
_ -> False
-- | Is the canonical type a struct type?
isCanonicalTypeStruct :: forall tag p.
(Normalize tag Canonical, PassId p, MacroUnderlying p ~ Type p)
=> TypeF tag p -> Bool
isCanonicalTypeStruct ty =
case getCanonicalType ty of
TypeRef ref ->
idNameKind (Proxy @p) ref == C.NameKindTagged C.TagKindStruct
_otherwise -> False
-- | Is the canonical type a union type?
isCanonicalTypeUnion :: forall tag p.
(Normalize tag Canonical, PassId p, MacroUnderlying p ~ Type p)
=> TypeF tag p -> Bool
isCanonicalTypeUnion ty =
case getCanonicalType ty of
TypeRef ref ->
idNameKind (Proxy @p) ref == C.NameKindTagged C.TagKindUnion
_otherwise -> False
-- | Is the canonical type an array type?
isCanonicalTypeArray ::
(Normalize tag Canonical, MacroUnderlying p ~ Type p)
=> TypeF tag p
-> Bool
isCanonicalTypeArray ty =
case getCanonicalType ty of
TypeConstArray{} -> True
TypeIncompleteArray{} -> True
_ -> False
-- | Is the erased type @const@-qualified?
isErasedTypeConstQualified ::
(Normalize tag Erased, MacroUnderlying p ~ Type p)
=> TypeF tag p
-> Bool
isErasedTypeConstQualified ty =
case getErasedType ty of
-- Types can be directly @const@-qualified,
TypeQual QualConst _ -> True
-- but arrays are also @const@-qualified if their element type is. Note
-- that elements of arrays can themselves be arrays, hence we recurse into
-- the array element type.
TypeConstArray _ ty' -> isErasedTypeConstQualified ty'
TypeIncompleteArray ty' -> isErasedTypeConstQualified ty'
-- And otherwise, the type is not considered to be @const@-qualified.
_ -> False
{-------------------------------------------------------------------------------
CoercePass
-------------------------------------------------------------------------------}
-- NOTE: types do not contain 'ScopedName's, so unlike most other 'CoercePass'
-- instances this one carries no @ScopedName p ~ ScopedName p'@ constraint. This
-- lets us reindex types across passes that change 'ScopedName' (e.g.
-- 'HsBindgen.Frontend.Pass.MangleNames.IsPass.CreateNames').
instance (
CoercePassId p p'
, CoercePassMacroId p p'
, CoercePassMacroUnderlying p p'
, CoercePassAnn "TypeFunArg" p p'
, ExtBinding p ~ ExtBinding p'
) => CoercePass Type p p' where
coercePass = \case
TypePrim prim -> TypePrim prim
TypeRef uid -> TypeRef (goId uid)
TypeEnum ref -> TypeEnum (Ref (goId ref.name) (coercePass ref.underlying))
TypeMacro ref -> TypeMacro (MacroRef (goMacroId ref.name) (goMacroUnderlying ref.underlying))
TypeTypedef ref -> TypeTypedef (Ref (goId ref.name) (coercePass ref.underlying))
TypePointers n typ -> TypePointers n (coercePass typ)
TypeFun args res -> TypeFun (map coercePass args) (coercePass res)
TypeVoid -> TypeVoid
TypeConstArray n typ -> TypeConstArray n (coercePass typ)
TypeIncompleteArray typ -> TypeIncompleteArray (coercePass typ)
TypeExtBinding ref -> TypeExtBinding (coercePassExtBindingRef ref)
TypeBlock typ -> TypeBlock (coercePass typ)
TypeQual qual typ -> TypeQual qual (coercePass typ)
TypeComplex prim -> TypeComplex prim
where
goId :: Id p -> Id p'
goId = coercePassId (Proxy @'(p, p'))
goMacroId :: MacroId p -> MacroId p'
goMacroId = coercePassMacroId (Proxy @'(p, p'))
goMacroUnderlying :: MacroUnderlying p -> MacroUnderlying p'
goMacroUnderlying = coercePassMacroUnderlying (Proxy @'(p, p'))
instance (
CoercePassId p p'
, CoercePassExtBindingRef p p'
) => CoercePass AnonRef p p' where
coercePass = \case
AnonRef ref -> AnonRef $ coercePassId (Proxy @'(p, p')) ref
AnonExtBinding ext -> AnonExtBinding $ coercePassExtBindingRef ext
class CoercePassExtBindingRef p p' where
coercePassExtBindingRef :: ExtBindingRef p -> ExtBindingRef p'
instance (
CoercePass Type p p'
, ExtBinding p ~ ExtBinding p'
) => CoercePassExtBindingRef p p' where
coercePassExtBindingRef ref = Ref ref.name (coercePass ref.underlying)
-- NOTE: see the 'CoercePass Type' instance for why there is no
-- @ScopedName p ~ ScopedName p'@ constraint here.
instance (
CoercePassId p p'
, CoercePassMacroId p p'
, CoercePassMacroUnderlying p p'
, CoercePassAnn "TypeFunArg" p p'
, ExtBinding p ~ ExtBinding p'
) => CoercePass TypeFunArg p p' where
coercePass arg = TypeFunArgF {
typ = coercePass arg.typ
, ann = coercePassAnn (Proxy @'("TypeFunArg", p, p')) arg.ann
}