Invalid C in the via-C backend due to EFF_
// Stg.h
#define EFF_(f) void f() /* See Note [External function prototypes] */
Whenever Cmm uses a foreign C symbol, the generated C will declare the symbol with EFF_
. Later, it will cast it to the appropriate type before calling it, often casting it to void *
beforehand. EFF_
is used in case the symbol is needed multiple times at incompatible types. This is invalid and discouraged in standard C for a number of reasons.
- Even with incomplete args lists, it is invalid to declare a function with an inaccurate return type.
- Incomplete args lists is considered "an obsolescent feature" of C.
- It is invalid to call a function declared with an incomplete args list at multiple incompatible types.
- It is invalid to cast a function to
void *
, as function pointers can technically have different width than regular pointers. This one obviously doesn't matter very much. - Using incomplete args lists to declare a function implemented with varargs is invalid, though this isn't really solvable for
foreign import ccall
without requiring the user to annotate such imports as varargs functions.
On all the platforms GHC currently supports, this happens to work out fine. The C ABI on most platforms makes all of this compatible. But I've been working on making GHC target WebAssembly with via-C, and WebAssembly does not currently support this. Although it is an LLVM bug that incomplete args lists are unsupported on WebAssembly, it's fair-game that the inaccurate return type and liberal casting is broken.
The fix is to declare externs with the proper types. We shouldn't use top level extern declarations in case the same symbol needs to be declared with multiple incompatible types (though frankly, I can't think of a good reason for this to occur, aside from varargs symbols, which is already invalid). We can use local externs to avoid the type incompatibilities.
void foo() {
extern int bar(int, int);
...
}
The difficulty here is that Cmm doesn't carry the information necessary to make such declarations.
module Test where
foreign import ccall unsafe "testCSymbol"
testHaskellSymbol :: Int -> Int -> Int -> Int
ghc -c Test.hs -o Test.o -ddump-cmm-raw
...
_s17E::I64 = I64[_s17D::P64 + 7];
_c188::I64 = testCSymbol;
_c189::I64 = _s17A::I64;
_c18a::I64 = _s17C::I64;
_c18b::I64 = _s17E::I64;
(_s17I::I64) = call "ccall" arg hints: [‘signed’, ‘signed’,
‘signed’] result hints: [‘signed’] (_c188::I64)(_c189::I64, _c18a::I64, _c18b::I64);
...
Cmm does not make any forward declaration of testCSymbol
. It just uses the name ad-hoc, assigning it to the c188
variable, eventually calling that variable at the proper type. This makes it nontrival to infer testCSymbol
's type (probably undecidable, considering anything can theoretically happen to these variables).
It seems to me that Cmm needs to outlaw implicit declarations of foreign C symbols, EFF_
needs to be removed, and there needs to be a local extern declaration syntax in Cmm.
Trac metadata
Trac field | Value |
---|---|
Version | 8.3 |
Type | Bug |
TypeOfFailure | OtherFailure |
Priority | normal |
Resolution | Unresolved |
Component | Compiler |
Test case | |
Differential revisions | |
BlockedBy | |
Related | |
Blocking | |
CC | slyfox |
Operating system | |
Architecture |