about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ci/docker/wasm32-unknown/Dockerfile1
-rw-r--r--src/librustc/dep_graph/dep_node.rs3
-rw-r--r--src/librustc/hir/check_attr.rs43
-rw-r--r--src/librustc/ich/impls_cstore.rs7
-rw-r--r--src/librustc/middle/cstore.rs6
-rw-r--r--src/librustc/ty/maps/config.rs18
-rw-r--r--src/librustc/ty/maps/mod.rs9
-rw-r--r--src/librustc/ty/maps/plumbing.rs5
-rw-r--r--src/librustc_metadata/creader.rs20
-rw-r--r--src/librustc_metadata/cstore.rs6
-rw-r--r--src/librustc_metadata/cstore_impl.rs22
-rw-r--r--src/librustc_metadata/decoder.rs10
-rw-r--r--src/librustc_metadata/encoder.rs12
-rw-r--r--src/librustc_metadata/foreign_modules.rs48
-rw-r--r--src/librustc_metadata/lib.rs1
-rw-r--r--src/librustc_metadata/native_libs.rs7
-rw-r--r--src/librustc_metadata/schema.rs3
-rw-r--r--src/librustc_trans/attributes.rs46
-rw-r--r--src/librustc_trans/back/link.rs1
-rw-r--r--src/librustc_trans/back/wasm.rs249
-rw-r--r--src/librustc_trans/base.rs52
-rw-r--r--src/librustc_trans/lib.rs3
-rw-r--r--src/libsyntax/feature_gate.rs7
-rw-r--r--src/test/run-make/wasm-custom-section/foo.js2
-rw-r--r--src/test/run-make/wasm-import-module/Makefile10
-rw-r--r--src/test/run-make/wasm-import-module/bar.rs29
-rw-r--r--src/test/run-make/wasm-import-module/foo.js28
-rw-r--r--src/test/run-make/wasm-import-module/foo.rs18
-rw-r--r--src/test/ui/feature-gate-wasm_custom_section.stderr2
-rw-r--r--src/test/ui/feature-gate-wasm_import_module.rs15
-rw-r--r--src/test/ui/feature-gate-wasm_import_module.stderr11
-rw-r--r--src/test/ui/wasm-import-module.rs20
-rw-r--r--src/test/ui/wasm-import-module.stderr14
33 files changed, 653 insertions, 75 deletions
diff --git a/src/ci/docker/wasm32-unknown/Dockerfile b/src/ci/docker/wasm32-unknown/Dockerfile
index 0972eb85191..6c0ec1ad9d4 100644
--- a/src/ci/docker/wasm32-unknown/Dockerfile
+++ b/src/ci/docker/wasm32-unknown/Dockerfile
@@ -26,6 +26,7 @@ ENV RUST_CONFIGURE_ARGS \
   --set rust.lld
 
 ENV SCRIPT python2.7 /checkout/x.py test --target $TARGETS \
+  src/test/run-make \
   src/test/ui \
   src/test/run-pass \
   src/test/compile-fail \
diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs
index 09d7ce599ab..8bcec79d99f 100644
--- a/src/librustc/dep_graph/dep_node.rs
+++ b/src/librustc/dep_graph/dep_node.rs
@@ -593,6 +593,7 @@ define_dep_nodes!( <'tcx>
     [] ImplementationsOfTrait { krate: CrateNum, trait_id: DefId },
     [] AllTraitImplementations(CrateNum),
 
+    [] DllimportForeignItems(CrateNum),
     [] IsDllimportForeignItem(DefId),
     [] IsStaticallyIncludedForeignItem(DefId),
     [] NativeLibraryKind(DefId),
@@ -655,6 +656,8 @@ define_dep_nodes!( <'tcx>
     [input] Features,
 
     [] ProgramClausesFor(DefId),
+    [] WasmImportModuleMap(CrateNum),
+    [] ForeignModules(CrateNum),
 );
 
 trait DepNodeParams<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> : fmt::Debug {
diff --git a/src/librustc/hir/check_attr.rs b/src/librustc/hir/check_attr.rs
index f141cac1614..9b2647ad4db 100644
--- a/src/librustc/hir/check_attr.rs
+++ b/src/librustc/hir/check_attr.rs
@@ -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);
     }
 
diff --git a/src/librustc/ich/impls_cstore.rs b/src/librustc/ich/impls_cstore.rs
index 18a02ff5c58..0071850e105 100644
--- a/src/librustc/ich/impls_cstore.rs
+++ b/src/librustc/ich/impls_cstore.rs
@@ -33,7 +33,12 @@ impl_stable_hash_for!(struct middle::cstore::NativeLibrary {
     kind,
     name,
     cfg,
-    foreign_items
+    foreign_module
+});
+
+impl_stable_hash_for!(struct middle::cstore::ForeignModule {
+    foreign_items,
+    def_id
 });
 
 impl_stable_hash_for!(enum middle::cstore::LinkagePreference {
diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs
index 3c451d7ae46..add9b621596 100644
--- a/src/librustc/middle/cstore.rs
+++ b/src/librustc/middle/cstore.rs
@@ -132,7 +132,13 @@ pub struct NativeLibrary {
     pub kind: NativeLibraryKind,
     pub name: Symbol,
     pub cfg: Option<ast::MetaItem>,
+    pub foreign_module: Option<DefId>,
+}
+
+#[derive(Clone, Hash, RustcEncodable, RustcDecodable)]
+pub struct ForeignModule {
     pub foreign_items: Vec<DefId>,
+    pub def_id: DefId,
 }
 
 pub enum LoadedMacro {
diff --git a/src/librustc/ty/maps/config.rs b/src/librustc/ty/maps/config.rs
index 0b41c3ab2fa..7565e90df98 100644
--- a/src/librustc/ty/maps/config.rs
+++ b/src/librustc/ty/maps/config.rs
@@ -430,6 +430,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::native_libraries<'tcx> {
     }
 }
 
+impl<'tcx> QueryDescription<'tcx> for queries::foreign_modules<'tcx> {
+    fn describe(_tcx: TyCtxt, _: CrateNum) -> String {
+        format!("looking up the foreign modules of a linked crate")
+    }
+}
+
 impl<'tcx> QueryDescription<'tcx> for queries::plugin_registrar_fn<'tcx> {
     fn describe(_tcx: TyCtxt, _: CrateNum) -> String {
         format!("looking up the plugin registrar for a crate")
@@ -705,6 +711,18 @@ impl<'tcx> QueryDescription<'tcx> for queries::program_clauses_for<'tcx> {
     }
 }
 
