Skip to content

Bogus unacceptable type in foreign declaration.

I'm trying to abstract FFI return type so that different errno scheme can work together, namely i create such a class:

class Integral (IOErrno r) => IOReturn r where
    -- | The errno type associated with 'r'
    data IOErrno r :: *
    -- | Is this return value indicate an error?
    isError   :: Integral a => r a -> Bool
    -- | How can i get a errno then?
    getErrno  :: Integral a => r a -> IO (IOErrno r)
    -- | Is this errno indicate interrupted blocking call?
    isInterrupt :: IOErrno r -> Bool
    -- | Is this errno indicate no data on a non-blocking device?
    isBlock   :: IOErrno r -> Bool
    -- | OK, i want my return value if no errno.
    getReturn :: Integral a => r a -> a
    -- | Read the errno name for me.
    nameErrno :: IOErrno r -> IO String
    -- | Read the errno description for me.
    descErrno :: IOErrno r -> IO String

But when i'm try to defining instance for it, such as a standard unix like:

newtype UnixReturn a = UnixReturn a
    deriving (Bounded, Enum, Eq, Integral, Num, Ord, Read, Real, Show, FiniteBits, Bits, Storable)

instance IOReturn UnixReturn where
    newtype IOErrno UnixReturn = UnixErrno CInt
        deriving (Bounded, Enum, Eq, Integral, Num, Ord, Read, Real, Show, FiniteBits, Bits, Storable)
    isError (UnixReturn r) = r == (-1)
    isInterrupt e = e == eINTR
    isBlock e = e == eAGAIN || e == eWOULDBLOCK
    getErrno _ = get_errno
    getReturn (UnixReturn r) = r
    nameErrno = return . nameUnixErrno
    descErrno e = strerror e >>= peekCString

eINTR           = UnixErrno (CONST_EINTR)
eWOULDBLOCK     = UnixErrno (CONST_EWOULDBLOCK)
...

foreign import ccall unsafe "string.h" strerror :: IOErrno UnixReturn -> IO CString
foreign import ccall unsafe "HsBase.h __hscore_get_errno" get_errno :: IO (IOErrno UnixReturn)

I got a error on GHC 8.2:

System/IO/Exception.hs:282:1: error:
    • Unacceptable argument type in foreign declaration:
        ‘IOErrno UnixReturn’ cannot be marshalled in a foreign call
    • When checking declaration:
        foreign import ccall unsafe "string.h" strerror
          :: IOErrno UnixReturn -> IO CString
    |
282 | foreign import ccall unsafe "string.h" strerror :: IOErrno UnixReturn -> IO CString
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
System/IO/Exception.hs:283:1: error:
    • Unacceptable result type in foreign declaration:
        ‘IOErrno UnixReturn’ cannot be marshalled in a foreign call
    • When checking declaration:
        foreign import ccall unsafe "HsBase.h __hscore_get_errno" get_errno
          :: IO (IOErrno UnixReturn)
    |
283 | foreign import ccall unsafe "HsBase.h __hscore_get_errno" get_errno :: IO (IOErrno UnixReturn)
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

And at every place i want mark my FFI return got this error too. e.g. following code compiles without problem on GHC < 8.2 (Note that the type and constructor are both available):

foreign import CALLCONV unsafe "HsNet.h recv"
    c_recv :: CInt -> Ptr Word8 -> CSize -> CInt -> IO (UnixReturn CSsize)

But on GHC 8.2 a Unacceptable result type in foreign declaration is emitted. And I think it's a regression.

Edited by winter
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information