1
Fork 0

rustc: Add a #[wasm_import_module] attribute

This commit adds a new attribute to the Rust compiler specific to the wasm
target (and no other targets). The `#[wasm_import_module]` attribute is used to
specify the module that a name is imported from, and is used like so:

    #[wasm_import_module = "./foo.js"]
    extern {
        fn some_js_function();
    }

Here the import of the symbol `some_js_function` is tagged with the `./foo.js`
module in the wasm output file. Wasm-the-format includes two fields on all
imports, a module and a field. The field is the symbol name (`some_js_function`
above) and the module has historically unconditionally been `"env"`. I'm not
sure if this `"env"` convention has asm.js or LLVM roots, but regardless we'd
like the ability to configure it!

The proposed ES module integration with wasm (aka a wasm module is "just another
ES module") requires that the import module of wasm imports is interpreted as an
ES module import, meaning that you'll need to encode paths, NPM packages, etc.
As a result, we'll need this to be something other than `"env"`!

Unfortunately neither our version of LLVM nor LLD supports custom import modules
(aka anything not `"env"`). My hope is that by the time LLVM 7 is released both
will have support, but in the meantime this commit adds some primitive
encoding/decoding of wasm files to the compiler. This way rustc postprocesses
the wasm module that LLVM emits to ensure it's got all the imports we'd like to
have in it.

Eventually I'd ideally like to unconditionally require this attribute to be
placed on all `extern { ... }` blocks. For now though it seemed prudent to add
it as an unstable attribute, so for now it's not required (as that'd force usage
of a feature gate). Hopefully it doesn't take too long to "stabilize" this!

cc rust-lang-nursery/rust-wasm#29
This commit is contained in:
Alex Crichton 2018-02-10 14:28:17 -08:00
parent 7df6f4161c
commit d889957dab
33 changed files with 662 additions and 84 deletions

View file

@ -26,6 +26,7 @@ enum Target {
Union,
Enum,
Const,
ForeignMod,
Other,
}
@ -37,6 +38,7 @@ impl Target {
hir::ItemUnion(..) => Target::Union,
hir::ItemEnum(..) => Target::Enum,
hir::ItemConst(..) => Target::Const,
hir::ItemForeignMod(..) => Target::ForeignMod,
_ => Target::Other,
}
}
@ -57,25 +59,42 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
.emit();
}
let mut has_wasm_import_module = false;
for attr in &item.attrs {
if let Some(name) = attr.name() {
if name == "inline" {
self.check_inline(attr, item, target)
if attr.check_name("inline") {
self.check_inline(attr, item, target)
} else if attr.check_name("wasm_import_module") {
has_wasm_import_module = true;
if attr.value_str().is_none() {
self.tcx.sess.span_err(attr.span, "\
must be of the form #[wasm_import_module = \"...\"]");
}
if target != Target::ForeignMod {
self.tcx.sess.span_err(attr.span, "\
must only be attached to foreign modules");
}
} else if attr.check_name("wasm_custom_section") {
if target != Target::Const {
self.tcx.sess.span_err(attr.span, "only allowed on consts");
}
if name == "wasm_custom_section" {
if target != Target::Const {
self.tcx.sess.span_err(attr.span, "only allowed on consts");
}
if attr.value_str().is_none() {
self.tcx.sess.span_err(attr.span, "must be of the form \
#[wasm_custom_section = \"foo\"]");
}
if attr.value_str().is_none() {
self.tcx.sess.span_err(attr.span, "must be of the form \
#[wasm_custom_section = \"foo\"]");
}
}
}
if target == Target::ForeignMod &&
!has_wasm_import_module &&
self.tcx.sess.target.target.arch == "wasm32" &&
false // FIXME: eventually enable this warning when stable
{
self.tcx.sess.span_warn(item.span, "\
must have a #[wasm_import_module = \"...\"] attribute, this \
will become a hard error before too long");
}
self.check_repr(item, target);
}