Adding new species of foreign calls could be made easier
Motivation
A Haskell program built with Asterius, which targets WebAssembly,
can make foreign calls to both JavaScript code and C code. Ideally Asterius
would use existing support for making foreign C calls while adding new support
for making foreign JavaScript calls. But GHC's current way of typechecking and
desugaring foreign calls (GHC.Tc.Gen.Foreign
and GHC.HsToCore.Foreign.Decl
)
makes that more difficult than one might like.
-
GHC currently provides hooks for typechecking and desugaring foreign calls, but the hooks (
tcForeignExportsHook
anddsForeignsHook
) appear to be designed only to replace existing code with new code, not to mix existing code with new code. While it might be possible to use the hooks together with a primed function liketcForeignImports'
ordsForeigns'
, we are not confident doing so. In particular, we are not confident that multiple calls todsForeigns'
could safely be composed. (The hooks in question aretcForeignImportsHook
,tcForeignExportsHook
,dsForeignsHook
.) -
A foreign calling convention is defined as an enumeration (type
CCallConv
in moduleGHC.Types.ForeignCall
). An enumeration can easily be extended, but we have not been able to find documentation explaining what obligations might be incurred by an extension. (There is a comment about allocation, but the comment mentions just two of the five calling conventions defined. There is also a link to http://www.programmersheaven.com/2/Calling-conventions, but this link has bit-rotted, and the site is not captured atarchive.org
.) And if the semantics of a calling convention is spread throughout the compiler, to any location where a value of typeCCallConv
is depended on, then as GHC evolves, a new calling convention may be difficult to maintain.
Proposal
There's more than one way to resolve this issue, but we (@nrnrnr and
@TerrorJack) like the idea of a minor refactoring. Instead of
yes-or-no hooks, each target could identify a finite map of supported
calling conventions. In that map, a key like ccall
, capi
, or
javascript
would be associated with a dictionary of
operations. (Like a type-class dictionary, but not tied to a type.)
Based on a cursory review of the source code, such a dictionary might
include values like these:
-
A way of type-checking declarations
-
A way of desugaring declarations
-
A Boolean saying whether the convention is C-ish
-
A unique key per convention used to make calling convention an instance of
Binary
(Care would have to be taken to ensure that all players agree on the meanings
of the Binary
keys.)
Or it might make more sense to define finer-grain values like these:
-
A value of type
Tcm ()
used to check whether the calling convention is valid with the current back end (e.g.,checkCg checkCOrAsmOrLlvmOrInterp
) -
A check on argument types to see whether they are supported by the calling convention
-
A function that gives a foreign export a stable name
These bullet points are just examples; the actual contents of a dictionary will be hard to settle on without an implementation, experience with which can then inform the design.