rustc: Stabilize #[wasm_import_module] as #[link(...)]

This commit stabilizes the `#[wasm_import_module]` attribute as
`#[link(wasm_import_module = "...")]`. Tracked by #52090 this new directive in
the `#[link]` attribute is used to configured the module name that the imports
are listed with. The WebAssembly specification indicates two utf-8 names are
associated with all imported items, one for the module the item comes from and
one for the item itself. The item itself is configurable in Rust via its
identifier or `#[link_name = "..."]`, but the module name was previously not
configurable and defaulted to `"env"`. This commit ensures that this is also
configurable.

Closes #52090
This commit is contained in:
Alex Crichton 2018-07-16 11:31:14 -07:00
parent 29ee65411c
commit b9024f8a75
16 changed files with 167 additions and 162 deletions

View File

@ -65,35 +65,14 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
.emit();
}
let mut has_wasm_import_module = false;
for attr in &item.attrs {
if attr.check_name("inline") {
self.check_inline(attr, &item.span, target)
} else if attr.check_name("non_exhaustive") {
self.check_non_exhaustive(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");
}
}
}
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);
self.check_used(item, target);
}

View File

@ -31,7 +31,8 @@ impl_stable_hash_for!(struct middle::cstore::NativeLibrary {
kind,
name,
cfg,
foreign_module
foreign_module,
wasm_import_module
});
impl_stable_hash_for!(struct middle::cstore::ForeignModule {

View File

@ -128,9 +128,10 @@ pub enum NativeLibraryKind {
#[derive(Clone, RustcEncodable, RustcDecodable)]
pub struct NativeLibrary {
pub kind: NativeLibraryKind,
pub name: Symbol,
pub name: Option<Symbol>,
pub cfg: Option<ast::MetaItem>,
pub foreign_module: Option<DefId>,
pub wasm_import_module: Option<Symbol>,
}
#[derive(Clone, Hash, RustcEncodable, RustcDecodable)]

View File

@ -226,13 +226,22 @@ pub fn provide(providers: &mut Providers) {
pub fn provide_extern(providers: &mut Providers) {
providers.wasm_import_module_map = |tcx, cnum| {
// Build up a map from DefId to a `NativeLibrary` structure, where
// `NativeLibrary` internally contains information about
// `#[link(wasm_import_module = "...")]` for example.
let native_libs = tcx.native_libraries(cnum);
let mut def_id_to_native_lib = FxHashMap();
for lib in native_libs.iter() {
if let Some(id) = lib.foreign_module {
def_id_to_native_lib.insert(id, lib);
}
}
let mut ret = FxHashMap();
for lib in tcx.foreign_modules(cnum).iter() {
let attrs = tcx.get_attrs(lib.def_id);
let mut module = None;
for attr in attrs.iter().filter(|a| a.check_name("wasm_import_module")) {
module = attr.value_str();
}
let module = def_id_to_native_lib
.get(&lib.def_id)
.and_then(|s| s.wasm_import_module);
let module = match module {
Some(s) => s,
None => continue,
@ -244,7 +253,7 @@ pub fn provide_extern(providers: &mut Providers) {
}
Lrc::new(ret)
}
};
}
fn wasm_import_module(tcx: TyCtxt, id: DefId) -> Option<CString> {

View File

@ -449,7 +449,9 @@ fn link_rlib<'a>(sess: &'a Session,
NativeLibraryKind::NativeFramework |
NativeLibraryKind::NativeUnknown => continue,
}
ab.add_native_library(&lib.name.as_str());
if let Some(name) = lib.name {
ab.add_native_library(&name.as_str());
}
}
// After adding all files to the archive, we need to update the
@ -583,21 +585,24 @@ fn link_staticlib(sess: &Session,
fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLibrary]) {
let lib_args: Vec<_> = all_native_libs.iter()
.filter(|l| relevant_lib(sess, l))
.filter_map(|lib| match lib.kind {
NativeLibraryKind::NativeStaticNobundle |
NativeLibraryKind::NativeUnknown => {
if sess.target.target.options.is_like_msvc {
Some(format!("{}.lib", lib.name))
} else {
Some(format!("-l{}", lib.name))
}
},
NativeLibraryKind::NativeFramework => {
// ld-only syntax, since there are no frameworks in MSVC
Some(format!("-framework {}", lib.name))
},
// These are included, no need to print them
NativeLibraryKind::NativeStatic => None,
.filter_map(|lib| {
let name = lib.name?;
match lib.kind {
NativeLibraryKind::NativeStaticNobundle |
NativeLibraryKind::NativeUnknown => {
if sess.target.target.options.is_like_msvc {
Some(format!("{}.lib", name))
} else {
Some(format!("-l{}", name))
}
},
NativeLibraryKind::NativeFramework => {
// ld-only syntax, since there are no frameworks in MSVC
Some(format!("-framework {}", name))
},
// These are included, no need to print them
NativeLibraryKind::NativeStatic => None,
}
})
.collect();
if !lib_args.is_empty() {
@ -1211,11 +1216,15 @@ fn add_local_native_libraries(cmd: &mut dyn Linker,
let search_path = archive_search_paths(sess);
for lib in relevant_libs {
let name = match lib.name {
Some(ref l) => l,
None => continue,
};
match lib.kind {
NativeLibraryKind::NativeUnknown => cmd.link_dylib(&lib.name.as_str()),
NativeLibraryKind::NativeFramework => cmd.link_framework(&lib.name.as_str()),
NativeLibraryKind::NativeStaticNobundle => cmd.link_staticlib(&lib.name.as_str()),
NativeLibraryKind::NativeStatic => cmd.link_whole_staticlib(&lib.name.as_str(),
NativeLibraryKind::NativeUnknown => cmd.link_dylib(&name.as_str()),
NativeLibraryKind::NativeFramework => cmd.link_framework(&name.as_str()),
NativeLibraryKind::NativeStaticNobundle => cmd.link_staticlib(&name.as_str()),
NativeLibraryKind::NativeStatic => cmd.link_whole_staticlib(&name.as_str(),
&search_path)
}
}
@ -1578,19 +1587,23 @@ fn add_upstream_native_libraries(cmd: &mut dyn Linker,
let crates = &codegen_results.crate_info.used_crates_static;
for &(cnum, _) in crates {
for lib in codegen_results.crate_info.native_libraries[&cnum].iter() {
let name = match lib.name {
Some(ref l) => l,
None => continue,
};
if !relevant_lib(sess, &lib) {
continue
}
match lib.kind {
NativeLibraryKind::NativeUnknown => cmd.link_dylib(&lib.name.as_str()),
NativeLibraryKind::NativeFramework => cmd.link_framework(&lib.name.as_str()),
NativeLibraryKind::NativeUnknown => cmd.link_dylib(&name.as_str()),
NativeLibraryKind::NativeFramework => cmd.link_framework(&name.as_str()),
NativeLibraryKind::NativeStaticNobundle => {
// Link "static-nobundle" native libs only if the crate they originate from
// is being linked statically to the current crate. If it's linked dynamically
// or is an rlib already included via some other dylib crate, the symbols from
// native libs will have already been included in that dylib.
if data[cnum.as_usize() - 1] == Linkage::Static {
cmd.link_staticlib(&lib.name.as_str())
cmd.link_staticlib(&name.as_str())
}
},
// ignore statically included native libraries here as we've

View File

@ -34,9 +34,9 @@ const WASM_EXTERNAL_KIND_GLOBAL: u8 = 3;
///
/// This function is intended as a hack for now where we manually rewrite the
/// wasm output by LLVM to have the correct import modules listed. The
/// `#[wasm_import_module]` attribute in Rust translates to the module that each
/// symbol is imported from, so here we manually go through the wasm file,
/// decode it, rewrite imports, and then rewrite the wasm module.
/// `#[link(wasm_import_module = "...")]` attribute in Rust translates to the
/// module that each symbol is imported from, so here we manually go through the
/// wasm file, decode it, rewrite imports, and then rewrite the wasm module.
///
/// Support for this was added to LLVM in
/// https://github.com/llvm-mirror/llvm/commit/0f32e1365, although support still

View File

@ -61,56 +61,75 @@ impl<'a, 'tcx> ItemLikeVisitor<'tcx> for Collector<'a, 'tcx> {
Some(item) => item,
None => continue,
};
let kind = items.iter().find(|k| {
k.check_name("kind")
}).and_then(|a| a.value_str()).map(Symbol::as_str);
let kind = match kind.as_ref().map(|s| &s[..]) {
Some("static") => cstore::NativeStatic,
Some("static-nobundle") => cstore::NativeStaticNobundle,
Some("dylib") => cstore::NativeUnknown,
Some("framework") => cstore::NativeFramework,
Some(k) => {
struct_span_err!(self.tcx.sess, m.span, E0458,
"unknown kind: `{}`", k)
.span_label(m.span, "unknown kind").emit();
cstore::NativeUnknown
}
None => cstore::NativeUnknown
};
let n = items.iter().find(|n| {
n.check_name("name")
}).and_then(|a| a.value_str());
let n = match n {
Some(n) => n,
None => {
struct_span_err!(self.tcx.sess, m.span, E0459,
"#[link(...)] specified without `name = \"foo\"`")
.span_label(m.span, "missing `name` argument").emit();
Symbol::intern("foo")
}
};
let cfg = items.iter().find(|k| {
k.check_name("cfg")
}).and_then(|a| a.meta_item_list());
let cfg = if let Some(list) = cfg {
if list.is_empty() {
self.tcx.sess.span_err(m.span(), "`cfg()` must have an argument");
return;
} else if let cfg @ Some(..) = list[0].meta_item() {
cfg.cloned()
} else {
self.tcx.sess.span_err(list[0].span(), "invalid argument for `cfg(..)`");
return;
}
} else {
None
};
let lib = NativeLibrary {
name: n,
kind,
cfg,
let mut lib = NativeLibrary {
name: None,
kind: cstore::NativeUnknown,
cfg: None,
foreign_module: Some(self.tcx.hir.local_def_id(it.id)),
wasm_import_module: None,
};
let mut kind_specified = false;
for item in items.iter() {
if item.check_name("kind") {
kind_specified = true;
let kind = match item.value_str() {
Some(name) => name,
None => continue, // skip like historical compilers
};
lib.kind = match &kind.as_str()[..] {
"static" => cstore::NativeStatic,
"static-nobundle" => cstore::NativeStaticNobundle,
"dylib" => cstore::NativeUnknown,
"framework" => cstore::NativeFramework,
k => {
struct_span_err!(self.tcx.sess, m.span, E0458,
"unknown kind: `{}`", k)
.span_label(item.span, "unknown kind").emit();
cstore::NativeUnknown
}
};
} else if item.check_name("name") {
lib.name = item.value_str();
} else if item.check_name("cfg") {
let cfg = match item.meta_item_list() {
Some(list) => list,
None => continue, // skip like historical compilers
};
if cfg.is_empty() {
self.tcx.sess.span_err(
item.span(),
"`cfg()` must have an argument",
);
} else if let cfg @ Some(..) = cfg[0].meta_item() {
lib.cfg = cfg.cloned();
} else {
self.tcx.sess.span_err(cfg[0].span(), "invalid argument for `cfg(..)`");
}
} else if item.check_name("wasm_import_module") {
match item.value_str() {
Some(s) => lib.wasm_import_module = Some(s),
None => {
let msg = "must be of the form #[link(wasm_import_module = \"...\")]";
self.tcx.sess.span_err(item.span(), msg);
}
}
} else {
// currently, like past compilers, ignore unknown
// directives here.
}
}
// In general we require #[link(name = "...")] but we allow
// #[link(wasm_import_module = "...")] without the `name`.
let requires_name = kind_specified || lib.wasm_import_module.is_none();
if lib.name.is_none() && requires_name {
struct_span_err!(self.tcx.sess, m.span, E0459,
"#[link(...)] specified without \
`name = \"foo\"`")
.span_label(m.span, "missing `name` argument")
.emit();
}
self.register_native_lib(Some(m.span), lib);
}
}
@ -121,7 +140,7 @@ impl<'a, 'tcx> ItemLikeVisitor<'tcx> for Collector<'a, 'tcx> {
impl<'a, 'tcx> Collector<'a, 'tcx> {
fn register_native_lib(&mut self, span: Option<Span>, lib: NativeLibrary) {
if lib.name.as_str().is_empty() {
if lib.name.as_ref().map(|s| s.as_str().is_empty()).unwrap_or(false) {
match span {
Some(span) => {
struct_span_err!(self.tcx.sess, span, E0454,
@ -167,10 +186,14 @@ impl<'a, 'tcx> Collector<'a, 'tcx> {
let mut renames = FxHashSet();
for &(ref name, ref new_name, _) in &self.tcx.sess.opts.libs {
if let &Some(ref new_name) = new_name {
let any_duplicate = self.libs
.iter()
.filter_map(|lib| lib.name.as_ref())
.any(|n| n == name);
if new_name.is_empty() {
self.tcx.sess.err(
&format!("an empty renaming target was specified for library `{}`",name));
} else if !self.libs.iter().any(|lib| lib.name == name as &str) {
} else if !any_duplicate {
self.tcx.sess.err(&format!("renaming of the library `{}` was specified, \
however this crate contains no #[link(...)] \
attributes referencing this library.", name));
@ -189,14 +212,18 @@ impl<'a, 'tcx> Collector<'a, 'tcx> {
for &(ref name, ref new_name, kind) in &self.tcx.sess.opts.libs {
let mut found = false;
for lib in self.libs.iter_mut() {
if lib.name == name as &str {
let lib_name = match lib.name {
Some(n) => n,
None => continue,
};
if lib_name == name as &str {
let mut changed = false;
if let Some(k) = kind {
lib.kind = k;
changed = true;
}
if let &Some(ref new_name) = new_name {
lib.name = Symbol::intern(new_name);
lib.name = Some(Symbol::intern(new_name));
changed = true;
}
if !changed {
@ -212,10 +239,11 @@ impl<'a, 'tcx> Collector<'a, 'tcx> {
// Add if not found
let new_name = new_name.as_ref().map(|s| &**s); // &Option<String> -> Option<&str>
let lib = NativeLibrary {
name: Symbol::intern(new_name.unwrap_or(name)),
name: Some(Symbol::intern(new_name.unwrap_or(name))),
kind: if let Some(k) = kind { k } else { cstore::NativeUnknown },
cfg: None,
foreign_module: None,
wasm_import_module: None,
};
self.register_native_lib(None, lib);
}

View File

@ -407,9 +407,6 @@ declare_features! (
// `use path as _;` and `extern crate c as _;`
(active, underscore_imports, "1.26.0", Some(48216), None),
// The #![wasm_import_module] attribute
(active, wasm_import_module, "1.26.0", Some(52090), None),
// Allows keywords to be escaped for use as identifiers
(active, raw_identifiers, "1.26.0", Some(48589), None),
@ -969,10 +966,6 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
"the `#[no_debug]` attribute was an experimental feature that has been \
deprecated due to lack of demand",
cfg_fn!(no_debug))),
("wasm_import_module", Normal, Gated(Stability::Unstable,
"wasm_import_module",
"experimental attribute",
cfg_fn!(wasm_import_module))),
("omit_gdb_pretty_printer_section", Whitelisted, Gated(Stability::Unstable,
"omit_gdb_pretty_printer_section",
"the `#[omit_gdb_pretty_printer_section]` \

View File

@ -9,12 +9,11 @@
// except according to those terms.
#![crate_type = "cdylib"]
#![feature(wasm_import_module)]
#![deny(warnings)]
extern crate foo;
#[wasm_import_module = "./me"]
#[link(wasm_import_module = "./me")]
extern {
#[link_name = "me_in_dep"]
fn dep();

View File

@ -9,10 +9,9 @@
// except according to those terms.
#![crate_type = "rlib"]
#![feature(wasm_import_module)]
#![deny(warnings)]
#[wasm_import_module = "./dep"]
#[link(wasm_import_module = "./dep")]
extern {
pub fn dep();
}

View File

@ -2,7 +2,9 @@ error[E0458]: unknown kind: `wonderful_unicorn`
--> $DIR/E0458.rs:11:1
|
LL | #[link(kind = "wonderful_unicorn")] extern {} //~ ERROR E0458
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unknown kind
| ^^^^^^^--------------------------^^
| |
| unknown kind
error[E0459]: #[link(...)] specified without `name = "foo"`
--> $DIR/E0458.rs:11:1

View File

@ -1,15 +0,0 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[wasm_import_module = "test"] //~ ERROR: experimental
extern {
}
fn main() {}

View File

@ -1,11 +0,0 @@
error[E0658]: experimental attribute (see issue #52090)
--> $DIR/feature-gate-wasm_import_module.rs:11:1
|
LL | #[wasm_import_module = "test"] //~ ERROR: experimental
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: add #![feature(wasm_import_module)] to the crate attributes to enable
error: aborting due to previous error
For more information about this error, try `rustc --explain E0658`.

View File

@ -1,8 +1,8 @@
error: `cfg()` must have an argument
--> $DIR/issue-43926.rs:11:1
--> $DIR/issue-43926.rs:11:20
|
LL | #[link(name="foo", cfg())] //~ ERROR `cfg()` must have an argument
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
| ^^^^^
error: aborting due to previous error

View File

@ -8,13 +8,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(wasm_import_module)]
#[wasm_import_module] //~ ERROR: must be of the form
#[link(name = "...", wasm_import_module)] //~ ERROR: must be of the form
extern {}
#[wasm_import_module = "foo"] //~ ERROR: must only be attached to
fn foo() {}
#[link(name = "...", wasm_import_module(x))] //~ ERROR: must be of the form
extern {}
#[link(name = "...", wasm_import_module())] //~ ERROR: must be of the form
extern {}
fn main() {}

View File

@ -1,14 +1,20 @@
error: must be of the form #[wasm_import_module = "..."]
--> $DIR/wasm-import-module.rs:13:1
error: must be of the form #[link(wasm_import_module = "...")]
--> $DIR/wasm-import-module.rs:11:22
|
LL | #[wasm_import_module] //~ ERROR: must be of the form
| ^^^^^^^^^^^^^^^^^^^^^
LL | #[link(name = "...", wasm_import_module)] //~ ERROR: must be of the form
| ^^^^^^^^^^^^^^^^^^
error: must only be attached to foreign modules
--> $DIR/wasm-import-module.rs:16:1
error: must be of the form #[link(wasm_import_module = "...")]
--> $DIR/wasm-import-module.rs:14:22
|
LL | #[wasm_import_module = "foo"] //~ ERROR: must only be attached to
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | #[link(name = "...", wasm_import_module(x))] //~ ERROR: must be of the form
| ^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors
error: must be of the form #[link(wasm_import_module = "...")]
--> $DIR/wasm-import-module.rs:17:22
|
LL | #[link(name = "...", wasm_import_module())] //~ ERROR: must be of the form
| ^^^^^^^^^^^^^^^^^^^^
error: aborting due to 3 previous errors