Skip to content

Commit d889957

Browse files
committed
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
1 parent 7df6f41 commit d889957

33 files changed

+653
-75
lines changed

src/ci/docker/wasm32-unknown/Dockerfile

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ ENV RUST_CONFIGURE_ARGS \
2626
--set rust.lld
2727

2828
ENV SCRIPT python2.7 /checkout/x.py test --target $TARGETS \
29+
src/test/run-make \
2930
src/test/ui \
3031
src/test/run-pass \
3132
src/test/compile-fail \

src/librustc/dep_graph/dep_node.rs

+3
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,7 @@ define_dep_nodes!( <'tcx>
593593
[] ImplementationsOfTrait { krate: CrateNum, trait_id: DefId },
594594
[] AllTraitImplementations(CrateNum),
595595

596+
[] DllimportForeignItems(CrateNum),
596597
[] IsDllimportForeignItem(DefId),
597598
[] IsStaticallyIncludedForeignItem(DefId),
598599
[] NativeLibraryKind(DefId),
@@ -655,6 +656,8 @@ define_dep_nodes!( <'tcx>
655656
[input] Features,
656657

657658
[] ProgramClausesFor(DefId),
659+
[] WasmImportModuleMap(CrateNum),
660+
[] ForeignModules(CrateNum),
658661
);
659662

660663
trait DepNodeParams<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> : fmt::Debug {

src/librustc/hir/check_attr.rs

+31-12
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ enum Target {
2626
Union,
2727
Enum,
2828
Const,
29+
ForeignMod,
2930
Other,
3031
}
3132

@@ -37,6 +38,7 @@ impl Target {
3738
hir::ItemUnion(..) => Target::Union,
3839
hir::ItemEnum(..) => Target::Enum,
3940
hir::ItemConst(..) => Target::Const,
41+
hir::ItemForeignMod(..) => Target::ForeignMod,
4042
_ => Target::Other,
4143
}
4244
}
@@ -57,25 +59,42 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
5759
.emit();
5860
}
5961

