I've been having trouble with an imported function that returns Int64/"long long". Haskell doesn't generate a prototype, so regular 'int' type is assumed. Why doesn't Haskell generate a prototype from the 'foreign import' declaration, as part of the -fvia-C compilation? When the prototype is omitted, a default one is assumed. Surely FFI can improve on the default with the information given by the 'foreign import' declaration? In the mean time, how do I work around the problem and provide a prototype myself? -- Ashley Yakeley, Seattle WA
Ashley Yakeley <ashley@semantic.org> writes:
I've been having trouble with an imported function that returns Int64/"long long". Haskell doesn't generate a prototype, so regular 'int' type is assumed.
The "proper" way to do it is to write the prototype yourself in a C header file (or use an existing header), and then use a compiler-specific mechanism to #include it.
Why doesn't Haskell generate a prototype from the 'foreign import' declaration, as part of the -fvia-C compilation?
The nhc98 compiler does generate a prototype (ghc does not). However, although this works quite well in the general case, occasionally the generated prototype may not be compatible with the real C type of the function (e.g. it cannot guess the `const' attribute). Regards, Malcolm
The "proper" way to do it is to write the prototype yourself in a C header file (or use an existing header), and then use a compiler-specific mechanism to #include it.
Shouldn't we encourage people to use the compiler-independent mechanism instead (and exclusively)? That is, you put all your flags, includes, prototypes, etc. into a header file: foo.h: #include "config.h" #define ENABLE_FEATURE_X #include <X11/Xlib.h> #ifdef HAVE_STDLIB_H #include <stdlib.h> #endif extern void my_helper_function(int x) {...} and then mention foo.h in all relevant foreign imports: Foo.hs: ... foreign import "foo.h" x :: ... foreign import "foo.h" y :: ... foreign import "foo.h" z :: ... foreign import "foo.h" my_helper_function :: ... Using this approach is easier for users and easier for tools which generate ffi output. (I recently modified the ffi backend of GreenCard to generate code like this instead of using the GHC-specific mechanism SimonM described.) They still have to use compiler-specific mechanisms to specify #include search paths, linker options, etc.
The nhc98 compiler does generate a prototype (ghc does not). However, although this works quite well in the general case, occasionally the generated prototype may not be compatible with the real C type of the function (e.g. it cannot guess the `const' attribute).
In a sense though, this doesn't matter because C compilers use the same calling conventions for const arguments and non-const arguments. Likewise, all compilers we know of pass data pointers around the same way no matter what the size and alignment of the thing they point at. (And if either of these properties is found to change, I believe we would change the ffi so that it is still feasible to generate correct machine code without being given a function prototype by the user.) Of course, this ignores the detail that while the C compilers are generating correct code, they may also be generating warnings about alleged type errors - which can be a bit disconcerting. -- Alastair Reid reid@cs.utah.edu http://www.cs.utah.edu/~reid/
Alastair Reid <reid@reid-consulting-uk.ltd.uk> writes:
Shouldn't we encourage people to use the compiler-independent mechanism instead (and exclusively)?
Yes, you are right. I'm all in favour of standard mechanisms.
That is, you put all your flags, includes, prototypes, etc. into a header file, ... and then mention foo.h in all relevant foreign imports:
foreign import "foo.h" x :: ...
Yup, this approach is the most platform-independent we managed to come up with. Now I just need to catch up with ghc and Hugs by implementing it in nhc98...
They still have to use compiler-specific mechanisms to specify #include search paths, linker options, etc.
Indeed, but these things are not only compiler-specific, they are also often OS-specific, so it is right that they should not be in the program source. Regards, Malcolm
I've been having trouble with an imported function that returns Int64/"long long". Haskell doesn't generate a prototype, so regular 'int' type is assumed.
Which compiler is this for? As you saw from the other replies, compilers vary in whether or not they add prototypes. GHC: yes, NHC: no, Hugs: depends which day you grabbed your copy from CVS. (I'm not sure that the ffi should allow compilers to vary in this way but it does.) -- Alastair Reid reid@cs.utah.edu http://www.cs.utah.edu/~reid/
Alastair Reid <reid@reid-consulting-uk.ltd.uk> wrote,
I've been having trouble with an imported function that returns Int64/"long long". Haskell doesn't generate a prototype, so regular 'int' type is assumed.
Which compiler is this for? As you saw from the other replies, compilers vary in whether or not they add prototypes. GHC: yes, NHC: no, Hugs: depends which day you grabbed your copy from CVS.
(I'm not sure that the ffi should allow compilers to vary in this way but it does.)
The FFI doesn't really allow compilers to emit prototypes (at least not in general) as this leads to semantic differences eg in argument promotion, for which Section 4.1.5 precisely specifies the behaviour. And GHC doesn't emit prototypes. If you have any suggestions as to how to make this point clearer in the spec, please let me know. Cheers, Manuel
Alastair:
(I'm not sure that the ffi should allow compilers to vary in this way but it does.)
Manuel:
The FFI doesn't really allow compilers to emit prototypes (at least not in general) as this leads to semantic differences eg in argument promotion, for which Section 4.1.5 precisely specifies the behaviour.
I just reread that section and can't see anything that contradicts the idea that the FFI implementation ignores any header files provided. For example, the phrase: "implements calls to C functions ... as if a function prototype for the called functions is in scope" can be interpreted as meaning: 1) Will generate code (by generating machine code directly, by generating C code which is then compiled, or by some other means) which assumes the existence of a prototype _even if the user does not supply one_. [I see this as the most obvious interpretation of the phrase.] 2) Will generate C code which #includes any header files the user has provided. [I see this as a less likely interpretation but it seems to be the one you intend?] My reading of the whole section is that every mention of users providing header files and function prototypes refers to compiling the C code being called not the Haskell code doing the calling. The examples of using foreign import don't show the use of header files though the second example needs a header file to compile correctly with GHC. If the intent is that any user-provided header files must be obeyed, then I think the spec should explicitly say: When a header file containing a function prototype is not provided, the function calling convention employed is undefined. It may vary between different operating systems, between different Haskell compilers, and between different functions. The 'between different functions' part is because GHC #includes a lot of header files into the code it compiles - it may well have a prototype for the function you're calling even though you don't provide one. Trickier yet, gcc likes to #include some files that aren't mentioned in any of the header files that you #include. These define functions like 'memmove' which gcc treates magically. Also, I'm using 'function calling convention' instead of 'argument passing convention' because some C compilers pass _and return_ small structs (such as 2 ints) specially and I think you need prototypes at both definition and call site to make the code work correctly. I also think the wording could be changed to clearly separate the notion of the user invoking a C compiler on a C library that they wish to call from Haskell and the Haskell compiler invoking a C compiler as part of its compilation process. I attach my rewording of the section. Note that I am trying to make it quite clear that an ffi declaration is not portable unless you provide function prototypes except in the special case that your C compiler generates the same code with or without a prototype. \subsubsection{C Argument Promotion} The argument passing conventions of C are dependant on whether a function prototype for the function is in scope at the site where the function is defined and at the sites where the function is called.. % In particular, if no function prototype is in scope, \emph{default argument promotion} is applied to integral and floating types. For example, consider the following C function definition, which lacks a prototype: % \begin{quote} \begin{verbatim} void foo (a) { float a; ... } \end{verbatim} \end{quote} % The lack of a prototype implies that a C compiler will apply default argument promotion to the parameter \code{a}, and thus, \code{foo} will expect to receive a value of type \code{double}, \emph{not} \code{float}. Hence, any callers should call \code{foo} as though it had been defined with type: % \begin{quote} \begin{verbatim} void foo (double a); \end{verbatim} \end{quote} % To ensure portability across different operating systems and compilers, the user should write a header file (called \code{"header.h"}, say) containing this prototype and should use this \code{foreign import} declaration. % \begin{quote} \begin{verbatim} foreign import ccall "header.h" foo :: Double -> IO () \end{verbatim} \end{quote} % (Of course, it is more reliable to use the same header file when compiling the function being called --- but that is not always possible.) Just as one must be consistent in providing function prototypes when compiling C code, one must be consistent in providing function prototypes when compiling Haskell code. % That is, when a header file containing a function prototype is not provided and the function calling convention is affected by default argument promotion, the function calling convention employed by foreign function declarations is undefined. % The calling convention employed may vary between different operating systems, between different Haskell compilers, and between different functions. A similar situation arises in the case of \code{foreign export} declarations that use types that would be altered under the C default argument promotion rules. When calling such Haskell functions from C, a function prototype matching the signature provided in the \code{foreign export} declaration must be in scope; otherwise, the C compiler will erroneously apply the promotion rules to all function arguments. Note that for a C function defined to a accept a variable number of arguments, all arguments beyond the explicitly typed arguments suffer argument promotion. However, because C permits the calling convention to be different for such functions; a Haskell system will, in general, not be able to make use of variable argument functions. Hence, their use is deprecated in portable code. -- Alastair ps I still think we're better off removing header files completely and having the Haskell type completely determine the calling convention employed. But since I'm not getting any takers on that, I'll settle for pinning down the spec as tightly as possible.
participants (4)
-
Alastair Reid -
Ashley Yakeley -
Malcolm Wallace -
Manuel M. T. Chakravarty