+impl<'tcx> QueryDescription<'tcx> for queries::wasm_import_module_map<'tcx> {
+    fn describe(_tcx: TyCtxt, _: CrateNum) -> String {
+        format!("wasm import module map")
+    }
+}
+
+impl<'tcx> QueryDescription<'tcx> for queries::dllimport_foreign_items<'tcx> {
+    fn describe(_tcx: TyCtxt, _: CrateNum) -> String {
+        format!("wasm import module map")
+    }
+}
+
 macro_rules! impl_disk_cacheable_query(
     ($query_name:ident, |$key:tt| $cond:expr) => {
         impl<'tcx> QueryDescription<'tcx> for queries::$query_name<'tcx> {
diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs
index 2b4c1992762..c16ad0d8ca1 100644
--- a/src/librustc/ty/maps/mod.rs
+++ b/src/librustc/ty/maps/mod.rs
@@ -18,7 +18,7 @@ use infer::canonical::{Canonical, QueryResult};
 use lint;
 use middle::borrowck::BorrowCheckResult;
 use middle::cstore::{ExternCrate, LinkagePreference, NativeLibrary,
-                     ExternBodyNestedBodies};
+                     ExternBodyNestedBodies, ForeignModule};
 use middle::cstore::{NativeLibraryKind, DepKind, CrateSource, ExternConstBody};
 use middle::privacy::AccessLevels;
 use middle::reachable::ReachableSet;
@@ -320,6 +320,9 @@ define_maps! { <'tcx>
 
 
     [] fn native_libraries: NativeLibraries(CrateNum) -> Lrc<Vec<NativeLibrary>>,
+
+    [] fn foreign_modules: ForeignModules(CrateNum) -> Lrc<Vec<ForeignModule>>,
+
     [] fn plugin_registrar_fn: PluginRegistrarFn(CrateNum) -> Option<DefId>,
     [] fn derive_registrar_fn: DeriveRegistrarFn(CrateNum) -> Option<DefId>,
     [] fn crate_disambiguator: CrateDisambiguator(CrateNum) -> CrateDisambiguator,
@@ -331,6 +334,8 @@ define_maps! { <'tcx>
     [] fn all_trait_implementations: AllTraitImplementations(CrateNum)
         -> Lrc<Vec<DefId>>,
 
+    [] fn dllimport_foreign_items: DllimportForeignItems(CrateNum)
+        -> Lrc<FxHashSet<DefId>>,
     [] fn is_dllimport_foreign_item: IsDllimportForeignItem(DefId) -> bool,
     [] fn is_statically_included_foreign_item: IsStaticallyIncludedForeignItem(DefId) -> bool,
     [] fn native_library_kind: NativeLibraryKind(DefId)
@@ -426,6 +431,8 @@ define_maps! { <'tcx>
     [] fn program_clauses_for: ProgramClausesFor(DefId) -> Lrc<Vec<Clause<'tcx>>>,
 
     [] fn wasm_custom_sections: WasmCustomSections(CrateNum) -> Lrc<Vec<DefId>>,
+    [] fn wasm_import_module_map: WasmImportModuleMap(CrateNum)
+        -> Lrc<FxHashMap<DefId, String>>,
 }
 
 //////////////////////////////////////////////////////////////////////
diff --git a/src/librustc/ty/maps/plumbing.rs b/src/librustc/ty/maps/plumbing.rs
index 910c00b832e..46106d8ec0e 100644
--- a/src/librustc/ty/maps/plumbing.rs
+++ b/src/librustc/ty/maps/plumbing.rs
@@ -886,6 +886,9 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
             force!(all_trait_implementations, krate!());
         }
 
+        DepKind::DllimportForeignItems => {
+            force!(dllimport_foreign_items, krate!());
+        }
         DepKind::IsDllimportForeignItem => {
             force!(is_dllimport_foreign_item, def_id!());
         }
@@ -941,6 +944,8 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
 
         DepKind::ProgramClausesFor => { force!(program_clauses_for, def_id!()); }
         DepKind::WasmCustomSections => { force!(wasm_custom_sections, krate!()); }
+        DepKind::WasmImportModuleMap => { force!(wasm_import_module_map, krate!()); }
+        DepKind::ForeignModules => { force!(foreign_modules, krate!()); }
     }
 
     true
diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs
index a0546b369a8..baaf57c8908 100644
--- a/src/librustc_metadata/creader.rs
+++ b/src/librustc_metadata/creader.rs
@@ -12,7 +12,6 @@
 
 use cstore::{self, CStore, CrateSource, MetadataBlob};
 use locator::{self, CratePaths};
-use native_libs::relevant_lib;
 use schema::CrateRoot;
 use rustc_data_structures::sync::{Lrc, RwLock, Lock};
 
@@ -230,7 +229,7 @@ impl<'a> CrateLoader<'a> {
             .map(|trait_impls| (trait_impls.trait_id, trait_impls.impls))
             .collect();
 
-        let mut cmeta = cstore::CrateMetadata {
+        let cmeta = cstore::CrateMetadata {
             name,
             extern_crate: Lock::new(None),
             def_path_table: Lrc::new(def_path_table),
@@ -250,25 +249,8 @@ impl<'a> CrateLoader<'a> {
                 rlib,
                 rmeta,
             },
-            // Initialize this with an empty set. The field is populated below
-            // after we were able to deserialize its contents.
-            dllimport_foreign_items: FxHashSet(),
         };
 
-        let dllimports: FxHashSet<_> = cmeta
-            .root
-            .native_libraries
-            .decode((&cmeta, self.sess))
-            .filter(|lib| relevant_lib(self.sess, lib) &&
-                          lib.kind == cstore::NativeLibraryKind::NativeUnknown)
-            .flat_map(|lib| {
-                assert!(lib.foreign_items.iter().all(|def_id| def_id.krate == cnum));
-                lib.foreign_items.into_iter().map(|def_id| def_id.index)
-            })
-            .collect();
-
-        cmeta.dllimport_foreign_items = dllimports;
-
         let cmeta = Lrc::new(cmeta);
         self.cstore.set_crate_data(cnum, cmeta.clone());
         (cnum, cmeta)
diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs
index bd5ad93946e..53986f07410 100644
--- a/src/librustc_metadata/cstore.rs
+++ b/src/librustc_metadata/cstore.rs
@@ -20,7 +20,7 @@ use rustc::middle::cstore::{DepKind, ExternCrate, MetadataLoader};
 use rustc::session::{Session, CrateDisambiguator};
 use rustc_back::PanicStrategy;
 use rustc_data_structures::indexed_vec::IndexVec;
-use rustc::util::nodemap::{FxHashMap, FxHashSet, NodeMap};
+use rustc::util::nodemap::{FxHashMap, NodeMap};
 
 use rustc_data_structures::sync::{Lrc, RwLock, Lock};
 use syntax::{ast, attr};
@@ -30,7 +30,7 @@ use syntax_pos;
 
 pub use rustc::middle::cstore::{NativeLibrary, NativeLibraryKind, LinkagePreference};
 pub use rustc::middle::cstore::NativeLibraryKind::*;
-pub use rustc::middle::cstore::{CrateSource, LibSource};
+pub use rustc::middle::cstore::{CrateSource, LibSource, ForeignModule};
 
 pub use cstore_impl::{provide, provide_extern};
 
@@ -84,8 +84,6 @@ pub struct CrateMetadata {
     pub source: CrateSource,
 
     pub proc_macros: Option<Vec<(ast::Name, Lrc<SyntaxExtension>)>>,
-    // Foreign items imported from a dylib (Windows only)
-    pub dllimport_foreign_items: FxHashSet<DefIndex>,
 }
 
 pub struct CStore {
diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs
index 3c2f984ef8b..e911a03bbe2 100644
--- a/src/librustc_metadata/cstore_impl.rs
+++ b/src/librustc_metadata/cstore_impl.rs
@@ -12,6 +12,7 @@ use cstore;
 use encoder;
 use link_args;
 use native_libs;
+use foreign_modules;
 use schema;
 
 use rustc::ty::maps::QueryConfig;
@@ -197,6 +198,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
         Lrc::new(reachable_non_generics)
     }
     native_libraries => { Lrc::new(cdata.get_native_libraries(tcx.sess)) }
+    foreign_modules => { Lrc::new(cdata.get_foreign_modules(tcx.sess)) }
     plugin_registrar_fn => {
         cdata.root.plugin_registrar_fn.map(|index| {
             DefId { krate: def_id.krate, index }
@@ -224,9 +226,6 @@ provide! { <'tcx> tcx, def_id, other, cdata,
         Lrc::new(result)
     }
 
-    is_dllimport_foreign_item => {
-        cdata.is_dllimport_foreign_item(def_id.index)
-    }
     visibility => { cdata.get_visibility(def_id.index) }
     dep_kind => {
         let r = *cdata.dep_kind.lock();
@@ -306,13 +305,28 @@ pub fn provide<'tcx>(providers: &mut Providers<'tcx>) {
             tcx.native_libraries(id.krate)
                 .iter()
                 .filter(|lib| native_libs::relevant_lib(&tcx.sess, lib))
-                .find(|l| l.foreign_items.contains(&id))
+                .find(|lib| {
+                    let fm_id = match lib.foreign_module {
+                        Some(id) => id,
+                        None => return false,
+                    };
+                    tcx.foreign_modules(id.krate)
+                        .iter()
+                        .find(|m| m.def_id == fm_id)
+                        .expect("failed to find foreign module")
+                        .foreign_items
+                        .contains(&id)
+                })
                 .map(|l| l.kind)
         },
         native_libraries: |tcx, cnum| {
             assert_eq!(cnum, LOCAL_CRATE);
             Lrc::new(native_libs::collect(tcx))
         },
+        foreign_modules: |tcx, cnum| {
+            assert_eq!(cnum, LOCAL_CRATE);
+            Lrc::new(foreign_modules::collect(tcx))
+        },
         link_args: |tcx, cnum| {
             assert_eq!(cnum, LOCAL_CRATE);
             Lrc::new(link_args::collect(tcx))
diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs
index 0e5df3142af..e72f9ddd82a 100644
--- a/src/librustc_metadata/decoder.rs
+++ b/src/librustc_metadata/decoder.rs
@@ -10,7 +10,7 @@
 
 // Decoding metadata from a single crate's metadata
 
-use cstore::{self, CrateMetadata, MetadataBlob, NativeLibrary};
+use cstore::{self, CrateMetadata, MetadataBlob, NativeLibrary, ForeignModule};
 use schema::*;
 
 use rustc_data_structures::sync::{Lrc, ReadGuard};
@@ -1031,6 +1031,10 @@ impl<'a, 'tcx> CrateMetadata {
         self.root.native_libraries.decode((self, sess)).collect()
     }
 
+    pub fn get_foreign_modules(&self, sess: &Session) -> Vec<ForeignModule> {
+        self.root.foreign_modules.decode((self, sess)).collect()
+    }
+
     pub fn get_dylib_dependency_formats(&self) -> Vec<(CrateNum, LinkagePreference)> {
         self.root
             .dylib_dependency_formats
@@ -1103,10 +1107,6 @@ impl<'a, 'tcx> CrateMetadata {
         }
     }
 
-    pub fn is_dllimport_foreign_item(&self, id: DefIndex) -> bool {
-        self.dllimport_foreign_items.contains(&id)
-    }
-
     pub fn fn_sig(&self,
                   id: DefIndex,
                   tcx: TyCtxt<'a, 'tcx, 'tcx>)
diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs
index 56981b8f4a1..39de1ec852e 100644
--- a/src/librustc_metadata/encoder.rs
+++ b/src/librustc_metadata/encoder.rs
@@ -14,7 +14,7 @@ use isolated_encoder::IsolatedEncoder;
 use schema::*;
 
 use rustc::middle::cstore::{LinkMeta, LinkagePreference, NativeLibrary,
-                            EncodedMetadata};
+                            EncodedMetadata, ForeignModule};
 use rustc::hir::def::CtorKind;
 use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefIndex, DefId, LocalDefId, LOCAL_CRATE};
 use rustc::hir::map::definitions::DefPathTable;
@@ -412,6 +412,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             ());
         let native_lib_bytes = self.position() - i;
 
+        let foreign_modules = self.tracked(
+            IsolatedEncoder::encode_foreign_modules,
+            ());
+
         // Encode codemap
         i = self.position();
         let codemap = self.encode_codemap();
@@ -480,6 +484,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             lang_items,
             lang_items_missing,
             native_libraries,
+            foreign_modules,
             codemap,
             def_path_table,
             impls,
@@ -1337,6 +1342,11 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> {
         self.lazy_seq(used_libraries.iter().cloned())
     }
 
+    fn encode_foreign_modules(&mut self, _: ()) -> LazySeq<ForeignModule> {
+        let foreign_modules = self.tcx.foreign_modules(LOCAL_CRATE);
+        self.lazy_seq(foreign_modules.iter().cloned())
+    }
+
     fn encode_crate_deps(&mut self, _: ()) -> LazySeq<CrateDep> {
         let crates = self.tcx.crates();
 
diff --git a/src/librustc_metadata/foreign_modules.rs b/src/librustc_metadata/foreign_modules.rs
new file mode 100644
index 00000000000..c44d891b7f3
--- /dev/null
+++ b/src/librustc_metadata/foreign_modules.rs
@@ -0,0 +1,48 @@
+// Copyright 2017 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.
+
+use rustc::hir::itemlikevisit::ItemLikeVisitor;
+use rustc::hir;
+use rustc::middle::cstore::ForeignModule;
+use rustc::ty::TyCtxt;
+
+pub fn collect<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Vec<ForeignModule> {
+    let mut collector = Collector {
+        tcx,
+        modules: Vec::new(),
+    };
+    tcx.hir.krate().visit_all_item_likes(&mut collector);
+    return collector.modules
+}
+
+struct Collector<'a, 'tcx: 'a> {
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    modules: Vec<ForeignModule>,
+}
+
+impl<'a, 'tcx> ItemLikeVisitor<'tcx> for Collector<'a, 'tcx> {
+    fn visit_item(&mut self, it: &'tcx hir::Item) {
+        let fm = match it.node {
+            hir::ItemForeignMod(ref fm) => fm,
+            _ => return,
+        };
+
+        let foreign_items = fm.items.iter()
+            .map(|it| self.tcx.hir.local_def_id(it.id))
+            .collect();
+        self.modules.push(ForeignModule {
+            foreign_items,
+            def_id: self.tcx.hir.local_def_id(it.id),
+        });
+    }
+
+    fn visit_trait_item(&mut self, _it: &'tcx hir::TraitItem) {}
+    fn visit_impl_item(&mut self, _it: &'tcx hir::ImplItem) {}
+}
diff --git a/src/librustc_metadata/lib.rs b/src/librustc_metadata/lib.rs
index f77c22bd895..85099667449 100644
--- a/src/librustc_metadata/lib.rs
+++ b/src/librustc_metadata/lib.rs
@@ -56,6 +56,7 @@ mod isolated_encoder;
 mod schema;
 mod native_libs;
 mod link_args;
+mod foreign_modules;
 
 pub mod creader;
 pub mod cstore;
diff --git a/src/librustc_metadata/native_libs.rs b/src/librustc_metadata/native_libs.rs
index 2504f8dc251..4bb6d8fb87c 100644
--- a/src/librustc_metadata/native_libs.rs
+++ b/src/librustc_metadata/native_libs.rs
@@ -105,14 +105,11 @@ impl<'a, 'tcx> ItemLikeVisitor<'tcx> for Collector<'a, 'tcx> {
             } else {
                 None
             };
-            let foreign_items = fm.items.iter()
-                .map(|it| self.tcx.hir.local_def_id(it.id))
-                .collect();
             let lib = NativeLibrary {
                 name: n,
                 kind,
                 cfg,
-                foreign_items,
+                foreign_module: Some(self.tcx.hir.local_def_id(it.id)),
             };
             self.register_native_lib(Some(m.span), lib);
         }
@@ -218,7 +215,7 @@ impl<'a, 'tcx> Collector<'a, 'tcx> {
                     name: Symbol::intern(new_name.unwrap_or(name)),
                     kind: if let Some(k) = kind { k } else { cstore::NativeUnknown },
                     cfg: None,
-                    foreign_items: Vec::new(),
+                    foreign_module: None,
                 };
                 self.register_native_lib(None, lib);
             }
diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs
index 001772623e7..98327945297 100644
--- a/src/librustc_metadata/schema.rs
+++ b/src/librustc_metadata/schema.rs
@@ -15,8 +15,8 @@ use rustc::hir;
 use rustc::hir::def::{self, CtorKind};
 use rustc::hir::def_id::{DefIndex, DefId, CrateNum};
 use rustc::ich::StableHashingContext;
-use rustc::middle::cstore::{DepKind, LinkagePreference, NativeLibrary};
 use rustc::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel};
+use rustc::middle::cstore::{DepKind, LinkagePreference, NativeLibrary, ForeignModule};
 use rustc::middle::lang_items;
 use rustc::mir;
 use rustc::session::CrateDisambiguator;
@@ -200,6 +200,7 @@ pub struct CrateRoot {
     pub lang_items: LazySeq<(DefIndex, usize)>,
     pub lang_items_missing: LazySeq<lang_items::LangItem>,
     pub native_libraries: LazySeq<NativeLibrary>,
+    pub foreign_modules: LazySeq<ForeignModule>,
     pub codemap: LazySeq<syntax_pos::FileMap>,
     pub def_path_table: Lazy<hir::map::definitions::DefPathTable>,
     pub impls: LazySeq<TraitImpls>,
diff --git a/src/librustc_trans/attributes.rs b/src/librustc_trans/attributes.rs
index 16253aa92ac..df78ccdd229 100644
--- a/src/librustc_trans/attributes.rs
+++ b/src/librustc_trans/attributes.rs
@@ -18,6 +18,7 @@ use rustc::session::config::Sanitizer;
 use rustc::ty::TyCtxt;
 use rustc::ty::maps::Providers;
 use rustc_data_structures::sync::Lrc;
+use rustc_data_structures::fx::FxHashMap;
 
 use llvm::{self, Attribute, ValueRef};
 use llvm::AttributePlace::Function;
@@ -141,6 +142,20 @@ pub fn from_fn_attrs(cx: &CodegenCx, llfn: ValueRef, id: DefId) {
             llfn, llvm::AttributePlace::Function,
             cstr("target-features\0"), &val);
     }
+
+    // Note that currently the `wasm-import-module` doesn't do anything, but
+    // eventually LLVM 7 should read this and ferry the appropriate import
+    // module to the output file.
+    if cx.tcx.sess.target.target.arch == "wasm32" {
+        if let Some(module) = wasm_import_module(cx.tcx, id) {
+            llvm::AddFunctionAttrStringValue(
+                llfn,
+                llvm::AttributePlace::Function,
+                cstr("wasm-import-module\0"),
+                &module,
+            );
+        }
+    }
 }
 
 fn cstr(s: &'static str) -> &CStr {
@@ -170,6 +185,8 @@ pub fn provide(providers: &mut Providers) {
         tcx.hir.krate().visit_all_item_likes(&mut finder);
         Lrc::new(finder.list)
     };
+
+    provide_extern(providers);
 }
 
 struct WasmSectionFinder<'a, 'tcx: 'a> {
@@ -192,3 +209,32 @@ impl<'a, 'tcx: 'a> ItemLikeVisitor<'tcx> for WasmSectionFinder<'a, 'tcx> {
 
     fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) {}
 }
