haskell / c2hs

c2hs is a pre-processor for Haskell FFI bindings to C libraries
http://hackage.haskell.org/package/c2hs
Other
198 stars 50 forks source link

c2hs >= 0.21.1 generates incorrect code for {#set#} of array in a struct #123

Closed jmillikin closed 9 years ago

jmillikin commented 9 years ago

C struct definition:

typedef struct {
    attr_t      attr;
    wchar_t     chars[CCHARW_MAX];
} cchar_t;

.c2hs file contains:

let cAttrs = undefined :: CInt
let cChars = undefined :: CWString
{# set cchar_t->attr #} (CCharT pBuf) cAttrs
{# set cchar_t->chars #} (CCharT pBuf) cChars

c2hs 0.20.1 and earlier generate:

(\(CCharT ptr) val -> do {pokeByteOff ptr 0 (val::CUInt)}) (CCharT pBuf) cAttrs
(\(CCharT ptr) val -> do {pokeByteOff ptr 4 (val::(CWString))}) (CCharT pBuf) cChars

c2hs 0.21.1 and later generate:

(\(CCharT ptr) val -> do {pokeByteOff ptr 0 (val::CUInt)}) (CCharT pBuf) cAttrs
(\(CCharT ptr) val -> do {poke ptr (val::(CWString))}) (CCharT pBuf) cChars

This new generated code won't typecheck; poke is Ptr a -> a -> IO (), so poke ptr is of type CCharT -> IO () which obviously won't accept a CWString.

I believe this was caused by https://github.com/haskell/c2hs/commit/5698388cd4c4006dbcafe5dba7fcd2769f920d69

ian-ross commented 9 years ago

@jmillikin Yes, that was definitely broken by that change, and is clearly wrong. I'll look at it in the next couple of days. I fear there's going to be a bit of juggling needed to get all the combinations of these different things work -- that change fixed issue #115 which is closely related, but I obviously didn't do it quite right.

ian-ross commented 9 years ago

@jmillikin I believe that I've now fixed this (or at least made it less wrong). The generated code is different from before, but I think it's actually better. Here's an example. If you have the following C structure definition:

typedef struct {
    int a[3]; /* An array of length 3. */
    int *p;   /* A pointer to an array. */
} array_t;

then the following C2HS code:

    cInts <- mallocArray 3
    pokeArray cInts [2, 4, 8]
    {#set array_t->p#} myStruct cInts
    p2 <- {#get array_t->p#} myStruct >>= peekArray 3
    print p2
    pokeArray cInts [3, 9, 27]
    {#set array_t->a#} myStruct cInts
    a2 <- {#get array_t->a#} myStruct >>= peekArray 3
    print a2

produces this Haskell:

    cInts <- mallocArray 3
    pokeArray cInts [2, 4, 8]
    (\ptr val -> do {pokeByteOff ptr 16 (val::(Ptr CInt))}) myStruct cInts
    p2 <- (\ptr -> do {peekByteOff ptr 16 ::IO (Ptr CInt)}) myStruct >>= peekArray 3
    print p2
    pokeArray cInts [3, 9, 27]
    (\ptr val -> do {copyArray (ptr `plusPtr` 0) (val::(Ptr CInt)) 3}) myStruct cInts
    a2 <- (\ptr -> do {return $ ptr `plusPtr` 0 ::IO (Ptr CInt)}) myStruct >>= peekArray 3
    print a2

Note the use of copyArray for dealing with the array within the structure -- I think this a more correct solution than the use of poke that was there before.

I'll close this for now, but feel free to reopen it if this doesn't solve your problem.