62+
let mut has_wasm_import_module = false;
6063
for attr in &item.attrs {
61-
if let Some(name) = attr.name() {
62-
if name == "inline" {
63-
self.check_inline(attr, item, target)
64+
if attr.check_name("inline") {
65+
self.check_inline(attr, item, target)
66+
} else if attr.check_name("wasm_import_module") {
67+
has_wasm_import_module = true;
68+
if attr.value_str().is_none() {
69+
self.tcx.sess.span_err(attr.span, "\
70+
must be of the form #[wasm_import_module = \"...\"]");
71+
}
72+
if target != Target::ForeignMod {
73+
self.tcx.sess.span_err(attr.span, "\
74+
must only be attached to foreign modules");
75+
}
76+
} else if attr.check_name("wasm_custom_section") {
77+
if target != Target::Const {
78+
self.tcx.sess.span_err(attr.span, "only allowed on consts");
6479
}
6580

66-
if name == "wasm_custom_section" {
67-
if target != Target::Const {
68-
self.tcx.sess.span_err(attr.span, "only allowed on consts");
69-
}
70-
71-
if attr.value_str().is_none() {
72-
self.tcx.sess.span_err(attr.span, "must be of the form \
73-
#[wasm_custom_section = \"foo\"]");
74-
}
81+
if attr.value_str().is_none() {
82+
self.tcx.sess.span_err(attr.span, "must be of the form \
83+
#[wasm_custom_section = \"foo\"]");
7584
}
7685
}
7786
}
7887

88+
if target == Target::ForeignMod &&
89+
!has_wasm_import_module &&
90+
self.tcx.sess.target.target.arch == "wasm32" &&
91+
false // FIXME: eventually enable this warning when stable
92+
{
93+
self.tcx.sess.span_warn(item.span, "\
94+
must have a #[wasm_import_module = \"...\"] attribute, this \
95+
will become a hard error before too long");
96+
}
97+
7998
self.check_repr(item, target);
8099
}
81100

src/librustc/ich/impls_cstore.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,12 @@ impl_stable_hash_for!(struct middle::cstore::NativeLibrary {
3333
kind,
3434
name,
3535
cfg,
36-
foreign_items
36+
foreign_module
37+
});
38+
39+
impl_stable_hash_for!(struct middle::cstore::ForeignModule {
40+
foreign_items,
41+
def_id
3742
});
3843

3944
impl_stable_hash_for!(enum middle::cstore::LinkagePreference {

src/librustc/middle/cstore.rs

+6
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,13 @@ pub struct NativeLibrary {
132132
pub kind: NativeLibraryKind,
133133
pub name: Symbol,
134134
pub cfg: Option<ast::MetaItem>,
135+
pub foreign_module: Option<DefId>,
136+
}
137+
138+
#[derive(Clone, Hash, RustcEncodable, RustcDecodable)]
139+
pub struct ForeignModule {
135140
pub foreign_items: Vec<DefId>,
141+
pub def_id: DefId,
136142
}
137143

138144
pub enum LoadedMacro {

src/librustc/ty/maps/config.rs

+18
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::native_libraries<'tcx> {
430430
}
431431
}
432432

433+
impl<'tcx> QueryDescription<'tcx> for queries::foreign_modules<'tcx> {
434+
fn describe(_tcx: TyCtxt, _: CrateNum) -> String {
435+
format!("looking up the foreign modules of a linked crate")
436+
}
437+
}
438+
433439
impl<'tcx> QueryDescription<'tcx> for queries::plugin_registrar_fn<'tcx> {
434440
fn describe(_tcx: TyCtxt, _: CrateNum) -> String {
435441
format!("looking up the plugin registrar for a crate")
@@ -705,6 +711,18 @@ impl<'tcx> QueryDescription<'tcx> for queries::program_clauses_for<'tcx> {
705711
}
706712
}
707713

714+
impl<'tcx> QueryDescription<'tcx> for queries::wasm_import_module_map<'tcx> {
715+
fn describe(_tcx: TyCtxt, _: CrateNum) -> String {
716+
format!("wasm import module map")
717+
}
718+
}
719+
720+
impl<'tcx> QueryDescription<'tcx> for queries::dllimport_foreign_items<'tcx> {
721+
fn describe(_tcx: TyCtxt, _: CrateNum) -> String {
722+
format!("wasm import module map")
723+
}
724+
}
725+
708726
macro_rules! impl_disk_cacheable_query(
709727
($query_name:ident, |$key:tt| $cond:expr) => {
710728
impl<'tcx> QueryDescription<'tcx> for queries::$query_name<'tcx> {

src/librustc/ty/maps/mod.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use infer::canonical::{Canonical, QueryResult};
1818
use lint;
1919
use middle::borrowck::BorrowCheckResult;
2020
use middle::cstore::{ExternCrate, LinkagePreference, NativeLibrary,
21-
ExternBodyNestedBodies};
21+
ExternBodyNestedBodies, ForeignModule};
2222
use middle::cstore::{NativeLibraryKind, DepKind, CrateSource, ExternConstBody};
2323
use middle::privacy::AccessLevels;
2424
use middle::reachable::ReachableSet;
@@ -320,6 +320,9 @@ define_maps! { <'tcx>
320320

321321

322322
[] fn native_libraries: NativeLibraries(CrateNum) -> Lrc<Vec<NativeLibrary>>,
323+
324+
[] fn foreign_modules: ForeignModules(CrateNum) -> Lrc<Vec<ForeignModule>>,
325+
323326
[] fn plugin_registrar_fn: PluginRegistrarFn(CrateNum) -> Option<DefId>,
324327
[] fn derive_registrar_fn: DeriveRegistrarFn(CrateNum) -> Option<DefId>,
325328
[] fn crate_disambiguator: CrateDisambiguator(CrateNum) -> CrateDisambiguator,
@@ -331,6 +334,8 @@ define_maps! { <'tcx>
331334
[] fn all_trait_implementations: AllTraitImplementations(CrateNum)
332335
-> Lrc<Vec<DefId>>,
333336

337+
[] fn dllimport_foreign_items: DllimportForeignItems(CrateNum)
338+
-> Lrc<FxHashSet<DefId>>,
334339
[] fn is_dllimport_foreign_item: IsDllimportForeignItem(DefId) -> bool,
335340
[] fn is_statically_included_foreign_item: IsStaticallyIncludedForeignItem(DefId) -> bool,
336341
[] fn native_library_kind: NativeLibraryKind(DefId)
@@ -426,6 +431,8 @@ define_maps! { <'tcx>
426431
[] fn program_clauses_for: ProgramClausesFor(DefId) -> Lrc<Vec<Clause<'tcx>>>,
427432

428433
[] fn wasm_custom_sections: WasmCustomSections(CrateNum) -> Lrc<Vec<DefId>>,
434+
[] fn wasm_import_module_map: WasmImportModuleMap(CrateNum)
435+
-> Lrc<FxHashMap<DefId, String>>,
429436
}
430437

431438
//////////////////////////////////////////////////////////////////////

src/librustc/ty/maps/plumbing.rs

+5
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,9 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
886886
force!(all_trait_implementations, krate!());
887887
}
888888

889+
DepKind::DllimportForeignItems => {
890+
force!(dllimport_foreign_items, krate!());
891+
}
889892
DepKind::IsDllimportForeignItem => {
890893
force!(is_dllimport_foreign_item, def_id!());
891894
}
@@ -941,6 +944,8 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
941944

942945
DepKind::ProgramClausesFor => { force!(program_clauses_for, def_id!()); }
943946
DepKind::WasmCustomSections => { force!(wasm_custom_sections, krate!()); }
947+
DepKind::WasmImportModuleMap => { force!(wasm_import_module_map, krate!()); }
948+
DepKind::ForeignModules => { force!(foreign_modules, krate!()); }
944949
}
945950

946951
true

src/librustc_metadata/creader.rs

+1-19
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
1313
use cstore::{self, CStore, CrateSource, MetadataBlob};
1414
use locator::{self, CratePaths};
15-
use native_libs::relevant_lib;
1615
use schema::CrateRoot;
1716
use rustc_data_structures::sync::{Lrc, RwLock, Lock};
1817

@@ -230,7 +229,7 @@ impl<'a> CrateLoader<'a> {
230229
.map(|trait_impls| (trait_impls.trait_id, trait_impls.impls))
231230
.collect();
232231

233-
let mut cmeta = cstore::CrateMetadata {
232+
let cmeta = cstore::CrateMetadata {
234233
name,
235234
extern_crate: Lock::new(None),
236235
def_path_table: Lrc::new(def_path_table),
@@ -250,25 +249,8 @@ impl<'a> CrateLoader<'a> {
250249
rlib,
251250
rmeta,
252251
},
253-
// Initialize this with an empty set. The field is populated below
254-
// after we were able to deserialize its contents.
255-
dllimport_foreign_items: FxHashSet(),
256252
};
257253

258-
let dllimports: FxHashSet<_> = cmeta
259-
.root
260-
.native_libraries
261-
.decode((&cmeta, self.sess))
262-
.filter(|lib| relevant_lib(self.sess, lib) &&
263-
lib.kind == cstore::NativeLibraryKind::NativeUnknown)
264-
.flat_map(|lib| {
265-
assert!(lib.foreign_items.iter().all(|def_id| def_id.krate == cnum));
266-
lib.foreign_items.into_iter().map(|def_id| def_id.index)
267-
})
268-
.collect();
269-
270-
cmeta.dllimport_foreign_items = dllimports;
271-
272254
let cmeta = Lrc::new(cmeta);
273255
self.cstore.set_crate_data(cnum, cmeta.clone());
274256
(cnum, cmeta)

src/librustc_metadata/cstore.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use rustc::middle::cstore::{DepKind, ExternCrate, MetadataLoader};
2020
use rustc::session::{Session, CrateDisambiguator};
2121
use rustc_back::PanicStrategy;
2222
use rustc_data_structures::indexed_vec::IndexVec;
23-
use rustc::util::nodemap::{FxHashMap, FxHashSet, NodeMap};
23+
use rustc::util::nodemap::{FxHashMap, NodeMap};
2424

2525
use rustc_data_structures::sync::{Lrc, RwLock, Lock};
2626
use syntax::{ast, attr};
@@ -30,7 +30,7 @@ use syntax_pos;
3030

3131
pub use rustc::middle::cstore::{NativeLibrary, NativeLibraryKind, LinkagePreference};
3232
pub use rustc::middle::cstore::NativeLibraryKind::*;
33-
pub use rustc::middle::cstore::{CrateSource, LibSource};
33+
pub use rustc::middle::cstore::{CrateSource, LibSource, ForeignModule};
3434

3535
pub use cstore_impl::{provide, provide_extern};
3636

@@ -84,8 +84,6 @@ pub struct CrateMetadata {
8484
pub source: CrateSource,
8585

8686
pub proc_macros: Option<Vec<(ast::Name, Lrc<SyntaxExtension>)>>,
87-
// Foreign items imported from a dylib (Windows only)
88-
pub dllimport_foreign_items: FxHashSet<DefIndex>,
8987
}
9088

9189
pub struct CStore {

src/librustc_metadata/cstore_impl.rs

+18-4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use cstore;
1212
use encoder;
1313
use link_args;
1414
use native_libs;
15+
use foreign_modules;
1516
use schema;
1617

1718
use rustc::ty::maps::QueryConfig;
@@ -197,6 +198,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
197198
Lrc::new(reachable_non_generics)
198199
}
199200
native_libraries => { Lrc::new(cdata.get_native_libraries(tcx.sess)) }
201+
foreign_modules => { Lrc::new(cdata.get_foreign_modules(tcx.sess)) }
200202
plugin_registrar_fn => {
201203
cdata.root.plugin_registrar_fn.map(|index| {
202204
DefId { krate: def_id.krate, index }
@@ -224,9 +226,6 @@ provide! { <'tcx> tcx, def_id, other, cdata,
224226
Lrc::new(result)
225227
}
226228

227-
is_dllimport_foreign_item => {
228-
cdata.is_dllimport_foreign_item(def_id.index)
229-
}
230229
visibility => { cdata.get_visibility(def_id.index) }
231230
dep_kind => {
232231
let r = *cdata.dep_kind.lock();
@@ -306,13 +305,28 @@ pub fn provide<'tcx>(providers: &mut Providers<'tcx>) {
306305
tcx.native_libraries(id.krate)
307306
.iter()
308307
.filter(|lib| native_libs::relevant_lib(&tcx.sess, lib))
309-
.find(|l| l.foreign_items.contains(&id))
308+
.find(|lib| {
309+
let fm_id = match lib.foreign_module {
310+
Some(id) => id,
311+
None => return false,
312+
};
313+
tcx.foreign_modules(id.krate)
314+
.iter()
315+
.find(|m| m.def_id == fm_id)
316+
.expect("failed to find foreign module")
317+
.foreign_items
318+
.contains(&id)
319+
})
310320
.map(|l| l.kind)
311321
},
312322
native_libraries: |tcx, cnum| {
313323
assert_eq!(cnum, LOCAL_CRATE);
314324
Lrc::new(native_libs::collect(tcx))
315325
},
326+
foreign_modules: |tcx, cnum| {
327+
assert_eq!(cnum, LOCAL_CRATE);
328+
Lrc::new(foreign_modules::collect(tcx))
329+
},
316330
link_args: |tcx, cnum| {
317331
assert_eq!(cnum, LOCAL_CRATE);
318332
Lrc::new(link_args::collect(tcx))

src/librustc_metadata/decoder.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
// Decoding metadata from a single crate's metadata
1212

13-
use cstore::{self, CrateMetadata, MetadataBlob, NativeLibrary};
13+
use cstore::{self, CrateMetadata, MetadataBlob, NativeLibrary, ForeignModule};
1414
use schema::*;
1515

1616
use rustc_data_structures::sync::{Lrc, ReadGuard};
@@ -1031,6 +1031,10 @@ impl<'a, 'tcx> CrateMetadata {
10311031
self.root.native_libraries.decode((self, sess)).collect()
10321032
}
10331033

1034+
pub fn get_foreign_modules(&self, sess: &Session) -> Vec<ForeignModule> {
1035+
self.root.foreign_modules.decode((self, sess)).collect()
1036+
}
1037+
10341038
pub fn get_dylib_dependency_formats(&self) -> Vec<(CrateNum, LinkagePreference)> {
10351039
self.root
10361040
.dylib_dependency_formats
@@ -1103,10 +1107,6 @@ impl<'a, 'tcx> CrateMetadata {
11031107
}
11041108
}
11051109

1106-
pub fn is_dllimport_foreign_item(&self, id: DefIndex) -> bool {
1107-
self.dllimport_foreign_items.contains(&id)
1108-
}
1109-
11101110
pub fn fn_sig(&self,
11111111
id: DefIndex,
11121112
tcx: TyCtxt<'a, 'tcx, 'tcx>)

0 commit comments

Comments
 (0)