+
+pub fn provide_extern(providers: &mut Providers) {
+    providers.wasm_import_module_map = |tcx, cnum| {
+        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 = match module {
+                Some(s) => s,
+                None => continue,
+            };
+            for id in lib.foreign_items.iter() {
+                assert_eq!(id.krate, cnum);
+                ret.insert(*id, module.to_string());
+            }
+        }
+
+        Lrc::new(ret)
+    }
+}
+
+fn wasm_import_module(tcx: TyCtxt, id: DefId) -> Option<CString> {
+    tcx.wasm_import_module_map(id.krate)
+        .get(&id)
+        .map(|s| CString::new(&s[..]).unwrap())
+}
diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs
index 8e8ba823b6f..542cdc5baad 100644
--- a/src/librustc_trans/back/link.rs
+++ b/src/librustc_trans/back/link.rs
@@ -813,6 +813,7 @@ fn link_natively(sess: &Session,
     }
 
     if sess.opts.target_triple == "wasm32-unknown-unknown" {
+        wasm::rewrite_imports(&out_filename, &trans.crate_info.wasm_imports);
         wasm::add_custom_sections(&out_filename,
                                   &trans.crate_info.wasm_custom_sections);
     }
diff --git a/src/librustc_trans/back/wasm.rs b/src/librustc_trans/back/wasm.rs
index 99f1e4b7e78..d6d386c9fbe 100644
--- a/src/librustc_trans/back/wasm.rs
+++ b/src/librustc_trans/back/wasm.rs
@@ -8,37 +8,254 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use std::collections::BTreeMap;
 use std::fs;
 use std::path::Path;
