hs-bindgen-1.0.0.0: src-internal/HsBindgen/Backend/Hs/Translation/ToFromFunPtr.hs
-- | Generate @ToFunPtr@ and @FromFunPtr@ instances
--
-- Intended for qualified import.
--
-- > HsBindgen.Backend.Hs.Translation.ToFromFunPtr qualified as ToFromFunPtr
module HsBindgen.Backend.Hs.Translation.ToFromFunPtr (
forFunction
, forNewtype
) where
import Text.SimplePrettyPrint qualified as PP
import HsBindgen.Backend.Hs.AST qualified as Hs
import HsBindgen.Backend.Hs.Origin qualified as Origin
import HsBindgen.Backend.Hs.Translation.ForeignImport (ImportFor (..))
import HsBindgen.Backend.Hs.Translation.ForeignImport qualified as Hs.ForeignImport
import HsBindgen.Backend.Hs.Translation.ForeignImport qualified as HsFI
import HsBindgen.Backend.HsModule.Pretty ()
import HsBindgen.Backend.SHs.Translation qualified as SHs
import HsBindgen.Backend.UniqueSymbol
import HsBindgen.Frontend.Pass.Final
import HsBindgen.Frontend.Pass.TranslateTypes.Translation qualified as Translation
import HsBindgen.IR.C qualified as C
import HsBindgen.IR.Hs qualified as Hs
import HsBindgen.Language.Haskell qualified as Hs
{-------------------------------------------------------------------------------
Main API
NOTE: The names we generate here exist in Haskell only (they do not have
C counterparts); it therefore suffices if they are locally unique.
-------------------------------------------------------------------------------}
-- | Generate ToFunPtr/FromFunPtr instances for nested function pointer types
--
-- This function analyses the function declaration arguments and return types,
-- and for each function pointer argument/return type containing at least one
-- non-orphan type, generates the FFI wrapper and dynamic stubs along with
-- the respective ToFunPtr and FromFunPtr instances.
--
-- These instances are placed in the main module to avoid orphan instances.
forFunction ::
([C.TypeFunArg Final], C.Type Final)
-> [Hs.Decl l]
forFunction (args, res) =
instancesFor
nameTo
nameFrom
funC
funHs
importFor
where
funC = C.TypeFun args res
funHs = Translation.topLevel funC
importFor :: ImportFor
importFor = ImportForFunction {
args = Translation.inContext Translation.FunArg <$> args
, res = Hs.IO $ Translation.inContext Translation.FunRes res
}
nameWith :: String -> UniqueSymbol
nameWith s =
locallyUnique $ "instance " <> s <> " (" <> prettyHsType funHs <> ")"
nameTo, nameFrom :: UniqueSymbol
nameTo = nameWith "ToFunPtr"
nameFrom = nameWith "FromFunPtr"
-- | Generate instances for newtype around functions
forNewtype ::
Hs.Newtype
-> ([C.TypeFunArg Final], C.Type Final)
-> [Hs.Decl l]
forNewtype newtyp (args, res) =
instancesFor
nameTo
nameFrom
funC
funHs
importFor
where
funC = C.TypeFun args res
funHs = Hs.TypRef newtyp.name (Just newtyp.field.typ)
importFor :: ImportFor
importFor = ImportForNewtype {
args = Translation.inContext Translation.FunArg <$> args
, res = Hs.IO $ Translation.inContext Translation.FunRes res
, newtyp = newtyp
}
nameWith :: String -> UniqueSymbol
nameWith s = locallyUnique $ s <> Hs.nameToStr newtyp.name
nameTo, nameFrom :: UniqueSymbol
nameTo = nameWith "to"
nameFrom = nameWith "from"
{-------------------------------------------------------------------------------
Internal auxiliary
-------------------------------------------------------------------------------}
instancesFor ::
UniqueSymbol -- ^ Name of the @toFunPtr@ fun
-> UniqueSymbol -- ^ Name of the @fromFunPtr@ fun
-> C.Type Final -- ^ Type of the C function
-> Hs.Type -- ^ Corresponding Haskell type
-> ImportFor
-> [Hs.Decl l]
instancesFor nameTo nameFrom funC funHs importFor = concat [
-- import for @ToFunPtr@ instance
HsFI.foreignImportWrapperDec
(Hs.ForeignImport.FunName nameTo)
funHs
importFor
(Origin.ToFunPtr funC)
-- import for @FromFunPtr@ instance
, HsFI.foreignImportDynamicDec
(Hs.ForeignImport.FunName nameFrom)
funHs
importFor
(Origin.ToFunPtr funC)
-- @ToFunPtr@ instance proper
, [ Hs.DeclDefineInstance Hs.DefineInstance{
comment = Nothing
, instanceDecl = Hs.InstanceToFunPtr Hs.ToFunPtrInstance{
typ = funHs
, body = nameTo
}
}
]
-- @FromFunPtr@ instance proper
, [ Hs.DeclDefineInstance Hs.DefineInstance{
comment = Nothing
, instanceDecl = Hs.InstanceFromFunPtr Hs.FromFunPtrInstance{
typ = funHs
, body = nameFrom
}
}
]
]
prettyHsType :: Hs.Type -> String
prettyHsType = show . PP.pretty . SHs.translateType