Closed dunhamsteve closed 3 weeks ago
Very nice! Thanks for taking the time to implement this.
Currently, it just aggregates the directives. The most recently loaded module is first. I was on the fence on whether this should be an error as I can see a case for overriding the declaration in the library and possibly a case for a backend wanting more than one declaration for a given name. So I took the simpler path and the handling of duplicates is left to the backend.
From auditing the code (I haven't experimented to verify):
"scheme,chez"
and if there is none, it will pick the first "scheme"
, etc. This happens in parseCC
.
["scheme,chez", "scheme", "C__collect_safe", "C"]
["scheme,racket", "scheme", "C"]
["RefC","C"]
["node","javascript"]
or ["browser","javascript"]
). This happens in searchForeign
.There is a little bit of ambiguity here, if two TTC are loaded that have an override for the same backend, the behavior depends on the load order. I do check that the name we override/augment is a %foreign
, ignoring visibility, and give an error if it is not a foreign function.
Aside from what is already implemented, if we want to detect these conflicts, we could either give a warning or an error when there is a duplicate. We could check:
foreignImpl
into the context
Compiler.Common.addForeignImpl
I think this capability may be too useful not to merge even with some shortcomings (though non-determinism is particularly unfortunate). Long term, I wonder if foreign functions would be more ergonomic if an error for ambiguity could be given and a hide directive could be used to address that error.
For now, I have added documentation for how duplicate declarations are handled.
Note that it is also possible to introduce ambiguity with the existing compiler by doing:
%foreign "javascript:foo"
"javascript:bar"
something : Nat -> Nat
Description
Currently in Idris, we can't add a new backend to an existing
%foreign
definition without editing the library containing the definition. This issue came up for me when I wanted to useControl.App
in node and needed to stub outprim__fork
for thenode
codegen. But it also arises when working with a third party backend.In a discord discussion @stefan-hoeck suggested the following syntax to add additional bindings to an existing
%foreign
definition:This implementation of that syntax follows the pattern of
%foreign
and allows an indented block of expressions that evaluate to strings. The test case uses the pragma to makeControl.App
work with thenode
codegen.The general idea is to update the definition of the
%foreign
function to add the new bindings. We store the additional information inOptions
, accumulate it as TTC files are read in, and then update the defs inCompiler.Common
. We can't directly modify the definition when the pragma is evaluated because the context is dropped after compiling the module.Should this change go in the CHANGELOG?
CHANGELOG_NEXT.md
(and potentially alsoCONTRIBUTORS.md
).