-use std::collections::BTreeMap;
+use std::str;
 
+use rustc_data_structures::fx::FxHashMap;
 use serialize::leb128;
 
+// https://webassembly.github.io/spec/core/binary/modules.html#binary-importsec
+const WASM_IMPORT_SECTION_ID: u8 = 2;
+
+const WASM_EXTERNAL_KIND_FUNCTION: u8 = 0;
+const WASM_EXTERNAL_KIND_TABLE: u8 = 1;
+const WASM_EXTERNAL_KIND_MEMORY: u8 = 2;
+const WASM_EXTERNAL_KIND_GLOBAL: u8 = 3;
+
+/// Append all the custom sections listed in `sections` to the wasm binary
+/// specified at `path`.
+///
+/// LLVM 6 which we're using right now doesn't have the ability to create custom
+/// sections in wasm files nor does LLD have the ability to merge these sections
+/// into one larger section when linking. It's expected that this will
+/// eventually get implemented, however!
+///
+/// Until that time though this is a custom implementation in rustc to append
+/// all sections to a wasm file to the finished product that LLD produces.
+///
+/// Support for this is landing in LLVM in https://reviews.llvm.org/D43097,
+/// although after that support will need to be in LLD as well.
 pub fn add_custom_sections(path: &Path, sections: &BTreeMap<String, Vec<u8>>) {
-    let mut wasm = fs::read(path).expect("failed to read wasm output");
+    if sections.len() == 0 {
+        return
+    }
+
+    let wasm = fs::read(path).expect("failed to read wasm output");
 
     // see https://webassembly.github.io/spec/core/binary/modules.html#custom-section
+    let mut wasm = WasmEncoder { data: wasm };
     for (section, bytes) in sections {
         // write the `id` identifier, 0 for a custom section
-        let len = wasm.len();
-        leb128::write_u32_leb128(&mut wasm, len, 0);
+        wasm.byte(0);
 
         // figure out how long our name descriptor will be
-        let mut name = Vec::new();
-        leb128::write_u32_leb128(&mut name, 0, section.len() as u32);
-        name.extend_from_slice(section.as_bytes());
+        let mut name = WasmEncoder::new();
+        name.str(section);
+
+        // write the length of the payload followed by all its contents
+        wasm.u32((bytes.len() + name.data.len()) as u32);
+        wasm.data.extend_from_slice(&name.data);
+        wasm.data.extend_from_slice(bytes);
+    }
+
+    fs::write(path, &wasm.data).expect("failed to write wasm output");
+}
+
+/// Rewrite the module imports are listed from in a wasm module given the field
+/// name to module name mapping in `import_map`.
+///
+/// LLVM 6 which we're using right now doesn't have the ability to configure the
+/// module a wasm symbol is import from. Rather all imported symbols come from
+/// the bland `"env"` module unconditionally. Furthermore we'd *also* need
+/// support in LLD for preserving these import modules, which it unfortunately
+/// currently does not.
+///
+/// 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.
+///
+/// Support for this was added to LLVM in
+/// https://github.com/llvm-mirror/llvm/commit/0f32e1365, although support still
+/// needs to be added (AFAIK at the time of this writing) to LLD
+pub fn rewrite_imports(path: &Path, import_map: &FxHashMap<String, String>) {
+    if import_map.len() == 0 {
+        return
+    }
+
+    let wasm = fs::read(path).expect("failed to read wasm output");
+    let mut ret = WasmEncoder::new();
+    ret.data.extend(&wasm[..8]);
+
+    // skip the 8 byte wasm/version header
+    for (id, raw) in WasmSections(WasmDecoder::new(&wasm[8..])) {
+        ret.byte(id);
+        if id == WASM_IMPORT_SECTION_ID {
+            info!("rewriting import section");
+            let data = rewrite_import_section(
+                &mut WasmDecoder::new(raw),
+                import_map,
+            );
+            ret.bytes(&data);
+        } else {
+            info!("carry forward section {}, {} bytes long", id, raw.len());
+            ret.bytes(raw);
+        }
+    }
+
+    fs::write(path, &ret.data).expect("failed to write wasm output");
+
+    fn rewrite_import_section(
+        wasm: &mut WasmDecoder,
+        import_map: &FxHashMap<String, String>,
+    )
+        -> Vec<u8>
+    {
+        let mut dst = WasmEncoder::new();
+        let n = wasm.u32();
+        dst.u32(n);
+        info!("rewriting {} imports", n);
+        for _ in 0..n {
+            rewrite_import_entry(wasm, &mut dst, import_map);
+        }
+        return dst.data
+    }
+
+    fn rewrite_import_entry(wasm: &mut WasmDecoder,
+                            dst: &mut WasmEncoder,
+                            import_map: &FxHashMap<String, String>) {
+        // More info about the binary format here is available at:
+        // https://webassembly.github.io/spec/core/binary/modules.html#import-section
+        //
+        // Note that you can also find the whole point of existence of this
+        // function here, where we map the `module` name to a different one if
+        // we've got one listed.
+        let module = wasm.str();
+        let field = wasm.str();
+        let new_module = if module == "env" {
+            import_map.get(field).map(|s| &**s).unwrap_or(module)
+        } else {
+            module
+        };
+        info!("import rewrite ({} => {}) / {}", module, new_module, field);
+        dst.str(new_module);
+        dst.str(field);
+        let kind = wasm.byte();
+        dst.byte(kind);
+        match kind {
+            WASM_EXTERNAL_KIND_FUNCTION => dst.u32(wasm.u32()),
+            WASM_EXTERNAL_KIND_TABLE => {
+                dst.byte(wasm.byte()); // element_type
+                dst.limits(wasm.limits());
+            }
+            WASM_EXTERNAL_KIND_MEMORY => dst.limits(wasm.limits()),
+            WASM_EXTERNAL_KIND_GLOBAL => {
+                dst.byte(wasm.byte()); // content_type
+                dst.bool(wasm.bool()); // mutable
+            }
+            b => panic!("unknown kind: {}", b),
+        }
+    }
+}
 
