RE: Generating Function Prototypes
I think this is a very misleading way to describe the problem. A major goal of the ffi is to provide a portable way of interfacing Haskell to C. If we regard the header file as an optional extra which some compilers need and some don't, then we have failed in that goal. The effort of producing a header file is sufficient that ffi code supplied without them will be very hard to port to ghc -fvia-C (and, as I understand it, -fvia-C is considered the more stable platform especially when doing ffi work).
Sure, it's a problem, a bug in GHC, whatever. But I don't see a way around it! We can't just generate prototypes because they might conflict with prototypes already in scope. Trying to guarantee that there are *no* prototypes in scope seems too fragile - I can't imagine requiring that everything in ghc/includes must not #include any system headers or declare prototypes for anything outside the RTS. Note that in most cases you already *have* a header file because you're writing a binding for an existing C library, so the act of producing a header file isn't that much of a chore. Also you do get a small amount of typechecking this way, whereas if we were somehow able to generate prototypes then there would be no type checking at all. Cheers, Simon
Sure, it's a problem, a bug in GHC, whatever. But I don't see a way around it!
Make the spec say that you have to have header files containing prototypes to get correct results. It is not ideal but it is something that can be readily implemented by all Haskell compilers so we can get portable results. Of course, compilers are free to provide extensions, compiler flags, etc. which render the header files unnecessary but people using those extensions are writing non-portable code. -- Alastair
[...] if we were somehow able to generate prototypes then there would be no type checking at all.
Here's 4 ways to get typechecking - all admittedly harder than relying on the fact that your Haskell compiler invokes a C compiler as part of its operation (as does Hugs - not trying to put GHC down). 1) modify a C compiler to perform a consistency check; or 2) find a way of writing code which would cause an unmodified C compiler to generate type errors if the Haskell type was completely inconsistent with the C type. I haven't explored this very thoroughly but C typechecking is most fussy about assigning pointer values so code along the lines of the following might be a good place to start from: // the user-supplied header file #include "math.h" // the type the Haskell compiler generates typedef HsDouble (*sin_ptr)(HsDouble); // the consistency test sin_ptr test = &sin; We'd probably have to use #define const /**/ at the start to make the compiler 'ignore' const-ness. 3) Find a way to extract information about the calling convention from information generated by a C compiler. gcc has a number of flags that dump additional information. .stabs entries are really informative. 4) Compare the output of the C compiler. Compile a piece of code which calls the function in question first using the header file and then using the prototype implied by the ffi declaration and compare the generated code. If one pushes a float while the other pushes a double, you know you have a problem. -- Alastair
The effort of producing a header file is sufficient that ffi code supplied without them will be very hard to port to ghc -fvia-C
Sure, it's a problem, a bug in GHC, whatever. But I don't see a way around it! We can't just generate prototypes because they might conflict with prototypes already in scope.
Here is nhc98's approach to solving the problem. The usual case is for the Haskell compiler to generate a C prototype for the FFI function based solely on the Haskell type. On the rare occasions where the generated prototype conflicts with a #include'd prototype, you can add the 'noproto' modifier to the Haskell FFI decl, and the prototype is omitted. This is both easy, and (I would guess) very portable. Here's a real example from the nhc98 implementation of the standard libraries: foreign import noproto rename :: CString -> CString -> IO Int The 'noproto' is needed because <stdio.h> has extern int rename (__const char *__old, __const char *__new) __THROW; (at least on Linux). In fact, out of ~300 foreign imports in the nhc98 libraries, this is the *only* occurrence of 'noproto', showing that this really is a rare case. Hence, generating prototypes by default in the -fvia-C route could be a serious proposition. Regards, Malcolm
participants (3)
-
Alastair Reid -
Malcolm Wallace -
Simon Marlow