-        // write the length of the payload
-        let len = wasm.len();
-        let total_len = bytes.len() + name.len();
-        leb128::write_u32_leb128(&mut wasm, len, total_len as u32);
+struct WasmSections<'a>(WasmDecoder<'a>);
 
-        // write out the name section
-        wasm.extend(name);
+impl<'a> Iterator for WasmSections<'a> {
+    type Item = (u8, &'a [u8]);
 
-        // and now the payload itself
-        wasm.extend_from_slice(bytes);
+    fn next(&mut self) -> Option<(u8, &'a [u8])> {
+        if self.0.data.len() == 0 {
+            return None
+        }
+
+        // see https://webassembly.github.io/spec/core/binary/modules.html#sections
+        let id = self.0.byte();
+        let section_len = self.0.u32();
+        info!("new section {} / {} bytes", id, section_len);
+        let section = self.0.skip(section_len as usize);
+        Some((id, section))
     }
+}
+
+struct WasmDecoder<'a> {
+    data: &'a [u8],
+}
 
-    fs::write(path, &wasm).expect("failed to write wasm output");
+impl<'a> WasmDecoder<'a> {
+    fn new(data: &'a [u8]) -> WasmDecoder<'a> {
+        WasmDecoder { data }
+    }
+
+    fn byte(&mut self) -> u8 {
+        self.skip(1)[0]
+    }
+
+    fn u32(&mut self) -> u32 {
+        let (n, l1) = leb128::read_u32_leb128(self.data);
+        self.data = &self.data[l1..];
+        return n
+    }
+
+    fn skip(&mut self, amt: usize) -> &'a [u8] {
+        let (data, rest) = self.data.split_at(amt);
+        self.data = rest;
+        data
+    }
+
+    fn str(&mut self) -> &'a str {
+        let len = self.u32();
+        str::from_utf8(self.skip(len as usize)).unwrap()
+    }
+
+    fn bool(&mut self) -> bool {
+        self.byte() == 1
+    }
+
+    fn limits(&mut self) -> (u32, Option<u32>) {
+        let has_max = self.bool();
+        (self.u32(), if has_max { Some(self.u32()) } else { None })
+    }
+}
+
+struct WasmEncoder {
+    data: Vec<u8>,
+}
+
+impl WasmEncoder {
+    fn new() -> WasmEncoder {
+        WasmEncoder { data: Vec::new() }
+    }
+
+    fn u32(&mut self, val: u32) {
+        let at = self.data.len();
+        leb128::write_u32_leb128(&mut self.data, at, val);
+    }
+
+    fn byte(&mut self, val: u8) {
+        self.data.push(val);
+    }
+
+    fn bytes(&mut self, val: &[u8]) {
+        self.u32(val.len() as u32);
+        self.data.extend_from_slice(val);
+    }
+
+    fn str(&mut self, val: &str) {
+        self.bytes(val.as_bytes())
+    }
+
+    fn bool(&mut self, b: bool) {
+        self.byte(b as u8);
+    }
+
+    fn limits(&mut self, limits: (u32, Option<u32>)) {
+        self.bool(limits.1.is_some());
+        self.u32(limits.0);
+        if let Some(c) = limits.1 {
+            self.u32(c);
+        }
+    }
 }
diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs
index 11f952bc5bc..56eece9f31e 100644
--- a/src/librustc_trans/base.rs
+++ b/src/librustc_trans/base.rs
@@ -72,6 +72,7 @@ use type_::Type;
 use type_of::LayoutLlvmExt;
 use rustc::util::nodemap::{FxHashMap, FxHashSet, DefIdSet};
 use CrateInfo;
+use rustc_data_structures::sync::Lrc;
 
 use std::any::Any;
 use std::collections::BTreeMap;
@@ -1072,14 +1073,15 @@ impl CrateInfo {
             used_crates_static: cstore::used_crates(tcx, LinkagePreference::RequireStatic),
             used_crate_source: FxHashMap(),
             wasm_custom_sections: BTreeMap::new(),
+            wasm_imports: FxHashMap(),
         };
 
-        let load_wasm_sections = tcx.sess.crate_types.borrow()
+        let load_wasm_items = tcx.sess.crate_types.borrow()
             .iter()
             .any(|c| *c != config::CrateTypeRlib) &&
             tcx.sess.opts.target_triple == "wasm32-unknown-unknown";
 
-        if load_wasm_sections {
+        if load_wasm_items {
             info!("attempting to load all wasm sections");
             for &id in tcx.wasm_custom_sections(LOCAL_CRATE).iter() {
                 let (name, contents) = fetch_wasm_section(tcx, id);
@@ -1087,6 +1089,7 @@ impl CrateInfo {
                     .or_insert(Vec::new())
                     .extend(contents);
             }
+            info.load_wasm_imports(tcx, LOCAL_CRATE);
         }
 
         for &cnum in tcx.crates().iter() {
@@ -1108,19 +1111,27 @@ impl CrateInfo {
             if tcx.is_no_builtins(cnum) {
                 info.is_no_builtins.insert(cnum);
             }
-            if load_wasm_sections {
+            if load_wasm_items {
                 for &id in tcx.wasm_custom_sections(cnum).iter() {
                     let (name, contents) = fetch_wasm_section(tcx, id);
                     info.wasm_custom_sections.entry(name)
                         .or_insert(Vec::new())
                         .extend(contents);
                 }
+                info.load_wasm_imports(tcx, cnum);
             }
         }
 
-
         return info
     }
+
+    fn load_wasm_imports(&mut self, tcx: TyCtxt, cnum: CrateNum) {
+        for (&id, module) in tcx.wasm_import_module_map(cnum).iter() {
+            let instance = Instance::mono(tcx, id);
+            let import_name = tcx.symbol_name(instance);
+            self.wasm_imports.insert(import_name.to_string(), module.clone());
+        }
+    }
 }
 
 fn is_translated_item(tcx: TyCtxt, id: DefId) -> bool {
@@ -1248,6 +1259,39 @@ pub fn provide(providers: &mut Providers) {
             .expect(&format!("failed to find cgu with name {:?}", name))
     };
     providers.compile_codegen_unit = compile_codegen_unit;
+
+    provide_extern(providers);
+}
+
+pub fn provide_extern(providers: &mut Providers) {
+    providers.dllimport_foreign_items = |tcx, krate| {
+        let module_map = tcx.foreign_modules(krate);
+        let module_map = module_map.iter()
+            .map(|lib| (lib.def_id, lib))
+            .collect::<FxHashMap<_, _>>();
+
+        let dllimports = tcx.native_libraries(krate)
+            .iter()
+            .filter(|lib| {
+                if lib.kind != cstore::NativeLibraryKind::NativeUnknown {
+                    return false
+                }
+                let cfg = match lib.cfg {
+                    Some(ref cfg) => cfg,
+                    None => return true,
+                };
+                attr::cfg_matches(cfg, &tcx.sess.parse_sess, None)
+            })
+            .filter_map(|lib| lib.foreign_module)
+            .map(|id| &module_map[&id])
+            .flat_map(|module| module.foreign_items.iter().cloned())
+            .collect();
+        Lrc::new(dllimports)
+    };
+
+    providers.is_dllimport_foreign_item = |tcx, def_id| {
+        tcx.dllimport_foreign_items(def_id.krate).contains(&def_id)
+    };
 }
 
 pub fn linkage_to_llvm(linkage: Linkage) -> llvm::Linkage {
diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs
index bb2aeca3748..9f654c8ab29 100644
--- a/src/librustc_trans/lib.rs
+++ b/src/librustc_trans/lib.rs
@@ -216,6 +216,8 @@ impl TransCrate for LlvmTransCrate {
 
     fn provide_extern(&self, providers: &mut ty::maps::Providers) {
         back::symbol_export::provide_extern(providers);
+        base::provide_extern(providers);
+        attributes::provide_extern(providers);
     }
 
     fn trans_crate<'a, 'tcx>(
@@ -403,6 +405,7 @@ struct CrateInfo {
     used_crates_static: Vec<(CrateNum, LibSource)>,
     used_crates_dynamic: Vec<(CrateNum, LibSource)>,
     wasm_custom_sections: BTreeMap<String, Vec<u8>>,
+    wasm_imports: FxHashMap<String, String>,
 }
 
 __build_diagnostic_array! { librustc_trans, DIAGNOSTICS }
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index dbcfee208ca..270cee26948 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -454,6 +454,9 @@ declare_features! (
 
     // The #[wasm_custom_section] attribute
     (active, wasm_custom_section, "1.26.0", None, None),
+
+    // The #![wasm_import_module] attribute
+    (active, wasm_import_module, "1.26.0", None, None),
 );
 
 declare_features! (
@@ -920,6 +923,10 @@ 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]` \
diff --git a/src/test/run-make/wasm-custom-section/foo.js b/src/test/run-make/wasm-custom-section/foo.js
index df69354f3a4..e7a4cfc344e 100644
--- a/src/test/run-make/wasm-custom-section/foo.js
+++ b/src/test/run-make/wasm-custom-section/foo.js
@@ -43,4 +43,4 @@ assert.strictEqual(section[1], 6);
 assert.strictEqual(section[2], 1);
 assert.strictEqual(section[3], 2);
 
-process.exit(1);
+process.exit(0);
diff --git a/src/test/run-make/wasm-import-module/Makefile b/src/test/run-make/wasm-import-module/Makefile
new file mode 100644
index 00000000000..399951e5163
--- /dev/null
+++ b/src/test/run-make/wasm-import-module/Makefile
@@ -0,0 +1,10 @@
+-include ../../run-make-fulldeps/tools.mk
+
+ifeq ($(TARGET),wasm32-unknown-unknown)
+all:
+	$(RUSTC) foo.rs --target wasm32-unknown-unknown
+	$(RUSTC) bar.rs -C lto -O --target wasm32-unknown-unknown
+	$(NODE) foo.js $(TMPDIR)/bar.wasm
+else
+all:
+endif
diff --git a/src/test/run-make/wasm-import-module/bar.rs b/src/test/run-make/wasm-import-module/bar.rs
new file mode 100644
index 00000000000..9e659223c65
--- /dev/null
+++ b/src/test/run-make/wasm-import-module/bar.rs
@@ -0,0 +1,29 @@
+// 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.
+
+#![crate_type = "cdylib"]
+#![feature(wasm_import_module)]
+#![deny(warnings)]
+
+extern crate foo;
+
+#[wasm_import_module = "./me"]
+extern {
+    #[link_name = "me_in_dep"]
+    fn dep();
+}
+
+#[no_mangle]
+pub extern fn foo() {
+    unsafe {
+        foo::dep();
+        dep();
+    }
+}
diff --git a/src/test/run-make/wasm-import-module/foo.js b/src/test/run-make/wasm-import-module/foo.js
new file mode 100644
index 00000000000..369962e55b1
--- /dev/null
+++ b/src/test/run-make/wasm-import-module/foo.js
@@ -0,0 +1,28 @@
+// 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.
+
+const fs = require('fs');
+const process = require('process');
+const assert = require('assert');
+const buffer = fs.readFileSync(process.argv[2]);
+
+let m = new WebAssembly.Module(buffer);
+let imports = WebAssembly.Module.imports(m);
+console.log('imports', imports);
+assert.strictEqual(imports.length, 2);
+
+assert.strictEqual(imports[0].kind, 'function');
+assert.strictEqual(imports[1].kind, 'function');
+
+let modules = [imports[0].module, imports[1].module];
+modules.sort();
+
+assert.strictEqual(modules[0], './dep');
+assert.strictEqual(modules[1], './me');
diff --git a/src/test/run-make/wasm-import-module/foo.rs b/src/test/run-make/wasm-import-module/foo.rs
new file mode 100644
index 00000000000..bcd2ca70bef
--- /dev/null
+++ b/src/test/run-make/wasm-import-module/foo.rs
@@ -0,0 +1,18 @@
+// 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.
+
+#![crate_type = "rlib"]
+#![feature(wasm_import_module)]
+#![deny(warnings)]
+
+#[wasm_import_module = "./dep"]
+extern {
+    pub fn dep();
+}
diff --git a/src/test/ui/feature-gate-wasm_custom_section.stderr b/src/test/ui/feature-gate-wasm_custom_section.stderr
index 5977ec9edad..1b4415539f4 100644
--- a/src/test/ui/feature-gate-wasm_custom_section.stderr
+++ b/src/test/ui/feature-gate-wasm_custom_section.stderr
@@ -8,4 +8,4 @@ LL | #[wasm_custom_section = "foo"] //~ ERROR: attribute is currently unstable
 
 error: aborting due to previous error
 
-If you want more information on this error, try using "rustc --explain E0658"
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/feature-gate-wasm_import_module.rs b/src/test/ui/feature-gate-wasm_import_module.rs
new file mode 100644
index 00000000000..c5898a9c126
--- /dev/null
+++ b/src/test/ui/feature-gate-wasm_import_module.rs
@@ -0,0 +1,15 @@
+// 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() {}
diff --git a/src/test/ui/feature-gate-wasm_import_module.stderr b/src/test/ui/feature-gate-wasm_import_module.stderr
new file mode 100644
index 00000000000..bae5fa9d595
--- /dev/null
+++ b/src/test/ui/feature-gate-wasm_import_module.stderr
@@ -0,0 +1,11 @@
+error[E0658]: experimental attribute
+  --> $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`.
diff --git a/src/test/ui/wasm-import-module.rs b/src/test/ui/wasm-import-module.rs
new file mode 100644
index 00000000000..0b743d9e486
--- /dev/null
+++ b/src/test/ui/wasm-import-module.rs
@@ -0,0 +1,20 @@
+// 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.
+
+#![feature(wasm_import_module)]
+
+#[wasm_import_module] //~ ERROR: must be of the form
+extern {}
+
+#[wasm_import_module = "foo"] //~ ERROR: must only be attached to
+fn foo() {}
+
+fn main() {}
+
diff --git a/src/test/ui/wasm-import-module.stderr b/src/test/ui/wasm-import-module.stderr
new file mode 100644
index 00000000000..bf301ce5269
--- /dev/null
+++ b/src/test/ui/wasm-import-module.stderr
@@ -0,0 +1,14 @@
+error: must be of the form #[wasm_import_module = "..."]
+  --> $DIR/wasm-import-module.rs:13:1
+   |
+LL | #[wasm_import_module] //~ ERROR: must be of the form
+   | ^^^^^^^^^^^^^^^^^^^^^
+
+error: must only be attached to foreign modules
+  --> $DIR/wasm-import-module.rs:16:1
+   |
+LL | #[wasm_import_module = "foo"] //~ ERROR: must only be attached to
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+