about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2016-03-25 18:09:28 -0700
committerbors <bors@rust-lang.org>2016-03-25 18:09:28 -0700
commit8d2d2be6c61c17da8027a72da91f87a0e2487f74 (patch)
treed48967badd66eabf6c53f32d0250ca1eba94ddf7
parenta1e29daf1a9ca4e26719887b0c934de5d1695031 (diff)
parent1ea93c2a6388b7dcddf1ae89105a9f6f2e1da9f1 (diff)
downloadrust-8d2d2be6c61c17da8027a72da91f87a0e2487f74.tar.gz
rust-8d2d2be6c61c17da8027a72da91f87a0e2487f74.zip
Auto merge of #32293 - nikomatsakis:incr-comp-def-path-munging, r=alexcrichton
Revamp symbol names for impls (and make them deterministic, etc)

This builds on @michaelwoerister's epic PR #31539 (note that his PR never landed, so I just incorporated it into this one). The main change here is that we remove the "name" from `DefPathData` for impls, since that name is synthetic and not sufficiently predictable for incr comp. However, just doing that would cause bad symbol names since those are based on the `DefPath`. Therefore, I introduce a new mechanism for getting symbol names (and also paths for user display) called `item_path`. This is kind of simplistic for now (based on strings) but I expect to expand it later to support richer types, hopefully generating C++-mangled names that gdb etc can understand. Along the way I cleaned up how we track the path that leads to an extern crate.

There is still some cleanup left undone here. Notably, I didn't remove the impl names altogether -- that would probably make sense. I also didn't try to remove the `item_symbols` vector. Mostly I want to unblock my other incr. comp. work. =)

r? @eddyb
cc @eddyb @alexcrichton @michaelwoerister
-rw-r--r--mk/tests.mk2
-rw-r--r--src/compiletest/common.rs4
-rw-r--r--src/compiletest/compiletest.rs12
-rw-r--r--src/compiletest/runtest.rs10
-rw-r--r--src/librbml/lib.rs4
-rw-r--r--src/librustc/front/map/collector.rs36
-rw-r--r--src/librustc/front/map/definitions.rs126
-rw-r--r--src/librustc/front/map/mod.rs19
-rw-r--r--src/librustc/middle/cstore.rs62
-rw-r--r--src/librustc/middle/traits/fulfill.rs2
-rw-r--r--src/librustc/middle/ty/context.rs28
-rw-r--r--src/librustc/middle/ty/item_path.rs317
-rw-r--r--src/librustc/middle/ty/mod.rs39
-rw-r--r--src/librustc/session/mod.rs10
-rw-r--r--src/librustc_back/svh.rs8
-rw-r--r--src/librustc_driver/driver.rs44
-rw-r--r--src/librustc_driver/lib.rs2
-rw-r--r--src/librustc_driver/test.rs1
-rw-r--r--src/librustc_metadata/astencode.rs10
-rw-r--r--src/librustc_metadata/common.rs1
-rw-r--r--src/librustc_metadata/creader.rs134
-rw-r--r--src/librustc_metadata/csearch.rs56
-rw-r--r--src/librustc_metadata/cstore.rs62
-rw-r--r--src/librustc_metadata/decoder.rs57
-rw-r--r--src/librustc_metadata/diagnostics.rs2
-rw-r--r--src/librustc_metadata/encoder.rs14
-rw-r--r--src/librustc_metadata/macro_import.rs11
-rw-r--r--src/librustc_metadata/tyencode.rs22
-rw-r--r--src/librustc_plugin/load.rs11
-rw-r--r--src/librustc_trans/back/link.rs237
-rw-r--r--src/librustc_trans/back/linker.rs1
-rw-r--r--src/librustc_trans/back/symbol_names.rs378
-rw-r--r--src/librustc_trans/lib.rs1
-rw-r--r--src/librustc_trans/save/mod.rs2
-rw-r--r--src/librustc_trans/trans/base.rs18
-rw-r--r--src/librustc_trans/trans/callee.rs10
-rw-r--r--src/librustc_trans/trans/closure.rs8
-rw-r--r--src/librustc_trans/trans/collector.rs22
-rw-r--r--src/librustc_trans/trans/consts.rs2
-rw-r--r--src/librustc_trans/trans/context.rs3
-rw-r--r--src/librustc_trans/trans/debuginfo/gdb.rs5
-rw-r--r--src/librustc_trans/trans/glue.rs9
-rw-r--r--src/librustc_trans/trans/inline.rs2
-rw-r--r--src/librustc_trans/trans/meth.rs5
-rw-r--r--src/librustc_trans/trans/mod.rs2
-rw-r--r--src/librustc_trans/trans/monomorphize.rs27
-rw-r--r--src/librustc_trans/trans/symbol_names_test.rs86
-rw-r--r--src/librustdoc/clean/mod.rs2
-rw-r--r--src/libsyntax/feature_gate.rs7
-rw-r--r--src/test/auxiliary/inline-default-methods.rs2
-rw-r--r--src/test/auxiliary/issue-13698.rs2
-rw-r--r--src/test/auxiliary/issue-15318.rs2
-rw-r--r--src/test/auxiliary/issue-17476.rs1
-rw-r--r--src/test/auxiliary/issue-17718-aux.rs (renamed from src/test/auxiliary/issue-17718.rs)0
-rw-r--r--src/test/auxiliary/issue-19190-3.rs2
-rw-r--r--src/test/auxiliary/issue-20646.rs2
-rw-r--r--src/test/auxiliary/issue-20727.rs2
-rw-r--r--src/test/auxiliary/issue-21092.rs2
-rw-r--r--src/test/auxiliary/issue-21801.rs2
-rw-r--r--src/test/auxiliary/issue-22025.rs2
-rw-r--r--src/test/auxiliary/issue-27362.rs2
-rw-r--r--src/test/auxiliary/issue-29584.rs2
-rw-r--r--src/test/auxiliary/typeid-intrinsic-aux1.rs (renamed from src/test/auxiliary/typeid-intrinsic.rs)0
-rw-r--r--src/test/auxiliary/typeid-intrinsic-aux2.rs (renamed from src/test/auxiliary/typeid-intrinsic2.rs)0
-rw-r--r--src/test/codegen-units/cross-crate-closures.rs8
-rw-r--r--src/test/codegen-units/cross-crate-generic-functions.rs8
-rw-r--r--src/test/codegen-units/cross-crate-trait-method.rs20
-rw-r--r--src/test/codegen-units/generic-drop-glue.rs10
-rw-r--r--src/test/codegen-units/generic-impl.rs24
-rw-r--r--src/test/codegen-units/impl-in-non-instantiated-generic.rs2
-rw-r--r--src/test/codegen-units/instantiation-through-vtable.rs8
-rw-r--r--src/test/codegen-units/non-generic-drop-glue.rs4
-rw-r--r--src/test/codegen-units/non-generic-functions.rs12
-rw-r--r--src/test/codegen-units/overloaded-operators.rs12
-rw-r--r--src/test/codegen-units/trait-implementations.rs20
-rw-r--r--src/test/codegen-units/trait-method-as-argument.rs2
-rw-r--r--src/test/codegen-units/transitive-drop-glue.rs6
-rw-r--r--src/test/codegen-units/tuple-drop-glue.rs2
-rw-r--r--src/test/codegen-units/unsizing.rs8
-rw-r--r--src/test/codegen-units/unused-traits-and-generics.rs2
-rw-r--r--src/test/compile-fail/symbol-names/basic.rs16
-rw-r--r--src/test/compile-fail/symbol-names/impl1.rs35
-rw-r--r--src/test/pretty/issue-4264.pp58
-rw-r--r--src/test/run-make/a-b-a-linker-guard/Makefile12
-rw-r--r--src/test/run-make/a-b-a-linker-guard/a.rs20
-rw-r--r--src/test/run-make/a-b-a-linker-guard/b.rs17
-rw-r--r--src/test/run-make/extern-overrides-distribution/Makefile2
-rw-r--r--src/test/run-make/issue-26006/Makefile2
-rw-r--r--src/test/run-make/relocation-model/Makefile4
-rw-r--r--src/test/run-make/reproducible-build/Makefile20
-rw-r--r--src/test/run-make/reproducible-build/reproducible-build-aux.rs38
-rw-r--r--src/test/run-make/reproducible-build/reproducible-build.rs128
-rw-r--r--src/test/run-pass/backtrace.rs22
-rw-r--r--src/test/run-pass/command-before-exec.rs2
-rw-r--r--src/test/run-pass/issue-17718.rs4
-rw-r--r--src/test/run-pass/typeid-intrinsic.rs8
96 files changed, 1833 insertions, 659 deletions
diff --git a/mk/tests.mk b/mk/tests.mk
index 7f5dbeff1e4..50c060c270a 100644
--- a/mk/tests.mk
+++ b/mk/tests.mk
@@ -383,7 +383,7 @@ $(3)/stage$(1)/test/$(4)test-$(2)$$(X_$(2)): \
 	@$$(call E, rustc: $$@)
 	$(Q)CFG_LLVM_LINKAGE_FILE=$$(LLVM_LINKAGE_PATH_$(2)) \
 	    $$(subst @,,$$(STAGE$(1)_T_$(2)_H_$(3))) -o $$@ $$< --test \
-		-L "$$(RT_OUTPUT_DIR_$(2))" \
+		-Cmetadata="test-crate" -L "$$(RT_OUTPUT_DIR_$(2))" \
 		$$(LLVM_LIBDIR_RUSTFLAGS_$(2)) \
 		$$(RUSTFLAGS_$(4))
 
diff --git a/src/compiletest/common.rs b/src/compiletest/common.rs
index 33ec974c527..bfcc1759b95 100644
--- a/src/compiletest/common.rs
+++ b/src/compiletest/common.rs
@@ -69,10 +69,10 @@ impl fmt::Display for Mode {
 #[derive(Clone)]
 pub struct Config {
     // The library paths required for running the compiler
-    pub compile_lib_path: String,
+    pub compile_lib_path: PathBuf,
 
     // The library paths required for running compiled programs
-    pub run_lib_path: String,
+    pub run_lib_path: PathBuf,
 
     // The rustc executable
     pub rustc_path: PathBuf,
diff --git a/src/compiletest/compiletest.rs b/src/compiletest/compiletest.rs
index 96b52eaa0ad..6c6a78a360b 100644
--- a/src/compiletest/compiletest.rs
+++ b/src/compiletest/compiletest.rs
@@ -118,9 +118,17 @@ pub fn parse_config(args: Vec<String> ) -> Config {
         }
     }
 
+    fn make_absolute(path: PathBuf) -> PathBuf {
+        if path.is_relative() {
+            env::current_dir().unwrap().join(path)
+        } else {
+            path
+        }
+    }
+
     Config {
-        compile_lib_path: matches.opt_str("compile-lib-path").unwrap(),
-        run_lib_path: matches.opt_str("run-lib-path").unwrap(),
+        compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
+        run_lib_path: make_absolute(opt_path(matches, "run-lib-path")),
         rustc_path: opt_path(matches, "rustc-path"),
         rustdoc_path: opt_path(matches, "rustdoc-path"),
         python: matches.opt_str("python").unwrap(),
diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs
index 5293eee9459..efad2038f82 100644
--- a/src/compiletest/runtest.rs
+++ b/src/compiletest/runtest.rs
@@ -316,7 +316,7 @@ fn run_pretty_test_revision(config: &Config,
                                      testpaths,
                                      pretty_type.to_owned()),
                         props.exec_env.clone(),
-                        &config.compile_lib_path,
+                        config.compile_lib_path.to_str().unwrap(),
                         Some(aux_dir.to_str().unwrap()),
                         Some(src))
     }
@@ -635,7 +635,7 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testpaths: &TestPa
                                                   testpaths,
                                                   proc_args,
                                                   environment,
-                                                  &config.run_lib_path,
+                                                  config.run_lib_path.to_str().unwrap(),
                                                   None,
                                                   None);
         }
@@ -1315,7 +1315,7 @@ fn exec_compiled_test(config: &Config, props: &TestProps,
                             testpaths,
                             make_run_args(config, props, testpaths),
                             env,
-                            &config.run_lib_path,
+                            config.run_lib_path.to_str().unwrap(),
                             Some(aux_dir.to_str().unwrap()),
                             None)
         }
@@ -1387,7 +1387,7 @@ fn compose_and_run_compiler(config: &Config, props: &TestProps,
                                      &aux_testpaths,
                                      aux_args,
                                      Vec::new(),
-                                     &config.compile_lib_path,
+                                     config.compile_lib_path.to_str().unwrap(),
                                      Some(aux_dir.to_str().unwrap()),
                                      None);
         if !auxres.status.success() {
@@ -1410,7 +1410,7 @@ fn compose_and_run_compiler(config: &Config, props: &TestProps,
                     testpaths,
                     args,
                     props.rustc_env.clone(),
-                    &config.compile_lib_path,
+                    config.compile_lib_path.to_str().unwrap(),
                     Some(aux_dir.to_str().unwrap()),
                     input)
 }
diff --git a/src/librbml/lib.rs b/src/librbml/lib.rs
index 34726a7a6c8..ef89b5d25b8 100644
--- a/src/librbml/lib.rs
+++ b/src/librbml/lib.rs
@@ -166,7 +166,7 @@ impl<'doc> Doc<'doc> {
         }
     }
 
-    pub fn get<'a>(&'a self, tag: usize) -> Doc<'a> {
+    pub fn get(&self, tag: usize) -> Doc<'doc> {
         reader::get_doc(*self, tag)
     }
 
@@ -174,7 +174,7 @@ impl<'doc> Doc<'doc> {
         self.start == self.end
     }
 
-    pub fn as_str_slice<'a>(&'a self) -> &'a str {
+    pub fn as_str_slice(&self) -> &'doc str {
         str::from_utf8(&self.data[self.start..self.end]).unwrap()
     }
 
diff --git a/src/librustc/front/map/collector.rs b/src/librustc/front/map/collector.rs
index 11aea372729..4ae03b1b9d7 100644
--- a/src/librustc/front/map/collector.rs
+++ b/src/librustc/front/map/collector.rs
@@ -14,7 +14,7 @@ use super::MapEntry::*;
 use rustc_front::hir::*;
 use rustc_front::util;
 use rustc_front::intravisit::{self, Visitor};
-use middle::def_id::{CRATE_DEF_INDEX, DefIndex};
+use middle::def_id::{CRATE_DEF_INDEX, DefId, DefIndex};
 use std::iter::repeat;
 use syntax::ast::{NodeId, CRATE_NODE_ID, DUMMY_NODE_ID};
 use syntax::codemap::Span;
@@ -50,6 +50,7 @@ impl<'ast> NodeCollector<'ast> {
                   parent: &'ast InlinedParent,
                   parent_node: NodeId,
                   parent_def_path: DefPath,
+                  parent_def_id: DefId,
                   map: Vec<MapEntry<'ast>>,
                   definitions: Definitions)
                   -> NodeCollector<'ast> {
@@ -60,8 +61,14 @@ impl<'ast> NodeCollector<'ast> {
             definitions: definitions,
         };
 
+        assert_eq!(parent_def_path.krate, parent_def_id.krate);
+        let root_path = Box::new(InlinedRootPath {
+            data: parent_def_path.data,
+            def_id: parent_def_id,
+        });
+
         collector.insert_entry(parent_node, RootInlinedParent(parent));
-        collector.create_def(parent_node, DefPathData::InlinedRoot(parent_def_path));
+        collector.create_def(parent_node, DefPathData::InlinedRoot(root_path));
 
         collector
     }
@@ -126,11 +133,16 @@ impl<'ast> Visitor<'ast> for NodeCollector<'ast> {
         // Pick the def data. This need not be unique, but the more
         // information we encapsulate into
         let def_data = match i.node {
-            ItemDefaultImpl(..) | ItemImpl(..) => DefPathData::Impl(i.name),
-            ItemEnum(..) | ItemStruct(..) | ItemTrait(..) => DefPathData::Type(i.name),
-            ItemExternCrate(..) | ItemMod(..) => DefPathData::Mod(i.name),
-            ItemStatic(..) | ItemConst(..) | ItemFn(..) => DefPathData::Value(i.name),
-            _ => DefPathData::Misc,
+            ItemDefaultImpl(..) | ItemImpl(..) =>
+                DefPathData::Impl,
+            ItemEnum(..) | ItemStruct(..) | ItemTrait(..) |
+            ItemExternCrate(..) | ItemMod(..) | ItemForeignMod(..) |
+            ItemTy(..) =>
+                DefPathData::TypeNs(i.name),
+            ItemStatic(..) | ItemConst(..) | ItemFn(..) =>
+                DefPathData::ValueNs(i.name),
+            ItemUse(..) =>
+                DefPathData::Misc,
         };
 
         self.insert_def(i.id, NodeItem(i), def_data);
@@ -195,7 +207,7 @@ impl<'ast> Visitor<'ast> for NodeCollector<'ast> {
     fn visit_foreign_item(&mut self, foreign_item: &'ast ForeignItem) {
         self.insert_def(foreign_item.id,
                         NodeForeignItem(foreign_item),
-                        DefPathData::Value(foreign_item.name));
+                        DefPathData::ValueNs(foreign_item.name));
 
         let parent_node = self.parent_node;
         self.parent_node = foreign_item.id;
@@ -215,8 +227,8 @@ impl<'ast> Visitor<'ast> for NodeCollector<'ast> {
 
     fn visit_trait_item(&mut self, ti: &'ast TraitItem) {
         let def_data = match ti.node {
-            MethodTraitItem(..) | ConstTraitItem(..) => DefPathData::Value(ti.name),
-            TypeTraitItem(..) => DefPathData::Type(ti.name),
+            MethodTraitItem(..) | ConstTraitItem(..) => DefPathData::ValueNs(ti.name),
+            TypeTraitItem(..) => DefPathData::TypeNs(ti.name),
         };
 
         self.insert(ti.id, NodeTraitItem(ti));
@@ -239,8 +251,8 @@ impl<'ast> Visitor<'ast> for NodeCollector<'ast> {
 
     fn visit_impl_item(&mut self, ii: &'ast ImplItem) {
         let def_data = match ii.node {
-            ImplItemKind::Method(..) | ImplItemKind::Const(..) => DefPathData::Value(ii.name),
-            ImplItemKind::Type(..) => DefPathData::Type(ii.name),
+            ImplItemKind::Method(..) | ImplItemKind::Const(..) => DefPathData::ValueNs(ii.name),
+            ImplItemKind::Type(..) => DefPathData::TypeNs(ii.name),
         };
 
         self.insert_def(ii.id, NodeImplItem(ii), def_data);
diff --git a/src/librustc/front/map/definitions.rs b/src/librustc/front/map/definitions.rs
index 0f99d85b083..82574b85229 100644
--- a/src/librustc/front/map/definitions.rs
+++ b/src/librustc/front/map/definitions.rs
@@ -59,23 +59,94 @@ pub struct DefData {
     pub node_id: ast::NodeId,
 }
 
-pub type DefPath = Vec<DisambiguatedDefPathData>;
+#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
+pub struct DefPath {
+    /// the path leading from the crate root to the item
+    pub data: Vec<DisambiguatedDefPathData>,
+
+    /// what krate root is this path relative to?
+    pub krate: ast::CrateNum,
+}
+
+impl DefPath {
+    pub fn is_local(&self) -> bool {
+        self.krate == LOCAL_CRATE
+    }
+
+    pub fn make<FN>(start_krate: ast::CrateNum,
+                    start_index: DefIndex,
+                    mut get_key: FN) -> DefPath
+        where FN: FnMut(DefIndex) -> DefKey
+    {
+        let mut krate = start_krate;
+        let mut data = vec![];
+        let mut index = Some(start_index);
+        loop {
+            let p = index.unwrap();
+            let key = get_key(p);
+            match key.disambiguated_data.data {
+                DefPathData::CrateRoot => {
+                    assert!(key.parent.is_none());
+                    break;
+                }
+                DefPathData::InlinedRoot(ref p) => {
+                    assert!(key.parent.is_none());
+                    assert!(!p.def_id.is_local());
+                    data.extend(p.data.iter().cloned().rev());
+                    krate = p.def_id.krate;
+                    break;
+                }
+                _ => {
+                    data.push(key.disambiguated_data);
+                    index = key.parent;
+                }
+            }
+        }
+        data.reverse();
+        DefPath { data: data, krate: krate }
+    }
+}
+
+/// Root of an inlined item. We track the `DefPath` of the item within
+/// the original crate but also its def-id. This is kind of an
+/// augmented version of a `DefPath` that includes a `DefId`. This is
+/// all sort of ugly but the hope is that inlined items will be going
+/// away soon anyway.
+///
+/// Some of the constraints that led to the current approach:
+///
+/// - I don't want to have a `DefId` in the main `DefPath` because
+///   that gets serialized for incr. comp., and when reloaded the
+///   `DefId` is no longer valid. I'd rather maintain the invariant
+///   that every `DefId` is valid, and a potentially outdated `DefId` is
+///   represented as a `DefPath`.
+///   - (We don't serialize def-paths from inlined items, so it's ok to have one here.)
+/// - We need to be able to extract the def-id from inline items to
+///   make the symbol name. In theory we could retrace it from the
+///   data, but the metadata doesn't have the required indices, and I
+///   don't want to write the code to create one just for this.
+/// - It may be that we don't actually need `data` at all. We'll have
+///   to see about that.
+#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
+pub struct InlinedRootPath {
+    pub data: Vec<DisambiguatedDefPathData>,
+    pub def_id: DefId,
+}
 
 #[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
 pub enum DefPathData {
     // Root: these should only be used for the root nodes, because
     // they are treated specially by the `def_path` function.
     CrateRoot,
-    InlinedRoot(DefPath),
+    InlinedRoot(Box<InlinedRootPath>),
 
     // Catch-all for random DefId things like DUMMY_NODE_ID
     Misc,
 
     // Different kinds of items and item-like things:
-    Impl(ast::Name),
-    Type(ast::Name),
-    Mod(ast::Name),
-    Value(ast::Name),
+    Impl,
+    TypeNs(ast::Name), // something in the type NS
+    ValueNs(ast::Name), // something in the value NS
     MacroDef(ast::Name),
     ClosureExpr,
 
@@ -87,10 +158,6 @@ pub enum DefPathData {
     StructCtor, // implicit ctor for a tuple-like struct
     Initializer, // initializer for a const
     Binding(ast::Name), // pattern binding
-
-    // An external crate that does not have an `extern crate` in this
-    // crate.
-    DetachedCrate(ast::Name),
 }
 
 impl Definitions {
@@ -116,7 +183,7 @@ impl Definitions {
     /// will be the path of the item in the external crate (but the
     /// path will begin with the path to the external crate).
     pub fn def_path(&self, index: DefIndex) -> DefPath {
-        make_def_path(index, |p| self.def_key(p))
+        DefPath::make(LOCAL_CRATE, index, |p| self.def_key(p))
     }
 
     pub fn opt_def_index(&self, node: ast::NodeId) -> Option<DefIndex> {
@@ -175,20 +242,21 @@ impl DefPathData {
     pub fn as_interned_str(&self) -> InternedString {
         use self::DefPathData::*;
         match *self {
-            Impl(name) |
-            Type(name) |
-            Mod(name) |
-            Value(name) |
+            TypeNs(name) |
+            ValueNs(name) |
             MacroDef(name) |
             TypeParam(name) |
             LifetimeDef(name) |
             EnumVariant(name) |
-            DetachedCrate(name) |
             Binding(name) |
             Field(name) => {
                 name.as_str()
             }
 
+            Impl => {
+                InternedString::new("{{impl}}")
+            }
+
             // note that this does not show up in user printouts
             CrateRoot => {
                 InternedString::new("{{root}}")
@@ -222,29 +290,3 @@ impl DefPathData {
     }
 }
 
-pub fn make_def_path<FN>(start_index: DefIndex, mut get_key: FN) -> DefPath
-    where FN: FnMut(DefIndex) -> DefKey
-{
-    let mut result = vec![];
-    let mut index = Some(start_index);
-    while let Some(p) = index {
-        let key = get_key(p);
-        match key.disambiguated_data.data {
-            DefPathData::CrateRoot => {
-                assert!(key.parent.is_none());
-                break;
-            }
-            DefPathData::InlinedRoot(ref p) => {
-                assert!(key.parent.is_none());
-                result.extend(p.iter().cloned().rev());
-                break;
-            }
-            _ => {
-                result.push(key.disambiguated_data);
-                index = key.parent;
-            }
-        }
-    }
-    result.reverse();
-    result
-}
diff --git a/src/librustc/front/map/mod.rs b/src/librustc/front/map/mod.rs
index dfc8560b58d..6d6f20c70ec 100644
--- a/src/librustc/front/map/mod.rs
+++ b/src/librustc/front/map/mod.rs
@@ -12,13 +12,14 @@ pub use self::Node::*;
 pub use self::PathElem::*;
 use self::MapEntry::*;
 use self::collector::NodeCollector;
-pub use self::definitions::{Definitions, DefKey, DefPath, DefPathData, DisambiguatedDefPathData};
+pub use self::definitions::{Definitions, DefKey, DefPath, DefPathData,
+                            DisambiguatedDefPathData, InlinedRootPath};
 
 use dep_graph::{DepGraph, DepNode};
 
 use middle::cstore::InlinedItem;
 use middle::cstore::InlinedItem as II;
-use middle::def_id::DefId;
+use middle::def_id::{CRATE_DEF_INDEX, DefId};
 
 use syntax::abi::Abi;
 use syntax::ast::{self, Name, NodeId, DUMMY_NODE_ID};
@@ -322,7 +323,8 @@ impl<'ast> Map<'ast> {
                     id = p,
 
                 RootCrate |
-                RootInlinedParent(_) => // FIXME(#2369) clarify story about cross-crate dep tracking
+                RootInlinedParent(_) =>
+                    // FIXME(#32015) clarify story about cross-crate dep tracking
                     return DepNode::Krate,
 
                 NotPresent =>
@@ -386,6 +388,15 @@ impl<'ast> Map<'ast> {
         self.forest.krate()
     }
 
+    /// Get the attributes on the krate. This is preferable to
+    /// invoking `krate.attrs` because it registers a tighter
+    /// dep-graph access.
+    pub fn krate_attrs(&self) -> &'ast [ast::Attribute] {
+        let crate_root_def_id = DefId::local(CRATE_DEF_INDEX);
+        self.dep_graph.read(DepNode::Hir(crate_root_def_id));
+        &self.forest.krate.attrs
+    }
+
     /// Retrieve the Node corresponding to `id`, panicking if it cannot
     /// be found.
     pub fn get(&self, id: NodeId) -> Node<'ast> {
@@ -958,6 +969,7 @@ pub fn map_crate<'ast>(forest: &'ast mut Forest) -> Map<'ast> {
 pub fn map_decoded_item<'ast, F: FoldOps>(map: &Map<'ast>,
                                           parent_path: Vec<PathElem>,
                                           parent_def_path: DefPath,
+                                          parent_def_id: DefId,
                                           ii: InlinedItem,
                                           fold_ops: F)
                                           -> &'ast InlinedItem {
@@ -987,6 +999,7 @@ pub fn map_decoded_item<'ast, F: FoldOps>(map: &Map<'ast>,
             ii_parent,
             ii_parent_id,
             parent_def_path,
+            parent_def_id,
             mem::replace(&mut *map.map.borrow_mut(), vec![]),
             mem::replace(&mut *map.definitions.borrow_mut(), Definitions::new()));
     ii_parent.ii.visit(&mut collector);
diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs
index f85d8741384..7cad9b10f85 100644
--- a/src/librustc/middle/cstore.rs
+++ b/src/librustc/middle/cstore.rs
@@ -42,6 +42,7 @@ use syntax::ast_util::{IdVisitingOperation};
 use syntax::attr;
 use syntax::codemap::Span;
 use syntax::ptr::P;
+use syntax::parse::token::InternedString;
 use rustc_back::target::Target;
 use rustc_front::hir;
 use rustc_front::intravisit::Visitor;
@@ -126,6 +127,27 @@ pub enum FoundAst<'ast> {
     NotFound,
 }
 
+#[derive(Copy, Clone, Debug)]
+pub struct ExternCrate {
+    /// def_id of an `extern crate` in the current crate that caused
+    /// this crate to be loaded; note that there could be multiple
+    /// such ids
+    pub def_id: DefId,
+
+    /// span of the extern crate that caused this to be loaded
+    pub span: Span,
+
+    /// If true, then this crate is the crate named by the extern
+    /// crate referenced above. If false, then this crate is a dep
+    /// of the crate.
+    pub direct: bool,
+
+    /// Number of links to reach the extern crate `def_id`
+    /// declaration; used to select the extern crate with the shortest
+    /// path
+    pub path_len: usize,
+}
+
 /// A store of Rust crates, through with their metadata
 /// can be accessed.
 ///
@@ -146,7 +168,7 @@ pub trait CrateStore<'tcx> : Any {
     fn repr_attrs(&self, def: DefId) -> Vec<attr::ReprAttr>;
     fn item_type(&self, tcx: &TyCtxt<'tcx>, def: DefId)
                  -> ty::TypeScheme<'tcx>;
-    fn item_path(&self, def: DefId) -> Vec<hir_map::PathElem>;
+    fn relative_item_path(&self, def: DefId) -> Vec<hir_map::PathElem>;
     fn extern_item_path(&self, def: DefId) -> Vec<hir_map::PathElem>;
     fn item_name(&self, def: DefId) -> ast::Name;
     fn item_predicates(&self, tcx: &TyCtxt<'tcx>, def: DefId)
@@ -202,9 +224,15 @@ pub trait CrateStore<'tcx> : Any {
     fn is_staged_api(&self, cnum: ast::CrateNum) -> bool;
     fn is_explicitly_linked(&self, cnum: ast::CrateNum) -> bool;
     fn is_allocator(&self, cnum: ast::CrateNum) -> bool;
+    fn extern_crate(&self, cnum: ast::CrateNum) -> Option<ExternCrate>;
     fn crate_attrs(&self, cnum: ast::CrateNum) -> Vec<ast::Attribute>;
-    fn crate_name(&self, cnum: ast::CrateNum) -> String;
+    /// The name of the crate as it is referred to in source code of the current
+    /// crate.
+    fn crate_name(&self, cnum: ast::CrateNum) -> InternedString;
+    /// The name of the crate as it is stored in the crate's metadata.
+    fn original_crate_name(&self, cnum: ast::CrateNum) -> InternedString;
     fn crate_hash(&self, cnum: ast::CrateNum) -> Svh;
+    fn crate_disambiguator(&self, cnum: ast::CrateNum) -> InternedString;
     fn crate_struct_field_attrs(&self, cnum: ast::CrateNum)
                                 -> FnvHashMap<DefId, Vec<ast::Attribute>>;
     fn plugin_registrar_fn(&self, cnum: ast::CrateNum) -> Option<DefId>;
@@ -212,7 +240,8 @@ pub trait CrateStore<'tcx> : Any {
     fn reachable_ids(&self, cnum: ast::CrateNum) -> Vec<DefId>;
 
     // resolve
-    fn def_path(&self, def: DefId) -> hir_map::DefPath;
+    fn def_key(&self, def: DefId) -> hir_map::DefKey;
+    fn relative_def_path(&self, def: DefId) -> hir_map::DefPath;
     fn variant_kind(&self, def_id: DefId) -> Option<VariantKind>;
     fn struct_ctor_def_id(&self, struct_def_id: DefId) -> Option<DefId>;
     fn tuple_struct_definition_if_ctor(&self, did: DefId) -> Option<DefId>;
@@ -236,7 +265,11 @@ pub trait CrateStore<'tcx> : Any {
     // utility functions
     fn metadata_filename(&self) -> &str;
     fn metadata_section_name(&self, target: &Target) -> &str;
-    fn encode_type(&self, tcx: &TyCtxt<'tcx>, ty: Ty<'tcx>) -> Vec<u8>;
+    fn encode_type(&self,
+                   tcx: &TyCtxt<'tcx>,
+                   ty: Ty<'tcx>,
+                   def_id_to_string: fn(&TyCtxt<'tcx>, DefId) -> String)
+                   -> Vec<u8>;
     fn used_crates(&self, prefer: LinkagePreference) -> Vec<(ast::CrateNum, Option<PathBuf>)>;
     fn used_crate_source(&self, cnum: ast::CrateNum) -> CrateSource;
     fn extern_mod_stmt_cnum(&self, emod_id: ast::NodeId) -> Option<ast::CrateNum>;
@@ -313,7 +346,7 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
     fn repr_attrs(&self, def: DefId) -> Vec<attr::ReprAttr> { unimplemented!() }
     fn item_type(&self, tcx: &TyCtxt<'tcx>, def: DefId)
                  -> ty::TypeScheme<'tcx> { unimplemented!() }
-    fn item_path(&self, def: DefId) -> Vec<hir_map::PathElem> { unimplemented!() }
+    fn relative_item_path(&self, def: DefId) -> Vec<hir_map::PathElem> { unimplemented!() }
     fn extern_item_path(&self, def: DefId) -> Vec<hir_map::PathElem> { unimplemented!() }
     fn item_name(&self, def: DefId) -> ast::Name { unimplemented!() }
     fn item_predicates(&self, tcx: &TyCtxt<'tcx>, def: DefId)
@@ -376,10 +409,15 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
     fn is_staged_api(&self, cnum: ast::CrateNum) -> bool { unimplemented!() }
     fn is_explicitly_linked(&self, cnum: ast::CrateNum) -> bool { unimplemented!() }
     fn is_allocator(&self, cnum: ast::CrateNum) -> bool { unimplemented!() }
+    fn extern_crate(&self, cnum: ast::CrateNum) -> Option<ExternCrate> { unimplemented!() }
     fn crate_attrs(&self, cnum: ast::CrateNum) -> Vec<ast::Attribute>
         { unimplemented!() }
-    fn crate_name(&self, cnum: ast::CrateNum) -> String { unimplemented!() }
+    fn crate_name(&self, cnum: ast::CrateNum) -> InternedString { unimplemented!() }
+    fn original_crate_name(&self, cnum: ast::CrateNum) -> InternedString {
+        unimplemented!()
+    }
     fn crate_hash(&self, cnum: ast::CrateNum) -> Svh { unimplemented!() }
+    fn crate_disambiguator(&self, cnum: ast::CrateNum) -> InternedString { unimplemented!() }
     fn crate_struct_field_attrs(&self, cnum: ast::CrateNum)
                                 -> FnvHashMap<DefId, Vec<ast::Attribute>>
         { unimplemented!() }
@@ -390,7 +428,8 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
     fn reachable_ids(&self, cnum: ast::CrateNum) -> Vec<DefId> { unimplemented!() }
 
     // resolve
-    fn def_path(&self, def: DefId) -> hir_map::DefPath { unimplemented!() }
+    fn def_key(&self, def: DefId) -> hir_map::DefKey { unimplemented!() }
+    fn relative_def_path(&self, def: DefId) -> hir_map::DefPath { unimplemented!() }
     fn variant_kind(&self, def_id: DefId) -> Option<VariantKind> { unimplemented!() }
     fn struct_ctor_def_id(&self, struct_def_id: DefId) -> Option<DefId>
         { unimplemented!() }
@@ -419,8 +458,13 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
     // utility functions
     fn metadata_filename(&self) -> &str { unimplemented!() }
     fn metadata_section_name(&self, target: &Target) -> &str { unimplemented!() }
-    fn encode_type(&self, tcx: &TyCtxt<'tcx>, ty: Ty<'tcx>) -> Vec<u8>
-        { unimplemented!() }
+    fn encode_type(&self,
+                   tcx: &TyCtxt<'tcx>,
+                   ty: Ty<'tcx>,
+                   def_id_to_string: fn(&TyCtxt<'tcx>, DefId) -> String)
+                   -> Vec<u8> {
+        unimplemented!()
+    }
     fn used_crates(&self, prefer: LinkagePreference) -> Vec<(ast::CrateNum, Option<PathBuf>)>
         { vec![] }
     fn used_crate_source(&self, cnum: ast::CrateNum) -> CrateSource { unimplemented!() }
diff --git a/src/librustc/middle/traits/fulfill.rs b/src/librustc/middle/traits/fulfill.rs
index 937196b5e8e..b2ba8a7a5f9 100644
--- a/src/librustc/middle/traits/fulfill.rs
+++ b/src/librustc/middle/traits/fulfill.rs
@@ -398,7 +398,7 @@ fn process_predicate<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
                          // purposes of the ancestor check, we retain
                          // the invariant that all type variables are
                          // fully refreshed.
-                         if !(&mut is_ancestor)(&obligation.predicate) {
+                         if !is_ancestor(&obligation.predicate) {
                              return None;
                          }
                      }
diff --git a/src/librustc/middle/ty/context.rs b/src/librustc/middle/ty/context.rs
index 03c13115aea..6acd094c1f9 100644
--- a/src/librustc/middle/ty/context.rs
+++ b/src/librustc/middle/ty/context.rs
@@ -15,7 +15,7 @@ use front::map as ast_map;
 use session::Session;
 use lint;
 use middle;
-use middle::cstore::CrateStore;
+use middle::cstore::{CrateStore, LOCAL_CRATE};
 use middle::def::DefMap;
 use middle::def_id::DefId;
 use middle::free_region::FreeRegionMap;
@@ -43,7 +43,7 @@ use std::hash::{Hash, Hasher};
 use std::rc::Rc;
 use syntax::ast::{self, Name, NodeId};
 use syntax::attr;
-use syntax::parse::token::special_idents;
+use syntax::parse::token::{self, special_idents};
 
 use rustc_front::hir;
 
@@ -415,9 +415,29 @@ pub struct TyCtxt<'tcx> {
     /// fragmented data to the set of unfragmented pieces that
     /// constitute it.
     pub fragment_infos: RefCell<DefIdMap<Vec<ty::FragmentInfo>>>,
+
+    /// The definite name of the current crate after taking into account
+    /// attributes, commandline parameters, etc.
+    pub crate_name: token::InternedString,
 }
 
 impl<'tcx> TyCtxt<'tcx> {
+    pub fn crate_name(&self, cnum: ast::CrateNum) -> token::InternedString {
+        if cnum == LOCAL_CRATE {
+            self.crate_name.clone()
+        } else {
+            self.sess.cstore.crate_name(cnum)
+        }
+    }
+
+    pub fn crate_disambiguator(&self, cnum: ast::CrateNum) -> token::InternedString {
+        if cnum == LOCAL_CRATE {
+            self.sess.crate_disambiguator.get().as_str()
+        } else {
+            self.sess.cstore.crate_name(cnum)
+        }
+    }
+
     pub fn type_parameter_def(&self,
                               node_id: NodeId)
                               -> ty::TypeParameterDef<'tcx>
@@ -511,6 +531,7 @@ impl<'tcx> TyCtxt<'tcx> {
                                  region_maps: RegionMaps,
                                  lang_items: middle::lang_items::LanguageItems,
                                  stability: stability::Index<'tcx>,
+                                 crate_name: &str,
                                  f: F) -> R
                                  where F: FnOnce(&TyCtxt<'tcx>) -> R
     {
@@ -570,7 +591,8 @@ impl<'tcx> TyCtxt<'tcx> {
             const_qualif_map: RefCell::new(NodeMap()),
             custom_coerce_unsized_kinds: RefCell::new(DefIdMap()),
             cast_kinds: RefCell::new(NodeMap()),
-            fragment_infos: RefCell::new(DefIdMap())
+            fragment_infos: RefCell::new(DefIdMap()),
+            crate_name: token::intern_and_get_ident(crate_name),
        }, f)
     }
 }
diff --git a/src/librustc/middle/ty/item_path.rs b/src/librustc/middle/ty/item_path.rs
new file mode 100644
index 00000000000..147230f5bdc
--- /dev/null
+++ b/src/librustc/middle/ty/item_path.rs
@@ -0,0 +1,317 @@
+// Copyright 2012-2015 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 front::map::DefPathData;
+use middle::cstore::LOCAL_CRATE;
+use middle::def_id::DefId;
+use middle::ty::{self, Ty, TyCtxt};
+use syntax::ast;
+
+impl<'tcx> TyCtxt<'tcx> {
+    /// Returns a string identifying this def-id. This string is
+    /// suitable for user output. It is relative to the current crate
+    /// root.
+    pub fn item_path_str(&self, def_id: DefId) -> String {
+        let mut buffer = LocalPathBuffer::new(RootMode::Local);
+        self.push_item_path(&mut buffer, def_id);
+        buffer.into_string()
+    }
+
+    /// Returns a string identifying this def-id. This string is
+    /// suitable for user output. It always begins with a crate identifier.
+    pub fn absolute_item_path_str(&self, def_id: DefId) -> String {
+        let mut buffer = LocalPathBuffer::new(RootMode::Absolute);
+        self.push_item_path(&mut buffer, def_id);
+        buffer.into_string()
+    }
+
+    /// Returns the "path" to a particular crate. This can proceed in
+    /// various ways, depending on the `root_mode` of the `buffer`.
+    /// (See `RootMode` enum for more details.)
+    pub fn push_krate_path<T>(&self, buffer: &mut T, cnum: ast::CrateNum)
+        where T: ItemPathBuffer
+    {
+        match *buffer.root_mode() {
+            RootMode::Local => {
+                // In local mode, when we encounter a crate other than
+                // LOCAL_CRATE, execution proceeds in one of two ways:
+                //
+                // 1. for a direct dependency, where user added an
+                //    `extern crate` manually, we put the `extern
+                //    crate` as the parent. So you wind up with
+                //    something relative to the current crate.
+                // 2. for an indirect crate, where there is no extern
+                //    crate, we just prepend the crate name.
+                //
+                // Returns `None` for the local crate.
+                if cnum != LOCAL_CRATE {
+                    let opt_extern_crate = self.sess.cstore.extern_crate(cnum);
+                    let opt_extern_crate = opt_extern_crate.and_then(|extern_crate| {
+                        if extern_crate.direct {
+                            Some(extern_crate.def_id)
+                        } else {
+                            None
+                        }
+                    });
+                    if let Some(extern_crate_def_id) = opt_extern_crate {
+                        self.push_item_path(buffer, extern_crate_def_id);
+                    } else {
+                        buffer.push(&self.crate_name(cnum));
+                    }
+                }
+            }
+            RootMode::Absolute => {
+                // In absolute mode, just write the crate name
+                // unconditionally.
+                buffer.push(&self.crate_name(cnum));
+            }
+        }
+    }
+
+    pub fn push_item_path<T>(&self, buffer: &mut T, def_id: DefId)
+        where T: ItemPathBuffer
+    {
+        let key = self.def_key(def_id);
+        match key.disambiguated_data.data {
+            DefPathData::CrateRoot => {
+                assert!(key.parent.is_none());
+                self.push_krate_path(buffer, def_id.krate);
+            }
+
+            DefPathData::InlinedRoot(ref root_path) => {
+                assert!(key.parent.is_none());
+                self.push_item_path(buffer, root_path.def_id);
+            }
+
+            DefPathData::Impl => {
+                self.push_impl_path(buffer, def_id);
+            }
+
+            // Unclear if there is any value in distinguishing these.
+            // Probably eventually (and maybe we would even want
+            // finer-grained distinctions, e.g. between enum/struct).
+            data @ DefPathData::Misc |
+            data @ DefPathData::TypeNs(..) |
+            data @ DefPathData::ValueNs(..) |
+            data @ DefPathData::TypeParam(..) |
+            data @ DefPathData::LifetimeDef(..) |
+            data @ DefPathData::EnumVariant(..) |
+            data @ DefPathData::Field(..) |
+            data @ DefPathData::StructCtor |
+            data @ DefPathData::Initializer |
+            data @ DefPathData::MacroDef(..) |
+            data @ DefPathData::ClosureExpr |
+            data @ DefPathData::Binding(..) => {
+                let parent_def_id = self.parent_def_id(def_id).unwrap();
+                self.push_item_path(buffer, parent_def_id);
+                buffer.push(&data.as_interned_str());
+            }
+        }
+    }
+
+    fn push_impl_path<T>(&self,
+                         buffer: &mut T,
+                         impl_def_id: DefId)
+        where T: ItemPathBuffer
+    {
+        let parent_def_id = self.parent_def_id(impl_def_id).unwrap();
+
+        let use_types = if !impl_def_id.is_local() {
+            // always have full types available for extern crates
+            true
+        } else {
+            // for local crates, check whether type info is
+            // available; typeck might not have completed yet
+            self.impl_trait_refs.borrow().contains_key(&impl_def_id)
+        };
+
+        if !use_types {
+            return self.push_impl_path_fallback(buffer, impl_def_id);
+        }
+
+        // Decide whether to print the parent path for the impl.
+        // Logically, since impls are global, it's never needed, but
+        // users may find it useful. Currently, we omit the parent if
+        // the impl is either in the same module as the self-type or
+        // as the trait.
+        let self_ty = self.lookup_item_type(impl_def_id).ty;
+        let in_self_mod = match self.characteristic_def_id_of_type(self_ty) {
+            None => false,
+            Some(ty_def_id) => self.parent_def_id(ty_def_id) == Some(parent_def_id),
+        };
+
+        let impl_trait_ref = self.impl_trait_ref(impl_def_id);
+        let in_trait_mod = match impl_trait_ref {
+            None => false,
+            Some(trait_ref) => self.parent_def_id(trait_ref.def_id) == Some(parent_def_id),
+        };
+
+        if !in_self_mod && !in_trait_mod {
+            // If the impl is not co-located with either self-type or
+            // trait-type, then fallback to a format that identifies
+            // the module more clearly.
+            self.push_item_path(buffer, parent_def_id);
+            if let Some(trait_ref) = impl_trait_ref {
+                buffer.push(&format!("<impl {} for {}>", trait_ref, self_ty));
+            } else {
+                buffer.push(&format!("<impl {}>", self_ty));
+            }
+            return;
+        }
+
+        // Otherwise, try to give a good form that would be valid language
+        // syntax. Preferably using associated item notation.
+
+        if let Some(trait_ref) = impl_trait_ref {
+            // Trait impls.
+            buffer.push(&format!("<{} as {}>",
+                                 self_ty,
+                                 trait_ref));
+            return;
+        }
+
+        // Inherent impls. Try to print `Foo::bar` for an inherent
+        // impl on `Foo`, but fallback to `<Foo>::bar` if self-type is
+        // anything other than a simple path.
+        match self_ty.sty {
+            ty::TyStruct(adt_def, substs) |
+            ty::TyEnum(adt_def, substs) => {
+                if substs.types.is_empty() { // ignore regions
+                    self.push_item_path(buffer, adt_def.did);
+                } else {
+                    buffer.push(&format!("<{}>", self_ty));
+                }
+            }
+
+            ty::TyBool |
+            ty::TyChar |
+            ty::TyInt(_) |
+            ty::TyUint(_) |
+            ty::TyFloat(_) |
+            ty::TyStr => {
+                buffer.push(&format!("{}", self_ty));
+            }
+
+            _ => {
+                buffer.push(&format!("<{}>", self_ty));
+            }
+        }
+    }
+
+    fn push_impl_path_fallback<T>(&self,
+                                  buffer: &mut T,
+                                  impl_def_id: DefId)
+        where T: ItemPathBuffer
+    {
+        // If no type info is available, fall back to
+        // pretty printing some span information. This should
+        // only occur very early in the compiler pipeline.
+        let parent_def_id = self.parent_def_id(impl_def_id).unwrap();
+        self.push_item_path(buffer, parent_def_id);
+        let node_id = self.map.as_local_node_id(impl_def_id).unwrap();
+        let item = self.map.expect_item(node_id);
+        let span_str = self.sess.codemap().span_to_string(item.span);
+        buffer.push(&format!("<impl at {}>", span_str));
+    }
+
+    /// As a heuristic, when we see an impl, if we see that the
+    /// 'self-type' is a type defined in the same module as the impl,
+    /// we can omit including the path to the impl itself. This
+    /// function tries to find a "characteristic def-id" for a
+    /// type. It's just a heuristic so it makes some questionable
+    /// decisions and we may want to adjust it later.
+    fn characteristic_def_id_of_type(&self, ty: Ty<'tcx>) -> Option<DefId> {
+        match ty.sty {
+            ty::TyStruct(adt_def, _) |
+            ty::TyEnum(adt_def, _) =>
+                Some(adt_def.did),
+
+            ty::TyTrait(ref data) =>
+                Some(data.principal_def_id()),
+
+            ty::TyBox(subty) =>
+                self.characteristic_def_id_of_type(subty),
+
+            ty::TyRawPtr(mt) |
+            ty::TyRef(_, mt) =>
+                self.characteristic_def_id_of_type(mt.ty),
+
+            ty::TyTuple(ref tys) =>
+                tys.iter()
+                   .filter_map(|ty| self.characteristic_def_id_of_type(ty))
+                   .next(),
+
+            _ =>
+                None
+        }
+    }
+
+    /// Returns the def-id of `def_id`'s parent in the def tree. If
+    /// this returns `None`, then `def_id` represents a crate root or
+    /// inlined root.
+    fn parent_def_id(&self, def_id: DefId) -> Option<DefId> {
+        let key = self.def_key(def_id);
+        key.parent.map(|index| DefId { krate: def_id.krate, index: index })
+    }
+}
+
+/// Unifying Trait for different kinds of item paths we might
+/// construct. The basic interface is that components get pushed: the
+/// instance can also customize how we handle the root of a crate.
+pub trait ItemPathBuffer {
+    fn root_mode(&self) -> &RootMode;
+    fn push(&mut self, text: &str);
+}
+
+#[derive(Debug)]
+pub enum RootMode {
+    /// Try to make a path relative to the local crate.  In
+    /// particular, local paths have no prefix, and if the path comes
+    /// from an extern crate, start with the path to the `extern
+    /// crate` declaration.
+    Local,
+
+    /// Always prepend the crate name to the path, forming an absolute
+    /// path from within a given set of crates.
+    Absolute,
+}
+
+#[derive(Debug)]
+struct LocalPathBuffer {
+    root_mode: RootMode,
+    str: String,
+}
+
+impl LocalPathBuffer {
+    fn new(root_mode: RootMode) -> LocalPathBuffer {
+        LocalPathBuffer {
+            root_mode: root_mode,
+            str: String::new()
+        }
+    }
+
+    fn into_string(self) -> String {
+        self.str
+    }
+
+}
+
+impl ItemPathBuffer for LocalPathBuffer {
+    fn root_mode(&self) -> &RootMode {
+        &self.root_mode
+    }
+
+    fn push(&mut self, text: &str) {
+        if !self.str.is_empty() {
+            self.str.push_str("::");
+        }
+        self.str.push_str(text);
+    }
+}
diff --git a/src/librustc/middle/ty/mod.rs b/src/librustc/middle/ty/mod.rs
index 050024d0e94..a4c3e82b633 100644
--- a/src/librustc/middle/ty/mod.rs
+++ b/src/librustc/middle/ty/mod.rs
@@ -86,6 +86,7 @@ pub mod cast;
 pub mod error;
 pub mod fast_reject;
 pub mod fold;
+pub mod item_path;
 pub mod _match;
 pub mod maps;
 pub mod outlives;
@@ -2218,15 +2219,22 @@ impl<'tcx> TyCtxt<'tcx> {
         self.def_map.borrow().get(&tr.ref_id).expect("no def-map entry for trait").def_id()
     }
 
-    pub fn item_path_str(&self, id: DefId) -> String {
-        self.with_path(id, |path| ast_map::path_to_string(path))
+    pub fn def_key(&self, id: DefId) -> ast_map::DefKey {
+        if id.is_local() {
+            self.map.def_key(id)
+        } else {
+            self.sess.cstore.def_key(id)
+        }
     }
 
+    /// Returns the `DefPath` of an item. Note that if `id` is not
+    /// local to this crate -- or is inlined into this crate -- the
+    /// result will be a non-local `DefPath`.
     pub fn def_path(&self, id: DefId) -> ast_map::DefPath {
         if id.is_local() {
             self.map.def_path(id)
         } else {
-            self.sess.cstore.def_path(id)
+            self.sess.cstore.relative_def_path(id)
         }
     }
 
@@ -2236,7 +2244,27 @@ impl<'tcx> TyCtxt<'tcx> {
         if let Some(id) = self.map.as_local_node_id(id) {
             self.map.with_path(id, f)
         } else {
-            f(self.sess.cstore.item_path(id).iter().cloned().chain(LinkedPath::empty()))
+            let mut path: Vec<_>;
+            if let Some(extern_crate) = self.sess.cstore.extern_crate(id.krate) {
+                if !extern_crate.direct {
+                    // this comes from some crate that we don't have a direct
+                    // path to; we'll settle for just prepending the name of
+                    // the crate.
+                    path = self.sess.cstore.extern_item_path(id)
+                } else {
+                    // start with the path to the extern crate, then
+                    // add the relative path to the actual item
+                    fn collector(elems: ast_map::PathElems) -> Vec<ast_map::PathElem> {
+                        elems.collect()
+                    }
+                    path = self.with_path(extern_crate.def_id, collector);
+                    path.extend(self.sess.cstore.relative_item_path(id));
+                }
+            } else {
+                // if this was injected, just make a path with name of crate
+                path = self.sess.cstore.extern_item_path(id);
+            }
+            f(path.iter().cloned().chain(LinkedPath::empty()))
         }
     }
 
@@ -2680,9 +2708,10 @@ impl<'tcx> TyCtxt<'tcx> {
     {
         dep_graph::visit_all_items_in_krate(self, dep_node_fn, visitor);
     }
+
     /// Looks up the span of `impl_did` if the impl is local; otherwise returns `Err`
     /// with the name of the crate containing the impl.
-    pub fn span_of_impl(&self, impl_did: DefId) -> Result<Span, String> {
+    pub fn span_of_impl(&self, impl_did: DefId) -> Result<Span, InternedString> {
         if impl_did.is_local() {
             let node_id = self.map.as_local_node_id(impl_did).unwrap();
             Ok(self.map.span(node_id))
diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs
index b198eda1812..3e687840868 100644
--- a/src/librustc/session/mod.rs
+++ b/src/librustc/session/mod.rs
@@ -24,6 +24,7 @@ use syntax::diagnostics;
 use syntax::feature_gate;
 use syntax::parse;
 use syntax::parse::ParseSess;
+use syntax::parse::token;
 use syntax::{ast, codemap};
 use syntax::feature_gate::AttributeType;
 
@@ -64,7 +65,12 @@ pub struct Session {
     pub plugin_attributes: RefCell<Vec<(String, AttributeType)>>,
     pub crate_types: RefCell<Vec<config::CrateType>>,
     pub dependency_formats: RefCell<dependency_format::Dependencies>,
-    pub crate_metadata: RefCell<Vec<String>>,
+    // The crate_disambiguator is constructed out of all the `-C metadata`
+    // arguments passed to the compiler. Its value together with the crate-name
+    // forms a unique global identifier for the crate. It is used to allow
+    // multiple crates with the same name to coexist. See the
+    // trans::back::symbol_names module for more information.
+    pub crate_disambiguator: Cell<ast::Name>,
     pub features: RefCell<feature_gate::Features>,
 
     /// The maximum recursion limit for potentially infinitely recursive
@@ -481,7 +487,7 @@ pub fn build_session_(sopts: config::Options,
         plugin_attributes: RefCell::new(Vec::new()),
         crate_types: RefCell::new(Vec::new()),
         dependency_formats: RefCell::new(FnvHashMap()),
-        crate_metadata: RefCell::new(Vec::new()),
+        crate_disambiguator: Cell::new(token::intern("")),
         features: RefCell::new(feature_gate::Features::new()),
         recursion_limit: Cell::new(64),
         next_node_id: Cell::new(1),
diff --git a/src/librustc_back/svh.rs b/src/librustc_back/svh.rs
index 3507a119e54..ec607314f45 100644
--- a/src/librustc_back/svh.rs
+++ b/src/librustc_back/svh.rs
@@ -66,7 +66,7 @@ impl Svh {
         &self.hash
     }
 
-    pub fn calculate(metadata: &Vec<String>, krate: &hir::Crate) -> Svh {
+    pub fn calculate(crate_disambiguator: &str, krate: &hir::Crate) -> Svh {
         // FIXME (#14132): This is better than it used to be, but it still not
         // ideal. We now attempt to hash only the relevant portions of the
         // Crate AST as well as the top-level crate attributes. (However,
@@ -78,9 +78,9 @@ impl Svh {
         //        avoid collisions.
         let mut state = SipHasher::new();
 
-        for data in metadata {
-            data.hash(&mut state);
-        }
+        "crate_disambiguator".hash(&mut state);
+        crate_disambiguator.len().hash(&mut state);
+        crate_disambiguator.hash(&mut state);
 
         {
             let mut visit = svh_visitor::make(&mut state, krate);
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index 55b873c0663..96e819ea91f 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -22,6 +22,7 @@ use rustc::middle::privacy::AccessLevels;
 use rustc::middle::ty::TyCtxt;
 use rustc::util::common::time;
 use rustc::util::nodemap::NodeSet;
+use rustc_back::sha2::{Sha256, Digest};
 use rustc_borrowck as borrowck;
 use rustc_resolve as resolve;
 use rustc_metadata::macro_import;
@@ -500,7 +501,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
     })?;
 
     *sess.crate_types.borrow_mut() = collect_crate_types(sess, &krate.attrs);
-    *sess.crate_metadata.borrow_mut() = collect_crate_metadata(sess, &krate.attrs);
+    sess.crate_disambiguator.set(token::intern(&compute_crate_disambiguator(sess)));
 
     time(time_passes, "recursion limit", || {
         middle::recursion_limit::update_recursion_limit(sess, &krate);
@@ -525,11 +526,15 @@ pub fn phase_2_configure_and_expand(sess: &Session,
 
     let macros = time(time_passes,
                       "macro loading",
-                      || macro_import::read_macro_defs(sess, &cstore, &krate));
+                      || macro_import::read_macro_defs(sess, &cstore, &krate, crate_name));
 
     let mut addl_plugins = Some(addl_plugins);
     let registrars = time(time_passes, "plugin loading", || {
-        plugin::load::load_plugins(sess, &cstore, &krate, addl_plugins.take().unwrap())
+        plugin::load::load_plugins(sess,
+                                   &cstore,
+                                   &krate,
+                                   crate_name,
+                                   addl_plugins.take().unwrap())
     });
 
     let mut registry = Registry::new(sess, &krate);
@@ -754,7 +759,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
 
     time(time_passes,
          "external crate/lib resolution",
-         || LocalCrateReader::new(sess, cstore, &hir_map).read_crates());
+         || LocalCrateReader::new(sess, cstore, &hir_map, name).read_crates());
 
     let lang_items = time(time_passes, "language item collection", || {
         sess.track_errors(|| {
@@ -817,6 +822,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
                                region_map,
                                lang_items,
                                index,
+                               name,
                                |tcx| {
         // passes are timed inside typeck
         try_with_f!(typeck::check_crate(tcx, trait_map), (tcx, None, analysis));
@@ -1121,8 +1127,34 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<c
         .collect()
 }
 
-pub fn collect_crate_metadata(session: &Session, _attrs: &[ast::Attribute]) -> Vec<String> {
-    session.opts.cg.metadata.clone()
+pub fn compute_crate_disambiguator(session: &Session) -> String {
+    let mut hasher = Sha256::new();
+
+    let mut metadata = session.opts.cg.metadata.clone();
+    // We don't want the crate_disambiguator to dependent on the order
+    // -C metadata arguments, so sort them:
+    metadata.sort();
+    // Every distinct -C metadata value is only incorporated once:
+    metadata.dedup();
+
+    hasher.input_str("metadata");
+    for s in &metadata {
+        // Also incorporate the length of a metadata string, so that we generate
+        // different values for `-Cmetadata=ab -Cmetadata=c` and
+        // `-Cmetadata=a -Cmetadata=bc`
+        hasher.input_str(&format!("{}", s.len())[..]);
+        hasher.input_str(&s[..]);
+    }
+
+    let mut hash = hasher.result_str();
+
+    // If this is an executable, add a special suffix, so that we don't get
+    // symbol conflicts when linking against a library of the same name.
+    if session.crate_types.borrow().contains(&config::CrateTypeExecutable) {
+       hash.push_str("-exe");
+    }
+
+    hash
 }
 
 pub fn build_output_filenames(input: &Input,
diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs
index dc30b4f91a9..9ba6abb962e 100644
--- a/src/librustc_driver/lib.rs
+++ b/src/librustc_driver/lib.rs
@@ -568,8 +568,6 @@ impl RustcDefaultCalls {
                         continue;
                     }
                     let crate_types = driver::collect_crate_types(sess, attrs);
-                    let metadata = driver::collect_crate_metadata(sess, attrs);
-                    *sess.crate_metadata.borrow_mut() = metadata;
                     for &style in &crate_types {
                         let fname = link::filename_for_input(sess, style, &id, &t_outputs);
                         println!("{}",
diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs
index 1a5e7cb54f2..111db3b1d38 100644
--- a/src/librustc_driver/test.rs
+++ b/src/librustc_driver/test.rs
@@ -146,6 +146,7 @@ fn test_env<F>(source_string: &str,
                                region_map,
                                lang_items,
                                index,
+                               "test_crate",
                                |tcx| {
                                    let infcx = infer::new_infer_ctxt(tcx,
                                                                      &tcx.tables,
diff --git a/src/librustc_metadata/astencode.rs b/src/librustc_metadata/astencode.rs
index 5c5574c3a83..60f71107646 100644
--- a/src/librustc_metadata/astencode.rs
+++ b/src/librustc_metadata/astencode.rs
@@ -125,6 +125,7 @@ pub fn decode_inlined_item<'tcx>(cdata: &cstore::crate_metadata,
                                  tcx: &TyCtxt<'tcx>,
                                  parent_path: Vec<ast_map::PathElem>,
                                  parent_def_path: ast_map::DefPath,
+                                 parent_did: DefId,
                                  ast_doc: rbml::Doc,
                                  orig_did: DefId)
                                  -> &'tcx InlinedItem {
@@ -149,6 +150,7 @@ pub fn decode_inlined_item<'tcx>(cdata: &cstore::crate_metadata,
     let ii = ast_map::map_decoded_item(&dcx.tcx.map,
                                        parent_path,
                                        parent_def_path,
+                                       parent_did,
                                        decode_ast(ast_doc),
                                        dcx);
     let name = match *ii {
@@ -349,8 +351,8 @@ fn simplify_ast(ii: InlinedItemRef) -> InlinedItem {
     }
 }
 
-fn decode_ast(par_doc: rbml::Doc) -> InlinedItem {
-    let chi_doc = par_doc.get(c::tag_tree as usize);
+fn decode_ast(item_doc: rbml::Doc) -> InlinedItem {
+    let chi_doc = item_doc.get(c::tag_tree as usize);
     let mut rbml_r = reader::Decoder::new(chi_doc);
     rbml_r.read_opaque(|decoder, _| Decodable::decode(decoder)).unwrap()
 }
@@ -1280,8 +1282,8 @@ fn encode_item_ast(rbml_w: &mut Encoder, item: &hir::Item) {
 }
 
 #[cfg(test)]
-fn decode_item_ast(par_doc: rbml::Doc) -> hir::Item {
-    let chi_doc = par_doc.get(c::tag_tree as usize);
+fn decode_item_ast(item_doc: rbml::Doc) -> hir::Item {
+    let chi_doc = item_doc.get(c::tag_tree as usize);
     let mut d = reader::Decoder::new(chi_doc);
     Decodable::decode(&mut d).unwrap()
 }
diff --git a/src/librustc_metadata/common.rs b/src/librustc_metadata/common.rs
index a0cbba279ac..22a5289f02b 100644
--- a/src/librustc_metadata/common.rs
+++ b/src/librustc_metadata/common.rs
@@ -73,6 +73,7 @@ pub const tag_crate_dep: usize = 0x35;
 
 pub const tag_crate_hash: usize = 0x103; // top-level only
 pub const tag_crate_crate_name: usize = 0x104; // top-level only
+pub const tag_crate_disambiguator: usize = 0x113; // top-level only
 
 pub const tag_crate_dep_crate_name: usize = 0x36;
 pub const tag_crate_dep_hash: usize = 0x37;
diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs
index d05728d11cd..d07179749d9 100644
--- a/src/librustc_metadata/creader.rs
+++ b/src/librustc_metadata/creader.rs
@@ -21,7 +21,7 @@ use rustc::back::svh::Svh;
 use rustc::dep_graph::DepNode;
 use rustc::session::{config, Session};
 use rustc::session::search_paths::PathKind;
-use rustc::middle::cstore::{CrateStore, validate_crate_name};
+use rustc::middle::cstore::{CrateStore, validate_crate_name, ExternCrate};
 use rustc::util::nodemap::FnvHashMap;
 use rustc::front::map as hir_map;
 
@@ -38,7 +38,6 @@ use syntax::attr;
 use syntax::attr::AttrMetaMethods;
 use syntax::errors::FatalError;
 use syntax::parse::token::InternedString;
-use syntax::util::small_vector::SmallVector;
 use rustc_front::intravisit::Visitor;
 use rustc_front::hir;
 use log;
@@ -55,6 +54,7 @@ pub struct CrateReader<'a> {
     cstore: &'a CStore,
     next_crate_num: ast::CrateNum,
     foreign_item_map: FnvHashMap<String, Vec<ast::NodeId>>,
+    local_crate_name: String,
 }
 
 impl<'a, 'b, 'hir> Visitor<'hir> for LocalCrateReader<'a, 'b> {
@@ -146,12 +146,15 @@ impl PMDSource {
 }
 
 impl<'a> CrateReader<'a> {
-    pub fn new(sess: &'a Session, cstore: &'a CStore) -> CrateReader<'a> {
+    pub fn new(sess: &'a Session,
+               cstore: &'a CStore,
+               local_crate_name: &str) -> CrateReader<'a> {
         CrateReader {
             sess: sess,
             cstore: cstore,
             next_crate_num: cstore.next_crate_num(),
             foreign_item_map: FnvHashMap(),
+            local_crate_name: local_crate_name.to_owned(),
         }
     }
 
@@ -272,6 +275,38 @@ impl<'a> CrateReader<'a> {
         }
     }
 
+    fn verify_no_symbol_conflicts(&self,
+                                  span: Span,
+                                  metadata: &MetadataBlob) {
+        let disambiguator = decoder::get_crate_disambiguator(metadata.as_slice());
+        let crate_name = decoder::get_crate_name(metadata.as_slice());
+
+        // Check for (potential) conflicts with the local crate
+        if self.local_crate_name == crate_name &&
+           self.sess.crate_disambiguator.get().as_str() == disambiguator {
+            span_fatal!(self.sess, span, E0519,
+                        "the current crate is indistinguishable from one of its \
+                         dependencies: it has the same crate-name `{}` and was \
+                         compiled with the same `-C metadata` arguments. This \
+                         will result in symbol conflicts between the two.",
+                        crate_name)
+        }
+
+        let svh = decoder::get_crate_hash(metadata.as_slice());
+        // Check for conflicts with any crate loaded so far
+        self.cstore.iter_crate_data(|_, other| {
+            if other.name() == crate_name && // same crate-name
+               other.disambiguator() == disambiguator &&  // same crate-disambiguator
+               other.hash() != svh { // but different SVH
+                span_fatal!(self.sess, span, E0523,
+                        "found two different crates with name `{}` that are \
+                         not distinguished by differing `-C metadata`. This \
+                         will result in symbol conflicts between the two.",
+                        crate_name)
+            }
+        });
+    }
+
     fn register_crate(&mut self,
                       root: &Option<CratePaths>,
                       ident: &str,
@@ -282,6 +317,7 @@ impl<'a> CrateReader<'a> {
                       -> (ast::CrateNum, Rc<cstore::crate_metadata>,
                           cstore::CrateSource) {
         self.verify_rustc_version(name, span, &lib.metadata);
+        self.verify_no_symbol_conflicts(span, &lib.metadata);
 
         // Claim this crate number and cache it
         let cnum = self.next_crate_num;
@@ -307,15 +343,13 @@ impl<'a> CrateReader<'a> {
 
         let cmeta = Rc::new(cstore::crate_metadata {
             name: name.to_string(),
-            local_path: RefCell::new(SmallVector::zero()),
-            local_def_path: RefCell::new(vec![]),
+            extern_crate: Cell::new(None),
             index: decoder::load_index(metadata.as_slice()),
             xref_index: decoder::load_xrefs(metadata.as_slice()),
             data: metadata,
             cnum_map: RefCell::new(cnum_map),
             cnum: cnum,
             codemap_import_info: RefCell::new(vec![]),
-            span: span,
             staged_api: staged_api,
             explicitly_linked: Cell::new(explicitly_linked),
         });
@@ -349,8 +383,7 @@ impl<'a> CrateReader<'a> {
                      span: Span,
                      kind: PathKind,
                      explicitly_linked: bool)
-                         -> (ast::CrateNum, Rc<cstore::crate_metadata>,
-                             cstore::CrateSource) {
+                     -> (ast::CrateNum, Rc<cstore::crate_metadata>, cstore::CrateSource) {
         enum LookupResult {
             Previous(ast::CrateNum),
             Loaded(loader::Library),
@@ -407,23 +440,54 @@ impl<'a> CrateReader<'a> {
         }
     }
 
+    fn update_extern_crate(&mut self,
+                           cnum: ast::CrateNum,
+                           mut extern_crate: ExternCrate)
+    {
+        let cmeta = self.cstore.get_crate_data(cnum);
+        let old_extern_crate = cmeta.extern_crate.get();
+
+        // Prefer:
+        // - something over nothing (tuple.0);
+        // - direct extern crate to indirect (tuple.1);
+        // - shorter paths to longer (tuple.2).
+        let new_rank = (true, extern_crate.direct, !extern_crate.path_len);
+        let old_rank = match old_extern_crate {
+            None => (false, false, !0),
+            Some(ref c) => (true, c.direct, !c.path_len),
+        };
+
+        if old_rank >= new_rank {
+            return; // no change needed
+        }
+
+        cmeta.extern_crate.set(Some(extern_crate));
+
+        // Propagate the extern crate info to dependencies.
+        extern_crate.direct = false;
+        for &dep_cnum in cmeta.cnum_map.borrow().values() {
+            self.update_extern_crate(dep_cnum, extern_crate);
+        }
+    }
+
     // Go through the crate metadata and load any crates that it references
     fn resolve_crate_deps(&mut self,
                           root: &Option<CratePaths>,
-                          cdata: &[u8], span : Span)
-                       -> cstore::cnum_map {
+                          cdata: &[u8],
+                          span : Span)
+                          -> cstore::cnum_map {
         debug!("resolving deps of external crate");
         // The map from crate numbers in the crate we're resolving to local crate
         // numbers
         decoder::get_crate_deps(cdata).iter().map(|dep| {
             debug!("resolving dep crate {} hash: `{}`", dep.name, dep.hash);
             let (local_cnum, _, _) = self.resolve_crate(root,
-                                                   &dep.name,
-                                                   &dep.name,
-                                                   Some(&dep.hash),
-                                                   span,
-                                                   PathKind::Dependency,
-                                                   dep.explicitly_linked);
+                                                        &dep.name,
+                                                        &dep.name,
+                                                        Some(&dep.hash),
+                                                        span,
+                                                        PathKind::Dependency,
+                                                        dep.explicitly_linked);
             (dep.cnum, local_cnum)
         }).collect()
     }
@@ -713,12 +777,15 @@ impl<'a> CrateReader<'a> {
 }
 
 impl<'a, 'b> LocalCrateReader<'a, 'b> {
-    pub fn new(sess: &'a Session, cstore: &'a CStore,
-               map: &'a hir_map::Map<'b>) -> LocalCrateReader<'a, 'b> {
+    pub fn new(sess: &'a Session,
+               cstore: &'a CStore,
+               map: &'a hir_map::Map<'b>,
+               local_crate_name: &str)
+               -> LocalCrateReader<'a, 'b> {
         LocalCrateReader {
             sess: sess,
             cstore: cstore,
-            creader: CrateReader::new(sess, cstore),
+            creader: CrateReader::new(sess, cstore, local_crate_name),
             ast_map: map,
         }
     }
@@ -762,19 +829,24 @@ impl<'a, 'b> LocalCrateReader<'a, 'b> {
 
                 match self.creader.extract_crate_info_hir(i) {
                     Some(info) => {
-                        let (cnum, cmeta, _) = self.creader.resolve_crate(&None,
-                                                              &info.ident,
-                                                              &info.name,
-                                                              None,
-                                                              i.span,
-                                                              PathKind::Crate,
-                                                              true);
+                        let (cnum, _, _) = self.creader.resolve_crate(&None,
+                                                                          &info.ident,
+                                                                          &info.name,
+                                                                          None,
+                                                                          i.span,
+                                                                          PathKind::Crate,
+                                                                          true);
                         let def_id = self.ast_map.local_def_id(i.id);
-                        let def_path = self.ast_map.def_path(def_id);
-                        cmeta.update_local_def_path(def_path);
-                        self.ast_map.with_path(i.id, |path| {
-                            cmeta.update_local_path(path)
-                        });
+
+                        let len = self.ast_map.def_path(def_id).data.len();
+
+                        self.creader.update_extern_crate(cnum,
+                                                         ExternCrate {
+                                                             def_id: def_id,
+                                                             span: i.span,
+                                                             direct: true,
+                                                             path_len: len,
+                                                         });
                         self.cstore.add_extern_mod_stmt_cnum(info.id, cnum);
                     }
                     None => ()
diff --git a/src/librustc_metadata/csearch.rs b/src/librustc_metadata/csearch.rs
index 9ac7216165c..25cc2f91753 100644
--- a/src/librustc_metadata/csearch.rs
+++ b/src/librustc_metadata/csearch.rs
@@ -13,7 +13,7 @@ use decoder;
 use encoder;
 use loader;
 
-use middle::cstore::{CrateStore, CrateSource, ChildItem, FoundAst};
+use middle::cstore::{CrateStore, CrateSource, ChildItem, ExternCrate, FoundAst};
 use middle::cstore::{NativeLibraryKind, LinkMeta, LinkagePreference};
 use middle::def;
 use middle::lang_items;
@@ -128,16 +128,9 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
         decoder::get_method_arg_names(&cdata, did.index)
     }
 
-    fn item_path(&self, def: DefId) -> Vec<hir_map::PathElem> {
+    fn relative_item_path(&self, def: DefId) -> Vec<hir_map::PathElem> {
         let cdata = self.get_crate_data(def.krate);
-        let path = decoder::get_item_path(&cdata, def.index);
-
-        cdata.with_local_path(|cpath| {
-            let mut r = Vec::with_capacity(cpath.len() + path.len());
-            r.extend_from_slice(cpath);
-            r.extend_from_slice(&path);
-            r
-        })
+        decoder::get_item_path(&cdata, def.index)
     }
 
     fn extern_item_path(&self, def: DefId) -> Vec<hir_map::PathElem> {
@@ -334,9 +327,19 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
         decoder::get_crate_attributes(self.get_crate_data(cnum).data())
     }
 
-    fn crate_name(&self, cnum: ast::CrateNum) -> String
+    fn crate_name(&self, cnum: ast::CrateNum) -> token::InternedString
+    {
+        token::intern_and_get_ident(&self.get_crate_data(cnum).name[..])
+    }
+
+    fn original_crate_name(&self, cnum: ast::CrateNum) -> token::InternedString
+    {
+        token::intern_and_get_ident(&self.get_crate_data(cnum).name())
+    }
+
+    fn extern_crate(&self, cnum: ast::CrateNum) -> Option<ExternCrate>
     {
-        self.get_crate_data(cnum).name.clone()
+        self.get_crate_data(cnum).extern_crate.get()
     }
 
     fn crate_hash(&self, cnum: ast::CrateNum) -> Svh
@@ -345,6 +348,12 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
         decoder::get_crate_hash(cdata.data())
     }
 
+    fn crate_disambiguator(&self, cnum: ast::CrateNum) -> token::InternedString
+    {
+        let cdata = self.get_crate_data(cnum);
+        token::intern_and_get_ident(decoder::get_crate_disambiguator(cdata.data()))
+    }
+
     fn crate_struct_field_attrs(&self, cnum: ast::CrateNum)
                                 -> FnvHashMap<DefId, Vec<ast::Attribute>>
     {
@@ -372,12 +381,17 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
         decoder::get_reachable_ids(&cdata)
     }
 
-    fn def_path(&self, def: DefId) -> hir_map::DefPath
-    {
+    /// Returns the `DefKey` for a given `DefId`. This indicates the
+    /// parent `DefId` as well as some idea of what kind of data the
+    /// `DefId` refers to.
+    fn def_key(&self, def: DefId) -> hir_map::DefKey {
+        let cdata = self.get_crate_data(def.krate);
+        decoder::def_key(&cdata, def.index)
+    }
+
+    fn relative_def_path(&self, def: DefId) -> hir_map::DefPath {
         let cdata = self.get_crate_data(def.krate);
-        let path = decoder::def_path(&cdata, def.index);
-        let local_path = cdata.local_def_path();
-        local_path.into_iter().chain(path).collect()
+        decoder::def_path(&cdata, def.index)
     }
 
     fn variant_kind(&self, def_id: DefId) -> Option<VariantKind> {
@@ -478,9 +492,13 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
     {
         loader::meta_section_name(target)
     }
-    fn encode_type(&self, tcx: &TyCtxt<'tcx>, ty: Ty<'tcx>) -> Vec<u8>
+    fn encode_type(&self,
+                   tcx: &TyCtxt<'tcx>,
+                   ty: Ty<'tcx>,
+                   def_id_to_string: fn(&TyCtxt<'tcx>, DefId) -> String)
+                   -> Vec<u8>
     {
-        encoder::encoded_ty(tcx, ty)
+        encoder::encoded_ty(tcx, ty, def_id_to_string)
     }
 
     fn used_crates(&self, prefer: LinkagePreference) -> Vec<(ast::CrateNum, Option<PathBuf>)>
diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs
index a96da6bf4d6..f092ee39198 100644
--- a/src/librustc_metadata/cstore.rs
+++ b/src/librustc_metadata/cstore.rs
@@ -21,7 +21,7 @@ use index;
 use loader;
 
 use rustc::back::svh::Svh;
-use rustc::front::map as ast_map;
+use rustc::middle::cstore::{ExternCrate};
 use rustc::util::nodemap::{FnvHashMap, NodeMap, NodeSet};
 
 use std::cell::{RefCell, Ref, Cell};
@@ -31,9 +31,7 @@ use flate::Bytes;
 use syntax::ast;
 use syntax::attr;
 use syntax::codemap;
-use syntax::parse::token;
 use syntax::parse::token::IdentInterner;
-use syntax::util::small_vector::SmallVector;
 
 pub use middle::cstore::{NativeLibraryKind, LinkagePreference};
 pub use middle::cstore::{NativeStatic, NativeFramework, NativeUnknown};
@@ -63,13 +61,16 @@ pub struct ImportedFileMap {
 
 pub struct crate_metadata {
     pub name: String,
-    pub local_path: RefCell<SmallVector<ast_map::PathElem>>,
-    pub local_def_path: RefCell<ast_map::DefPath>,
+
+    /// Information about the extern crate that caused this crate to
+    /// be loaded. If this is `None`, then the crate was injected
+    /// (e.g., by the allocator)
+    pub extern_crate: Cell<Option<ExternCrate>>,
+
     pub data: MetadataBlob,
     pub cnum_map: RefCell<cnum_map>,
     pub cnum: ast::CrateNum,
     pub codemap_import_info: RefCell<Vec<ImportedFileMap>>,
-    pub span: codemap::Span,
     pub staged_api: bool,
 
     pub index: index::Index,
@@ -248,8 +249,11 @@ impl CStore {
 
 impl crate_metadata {
     pub fn data<'a>(&'a self) -> &'a [u8] { self.data.as_slice() }
-    pub fn name(&self) -> String { decoder::get_crate_name(self.data()) }
+    pub fn name(&self) -> &str { decoder::get_crate_name(self.data()) }
     pub fn hash(&self) -> Svh { decoder::get_crate_hash(self.data()) }
+    pub fn disambiguator(&self) -> &str {
+        decoder::get_crate_disambiguator(self.data())
+    }
     pub fn imported_filemaps<'a>(&'a self, codemap: &codemap::CodeMap)
                                  -> Ref<'a, Vec<ImportedFileMap>> {
         let filemaps = self.codemap_import_info.borrow();
@@ -265,50 +269,6 @@ impl crate_metadata {
         }
     }
 
-    pub fn with_local_path<T, F>(&self, f: F) -> T
-        where F: Fn(&[ast_map::PathElem]) -> T
-    {
-        let cpath = self.local_path.borrow();
-        if cpath.is_empty() {
-            let name = ast_map::PathMod(token::intern(&self.name));
-            f(&[name])
-        } else {
-            f(cpath.as_slice())
-        }
-    }
-
-    pub fn update_local_path<'a, 'b>(&self, candidate: ast_map::PathElems<'a, 'b>) {
-        let mut cpath = self.local_path.borrow_mut();
-        let cap = cpath.len();
-        match cap {
-            0 => *cpath = candidate.collect(),
-            1 => (),
-            _ => {
-                let candidate: SmallVector<_> = candidate.collect();
-                if candidate.len() < cap {
-                    *cpath = candidate;
-                }
-            },
-        }
-    }
-
-    pub fn local_def_path(&self) -> ast_map::DefPath {
-        let local_def_path = self.local_def_path.borrow();
-        if local_def_path.is_empty() {
-            let name = ast_map::DefPathData::DetachedCrate(token::intern(&self.name));
-            vec![ast_map::DisambiguatedDefPathData { data: name, disambiguator: 0 }]
-        } else {
-            local_def_path.clone()
-        }
-    }
-
-    pub fn update_local_def_path(&self, candidate: ast_map::DefPath) {
-        let mut local_def_path = self.local_def_path.borrow_mut();
-        if local_def_path.is_empty() || candidate.len() < local_def_path.len() {
-            *local_def_path = candidate;
-        }
-    }
-
     pub fn is_allocator(&self) -> bool {
         let attrs = decoder::get_crate_attributes(self.data());
         attr::contains_name(&attrs, "allocator")
diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs
index 0c736feeefe..a4eeee44fb7 100644
--- a/src/librustc_metadata/decoder.rs
+++ b/src/librustc_metadata/decoder.rs
@@ -803,25 +803,43 @@ pub fn maybe_get_item_ast<'tcx>(cdata: Cmd, tcx: &TyCtxt<'tcx>, id: DefIndex)
     debug!("Looking up item: {:?}", id);
     let item_doc = cdata.lookup_item(id);
     let item_did = item_def_id(item_doc, cdata);
+    let parent_def_id = DefId {
+        krate: cdata.cnum,
+        index: def_key(cdata, id).parent.unwrap()
+    };
     let mut parent_path = item_path(item_doc);
     parent_path.pop();
     let mut parent_def_path = def_path(cdata, id);
-    parent_def_path.pop();
+    parent_def_path.data.pop();
     if let Some(ast_doc) = reader::maybe_get_doc(item_doc, tag_ast as usize) {
-        let ii = decode_inlined_item(cdata, tcx, parent_path,
+        let ii = decode_inlined_item(cdata,
+                                     tcx,
+                                     parent_path,
                                      parent_def_path,
-                                     ast_doc, item_did);
+                                     parent_def_id,
+                                     ast_doc,
+                                     item_did);
         return FoundAst::Found(ii);
     } else if let Some(parent_did) = item_parent_item(cdata, item_doc) {
         // Remove the last element from the paths, since we are now
         // trying to inline the parent.
-        parent_path.pop();
-        parent_def_path.pop();
+        let grandparent_def_id = DefId {
+            krate: cdata.cnum,
+            index: def_key(cdata, parent_def_id.index).parent.unwrap()
+        };
+        let mut grandparent_path = parent_path;
+        grandparent_path.pop();
+        let mut grandparent_def_path = parent_def_path;
+        grandparent_def_path.data.pop();
         let parent_doc = cdata.lookup_item(parent_did.index);
         if let Some(ast_doc) = reader::maybe_get_doc(parent_doc, tag_ast as usize) {
-            let ii = decode_inlined_item(cdata, tcx, parent_path,
-                                         parent_def_path,
-                                         ast_doc, parent_did);
+            let ii = decode_inlined_item(cdata,
+                                         tcx,
+                                         grandparent_path,
+                                         grandparent_def_path,
+                                         grandparent_def_id,
+                                         ast_doc,
+                                         parent_did);
             if let &InlinedItem::Item(ref i) = ii {
                 return FoundAst::FoundParent(parent_did, i);
             }
@@ -1288,20 +1306,27 @@ pub fn get_crate_hash(data: &[u8]) -> Svh {
     Svh::new(hashdoc.as_str_slice())
 }
 
-pub fn maybe_get_crate_name(data: &[u8]) -> Option<String> {
+pub fn maybe_get_crate_name(data: &[u8]) -> Option<&str> {
     let cratedoc = rbml::Doc::new(data);
     reader::maybe_get_doc(cratedoc, tag_crate_crate_name).map(|doc| {
-        doc.as_str_slice().to_string()
+        doc.as_str_slice()
     })
 }
 
+pub fn get_crate_disambiguator<'a>(data: &'a [u8]) -> &'a str {
+    let crate_doc = rbml::Doc::new(data);
+    let disambiguator_doc = reader::get_doc(crate_doc, tag_crate_disambiguator);
+    let slice: &'a str = disambiguator_doc.as_str_slice();
+    slice
+}
+
 pub fn get_crate_triple(data: &[u8]) -> Option<String> {
     let cratedoc = rbml::Doc::new(data);
     let triple_doc = reader::maybe_get_doc(cratedoc, tag_crate_triple);
     triple_doc.map(|s| s.as_str().to_string())
 }
 
-pub fn get_crate_name(data: &[u8]) -> String {
+pub fn get_crate_name(data: &[u8]) -> &str {
     maybe_get_crate_name(data).expect("no crate name in crate")
 }
 
@@ -1738,7 +1763,9 @@ pub fn closure_ty<'tcx>(cdata: Cmd, closure_id: DefIndex, tcx: &TyCtxt<'tcx>)
         .parse_closure_ty()
 }
 
-fn def_key(item_doc: rbml::Doc) -> hir_map::DefKey {
+pub fn def_key(cdata: Cmd, id: DefIndex) -> hir_map::DefKey {
+    debug!("def_key: id={:?}", id);
+    let item_doc = cdata.lookup_item(id);
     match reader::maybe_get_doc(item_doc, tag_def_key) {
         Some(def_key_doc) => {
             let mut decoder = reader::Decoder::new(def_key_doc);
@@ -1754,9 +1781,5 @@ fn def_key(item_doc: rbml::Doc) -> hir_map::DefKey {
 
 pub fn def_path(cdata: Cmd, id: DefIndex) -> hir_map::DefPath {
     debug!("def_path(id={:?})", id);
-    hir_map::definitions::make_def_path(id, |parent| {
-        debug!("def_path: parent={:?}", parent);
-        let parent_doc = cdata.lookup_item(parent);
-        def_key(parent_doc)
-    })
+    hir_map::DefPath::make(cdata.cnum, id, |parent| def_key(cdata, parent))
 }
diff --git a/src/librustc_metadata/diagnostics.rs b/src/librustc_metadata/diagnostics.rs
index 50b9ea57550..8fa23de9a2d 100644
--- a/src/librustc_metadata/diagnostics.rs
+++ b/src/librustc_metadata/diagnostics.rs
@@ -87,4 +87,6 @@ register_diagnostics! {
     E0468, // an `extern crate` loading macros must be at the crate root
     E0469, // imported macro not found
     E0470, // reexported macro not found
+    E0519, // local crate and dependency have same (crate-name, disambiguator)
+    E0523, // two dependencies have same (crate-name, disambiguator) but different SVH
 }
diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs
index e677ea962f9..73ac64adbb2 100644
--- a/src/librustc_metadata/encoder.rs
+++ b/src/librustc_metadata/encoder.rs
@@ -143,7 +143,7 @@ pub fn def_to_u64(did: DefId) -> u64 {
     (did.krate as u64) << 32 | (did.index.as_usize() as u64)
 }
 
-pub fn def_to_string(did: DefId) -> String {
+pub fn def_to_string(_tcx: &TyCtxt, did: DefId) -> String {
     format!("{}:{}", did.krate, did.index.as_usize())
 }
 
@@ -1877,6 +1877,10 @@ fn encode_crate_name(rbml_w: &mut Encoder, crate_name: &str) {
     rbml_w.wr_tagged_str(tag_crate_crate_name, crate_name);
 }
 
+fn encode_crate_disambiguator(rbml_w: &mut Encoder, crate_disambiguator: &str) {
+    rbml_w.wr_tagged_str(tag_crate_disambiguator, crate_disambiguator);
+}
+
 fn encode_crate_triple(rbml_w: &mut Encoder, triple: &str) {
     rbml_w.wr_tagged_str(tag_crate_triple, triple);
 }
@@ -1987,6 +1991,7 @@ fn encode_metadata_inner(rbml_w: &mut Encoder,
     encode_crate_name(rbml_w, &ecx.link_meta.crate_name);
     encode_crate_triple(rbml_w, &ecx.tcx.sess.opts.target_triple);
     encode_hash(rbml_w, &ecx.link_meta.crate_hash);
+    encode_crate_disambiguator(rbml_w, &ecx.tcx.sess.crate_disambiguator.get().as_str());
     encode_dylib_dependency_formats(rbml_w, &ecx);
 
     let mut i = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap();
@@ -2078,11 +2083,14 @@ fn encode_metadata_inner(rbml_w: &mut Encoder,
 }
 
 // Get the encoded string for a type
-pub fn encoded_ty<'tcx>(tcx: &TyCtxt<'tcx>, t: Ty<'tcx>) -> Vec<u8> {
+pub fn encoded_ty<'tcx>(tcx: &TyCtxt<'tcx>,
+                        t: Ty<'tcx>,
+                        def_id_to_string: fn(&TyCtxt<'tcx>, DefId) -> String)
+                        -> Vec<u8> {
     let mut wr = Cursor::new(Vec::new());
     tyencode::enc_ty(&mut wr, &tyencode::ctxt {
         diag: tcx.sess.diagnostic(),
-        ds: def_to_string,
+        ds: def_id_to_string,
         tcx: tcx,
         abbrevs: &RefCell::new(FnvHashMap())
     }, t);
diff --git a/src/librustc_metadata/macro_import.rs b/src/librustc_metadata/macro_import.rs
index 102bcc10fac..911ca7e315c 100644
--- a/src/librustc_metadata/macro_import.rs
+++ b/src/librustc_metadata/macro_import.rs
@@ -32,11 +32,11 @@ struct MacroLoader<'a> {
 }
 
 impl<'a> MacroLoader<'a> {
-    fn new(sess: &'a Session, cstore: &'a CStore) -> MacroLoader<'a> {
+    fn new(sess: &'a Session, cstore: &'a CStore, crate_name: &str) -> MacroLoader<'a> {
         MacroLoader {
             sess: sess,
             span_whitelist: HashSet::new(),
-            reader: CrateReader::new(sess, cstore),
+            reader: CrateReader::new(sess, cstore, crate_name),
             macros: vec![],
         }
     }
@@ -47,10 +47,13 @@ pub fn call_bad_macro_reexport(a: &Session, b: Span) {
 }
 
 /// Read exported macros.
-pub fn read_macro_defs(sess: &Session, cstore: &CStore, krate: &ast::Crate)
+pub fn read_macro_defs(sess: &Session,
+                       cstore: &CStore,
+                       krate: &ast::Crate,
+                       crate_name: &str)
                        -> Vec<ast::MacroDef>
 {
-    let mut loader = MacroLoader::new(sess, cstore);
+    let mut loader = MacroLoader::new(sess, cstore, crate_name);
 
     // We need to error on `#[macro_use] extern crate` when it isn't at the
     // crate root, because `$crate` won't work properly. Identify these by
diff --git a/src/librustc_metadata/tyencode.rs b/src/librustc_metadata/tyencode.rs
index 4718732c8a0..67e77ba3315 100644
--- a/src/librustc_metadata/tyencode.rs
+++ b/src/librustc_metadata/tyencode.rs
@@ -37,7 +37,7 @@ use encoder;
 pub struct ctxt<'a, 'tcx: 'a> {
     pub diag: &'a Handler,
     // Def -> str Callback:
-    pub ds: fn(DefId) -> String,
+    pub ds: fn(&TyCtxt<'tcx>, DefId) -> String,
     // The type context.
     pub tcx: &'a TyCtxt<'tcx>,
     pub abbrevs: &'a abbrev_map<'tcx>
@@ -99,7 +99,7 @@ pub fn enc_ty<'a, 'tcx>(w: &mut Cursor<Vec<u8>>, cx: &ctxt<'a, 'tcx>, t: Ty<'tcx
             };
         }
         ty::TyEnum(def, substs) => {
-            write!(w, "t[{}|", (cx.ds)(def.did));
+            write!(w, "t[{}|", (cx.ds)(cx.tcx, def.did));
             enc_substs(w, cx, substs);
             write!(w, "]");
         }
@@ -137,7 +137,7 @@ pub fn enc_ty<'a, 'tcx>(w: &mut Cursor<Vec<u8>>, cx: &ctxt<'a, 'tcx>, t: Ty<'tcx
         }
         ty::TyFnDef(def_id, substs, f) => {
             write!(w, "F");
-            write!(w, "{}|", (cx.ds)(def_id));
+            write!(w, "{}|", (cx.ds)(cx.tcx, def_id));
             enc_substs(w, cx, substs);
             enc_bare_fn_ty(w, cx, f);
         }
@@ -152,12 +152,12 @@ pub fn enc_ty<'a, 'tcx>(w: &mut Cursor<Vec<u8>>, cx: &ctxt<'a, 'tcx>, t: Ty<'tcx
             write!(w, "p[{}|{}|{}]", idx, space.to_uint(), name);
         }
         ty::TyStruct(def, substs) => {
-            write!(w, "a[{}|", (cx.ds)(def.did));
+            write!(w, "a[{}|", (cx.ds)(cx.tcx, def.did));
             enc_substs(w, cx, substs);
             write!(w, "]");
         }
         ty::TyClosure(def, ref substs) => {
-            write!(w, "k[{}|", (cx.ds)(def));
+            write!(w, "k[{}|", (cx.ds)(cx.tcx, def));
             enc_substs(w, cx, &substs.func_substs);
             for ty in &substs.upvar_tys {
                 enc_ty(w, cx, ty);
@@ -310,7 +310,7 @@ fn enc_bound_region(w: &mut Cursor<Vec<u8>>, cx: &ctxt, br: ty::BoundRegion) {
         }
         ty::BrNamed(d, name) => {
             write!(w, "[{}|{}]",
-                     (cx.ds)(d),
+                     (cx.ds)(cx.tcx, d),
                      name);
         }
         ty::BrFresh(id) => {
@@ -324,7 +324,7 @@ fn enc_bound_region(w: &mut Cursor<Vec<u8>>, cx: &ctxt, br: ty::BoundRegion) {
 
 pub fn enc_trait_ref<'a, 'tcx>(w: &mut Cursor<Vec<u8>>, cx: &ctxt<'a, 'tcx>,
                                s: ty::TraitRef<'tcx>) {
-    write!(w, "{}|", (cx.ds)(s.def_id));
+    write!(w, "{}|", (cx.ds)(cx.tcx, s.def_id));
     enc_substs(w, cx, s.substs);
 }
 
@@ -408,8 +408,8 @@ pub fn enc_existential_bounds<'a,'tcx>(w: &mut Cursor<Vec<u8>>,
 pub fn enc_type_param_def<'a, 'tcx>(w: &mut Cursor<Vec<u8>>, cx: &ctxt<'a, 'tcx>,
                                     v: &ty::TypeParameterDef<'tcx>) {
     write!(w, "{}:{}|{}|{}|{}|",
-             v.name, (cx.ds)(v.def_id),
-             v.space.to_uint(), v.index, (cx.ds)(v.default_def_id));
+             v.name, (cx.ds)(cx.tcx, v.def_id),
+             v.space.to_uint(), v.index, (cx.ds)(cx.tcx, v.default_def_id));
     enc_opt(w, v.default, |w, t| enc_ty(w, cx, t));
     enc_object_lifetime_default(w, cx, v.object_lifetime_default);
 }
@@ -417,7 +417,7 @@ pub fn enc_type_param_def<'a, 'tcx>(w: &mut Cursor<Vec<u8>>, cx: &ctxt<'a, 'tcx>
 pub fn enc_region_param_def(w: &mut Cursor<Vec<u8>>, cx: &ctxt,
                             v: &ty::RegionParameterDef) {
     write!(w, "{}:{}|{}|{}|",
-             v.name, (cx.ds)(v.def_id),
+             v.name, (cx.ds)(cx.tcx, v.def_id),
              v.space.to_uint(), v.index);
     for &r in &v.bounds {
         write!(w, "R");
@@ -477,7 +477,7 @@ pub fn enc_predicate<'a, 'tcx>(w: &mut Cursor<Vec<u8>>,
             enc_ty(w, cx, data);
         }
         ty::Predicate::ObjectSafe(trait_def_id) => {
-            write!(w, "O{}|", (cx.ds)(trait_def_id));
+            write!(w, "O{}|", (cx.ds)(cx.tcx, trait_def_id));
         }
     }
 }
diff --git a/src/librustc_plugin/load.rs b/src/librustc_plugin/load.rs
index a950198a4e4..ac40215bbb1 100644
--- a/src/librustc_plugin/load.rs
+++ b/src/librustc_plugin/load.rs
@@ -44,9 +44,12 @@ fn call_malformed_plugin_attribute(a: &Session, b: Span) {
 }
 
 /// Read plugin metadata and dynamically load registrar functions.
-pub fn load_plugins(sess: &Session, cstore: &CStore, krate: &ast::Crate,
+pub fn load_plugins(sess: &Session,
+                    cstore: &CStore,
+                    krate: &ast::Crate,
+                    crate_name: &str,
                     addl_plugins: Option<Vec<String>>) -> Vec<PluginRegistrar> {
-    let mut loader = PluginLoader::new(sess, cstore);
+    let mut loader = PluginLoader::new(sess, cstore, crate_name);
 
     for attr in &krate.attrs {
         if !attr.check_name("plugin") {
@@ -82,10 +85,10 @@ pub fn load_plugins(sess: &Session, cstore: &CStore, krate: &ast::Crate,
 }
 
 impl<'a> PluginLoader<'a> {
-    fn new(sess: &'a Session, cstore: &'a CStore) -> PluginLoader<'a> {
+    fn new(sess: &'a Session, cstore: &'a CStore, crate_name: &str) -> PluginLoader<'a> {
         PluginLoader {
             sess: sess,
-            reader: CrateReader::new(sess, cstore),
+            reader: CrateReader::new(sess, cstore, crate_name),
             plugins: vec![],
         }
     }
diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs
index acb458f8cc6..64d11710054 100644
--- a/src/librustc_trans/back/link.rs
+++ b/src/librustc_trans/back/link.rs
@@ -23,11 +23,8 @@ use session::Session;
 use middle::cstore::{self, CrateStore, LinkMeta};
 use middle::cstore::{LinkagePreference, NativeLibraryKind};
 use middle::dependency_format::Linkage;
-use middle::ty::{Ty, TyCtxt};
-use rustc::front::map::DefPath;
-use trans::{CrateContext, CrateTranslation, gensym_name};
+use trans::CrateTranslation;
 use util::common::time;
-use util::sha2::{Digest, Sha256};
 use util::fs::fix_windows_verbatim_for_gcc;
 use rustc_back::tempdir::TempDir;
 
@@ -37,16 +34,13 @@ use std::env;
 use std::ffi::OsString;
 use std::fs;
 use std::io::{self, Read, Write};
-use std::iter::once;
 use std::mem;
 use std::path::{Path, PathBuf};
 use std::process::Command;
 use std::str;
 use flate;
-use serialize::hex::ToHex;
 use syntax::ast;
 use syntax::codemap::Span;
-use syntax::parse::token::{self, InternedString};
 use syntax::attr::AttrMetaMethods;
 
 use rustc_front::hir;
@@ -81,58 +75,6 @@ pub const RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET: usize =
     RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET + 8;
 
 
-/*
- * Name mangling and its relationship to metadata. This is complex. Read
- * carefully.
- *
- * The semantic model of Rust linkage is, broadly, that "there's no global
- * namespace" between crates. Our aim is to preserve the illusion of this
- * model despite the fact that it's not *quite* possible to implement on
- * modern linkers. We initially didn't use system linkers at all, but have
- * been convinced of their utility.
- *
- * There are a few issues to handle:
- *
- *  - Linkers operate on a flat namespace, so we have to flatten names.
- *    We do this using the C++ namespace-mangling technique. Foo::bar
- *    symbols and such.
- *
- *  - Symbols with the same name but different types need to get different
- *    linkage-names. We do this by hashing a string-encoding of the type into
- *    a fixed-size (currently 16-byte hex) cryptographic hash function (CHF:
- *    we use SHA256) to "prevent collisions". This is not airtight but 16 hex
- *    digits on uniform probability means you're going to need 2**32 same-name
- *    symbols in the same process before you're even hitting birthday-paradox
- *    collision probability.
- *
- *  - Symbols in different crates but with same names "within" the crate need
- *    to get different linkage-names.
- *
- *  - The hash shown in the filename needs to be predictable and stable for
- *    build tooling integration. It also needs to be using a hash function
- *    which is easy to use from Python, make, etc.
- *
- * So here is what we do:
- *
- *  - Consider the package id; every crate has one (specified with crate_id
- *    attribute).  If a package id isn't provided explicitly, we infer a
- *    versionless one from the output name. The version will end up being 0.0
- *    in this case. CNAME and CVERS are taken from this package id. For
- *    example, github.com/mozilla/CNAME#CVERS.
- *
- *  - Define CMH as SHA256(crateid).
- *
- *  - Define CMH8 as the first 8 characters of CMH.
- *
- *  - Compile our crate to lib CNAME-CMH8-CVERS.so
- *
- *  - Define STH(sym) as SHA256(CMH, type_str(sym))
- *
- *  - Suffix a mangled sym with ::STH@CVERS, so that it is unique in the
- *    name, non-name metadata, and type sense, and versioned in the way
- *    system linkers understand.
- */
-
 pub fn find_crate_name(sess: Option<&Session>,
                        attrs: &[ast::Attribute],
                        input: &Input) -> String {
@@ -188,187 +130,12 @@ pub fn build_link_meta(sess: &Session,
                        -> LinkMeta {
     let r = LinkMeta {
         crate_name: name.to_owned(),
-        crate_hash: Svh::calculate(&sess.opts.cg.metadata, krate),
+        crate_hash: Svh::calculate(&sess.crate_disambiguator.get().as_str(), krate),
     };
     info!("{:?}", r);
     return r;
 }
 
-fn truncated_hash_result(symbol_hasher: &mut Sha256) -> String {
-    let output = symbol_hasher.result_bytes();
-    // 64 bits should be enough to avoid collisions.
-    output[.. 8].to_hex().to_string()
-}
-
-
-// This calculates STH for a symbol, as defined above
-fn symbol_hash<'tcx>(tcx: &TyCtxt<'tcx>,
-                     symbol_hasher: &mut Sha256,
-                     t: Ty<'tcx>,
-                     link_meta: &LinkMeta)
-                     -> String {
-    // NB: do *not* use abbrevs here as we want the symbol names
-    // to be independent of one another in the crate.
-
-    symbol_hasher.reset();
-    symbol_hasher.input_str(&link_meta.crate_name);
-    symbol_hasher.input_str("-");
-    symbol_hasher.input_str(link_meta.crate_hash.as_str());
-    for meta in tcx.sess.crate_metadata.borrow().iter() {
-        symbol_hasher.input_str(&meta[..]);
-    }
-    symbol_hasher.input_str("-");
-    symbol_hasher.input(&tcx.sess.cstore.encode_type(tcx, t));
-    // Prefix with 'h' so that it never blends into adjacent digits
-    let mut hash = String::from("h");
-    hash.push_str(&truncated_hash_result(symbol_hasher));
-    hash
-}
-
-fn get_symbol_hash<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> String {
-    if let Some(h) = ccx.type_hashcodes().borrow().get(&t) {
-        return h.to_string()
-    }
-
-    let mut symbol_hasher = ccx.symbol_hasher().borrow_mut();
-    let hash = symbol_hash(ccx.tcx(), &mut *symbol_hasher, t, ccx.link_meta());
-    ccx.type_hashcodes().borrow_mut().insert(t, hash.clone());
-    hash
-}
-
-
-// Name sanitation. LLVM will happily accept identifiers with weird names, but
-// gas doesn't!
-// gas accepts the following characters in symbols: a-z, A-Z, 0-9, ., _, $
-pub fn sanitize(s: &str) -> String {
-    let mut result = String::new();
-    for c in s.chars() {
-        match c {
-            // Escape these with $ sequences
-            '@' => result.push_str("$SP$"),
-            '*' => result.push_str("$BP$"),
-            '&' => result.push_str("$RF$"),
-            '<' => result.push_str("$LT$"),
-            '>' => result.push_str("$GT$"),
-            '(' => result.push_str("$LP$"),
-            ')' => result.push_str("$RP$"),
-            ',' => result.push_str("$C$"),
-
-            // '.' doesn't occur in types and functions, so reuse it
-            // for ':' and '-'
-            '-' | ':' => result.push('.'),
-
-            // These are legal symbols
-            'a' ... 'z'
-            | 'A' ... 'Z'
-            | '0' ... '9'
-            | '_' | '.' | '$' => result.push(c),
-
-            _ => {
-                result.push('$');
-                for c in c.escape_unicode().skip(1) {
-                    match c {
-                        '{' => {},
-                        '}' => result.push('$'),
-                        c => result.push(c),
-                    }
-                }
-            }
-        }
-    }
-
-    // Underscore-qualify anything that didn't start as an ident.
-    if !result.is_empty() &&
-        result.as_bytes()[0] != '_' as u8 &&
-        ! (result.as_bytes()[0] as char).is_xid_start() {
-        return format!("_{}", &result[..]);
-    }
-
-    return result;
-}
-
-pub fn mangle<PI: Iterator<Item=InternedString>>(path: PI, hash: Option<&str>) -> String {
-    // Follow C++ namespace-mangling style, see
-    // http://en.wikipedia.org/wiki/Name_mangling for more info.
-    //
-    // It turns out that on OSX you can actually have arbitrary symbols in
-    // function names (at least when given to LLVM), but this is not possible
-    // when using unix's linker. Perhaps one day when we just use a linker from LLVM
-    // we won't need to do this name mangling. The problem with name mangling is
-    // that it seriously limits the available characters. For example we can't
-    // have things like &T in symbol names when one would theoretically
-    // want them for things like impls of traits on that type.
-    //
-    // To be able to work on all platforms and get *some* reasonable output, we
-    // use C++ name-mangling.
-
-    let mut n = String::from("_ZN"); // _Z == Begin name-sequence, N == nested
-
-    fn push(n: &mut String, s: &str) {
-        let sani = sanitize(s);
-        n.push_str(&format!("{}{}", sani.len(), sani));
-    }
-
-    // First, connect each component with <len, name> pairs.
-    for data in path {
-        push(&mut n, &data);
-    }
-
-    if let Some(s) = hash {
-        push(&mut n, s)
-    }
-
-    n.push('E'); // End name-sequence.
-    n
-}
-
-pub fn exported_name(path: DefPath, hash: &str) -> String {
-    let path = path.into_iter()
-                   .map(|e| e.data.as_interned_str());
-    mangle(path, Some(hash))
-}
-
-pub fn mangle_exported_name<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, path: DefPath,
-                                      t: Ty<'tcx>, id: ast::NodeId) -> String {
-    let mut hash = get_symbol_hash(ccx, t);
-
-    // Paths can be completely identical for different nodes,
-    // e.g. `fn foo() { { fn a() {} } { fn a() {} } }`, so we
-    // generate unique characters from the node id. For now
-    // hopefully 3 characters is enough to avoid collisions.
-    const EXTRA_CHARS: &'static str =
-        "abcdefghijklmnopqrstuvwxyz\
-         ABCDEFGHIJKLMNOPQRSTUVWXYZ\
-         0123456789";
-    let id = id as usize;
-    let extra1 = id % EXTRA_CHARS.len();
-    let id = id / EXTRA_CHARS.len();
-    let extra2 = id % EXTRA_CHARS.len();
-    let id = id / EXTRA_CHARS.len();
-    let extra3 = id % EXTRA_CHARS.len();
-    hash.push(EXTRA_CHARS.as_bytes()[extra1] as char);
-    hash.push(EXTRA_CHARS.as_bytes()[extra2] as char);
-    hash.push(EXTRA_CHARS.as_bytes()[extra3] as char);
-
-    exported_name(path, &hash[..])
-}
-
-pub fn mangle_internal_name_by_type_and_seq<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                                      t: Ty<'tcx>,
-                                                      name: &str) -> String {
-    let path = [token::intern(&t.to_string()).as_str(), gensym_name(name).as_str()];
-    let hash = get_symbol_hash(ccx, t);
-    mangle(path.iter().cloned(), Some(&hash[..]))
-}
-
-pub fn mangle_internal_name_by_path_and_seq(path: DefPath, flav: &str) -> String {
-    let names =
-        path.into_iter()
-            .map(|e| e.data.as_interned_str())
-            .chain(once(gensym_name(flav).as_str())); // append unique version of "flav"
-    mangle(names, None)
-}
-
 pub fn get_linker(sess: &Session) -> (String, Command) {
     if let Some(ref linker) = sess.opts.cg.linker {
         (linker.clone(), Command::new(linker))
diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs
index b6b330c3734..c6576b7fe0d 100644
--- a/src/librustc_trans/back/linker.rs
+++ b/src/librustc_trans/back/linker.rs
@@ -359,6 +359,7 @@ impl<'a> Linker for MsvcLinker<'a> {
             for symbol in symbols {
                 writeln!(f, "  {}", symbol)?;
             }
+
             Ok(())
         })();
         if let Err(e) = res {
diff --git a/src/librustc_trans/back/symbol_names.rs b/src/librustc_trans/back/symbol_names.rs
new file mode 100644
index 00000000000..5d578011da8
--- /dev/null
+++ b/src/librustc_trans/back/symbol_names.rs
@@ -0,0 +1,378 @@
+// Copyright 2016 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.
+
+//! The Rust Linkage Model and Symbol Names
+//! =======================================
+//!
+//! The semantic model of Rust linkage is, broadly, that "there's no global
+//! namespace" between crates. Our aim is to preserve the illusion of this
+//! model despite the fact that it's not *quite* possible to implement on
+//! modern linkers. We initially didn't use system linkers at all, but have
+//! been convinced of their utility.
+//!
+//! There are a few issues to handle:
+//!
+//!  - Linkers operate on a flat namespace, so we have to flatten names.
+//!    We do this using the C++ namespace-mangling technique. Foo::bar
+//!    symbols and such.
+//!
+//!  - Symbols for distinct items with the same *name* need to get different
+//!    linkage-names. Examples of this are monomorphizations of functions or
+//!    items within anonymous scopes that end up having the same path.
+//!
+//!  - Symbols in different crates but with same names "within" the crate need
+//!    to get different linkage-names.
+//!
+//!  - Symbol names should be deterministic: Two consecutive runs of the
+//!    compiler over the same code base should produce the same symbol names for
+//!    the same items.
+//!
+//!  - Symbol names should not depend on any global properties of the code base,
+//!    so that small modifications to the code base do not result in all symbols
+//!    changing. In previous versions of the compiler, symbol names incorporated
+//!    the SVH (Stable Version Hash) of the crate. This scheme turned out to be
+//!    infeasible when used in conjunction with incremental compilation because
+//!    small code changes would invalidate all symbols generated previously.
+//!
+//!  - Even symbols from different versions of the same crate should be able to
+//!    live next to each other without conflict.
+//!
+//! In order to fulfill the above requirements the following scheme is used by
+//! the compiler:
+//!
+//! The main tool for avoiding naming conflicts is the incorporation of a 64-bit
+//! hash value into every exported symbol name. Anything that makes a difference
+//! to the symbol being named, but does not show up in the regular path needs to
+//! be fed into this hash:
+//!
+//! - Different monomorphizations of the same item have the same path but differ
+//!   in their concrete type parameters, so these parameters are part of the
+//!   data being digested for the symbol hash.
+//!
+//! - Rust allows items to be defined in anonymous scopes, such as in
+//!   `fn foo() { { fn bar() {} } { fn bar() {} } }`. Both `bar` functions have
+//!   the path `foo::bar`, since the anonymous scopes do not contribute to the
+//!   path of an item. The compiler already handles this case via so-called
+//!   disambiguating `DefPaths` which use indices to distinguish items with the
+//!   same name. The DefPaths of the functions above are thus `foo[0]::bar[0]`
+//!   and `foo[0]::bar[1]`. In order to incorporate this disambiguation
+//!   information into the symbol name too, these indices are fed into the
+//!   symbol hash, so that the above two symbols would end up with different
+//!   hash values.
+//!
+//! The two measures described above suffice to avoid intra-crate conflicts. In
+//! order to also avoid inter-crate conflicts two more measures are taken:
+//!
+//! - The name of the crate containing the symbol is prepended to the symbol
+//!   name, i.e. symbols are "crate qualified". For example, a function `foo` in
+//!   module `bar` in crate `baz` would get a symbol name like
+//!   `baz::bar::foo::{hash}` instead of just `bar::foo::{hash}`. This avoids
+//!   simple conflicts between functions from different crates.
+//!
+//! - In order to be able to also use symbols from two versions of the same
+//!   crate (which naturally also have the same name), a stronger measure is
+//!   required: The compiler accepts an arbitrary "disambiguator" value via the
+//!   `-C metadata` commandline argument. This disambiguator is then fed into
+//!   the symbol hash of every exported item. Consequently, the symbols in two
+//!   identical crates but with different disambiguators are not in conflict
+//!   with each other. This facility is mainly intended to be used by build
+//!   tools like Cargo.
+//!
+//! A note on symbol name stability
+//! -------------------------------
+//! Previous versions of the compiler resorted to feeding NodeIds into the
+//! symbol hash in order to disambiguate between items with the same path. The
+//! current version of the name generation algorithm takes great care not to do
+//! that, since NodeIds are notoriously unstable: A small change to the
+//! code base will offset all NodeIds after the change and thus, much as using
+//! the SVH in the hash, invalidate an unbounded number of symbol names. This
+//! makes re-using previously compiled code for incremental compilation
+//! virtually impossible. Thus, symbol hash generation exclusively relies on
+//! DefPaths which are much more robust in the face of changes to the code base.
+
+use trans::{CrateContext, Instance, gensym_name};
+use util::sha2::{Digest, Sha256};
+
+use rustc::middle::cstore;
+use rustc::middle::def_id::DefId;
+use rustc::middle::ty::{self, TypeFoldable};
+use rustc::middle::ty::item_path::{ItemPathBuffer, RootMode};
+use rustc::front::map::definitions::{DefPath, DefPathData};
+
+use std::fmt::Write;
+use syntax::parse::token::{self, InternedString};
+use serialize::hex::ToHex;
+
+pub fn def_id_to_string<'tcx>(tcx: &ty::TyCtxt<'tcx>, def_id: DefId) -> String {
+    let def_path = tcx.def_path(def_id);
+    def_path_to_string(tcx, &def_path)
+}
+
+pub fn def_path_to_string<'tcx>(tcx: &ty::TyCtxt<'tcx>, def_path: &DefPath) -> String {
+    let mut s = String::with_capacity(def_path.data.len() * 16);
+
+    s.push_str(&tcx.crate_name(def_path.krate));
+    s.push_str("/");
+    s.push_str(&tcx.crate_disambiguator(def_path.krate));
+
+    for component in &def_path.data {
+        write!(s,
+               "::{}[{}]",
+               component.data.as_interned_str(),
+               component.disambiguator)
+            .unwrap();
+    }
+
+    s
+}
+
+fn get_symbol_hash<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+
+                             // path to the item this name is for
+                             def_path: &DefPath,
+
+                             // type of the item, without any generic
+                             // parameters substituted; this is
+                             // included in the hash as a kind of
+                             // safeguard.
+                             item_type: ty::Ty<'tcx>,
+
+                             // values for generic type parameters,
+                             // if any.
+                             parameters: &[ty::Ty<'tcx>])
+                             -> String {
+    debug!("get_symbol_hash(def_path={:?}, parameters={:?})",
+           def_path, parameters);
+
+    let tcx = ccx.tcx();
+
+    let mut hash_state = ccx.symbol_hasher().borrow_mut();
+
+    hash_state.reset();
+
+    // the main symbol name is not necessarily unique; hash in the
+    // compiler's internal def-path, guaranteeing each symbol has a
+    // truly unique path
+    hash_state.input_str(&def_path_to_string(tcx, def_path));
+
+    // Include the main item-type. Note that, in this case, the
+    // assertions about `needs_subst` may not hold, but this item-type
+    // ought to be the same for every reference anyway.
+    assert!(!item_type.has_erasable_regions());
+    let encoded_item_type = tcx.sess.cstore.encode_type(tcx, item_type, def_id_to_string);
+    hash_state.input(&encoded_item_type[..]);
+
+    // also include any type parameters (for generic items)
+    for t in parameters {
+       assert!(!t.has_erasable_regions());
+       assert!(!t.needs_subst());
+       let encoded_type = tcx.sess.cstore.encode_type(tcx, t, def_id_to_string);
+       hash_state.input(&encoded_type[..]);
+    }
+
+    return format!("h{}", truncated_hash_result(&mut *hash_state));
+
+    fn truncated_hash_result(symbol_hasher: &mut Sha256) -> String {
+        let output = symbol_hasher.result_bytes();
+        // 64 bits should be enough to avoid collisions.
+        output[.. 8].to_hex()
+    }
+}
+
+fn exported_name_with_opt_suffix<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+                                           instance: &Instance<'tcx>,
+                                           suffix: Option<&str>)
+                                           -> String {
+    let &Instance { def: mut def_id, ref substs } = instance;
+
+    debug!("exported_name_with_opt_suffix(def_id={:?}, substs={:?}, suffix={:?})",
+           def_id, substs, suffix);
+
+    if let Some(node_id) = ccx.tcx().map.as_local_node_id(def_id) {
+        if let Some(&src_def_id) = ccx.external_srcs().borrow().get(&node_id) {
+            def_id = src_def_id;
+        }
+    }
+
+    let def_path = ccx.tcx().def_path(def_id);
+    assert_eq!(def_path.krate, def_id.krate);
+
+    // We want to compute the "type" of this item. Unfortunately, some
+    // kinds of items (e.g., closures) don't have an entry in the
+    // item-type array. So walk back up the find the closest parent
+    // that DOES have an entry.
+    let mut ty_def_id = def_id;
+    let instance_ty;
+    loop {
+        let key = ccx.tcx().def_key(ty_def_id);
+        match key.disambiguated_data.data {
+            DefPathData::TypeNs(_) |
+            DefPathData::ValueNs(_) => {
+                instance_ty = ccx.tcx().lookup_item_type(ty_def_id);
+                break;
+            }
+            _ => {
+                // if we're making a symbol for something, there ought
+                // to be a value or type-def or something in there
+                // *somewhere*
+                ty_def_id.index = key.parent.unwrap_or_else(|| {
+                    panic!("finding type for {:?}, encountered def-id {:?} with no \
+                            parent", def_id, ty_def_id);
+                });
+            }
+        }
+    }
+
+    // Erase regions because they may not be deterministic when hashed
+    // and should not matter anyhow.
+    let instance_ty = ccx.tcx().erase_regions(&instance_ty.ty);
+
+    let hash = get_symbol_hash(ccx, &def_path, instance_ty, substs.types.as_slice());
+
+    let mut buffer = SymbolPathBuffer {
+        names: Vec::with_capacity(def_path.data.len())
+    };
+    ccx.tcx().push_item_path(&mut buffer, def_id);
+
+    if let Some(suffix) = suffix {
+        buffer.push(suffix);
+    }
+
+    mangle(buffer.names.into_iter(), Some(&hash[..]))
+}
+
+struct SymbolPathBuffer {
+    names: Vec<InternedString>,
+}
+
+impl ItemPathBuffer for SymbolPathBuffer {
+    fn root_mode(&self) -> &RootMode {
+        const ABSOLUTE: &'static RootMode = &RootMode::Absolute;
+        ABSOLUTE
+    }
+
+    fn push(&mut self, text: &str) {
+        self.names.push(token::intern(text).as_str());
+    }
+}
+
+pub fn exported_name<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+                               instance: &Instance<'tcx>)
+                               -> String {
+    exported_name_with_opt_suffix(ccx, instance, None)
+}
+
+pub fn exported_name_with_suffix<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+                                           instance: &Instance<'tcx>,
+                                           suffix: &str)
+                                           -> String {
+   exported_name_with_opt_suffix(ccx, instance, Some(suffix))
+}
+
+/// Only symbols that are invisible outside their compilation unit should use a
+/// name generated by this function.
+pub fn internal_name_from_type_and_suffix<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+                                                    t: ty::Ty<'tcx>,
+                                                    suffix: &str)
+                                                    -> String {
+    let path = [token::intern(&t.to_string()).as_str(),
+                gensym_name(suffix).as_str()];
+    let def_path = DefPath {
+        data: vec![],
+        krate: cstore::LOCAL_CRATE,
+    };
+    let hash = get_symbol_hash(ccx, &def_path, t, &[]);
+    mangle(path.iter().cloned(), Some(&hash[..]))
+}
+
+// Name sanitation. LLVM will happily accept identifiers with weird names, but
+// gas doesn't!
+// gas accepts the following characters in symbols: a-z, A-Z, 0-9, ., _, $
+pub fn sanitize(s: &str) -> String {
+    let mut result = String::new();
+    for c in s.chars() {
+        match c {
+            // Escape these with $ sequences
+            '@' => result.push_str("$SP$"),
+            '*' => result.push_str("$BP$"),
+            '&' => result.push_str("$RF$"),
+            '<' => result.push_str("$LT$"),
+            '>' => result.push_str("$GT$"),
+            '(' => result.push_str("$LP$"),
+            ')' => result.push_str("$RP$"),
+            ',' => result.push_str("$C$"),
+
+            // '.' doesn't occur in types and functions, so reuse it
+            // for ':' and '-'
+            '-' | ':' => result.push('.'),
+
+            // These are legal symbols
+            'a' ... 'z'
+            | 'A' ... 'Z'
+            | '0' ... '9'
+            | '_' | '.' | '$' => result.push(c),
+
+            _ => {
+                result.push('$');
+                for c in c.escape_unicode().skip(1) {
+                    match c {
+                        '{' => {},
+                        '}' => result.push('$'),
+                        c => result.push(c),
+                    }
+                }
+            }
+        }
+    }
+
+    // Underscore-qualify anything that didn't start as an ident.
+    if !result.is_empty() &&
+        result.as_bytes()[0] != '_' as u8 &&
+        ! (result.as_bytes()[0] as char).is_xid_start() {
+        return format!("_{}", &result[..]);
+    }
+
+    return result;
+}
+
+pub fn mangle<PI: Iterator<Item=InternedString>>(path: PI, hash: Option<&str>) -> String {
+    // Follow C++ namespace-mangling style, see
+    // http://en.wikipedia.org/wiki/Name_mangling for more info.
+    //
+    // It turns out that on OSX you can actually have arbitrary symbols in
+    // function names (at least when given to LLVM), but this is not possible
+    // when using unix's linker. Perhaps one day when we just use a linker from LLVM
+    // we won't need to do this name mangling. The problem with name mangling is
+    // that it seriously limits the available characters. For example we can't
+    // have things like &T in symbol names when one would theoretically
+    // want them for things like impls of traits on that type.
+    //
+    // To be able to work on all platforms and get *some* reasonable output, we
+    // use C++ name-mangling.
+
+    let mut n = String::from("_ZN"); // _Z == Begin name-sequence, N == nested
+
+    fn push(n: &mut String, s: &str) {
+        let sani = sanitize(s);
+        n.push_str(&format!("{}{}", sani.len(), sani));
+    }
+
+    // First, connect each component with <len, name> pairs.
+    for data in path {
+        push(&mut n, &data);
+    }
+
+    if let Some(s) = hash {
+        push(&mut n, s)
+    }
+
+    n.push('E'); // End name-sequence.
+    n
+}
diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs
index f8bbde60109..b9f92bcc4d9 100644
--- a/src/librustc_trans/lib.rs
+++ b/src/librustc_trans/lib.rs
@@ -69,6 +69,7 @@ pub mod back {
     pub mod linker;
     pub mod link;
     pub mod lto;
+    pub mod symbol_names;
     pub mod write;
     pub mod msvc;
 }
diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs
index 78e91e00baa..4bbb7624693 100644
--- a/src/librustc_trans/save/mod.rs
+++ b/src/librustc_trans/save/mod.rs
@@ -90,7 +90,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
 
         for n in self.tcx.sess.cstore.crates() {
             result.push(CrateData {
-                name: self.tcx.sess.cstore.crate_name(n),
+                name: (&self.tcx.sess.cstore.crate_name(n)[..]).to_owned(),
                 number: n,
             });
         }
diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs
index 1d8daf9d86b..7231304ec4c 100644
--- a/src/librustc_trans/trans/base.rs
+++ b/src/librustc_trans/trans/base.rs
@@ -7,6 +7,7 @@
 // <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.
+
 //! Translate the completed AST to the LLVM IR.
 //!
 //! Some functions here, such as trans_block and trans_expr, return a value --
@@ -29,8 +30,7 @@ pub use self::ValueOrigin::*;
 use super::CrateTranslation;
 use super::ModuleTranslation;
 
-use back::link::mangle_exported_name;
-use back::link;
+use back::{link, symbol_names};
 use lint;
 use llvm::{BasicBlockRef, Linkage, ValueRef, Vector, get_param};
 use llvm;
@@ -84,6 +84,7 @@ use trans::machine::{llalign_of_min, llsize_of, llsize_of_real};
 use trans::meth;
 use trans::mir;
 use trans::monomorphize::{self, Instance};
+use trans::symbol_names_test;
 use trans::tvec;
 use trans::type_::Type;
 use trans::type_of;
@@ -2421,10 +2422,11 @@ pub fn create_entry_wrapper(ccx: &CrateContext, sp: Span, main_llfn: ValueRef) {
 }
 
 pub fn exported_name<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                               id: ast::NodeId,
-                               ty: Ty<'tcx>,
+                               instance: Instance<'tcx>,
                                attrs: &[ast::Attribute])
                                -> String {
+    let id = ccx.tcx().map.as_local_node_id(instance.def).unwrap();
+
     match ccx.external_srcs().borrow().get(&id) {
         Some(&did) => {
             let sym = ccx.sess().cstore.item_symbol(did);
@@ -2438,16 +2440,16 @@ pub fn exported_name<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         // Use provided name
         Some(name) => name.to_string(),
         _ => {
-            let path = ccx.tcx().map.def_path_from_id(id);
             if attr::contains_name(attrs, "no_mangle") {
                 // Don't mangle
-                path.last().unwrap().data.to_string()
+                let path = ccx.tcx().map.def_path_from_id(id);
+                path.data.last().unwrap().data.to_string()
             } else {
                 match weak_lang_items::link_name(attrs) {
                     Some(name) => name.to_string(),
                     None => {
                         // Usual name mangling
-                        mangle_exported_name(ccx, path, ty, id)
+                        symbol_names::exported_name(ccx, &instance)
                     }
                 }
             }
@@ -2755,6 +2757,8 @@ pub fn trans_crate<'tcx>(tcx: &TyCtxt<'tcx>,
         }
 
         collector::print_collection_results(&ccx);
+
+        symbol_names_test::report_symbol_names(&ccx);
     }
 
     for ccx in shared_ccx.iter() {
diff --git a/src/librustc_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs
index 1013c5d64b6..6fe4598c1ba 100644
--- a/src/librustc_trans/trans/callee.rs
+++ b/src/librustc_trans/trans/callee.rs
@@ -18,7 +18,7 @@ pub use self::CalleeData::*;
 pub use self::CallArgs::*;
 
 use arena::TypedArena;
-use back::link;
+use back::symbol_names;
 use llvm::{self, ValueRef, get_params};
 use middle::cstore::LOCAL_CRATE;
 use middle::def_id::DefId;
@@ -378,8 +378,10 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>(
     debug!("tuple_fn_ty: {:?}", tuple_fn_ty);
 
     //
-    let function_name = link::mangle_internal_name_by_type_and_seq(ccx, bare_fn_ty,
-                                                                   "fn_pointer_shim");
+    let function_name =
+        symbol_names::internal_name_from_type_and_suffix(ccx,
+                                                         bare_fn_ty,
+                                                         "fn_pointer_shim");
     let llfn = declare::define_internal_fn(ccx, &function_name, tuple_fn_ty);
 
     //
@@ -513,7 +515,7 @@ fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         Some(hir_map::NodeImplItem(&hir::ImplItem {
             ref attrs, id, span, node: hir::ImplItemKind::Method(..), ..
         })) => {
-            let sym = exported_name(ccx, id, ty, attrs);
+            let sym = exported_name(ccx, instance, attrs);
 
             if declare::get_defined_value(ccx, &sym).is_some() {
                 ccx.sess().span_fatal(span,
diff --git a/src/librustc_trans/trans/closure.rs b/src/librustc_trans/trans/closure.rs
index dbabc3f54c5..ff3235385a9 100644
--- a/src/librustc_trans/trans/closure.rs
+++ b/src/librustc_trans/trans/closure.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use arena::TypedArena;
-use back::link::{self, mangle_internal_name_by_path_and_seq};
+use back::symbol_names;
 use llvm::{ValueRef, get_param, get_params};
 use middle::def_id::DefId;
 use middle::infer;
@@ -152,8 +152,7 @@ fn get_or_create_closure_declaration<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         return llfn;
     }
 
-    let path = tcx.def_path(closure_id);
-    let symbol = mangle_internal_name_by_path_and_seq(path, "closure");
+    let symbol = symbol_names::exported_name(ccx, &instance);
 
     // Compute the rust-call form of the closure call method.
     let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables, ProjectionMode::Any);
@@ -383,7 +382,8 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>(
     });
 
     // Create the by-value helper.
-    let function_name = link::mangle_internal_name_by_type_and_seq(ccx, llonce_fn_ty, "once_shim");
+    let function_name =
+        symbol_names::internal_name_from_type_and_suffix(ccx, llonce_fn_ty, "once_shim");
     let lloncefn = declare::define_internal_fn(ccx, &function_name, llonce_fn_ty);
 
     let (block_arena, fcx): (TypedArena<_>, FunctionContext);
diff --git a/src/librustc_trans/trans/collector.rs b/src/librustc_trans/trans/collector.rs
index ab8f7d6bec3..3f3da36be06 100644
--- a/src/librustc_trans/trans/collector.rs
+++ b/src/librustc_trans/trans/collector.rs
@@ -1261,28 +1261,20 @@ pub fn push_unique_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
 fn push_item_name(ccx: &CrateContext,
                   def_id: DefId,
                   output: &mut String) {
-    if def_id.is_local() {
-        let node_id = ccx.tcx().map.as_local_node_id(def_id).unwrap();
-        let inlined_from = ccx.external_srcs()
-                              .borrow()
-                              .get(&node_id)
-                              .map(|def_id| *def_id);
-
-        if let Some(extern_def_id) = inlined_from {
-            push_item_name(ccx, extern_def_id, output);
-            return;
-        }
+    let def_path = ccx.tcx().def_path(def_id);
 
-        output.push_str(&ccx.link_meta().crate_name);
-        output.push_str("::");
-    }
+    // some_crate::
+    output.push_str(&ccx.tcx().crate_name(def_path.krate));
+    output.push_str("::");
 
-    for part in ccx.tcx().def_path(def_id) {
+    // foo::bar::ItemName::
+    for part in ccx.tcx().def_path(def_id).data {
         output.push_str(&format!("{}[{}]::",
                         part.data.as_interned_str(),
                         part.disambiguator));
     }
 
+    // remove final "::"
     output.pop();
     output.pop();
 }
diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs
index 7e6b2496910..4cdb64a9bfb 100644
--- a/src/librustc_trans/trans/consts.rs
+++ b/src/librustc_trans/trans/consts.rs
@@ -1032,7 +1032,7 @@ pub fn get_static<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, def_id: DefId)
                 // we need to get the symbol from metadata instead of
                 // using the current crate's name/version
                 // information in the hash of the symbol
-                let sym = exported_name(ccx, id, ty, attrs);
+                let sym = exported_name(ccx, instance, attrs);
                 debug!("making {}", sym);
 
                 // Create the global before evaluating the initializer;
diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs
index 046e05dd071..a3d387afa96 100644
--- a/src/librustc_trans/trans/context.rs
+++ b/src/librustc_trans/trans/context.rs
@@ -25,7 +25,8 @@ use trans::debuginfo;
 use trans::declare;
 use trans::glue::DropGlueKind;
 use trans::mir::CachedMir;
-use trans::monomorphize::Instance;
+use trans::Instance;
+
 use trans::collector::{TransItem, TransItemState};
 use trans::type_::{Type, TypeNames};
 use middle::subst::{Substs, VecPerParamSpace};
diff --git a/src/librustc_trans/trans/debuginfo/gdb.rs b/src/librustc_trans/trans/debuginfo/gdb.rs
index 4e3fadd0fa9..7740f277586 100644
--- a/src/librustc_trans/trans/debuginfo/gdb.rs
+++ b/src/librustc_trans/trans/debuginfo/gdb.rs
@@ -90,10 +90,7 @@ pub fn get_or_insert_gdb_debug_scripts_section_global(ccx: &CrateContext)
 
 pub fn needs_gdb_debug_scripts_section(ccx: &CrateContext) -> bool {
     let omit_gdb_pretty_printer_section =
-        attr::contains_name(&ccx.tcx()
-                                .map
-                                .krate()
-                                .attrs,
+        attr::contains_name(&ccx.tcx().map.krate_attrs(),
                             "omit_gdb_pretty_printer_section");
 
     !omit_gdb_pretty_printer_section &&
diff --git a/src/librustc_trans/trans/glue.rs b/src/librustc_trans/trans/glue.rs
index de4b1ba858a..aa205898114 100644
--- a/src/librustc_trans/trans/glue.rs
+++ b/src/librustc_trans/trans/glue.rs
@@ -14,7 +14,7 @@
 
 use std;
 
-use back::link;
+use back::symbol_names;
 use llvm;
 use llvm::{ValueRef, get_param};
 use middle::lang_items::ExchangeFreeFnLangItem;
@@ -259,7 +259,12 @@ fn get_drop_glue_core<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         return llfn;
     };
 
-    let fn_nm = link::mangle_internal_name_by_type_and_seq(ccx, t, "drop");
+    let suffix = match g {
+        DropGlueKind::Ty(_) => "drop",
+        DropGlueKind::TyContents(_) => "drop_contents",
+    };
+
+    let fn_nm = symbol_names::internal_name_from_type_and_suffix(ccx, t, suffix);
     assert!(declare::get_defined_value(ccx, &fn_nm).is_none());
     let llfn = declare::declare_cfn(ccx, &fn_nm, llfnty);
     ccx.available_drop_glues().borrow_mut().insert(g, fn_nm);
diff --git a/src/librustc_trans/trans/inline.rs b/src/librustc_trans/trans/inline.rs
index 530b99cba92..a9c94a4522b 100644
--- a/src/librustc_trans/trans/inline.rs
+++ b/src/librustc_trans/trans/inline.rs
@@ -97,6 +97,7 @@ fn instantiate_inline(ccx: &CrateContext, fn_id: DefId) -> Option<DefId> {
                     for (ast_v, ty_v) in ast_vs.iter().zip(ty_vs.iter()) {
                         if ty_v.did == fn_id { my_id = ast_v.node.data.id(); }
                         ccx.external().borrow_mut().insert(ty_v.did, Some(ast_v.node.data.id()));
+                        ccx.external_srcs().borrow_mut().insert(ast_v.node.data.id(), ty_v.did);
                     }
                 }
                 hir::ItemStruct(ref struct_def, _) => {
@@ -105,6 +106,7 @@ fn instantiate_inline(ccx: &CrateContext, fn_id: DefId) -> Option<DefId> {
                                                                  non-tuple struct")
                     } else {
                         ccx.external().borrow_mut().insert(fn_id, Some(struct_def.id()));
+                        ccx.external_srcs().borrow_mut().insert(struct_def.id(), fn_id);
                         my_id = struct_def.id();
                     }
                 }
diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs
index ae619ceb30b..f45de10bca8 100644
--- a/src/librustc_trans/trans/meth.rs
+++ b/src/librustc_trans/trans/meth.rs
@@ -11,7 +11,7 @@
 use std::rc::Rc;
 
 use arena::TypedArena;
-use back::link;
+use back::symbol_names;
 use llvm::{ValueRef, get_params};
 use middle::def_id::DefId;
 use middle::infer;
@@ -89,7 +89,8 @@ pub fn trans_object_shim<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
     let sig = infer::normalize_associated_type(tcx, &sig);
     let fn_ty = FnType::new(ccx, method_ty.fn_abi(), &sig, &[]);
 
-    let function_name = link::mangle_internal_name_by_type_and_seq(ccx, method_ty, "object_shim");
+    let function_name =
+        symbol_names::internal_name_from_type_and_suffix(ccx, method_ty, "object_shim");
     let llfn = declare::define_internal_fn(ccx, &function_name, method_ty);
 
     let empty_substs = tcx.mk_substs(Substs::empty());
diff --git a/src/librustc_trans/trans/mod.rs b/src/librustc_trans/trans/mod.rs
index c5ab0d4e744..930f37ce256 100644
--- a/src/librustc_trans/trans/mod.rs
+++ b/src/librustc_trans/trans/mod.rs
@@ -15,6 +15,7 @@ pub use self::base::trans_crate;
 pub use self::context::CrateContext;
 pub use self::common::gensym_name;
 pub use self::disr::Disr;
+pub use self::monomorphize::Instance;
 
 #[macro_use]
 mod macros;
@@ -58,6 +59,7 @@ mod meth;
 mod mir;
 mod monomorphize;
 mod collector;
+mod symbol_names_test;
 mod tvec;
 mod type_;
 mod type_of;
diff --git a/src/librustc_trans/trans/monomorphize.rs b/src/librustc_trans/trans/monomorphize.rs
index 63fb8c5fb5e..6dd8d651012 100644
--- a/src/librustc_trans/trans/monomorphize.rs
+++ b/src/librustc_trans/trans/monomorphize.rs
@@ -8,13 +8,14 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use back::link::exported_name;
+use back::symbol_names;
 use llvm::ValueRef;
 use llvm;
 use middle::def_id::DefId;
 use middle::infer::normalize_associated_type;
 use middle::subst;
 use middle::subst::{Subst, Substs};
+use middle::ty::{self, Ty, TyCtxt};
 use middle::ty::fold::{TypeFolder, TypeFoldable};
 use trans::attributes;
 use trans::base::{push_ctxt};
@@ -22,7 +23,6 @@ use trans::base::trans_fn;
 use trans::base;
 use trans::common::*;
 use trans::declare;
-use middle::ty::{self, Ty, TyCtxt};
 use trans::Disr;
 use rustc::front::map as hir_map;
 use rustc::util::ppaux;
@@ -33,7 +33,6 @@ use syntax::attr;
 use syntax::errors;
 
 use std::fmt;
-use std::hash::{Hasher, Hash, SipHasher};
 
 pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                 fn_id: DefId,
@@ -90,22 +89,13 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         monomorphizing.insert(fn_id, depth + 1);
     }
 
-    let hash;
-    let s = {
-        let mut state = SipHasher::new();
-        instance.hash(&mut state);
-        mono_ty.hash(&mut state);
-
-        hash = format!("h{}", state.finish());
-        let path = ccx.tcx().map.def_path(fn_id);
-        exported_name(path, &hash[..])
-    };
+    let symbol = symbol_names::exported_name(ccx, &instance);
 
-    debug!("monomorphize_fn mangled to {}", s);
-    assert!(declare::get_defined_value(ccx, &s).is_none());
+    debug!("monomorphize_fn mangled to {}", symbol);
+    assert!(declare::get_defined_value(ccx, &symbol).is_none());
 
     // FIXME(nagisa): perhaps needs a more fine grained selection?
-    let lldecl = declare::define_internal_fn(ccx, &s, mono_ty);
+    let lldecl = declare::define_internal_fn(ccx, &symbol, mono_ty);
     // FIXME(eddyb) Doubt all extern fn should allow unwinding.
     attributes::unwind(lldecl, true);
 
@@ -137,9 +127,10 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
             base::update_linkage(ccx, lldecl, None, base::OriginalTranslation);
             attributes::from_fn_attrs(ccx, attrs, lldecl);
 
-            let is_first = !ccx.available_monomorphizations().borrow().contains(&s);
+            let is_first = !ccx.available_monomorphizations().borrow()
+                                                             .contains(&symbol);
             if is_first {
-                ccx.available_monomorphizations().borrow_mut().insert(s.clone());
+                ccx.available_monomorphizations().borrow_mut().insert(symbol.clone());
             }
 
             let trans_everywhere = attr::requests_inline(attrs);
diff --git a/src/librustc_trans/trans/symbol_names_test.rs b/src/librustc_trans/trans/symbol_names_test.rs
new file mode 100644
index 00000000000..63abbfd53b6
--- /dev/null
+++ b/src/librustc_trans/trans/symbol_names_test.rs
@@ -0,0 +1,86 @@
+// Copyright 2012-2015 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.
+
+//! Walks the crate looking for items/impl-items/trait-items that have
+//! either a `rustc_symbol_name` or `rustc_item_path` attribute and
+//! generates an error giving, respectively, the symbol name or
+//! item-path. This is used for unit testing the code that generates
+//! paths etc in all kinds of annoying scenarios.
+
+use back::symbol_names;
+use rustc::middle::ty::TyCtxt;
+use rustc_front::hir;
+use rustc_front::intravisit::{self, Visitor};
+use syntax::ast;
+use syntax::attr::AttrMetaMethods;
+use trans::common::CrateContext;
+use trans::monomorphize::Instance;
+
+const SYMBOL_NAME: &'static str = "rustc_symbol_name";
+const ITEM_PATH: &'static str = "rustc_item_path";
+
+pub fn report_symbol_names(ccx: &CrateContext) {
+    // if the `rustc_attrs` feature is not enabled, then the
+    // attributes we are interested in cannot be present anyway, so
+    // skip the walk.
+    let tcx = ccx.tcx();
+    if !tcx.sess.features.borrow().rustc_attrs {
+        return;
+    }
+
+    let _ignore = tcx.dep_graph.in_ignore();
+    let mut visitor = SymbolNamesTest { ccx: ccx, tcx: tcx };
+    tcx.map.krate().visit_all_items(&mut visitor);
+}
+
+struct SymbolNamesTest<'a, 'tcx:'a> {
+    ccx: &'a CrateContext<'a, 'tcx>,
+    tcx: &'a TyCtxt<'tcx>,
+}
+
+impl<'a, 'tcx> SymbolNamesTest<'a, 'tcx> {
+    fn process_attrs(&mut self,
+                     node_id: ast::NodeId) {
+        let def_id = self.tcx.map.local_def_id(node_id);
+        for attr in self.tcx.get_attrs(def_id).iter() {
+            if attr.check_name(SYMBOL_NAME) {
+                // for now, can only use on monomorphic names
+                let instance = Instance::mono(self.tcx, def_id);
+                let name = symbol_names::exported_name(self.ccx, &instance);
+                self.tcx.sess.span_err(attr.span, &format!("symbol-name({})", name));
+            } else if attr.check_name(ITEM_PATH) {
+                let path = self.tcx.item_path_str(def_id);
+                self.tcx.sess.span_err(attr.span, &format!("item-path({})", path));
+            }
+
+            // (*) The formatting of `tag({})` is chosen so that tests can elect
+            // to test the entirety of the string, if they choose, or else just
+            // some subset.
+        }
+    }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for SymbolNamesTest<'a, 'tcx> {
+    fn visit_item(&mut self, item: &'tcx hir::Item) {
+        self.process_attrs(item.id);
+        intravisit::walk_item(self, item);
+    }
+
+    fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem) {
+        self.process_attrs(ti.id);
+        intravisit::walk_trait_item(self, ti)
+    }
+
+    fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem) {
+        self.process_attrs(ii.id);
+        intravisit::walk_impl_item(self, ii)
+    }
+}
+
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 15aeca9204a..aab5c960df3 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -241,7 +241,7 @@ impl Clean<ExternalCrate> for CrateNum {
             }
         });
         ExternalCrate {
-            name: cx.sess().cstore.crate_name(self.0),
+            name: (&cx.sess().cstore.crate_name(self.0)[..]).to_owned(),
             attrs: cx.sess().cstore.crate_attrs(self.0).clean(cx),
             primitives: primitives,
         }
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 299b7d8b9ba..80e1ae111a2 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -349,6 +349,10 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat
                                        "the `#[rustc_if_this_changed]` attribute \
                                         is just used for rustc unit tests \
                                         and will never be stable")),
+    ("rustc_symbol_name", Whitelisted, Gated("rustc_attrs",
+                                       "internal rustc attributes will never be stable")),
+    ("rustc_item_path", Whitelisted, Gated("rustc_attrs",
+                                       "internal rustc attributes will never be stable")),
     ("rustc_move_fragments", Normal, Gated("rustc_attrs",
                                            "the `#[rustc_move_fragments]` attribute \
                                             is just used for rustc unit tests \
@@ -579,6 +583,7 @@ pub struct Features {
     pub const_indexing: bool,
     pub static_recursion: bool,
     pub default_type_parameter_fallback: bool,
+    pub rustc_attrs: bool,
     pub type_macros: bool,
     pub cfg_target_feature: bool,
     pub cfg_target_vendor: bool,
@@ -614,6 +619,7 @@ impl Features {
             const_indexing: false,
             static_recursion: false,
             default_type_parameter_fallback: false,
+            rustc_attrs: false,
             type_macros: false,
             cfg_target_feature: false,
             cfg_target_vendor: false,
@@ -1225,6 +1231,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &Handler,
         const_indexing: cx.has_feature("const_indexing"),
         static_recursion: cx.has_feature("static_recursion"),
         default_type_parameter_fallback: cx.has_feature("default_type_parameter_fallback"),
+        rustc_attrs: cx.has_feature("rustc_attrs"),
         type_macros: cx.has_feature("type_macros"),
         cfg_target_feature: cx.has_feature("cfg_target_feature"),
         cfg_target_vendor: cx.has_feature("cfg_target_vendor"),
diff --git a/src/test/auxiliary/inline-default-methods.rs b/src/test/auxiliary/inline-default-methods.rs
index 5f1bd7ab522..e21e6ad2043 100644
--- a/src/test/auxiliary/inline-default-methods.rs
+++ b/src/test/auxiliary/inline-default-methods.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// compile-flags: -Cmetadata=aux
+
 pub trait Foo {
     fn bar(&self);
     fn foo(&mut self) {}
diff --git a/src/test/auxiliary/issue-13698.rs b/src/test/auxiliary/issue-13698.rs
index 0bb2133c833..ecddfe99b3b 100644
--- a/src/test/auxiliary/issue-13698.rs
+++ b/src/test/auxiliary/issue-13698.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// compile-flags: -Cmetadata=aux
+
 pub trait Foo {
     #[doc(hidden)]
     fn foo(&self) {}
diff --git a/src/test/auxiliary/issue-15318.rs b/src/test/auxiliary/issue-15318.rs
index 9e42dbfbc6b..145b4df6299 100644
--- a/src/test/auxiliary/issue-15318.rs
+++ b/src/test/auxiliary/issue-15318.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// compile-flags: -Cmetadata=aux
+
 #![doc(html_root_url = "http://example.com/")]
 
 /// dox
diff --git a/src/test/auxiliary/issue-17476.rs b/src/test/auxiliary/issue-17476.rs
index d3a86035742..644d1634e9d 100644
--- a/src/test/auxiliary/issue-17476.rs
+++ b/src/test/auxiliary/issue-17476.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// compile-flags: -Cmetadata=aux
 
 #![doc(html_root_url = "http://example.com")]
 
diff --git a/src/test/auxiliary/issue-17718.rs b/src/test/auxiliary/issue-17718-aux.rs
index 373fc042175..373fc042175 100644
--- a/src/test/auxiliary/issue-17718.rs
+++ b/src/test/auxiliary/issue-17718-aux.rs
diff --git a/src/test/auxiliary/issue-19190-3.rs b/src/test/auxiliary/issue-19190-3.rs
index 7403bcf4afb..2c9271202a6 100644
--- a/src/test/auxiliary/issue-19190-3.rs
+++ b/src/test/auxiliary/issue-19190-3.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// compile-flags: -Cmetadata=aux
+
 use std::ops::Deref;
 
 pub struct Foo;
diff --git a/src/test/auxiliary/issue-20646.rs b/src/test/auxiliary/issue-20646.rs
index 150d8018f08..815b78a91d9 100644
--- a/src/test/auxiliary/issue-20646.rs
+++ b/src/test/auxiliary/issue-20646.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// compile-flags: -Cmetadata=aux
+
 pub trait Trait {
     type Output;
 }
diff --git a/src/test/auxiliary/issue-20727.rs b/src/test/auxiliary/issue-20727.rs
index aea8b429d9f..2ec761fad96 100644
--- a/src/test/auxiliary/issue-20727.rs
+++ b/src/test/auxiliary/issue-20727.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// compile-flags: -Cmetadata=aux
+
 pub trait Deref {
     type Target: ?Sized;
 
diff --git a/src/test/auxiliary/issue-21092.rs b/src/test/auxiliary/issue-21092.rs
index 6d6046cc7bf..e906311e3ae 100644
--- a/src/test/auxiliary/issue-21092.rs
+++ b/src/test/auxiliary/issue-21092.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// compile-flags: -Cmetadata=aux
+
 pub trait Foo {
     type Bar;
     fn foo(&self) {}
diff --git a/src/test/auxiliary/issue-21801.rs b/src/test/auxiliary/issue-21801.rs
index ada6c692502..f618edec598 100644
--- a/src/test/auxiliary/issue-21801.rs
+++ b/src/test/auxiliary/issue-21801.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// compile-flags: -Cmetadata=aux
+
 pub struct Foo;
 
 impl Foo {
diff --git a/src/test/auxiliary/issue-22025.rs b/src/test/auxiliary/issue-22025.rs
index 554b580ae2b..35a37e27d91 100644
--- a/src/test/auxiliary/issue-22025.rs
+++ b/src/test/auxiliary/issue-22025.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// compile-flags: -Cmetadata=aux
+
 pub mod foo {
 
     pub trait Foo {}
diff --git a/src/test/auxiliary/issue-27362.rs b/src/test/auxiliary/issue-27362.rs
index e551d623ae7..25de698cad1 100644
--- a/src/test/auxiliary/issue-27362.rs
+++ b/src/test/auxiliary/issue-27362.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// compile-flags: -Cmetadata=aux
+
 #![feature(const_fn)]
 
 pub const fn foo() {}
diff --git a/src/test/auxiliary/issue-29584.rs b/src/test/auxiliary/issue-29584.rs
index 4a9e6126fc6..63c79f875ef 100644
--- a/src/test/auxiliary/issue-29584.rs
+++ b/src/test/auxiliary/issue-29584.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// compile-flags: -Cmetadata=aux
+
 pub struct Foo;
 
 #[doc(hidden)]
diff --git a/src/test/auxiliary/typeid-intrinsic.rs b/src/test/auxiliary/typeid-intrinsic-aux1.rs
index 388d3238d42..388d3238d42 100644
--- a/src/test/auxiliary/typeid-intrinsic.rs
+++ b/src/test/auxiliary/typeid-intrinsic-aux1.rs
diff --git a/src/test/auxiliary/typeid-intrinsic2.rs b/src/test/auxiliary/typeid-intrinsic-aux2.rs
index 3ad307fd3b5..3ad307fd3b5 100644
--- a/src/test/auxiliary/typeid-intrinsic2.rs
+++ b/src/test/auxiliary/typeid-intrinsic-aux2.rs
diff --git a/src/test/codegen-units/cross-crate-closures.rs b/src/test/codegen-units/cross-crate-closures.rs
index 32b07d42fec..30f3ef12d07 100644
--- a/src/test/codegen-units/cross-crate-closures.rs
+++ b/src/test/codegen-units/cross-crate-closures.rs
@@ -19,12 +19,12 @@ extern crate cgu_extern_closures;
 //~ TRANS_ITEM fn cross_crate_closures::main[0]
 fn main() {
 
-    //~ TRANS_ITEM fn cgu_extern_closures[0]::inlined_fn[0]
-    //~ TRANS_ITEM fn cgu_extern_closures[0]::inlined_fn[0]::{{closure}}[0]
+    //~ TRANS_ITEM fn cgu_extern_closures::inlined_fn[0]
+    //~ TRANS_ITEM fn cgu_extern_closures::inlined_fn[0]::{{closure}}[0]
     let _ = cgu_extern_closures::inlined_fn(1, 2);
 
-    //~ TRANS_ITEM fn cgu_extern_closures[0]::inlined_fn_generic[0]<i32>
-    //~ TRANS_ITEM fn cgu_extern_closures[0]::inlined_fn_generic[0]::{{closure}}[0]<i32>
+    //~ TRANS_ITEM fn cgu_extern_closures::inlined_fn_generic[0]<i32>
+    //~ TRANS_ITEM fn cgu_extern_closures::inlined_fn_generic[0]::{{closure}}[0]<i32>
     let _ = cgu_extern_closures::inlined_fn_generic(3, 4, 5i32);
 
     // Nothing should be generated for this call, we just link to the instance instance
diff --git a/src/test/codegen-units/cross-crate-generic-functions.rs b/src/test/codegen-units/cross-crate-generic-functions.rs
index 82d940a1548..ada1234b852 100644
--- a/src/test/codegen-units/cross-crate-generic-functions.rs
+++ b/src/test/codegen-units/cross-crate-generic-functions.rs
@@ -19,12 +19,12 @@ extern crate cgu_generic_function;
 //~ TRANS_ITEM fn cross_crate_generic_functions::main[0]
 fn main()
 {
-    //~ TRANS_ITEM fn cgu_generic_function[0]::bar[0]<u32>
-    //~ TRANS_ITEM fn cgu_generic_function[0]::foo[0]<u32>
+    //~ TRANS_ITEM fn cgu_generic_function::bar[0]<u32>
+    //~ TRANS_ITEM fn cgu_generic_function::foo[0]<u32>
     let _ = cgu_generic_function::foo(1u32);
 
-    //~ TRANS_ITEM fn cgu_generic_function[0]::bar[0]<u64>
-    //~ TRANS_ITEM fn cgu_generic_function[0]::foo[0]<u64>
+    //~ TRANS_ITEM fn cgu_generic_function::bar[0]<u64>
+    //~ TRANS_ITEM fn cgu_generic_function::foo[0]<u64>
     let _ = cgu_generic_function::foo(2u64);
 
     // This should not introduce a codegen item
diff --git a/src/test/codegen-units/cross-crate-trait-method.rs b/src/test/codegen-units/cross-crate-trait-method.rs
index aa1f6b06c81..9f29a90bffb 100644
--- a/src/test/codegen-units/cross-crate-trait-method.rs
+++ b/src/test/codegen-units/cross-crate-trait-method.rs
@@ -29,31 +29,31 @@ fn main()
     // Currently, no object code is generated for trait methods with default
     // implemenations, unless they are actually called from somewhere. Therefore
     // we cannot import the implementations and have to create our own inline.
-    //~ TRANS_ITEM fn cgu_export_trait_method[0]::Trait[0]::with_default_impl[0]<u32>
+    //~ TRANS_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl[0]<u32>
     let _ = Trait::with_default_impl(0u32);
-    //~ TRANS_ITEM fn cgu_export_trait_method[0]::Trait[0]::with_default_impl[0]<char>
+    //~ TRANS_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl[0]<char>
     let _ = Trait::with_default_impl('c');
 
 
 
-    //~ TRANS_ITEM fn cgu_export_trait_method[0]::Trait[0]::with_default_impl_generic[0]<u32, &str>
+    //~ TRANS_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl_generic[0]<u32, &str>
     let _ = Trait::with_default_impl_generic(0u32, "abc");
-    //~ TRANS_ITEM fn cgu_export_trait_method[0]::Trait[0]::with_default_impl_generic[0]<u32, bool>
+    //~ TRANS_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl_generic[0]<u32, bool>
     let _ = Trait::with_default_impl_generic(0u32, false);
 
-    //~ TRANS_ITEM fn cgu_export_trait_method[0]::Trait[0]::with_default_impl_generic[0]<char, i16>
+    //~ TRANS_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl_generic[0]<char, i16>
     let _ = Trait::with_default_impl_generic('x', 1i16);
-    //~ TRANS_ITEM fn cgu_export_trait_method[0]::Trait[0]::with_default_impl_generic[0]<char, i32>
+    //~ TRANS_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl_generic[0]<char, i32>
     let _ = Trait::with_default_impl_generic('y', 0i32);
 
-    //~ TRANS_ITEM fn cgu_export_trait_method[0]::u32.Trait[0]::without_default_impl_generic[0]<char>
+    //~ TRANS_ITEM fn cgu_export_trait_method::{{impl}}[1]::without_default_impl_generic[0]<char>
     let _: (u32, char) = Trait::without_default_impl_generic('c');
-    //~ TRANS_ITEM fn cgu_export_trait_method[0]::u32.Trait[0]::without_default_impl_generic[0]<bool>
+    //~ TRANS_ITEM fn cgu_export_trait_method::{{impl}}[1]::without_default_impl_generic[0]<bool>
     let _: (u32, bool) = Trait::without_default_impl_generic(false);
 
-    //~ TRANS_ITEM fn cgu_export_trait_method[0]::char.Trait[0]::without_default_impl_generic[0]<char>
+    //~ TRANS_ITEM fn cgu_export_trait_method::{{impl}}[0]::without_default_impl_generic[0]<char>
     let _: (char, char) = Trait::without_default_impl_generic('c');
-    //~ TRANS_ITEM fn cgu_export_trait_method[0]::char.Trait[0]::without_default_impl_generic[0]<bool>
+    //~ TRANS_ITEM fn cgu_export_trait_method::{{impl}}[0]::without_default_impl_generic[0]<bool>
     let _: (char, bool) = Trait::without_default_impl_generic(false);
 }
 
diff --git a/src/test/codegen-units/generic-drop-glue.rs b/src/test/codegen-units/generic-drop-glue.rs
index f89d6e61bc5..476c84044e6 100644
--- a/src/test/codegen-units/generic-drop-glue.rs
+++ b/src/test/codegen-units/generic-drop-glue.rs
@@ -49,17 +49,17 @@ struct NonGenericWithDrop(i32);
 
 impl Drop for NonGenericWithDrop {
     fn drop(&mut self) {}
-//~ TRANS_ITEM fn generic_drop_glue::NonGenericWithDrop.Drop[0]::drop[0]
+//~ TRANS_ITEM fn generic_drop_glue::{{impl}}[2]::drop[0]
 }
 
 //~ TRANS_ITEM fn generic_drop_glue::main[0]
 fn main() {
     //~ TRANS_ITEM drop-glue generic_drop_glue::StructWithDrop[0]<i8, char>
-    //~ TRANS_ITEM fn generic_drop_glue::StructWithDrop<T1, T2>.Drop[0]::drop[0]<i8, char>
+    //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[0]::drop[0]<i8, char>
     let _ = StructWithDrop { x: 0i8, y: 'a' }.x;
 
     //~ TRANS_ITEM drop-glue generic_drop_glue::StructWithDrop[0]<&str, generic_drop_glue::NonGenericNoDrop[0]>
-    //~ TRANS_ITEM fn generic_drop_glue::StructWithDrop<T1, T2>.Drop[0]::drop[0]<&str, generic_drop_glue::NonGenericNoDrop[0]>
+    //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[0]::drop[0]<&str, generic_drop_glue::NonGenericNoDrop[0]>
     let _ = StructWithDrop { x: "&str", y: NonGenericNoDrop(0) }.y;
 
     // Should produce no drop glue
@@ -71,14 +71,14 @@ fn main() {
     let _ = StructNoDrop { x: NonGenericWithDrop(0), y: 0f64 }.y;
 
     //~ TRANS_ITEM drop-glue generic_drop_glue::EnumWithDrop[0]<i32, i64>
-    //~ TRANS_ITEM fn generic_drop_glue::EnumWithDrop<T1, T2>.Drop[0]::drop[0]<i32, i64>
+    //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[1]::drop[0]<i32, i64>
     let _ = match EnumWithDrop::A::<i32, i64>(0) {
         EnumWithDrop::A(x) => x,
         EnumWithDrop::B(x) => x as i32
     };
 
     //~ TRANS_ITEM drop-glue generic_drop_glue::EnumWithDrop[0]<f64, f32>
-    //~ TRANS_ITEM fn generic_drop_glue::EnumWithDrop<T1, T2>.Drop[0]::drop[0]<f64, f32>
+    //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[1]::drop[0]<f64, f32>
     let _ = match EnumWithDrop::B::<f64, f32>(1.0) {
         EnumWithDrop::A(x) => x,
         EnumWithDrop::B(x) => x as f64
diff --git a/src/test/codegen-units/generic-impl.rs b/src/test/codegen-units/generic-impl.rs
index 6e6bb5cbf53..a27515fd39b 100644
--- a/src/test/codegen-units/generic-impl.rs
+++ b/src/test/codegen-units/generic-impl.rs
@@ -40,11 +40,11 @@ pub struct LifeTimeOnly<'a> {
 
 impl<'a> LifeTimeOnly<'a> {
 
-    //~ TRANS_ITEM fn generic_impl::LifeTimeOnly<'a>[0]::foo[0]
+    //~ TRANS_ITEM fn generic_impl::{{impl}}[1]::foo[0]
     pub fn foo(&self) {}
-    //~ TRANS_ITEM fn generic_impl::LifeTimeOnly<'a>[0]::bar[0]
+    //~ TRANS_ITEM fn generic_impl::{{impl}}[1]::bar[0]
     pub fn bar(&'a self) {}
-    //~ TRANS_ITEM fn generic_impl::LifeTimeOnly<'a>[0]::baz[0]
+    //~ TRANS_ITEM fn generic_impl::{{impl}}[1]::baz[0]
     pub fn baz<'b>(&'b self) {}
 
     pub fn non_instantiated<T>(&self) {}
@@ -53,27 +53,27 @@ impl<'a> LifeTimeOnly<'a> {
 
 //~ TRANS_ITEM fn generic_impl::main[0]
 fn main() {
-    //~ TRANS_ITEM fn generic_impl::Struct<T>[0]::new[0]<i32>
+    //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::new[0]<i32>
     //~ TRANS_ITEM fn generic_impl::id[0]<i32>
-    //~ TRANS_ITEM fn generic_impl::Struct<T>[0]::get[0]<i32, i16>
+    //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::get[0]<i32, i16>
     let _ = Struct::new(0i32).get(0i16);
 
-    //~ TRANS_ITEM fn generic_impl::Struct<T>[0]::new[0]<i64>
+    //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::new[0]<i64>
     //~ TRANS_ITEM fn generic_impl::id[0]<i64>
-    //~ TRANS_ITEM fn generic_impl::Struct<T>[0]::get[0]<i64, i16>
+    //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::get[0]<i64, i16>
     let _ = Struct::new(0i64).get(0i16);
 
-    //~ TRANS_ITEM fn generic_impl::Struct<T>[0]::new[0]<char>
+    //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::new[0]<char>
     //~ TRANS_ITEM fn generic_impl::id[0]<char>
-    //~ TRANS_ITEM fn generic_impl::Struct<T>[0]::get[0]<char, i16>
+    //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::get[0]<char, i16>
     let _ = Struct::new('c').get(0i16);
 
-    //~ TRANS_ITEM fn generic_impl::Struct<T>[0]::new[0]<&str>
+    //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::new[0]<&str>
     //~ TRANS_ITEM fn generic_impl::id[0]<&str>
-    //~ TRANS_ITEM fn generic_impl::Struct<T>[0]::get[0]<generic_impl::Struct[0]<&str>, i16>
+    //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::get[0]<generic_impl::Struct[0]<&str>, i16>
     let _ = Struct::new(Struct::new("str")).get(0i16);
 
-    //~ TRANS_ITEM fn generic_impl::Struct<T>[0]::new[0]<generic_impl::Struct[0]<&str>>
+    //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::new[0]<generic_impl::Struct[0]<&str>>
     //~ TRANS_ITEM fn generic_impl::id[0]<generic_impl::Struct[0]<&str>>
     let _ = (Struct::new(Struct::new("str")).f)(Struct::new("str"));
 }
diff --git a/src/test/codegen-units/impl-in-non-instantiated-generic.rs b/src/test/codegen-units/impl-in-non-instantiated-generic.rs
index e17a1a7094f..a3bfa67e1ae 100644
--- a/src/test/codegen-units/impl-in-non-instantiated-generic.rs
+++ b/src/test/codegen-units/impl-in-non-instantiated-generic.rs
@@ -21,7 +21,7 @@ trait SomeTrait {
 // discovered.
 pub fn generic_function<T>(x: T) -> (T, i32) {
     impl SomeTrait for i64 {
-        //~ TRANS_ITEM fn impl_in_non_instantiated_generic::generic_function[0]::i64.SomeTrait[0]::foo[0]
+        //~ TRANS_ITEM fn impl_in_non_instantiated_generic::generic_function[0]::{{impl}}[0]::foo[0]
         fn foo(&self) {}
     }
 
diff --git a/src/test/codegen-units/instantiation-through-vtable.rs b/src/test/codegen-units/instantiation-through-vtable.rs
index 46587b2b0a1..b7725251220 100644
--- a/src/test/codegen-units/instantiation-through-vtable.rs
+++ b/src/test/codegen-units/instantiation-through-vtable.rs
@@ -31,12 +31,12 @@ impl<T> Trait for Struct<T> {
 fn main() {
     let s1 = Struct { _a: 0u32 };
 
-    //~ TRANS_ITEM fn instantiation_through_vtable::Struct<T>.Trait[0]::foo[0]<u32>
-    //~ TRANS_ITEM fn instantiation_through_vtable::Struct<T>.Trait[0]::bar[0]<u32>
+    //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::foo[0]<u32>
+    //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::bar[0]<u32>
     let _ = &s1 as &Trait;
 
     let s1 = Struct { _a: 0u64 };
-    //~ TRANS_ITEM fn instantiation_through_vtable::Struct<T>.Trait[0]::foo[0]<u64>
-    //~ TRANS_ITEM fn instantiation_through_vtable::Struct<T>.Trait[0]::bar[0]<u64>
+    //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::foo[0]<u64>
+    //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::bar[0]<u64>
     let _ = &s1 as &Trait;
 }
diff --git a/src/test/codegen-units/non-generic-drop-glue.rs b/src/test/codegen-units/non-generic-drop-glue.rs
index a82e85b7a53..bd8b0c605ae 100644
--- a/src/test/codegen-units/non-generic-drop-glue.rs
+++ b/src/test/codegen-units/non-generic-drop-glue.rs
@@ -19,7 +19,7 @@ struct StructWithDrop {
 }
 
 impl Drop for StructWithDrop {
-    //~ TRANS_ITEM fn non_generic_drop_glue::StructWithDrop.Drop[0]::drop[0]
+    //~ TRANS_ITEM fn non_generic_drop_glue::{{impl}}[0]::drop[0]
     fn drop(&mut self) {}
 }
 
@@ -33,7 +33,7 @@ enum EnumWithDrop {
 }
 
 impl Drop for EnumWithDrop {
-    //~ TRANS_ITEM fn non_generic_drop_glue::EnumWithDrop.Drop[0]::drop[0]
+    //~ TRANS_ITEM fn non_generic_drop_glue::{{impl}}[1]::drop[0]
     fn drop(&mut self) {}
 }
 
diff --git a/src/test/codegen-units/non-generic-functions.rs b/src/test/codegen-units/non-generic-functions.rs
index 687ce7fa05c..4e2a7c85084 100644
--- a/src/test/codegen-units/non-generic-functions.rs
+++ b/src/test/codegen-units/non-generic-functions.rs
@@ -38,31 +38,31 @@ fn bar() {
 struct Struct { _x: i32 }
 
 impl Struct {
-    //~ TRANS_ITEM fn non_generic_functions::Struct[0]::foo[0]
+    //~ TRANS_ITEM fn non_generic_functions::{{impl}}[0]::foo[0]
     fn foo() {
         {
-            //~ TRANS_ITEM fn non_generic_functions::Struct[0]::foo[0]::foo[0]
+            //~ TRANS_ITEM fn non_generic_functions::{{impl}}[0]::foo[0]::foo[0]
             fn foo() {}
             foo();
         }
 
         {
-            //~ TRANS_ITEM fn non_generic_functions::Struct[0]::foo[0]::foo[1]
+            //~ TRANS_ITEM fn non_generic_functions::{{impl}}[0]::foo[0]::foo[1]
             fn foo() {}
             foo();
         }
     }
 
-    //~ TRANS_ITEM fn non_generic_functions::Struct[0]::bar[0]
+    //~ TRANS_ITEM fn non_generic_functions::{{impl}}[0]::bar[0]
     fn bar(&self) {
         {
-            //~ TRANS_ITEM fn non_generic_functions::Struct[0]::bar[0]::foo[0]
+            //~ TRANS_ITEM fn non_generic_functions::{{impl}}[0]::bar[0]::foo[0]
             fn foo() {}
             foo();
         }
 
         {
-            //~ TRANS_ITEM fn non_generic_functions::Struct[0]::bar[0]::foo[1]
+            //~ TRANS_ITEM fn non_generic_functions::{{impl}}[0]::bar[0]::foo[1]
             fn foo() {}
             foo();
         }
diff --git a/src/test/codegen-units/overloaded-operators.rs b/src/test/codegen-units/overloaded-operators.rs
index 134110222f3..c275eb954b0 100644
--- a/src/test/codegen-units/overloaded-operators.rs
+++ b/src/test/codegen-units/overloaded-operators.rs
@@ -23,7 +23,7 @@ pub struct Indexable {
 impl Index<usize> for Indexable {
     type Output = u8;
 
-    //~ TRANS_ITEM fn overloaded_operators::Indexable.Index<usize>[0]::index[0]
+    //~ TRANS_ITEM fn overloaded_operators::{{impl}}[0]::index[0]
     fn index(&self, index: usize) -> &Self::Output {
         if index >= 3 {
             &self.data[0]
@@ -34,7 +34,7 @@ impl Index<usize> for Indexable {
 }
 
 impl IndexMut<usize> for Indexable {
-    //~ TRANS_ITEM fn overloaded_operators::Indexable.IndexMut<usize>[0]::index_mut[0]
+    //~ TRANS_ITEM fn overloaded_operators::{{impl}}[1]::index_mut[0]
     fn index_mut(&mut self, index: usize) -> &mut Self::Output {
         if index >= 3 {
             &mut self.data[0]
@@ -45,8 +45,8 @@ impl IndexMut<usize> for Indexable {
 }
 
 
-//~ TRANS_ITEM fn overloaded_operators::Equatable.::std::cmp::PartialEq[0]::eq[0]
-//~ TRANS_ITEM fn overloaded_operators::Equatable.::std::cmp::PartialEq[0]::ne[0]
+//~ TRANS_ITEM fn overloaded_operators::{{impl}}[2]::eq[0]
+//~ TRANS_ITEM fn overloaded_operators::{{impl}}[2]::ne[0]
 #[derive(PartialEq)]
 pub struct Equatable(u32);
 
@@ -54,7 +54,7 @@ pub struct Equatable(u32);
 impl Add<u32> for Equatable {
     type Output = u32;
 
-    //~ TRANS_ITEM fn overloaded_operators::Equatable.Add<u32>[0]::add[0]
+    //~ TRANS_ITEM fn overloaded_operators::{{impl}}[3]::add[0]
     fn add(self, rhs: u32) -> u32 {
         self.0 + rhs
     }
@@ -63,7 +63,7 @@ impl Add<u32> for Equatable {
 impl Deref for Equatable {
     type Target = u32;
 
-    //~ TRANS_ITEM fn overloaded_operators::Equatable.Deref[0]::deref[0]
+    //~ TRANS_ITEM fn overloaded_operators::{{impl}}[4]::deref[0]
     fn deref(&self) -> &Self::Target {
         &self.0
     }
diff --git a/src/test/codegen-units/trait-implementations.rs b/src/test/codegen-units/trait-implementations.rs
index 590859f15a3..2eb2212f0ca 100644
--- a/src/test/codegen-units/trait-implementations.rs
+++ b/src/test/codegen-units/trait-implementations.rs
@@ -20,7 +20,7 @@ pub trait SomeTrait {
 
 impl SomeTrait for i64 {
 
-    //~ TRANS_ITEM fn trait_implementations::i64.SomeTrait[0]::foo[0]
+    //~ TRANS_ITEM fn trait_implementations::{{impl}}[0]::foo[0]
     fn foo(&self) {}
 
     fn bar<T>(&self, _: T) {}
@@ -28,7 +28,7 @@ impl SomeTrait for i64 {
 
 impl SomeTrait for i32 {
 
-    //~ TRANS_ITEM fn trait_implementations::i32.SomeTrait[0]::foo[0]
+    //~ TRANS_ITEM fn trait_implementations::{{impl}}[1]::foo[0]
     fn foo(&self) {}
 
     fn bar<T>(&self, _: T) {}
@@ -42,7 +42,7 @@ pub trait SomeGenericTrait<T> {
 // Concrete impl of generic trait
 impl SomeGenericTrait<u32> for f64 {
 
-    //~ TRANS_ITEM fn trait_implementations::f64.SomeGenericTrait<u32>[0]::foo[0]
+    //~ TRANS_ITEM fn trait_implementations::{{impl}}[2]::foo[0]
     fn foo(&self, _: u32) {}
 
     fn bar<T2>(&self, _: u32, _: T2) {}
@@ -57,25 +57,25 @@ impl<T> SomeGenericTrait<T> for f32 {
 
 //~ TRANS_ITEM fn trait_implementations::main[0]
 fn main() {
-   //~ TRANS_ITEM fn trait_implementations::i32.SomeTrait[0]::bar[0]<char>
+   //~ TRANS_ITEM fn trait_implementations::{{impl}}[1]::bar[0]<char>
    0i32.bar('x');
 
-   //~ TRANS_ITEM fn trait_implementations::f64.SomeGenericTrait<u32>[0]::bar[0]<&str>
+   //~ TRANS_ITEM fn trait_implementations::{{impl}}[2]::bar[0]<&str>
    0f64.bar(0u32, "&str");
 
-   //~ TRANS_ITEM fn trait_implementations::f64.SomeGenericTrait<u32>[0]::bar[0]<()>
+   //~ TRANS_ITEM fn trait_implementations::{{impl}}[2]::bar[0]<()>
    0f64.bar(0u32, ());
 
-   //~ TRANS_ITEM fn trait_implementations::f32.SomeGenericTrait<T>[0]::foo[0]<char>
+   //~ TRANS_ITEM fn trait_implementations::{{impl}}[3]::foo[0]<char>
    0f32.foo('x');
 
-   //~ TRANS_ITEM fn trait_implementations::f32.SomeGenericTrait<T>[0]::foo[0]<i64>
+   //~ TRANS_ITEM fn trait_implementations::{{impl}}[3]::foo[0]<i64>
    0f32.foo(-1i64);
 
-   //~ TRANS_ITEM fn trait_implementations::f32.SomeGenericTrait<T>[0]::bar[0]<u32, ()>
+   //~ TRANS_ITEM fn trait_implementations::{{impl}}[3]::bar[0]<u32, ()>
    0f32.bar(0u32, ());
 
-   //~ TRANS_ITEM fn trait_implementations::f32.SomeGenericTrait<T>[0]::bar[0]<&str, &str>
+   //~ TRANS_ITEM fn trait_implementations::{{impl}}[3]::bar[0]<&str, &str>
    0f32.bar("&str", "&str");
 }
 
diff --git a/src/test/codegen-units/trait-method-as-argument.rs b/src/test/codegen-units/trait-method-as-argument.rs
index fdf63df5471..e7006d73ef1 100644
--- a/src/test/codegen-units/trait-method-as-argument.rs
+++ b/src/test/codegen-units/trait-method-as-argument.rs
@@ -39,7 +39,7 @@ fn take_foo_mut<T, F: FnMut(T) -> T>(mut f: F, arg: T) -> T {
 //~ TRANS_ITEM fn trait_method_as_argument::main[0]
 fn main() {
     //~ TRANS_ITEM fn trait_method_as_argument::take_foo_once[0]<u32, fn(u32) -> u32>
-    //~ TRANS_ITEM fn trait_method_as_argument::u32.Trait[0]::foo[0]
+    //~ TRANS_ITEM fn trait_method_as_argument::{{impl}}[0]::foo[0]
     take_foo_once(Trait::foo, 0u32);
 
     //~ TRANS_ITEM fn trait_method_as_argument::take_foo_once[0]<char, fn(char) -> char>
diff --git a/src/test/codegen-units/transitive-drop-glue.rs b/src/test/codegen-units/transitive-drop-glue.rs
index 6982cb9299a..21bb29199a6 100644
--- a/src/test/codegen-units/transitive-drop-glue.rs
+++ b/src/test/codegen-units/transitive-drop-glue.rs
@@ -21,7 +21,7 @@ struct Intermediate(Leaf);
 struct Leaf;
 
 impl Drop for Leaf {
-    //~ TRANS_ITEM fn transitive_drop_glue::Leaf.Drop[0]::drop[0]
+    //~ TRANS_ITEM fn transitive_drop_glue::{{impl}}[0]::drop[0]
     fn drop(&mut self) {}
 }
 
@@ -44,12 +44,12 @@ fn main() {
     //~ TRANS_ITEM drop-glue transitive_drop_glue::RootGen[0]<u32>
     //~ TRANS_ITEM drop-glue transitive_drop_glue::IntermediateGen[0]<u32>
     //~ TRANS_ITEM drop-glue transitive_drop_glue::LeafGen[0]<u32>
-    //~ TRANS_ITEM fn transitive_drop_glue::LeafGen<T>.Drop[0]::drop[0]<u32>
+    //~ TRANS_ITEM fn transitive_drop_glue::{{impl}}[1]::drop[0]<u32>
     let _ = RootGen(IntermediateGen(LeafGen(0u32)));
 
     //~ TRANS_ITEM drop-glue transitive_drop_glue::RootGen[0]<i16>
     //~ TRANS_ITEM drop-glue transitive_drop_glue::IntermediateGen[0]<i16>
     //~ TRANS_ITEM drop-glue transitive_drop_glue::LeafGen[0]<i16>
-    //~ TRANS_ITEM fn transitive_drop_glue::LeafGen<T>.Drop[0]::drop[0]<i16>
+    //~ TRANS_ITEM fn transitive_drop_glue::{{impl}}[1]::drop[0]<i16>
     let _ = RootGen(IntermediateGen(LeafGen(0i16)));
 }
diff --git a/src/test/codegen-units/tuple-drop-glue.rs b/src/test/codegen-units/tuple-drop-glue.rs
index 87fcb00eab8..1bc235de88e 100644
--- a/src/test/codegen-units/tuple-drop-glue.rs
+++ b/src/test/codegen-units/tuple-drop-glue.rs
@@ -17,7 +17,7 @@
 struct Dropped;
 
 impl Drop for Dropped {
-    //~ TRANS_ITEM fn tuple_drop_glue::Dropped.Drop[0]::drop[0]
+    //~ TRANS_ITEM fn tuple_drop_glue::{{impl}}[0]::drop[0]
     fn drop(&mut self) {}
 }
 
diff --git a/src/test/codegen-units/unsizing.rs b/src/test/codegen-units/unsizing.rs
index dd90d32858f..45ba441bc8b 100644
--- a/src/test/codegen-units/unsizing.rs
+++ b/src/test/codegen-units/unsizing.rs
@@ -57,11 +57,11 @@ fn main()
 {
     // simple case
     let bool_sized = &true;
-    //~ TRANS_ITEM fn unsizing::bool.Trait[0]::foo[0]
+    //~ TRANS_ITEM fn unsizing::{{impl}}[0]::foo[0]
     let _bool_unsized = bool_sized as &Trait;
 
     let char_sized = &true;
-    //~ TRANS_ITEM fn unsizing::char.Trait[0]::foo[0]
+    //~ TRANS_ITEM fn unsizing::{{impl}}[1]::foo[0]
     let _char_unsized = char_sized as &Trait;
 
     // struct field
@@ -70,11 +70,11 @@ fn main()
         _b: 2,
         _c: 3.0f64
     };
-    //~ TRANS_ITEM fn unsizing::f64.Trait[0]::foo[0]
+    //~ TRANS_ITEM fn unsizing::{{impl}}[2]::foo[0]
     let _struct_unsized = struct_sized as &Struct<Trait>;
 
     // custom coercion
     let wrapper_sized = Wrapper(&0u32);
-    //~ TRANS_ITEM fn unsizing::u32.Trait[0]::foo[0]
+    //~ TRANS_ITEM fn unsizing::{{impl}}[3]::foo[0]
     let _wrapper_sized = wrapper_sized as Wrapper<Trait>;
 }
diff --git a/src/test/codegen-units/unused-traits-and-generics.rs b/src/test/codegen-units/unused-traits-and-generics.rs
index a4c5099ab97..8689beb3fb7 100644
--- a/src/test/codegen-units/unused-traits-and-generics.rs
+++ b/src/test/codegen-units/unused-traits-and-generics.rs
@@ -85,5 +85,5 @@ impl NonGeneric {
 }
 
 // Only the non-generic methods should be instantiated:
-//~ TRANS_ITEM fn unused_traits_and_generics::NonGeneric[0]::foo[0]
+//~ TRANS_ITEM fn unused_traits_and_generics::{{impl}}[3]::foo[0]
 //~ TRANS_ITEM drop-glue i8
diff --git a/src/test/compile-fail/symbol-names/basic.rs b/src/test/compile-fail/symbol-names/basic.rs
new file mode 100644
index 00000000000..0095774fcb8
--- /dev/null
+++ b/src/test/compile-fail/symbol-names/basic.rs
@@ -0,0 +1,16 @@
+// Copyright 2012-2015 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(rustc_attrs)]
+
+#[rustc_symbol_name] //~ ERROR _ZN5basic4main
+#[rustc_item_path] //~ ERROR item-path(main)
+fn main() {
+}
diff --git a/src/test/compile-fail/symbol-names/impl1.rs b/src/test/compile-fail/symbol-names/impl1.rs
new file mode 100644
index 00000000000..39bee26da20
--- /dev/null
+++ b/src/test/compile-fail/symbol-names/impl1.rs
@@ -0,0 +1,35 @@
+// Copyright 2012-2015 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(rustc_attrs)]
+#![allow(dead_code)]
+
+mod foo {
+    pub struct Foo { x: u32 }
+
+    impl Foo {
+        #[rustc_symbol_name] //~ ERROR _ZN5impl13foo3Foo3bar
+        #[rustc_item_path] //~ ERROR item-path(foo::Foo::bar)
+        fn bar() { }
+    }
+}
+
+mod bar {
+    use foo::Foo;
+
+    impl Foo {
+        #[rustc_symbol_name] //~ ERROR _ZN5impl13bar26_$LT$impl$u20$foo..Foo$GT$3baz
+        #[rustc_item_path] //~ ERROR item-path(bar::<impl foo::Foo>::baz)
+        fn baz() { }
+    }
+}
+
+fn main() {
+}
diff --git a/src/test/pretty/issue-4264.pp b/src/test/pretty/issue-4264.pp
index fedb68a26af..fe4337eedc6 100644
--- a/src/test/pretty/issue-4264.pp
+++ b/src/test/pretty/issue-4264.pp
@@ -41,37 +41,37 @@ pub fn bar() {
     ((::std::fmt::format as
          fn(core::fmt::Arguments<'_>) -> collections::string::String {collections::fmt::format})(((::std::fmt::Arguments::new_v1
                                                                                                       as
-                                                                                                      fn(&[&str], &[core::fmt::ArgumentV1<'_>]) -> core::fmt::Arguments<'_> {core::fmt::Arguments<'a><'_>::new_v1})(({
-                                                                                                                                                                                                                         static __STATIC_FMTSTR:
-                                                                                                                                                                                                                                &'static [&'static str]
-                                                                                                                                                                                                                                =
-                                                                                                                                                                                                                             (&([("test"
-                                                                                                                                                                                                                                     as
-                                                                                                                                                                                                                                     &'static str)]
-                                                                                                                                                                                                                                   as
-                                                                                                                                                                                                                                   [&'static str; 1])
+                                                                                                      fn(&[&str], &[core::fmt::ArgumentV1<'_>]) -> core::fmt::Arguments<'_> {core::fmt::Arguments<'_>::new_v1})(({
+                                                                                                                                                                                                                     static __STATIC_FMTSTR:
+                                                                                                                                                                                                                            &'static [&'static str]
+                                                                                                                                                                                                                            =
+                                                                                                                                                                                                                         (&([("test"
                                                                                                                                                                                                                                  as
-                                                                                                                                                                                                                                 &'static [&'static str; 1]);
-                                                                                                                                                                                                                         (__STATIC_FMTSTR
-                                                                                                                                                                                                                             as
-                                                                                                                                                                                                                             &'static [&'static str])
-                                                                                                                                                                                                                     }
-                                                                                                                                                                                                                        as
-                                                                                                                                                                                                                        &[&str]),
-                                                                                                                                                                                                                    (&(match (()
-                                                                                                                                                                                                                                 as
-                                                                                                                                                                                                                                 ())
-                                                                                                                                                                                                                           {
-                                                                                                                                                                                                                           ()
-                                                                                                                                                                                                                           =>
-                                                                                                                                                                                                                           ([]
+                                                                                                                                                                                                                                 &'static str)]
                                                                                                                                                                                                                                as
-                                                                                                                                                                                                                               [core::fmt::ArgumentV1<'_>; 0]),
-                                                                                                                                                                                                                       }
-                                                                                                                                                                                                                          as
-                                                                                                                                                                                                                          [core::fmt::ArgumentV1<'_>; 0])
-                                                                                                                                                                                                                        as
-                                                                                                                                                                                                                        &[core::fmt::ArgumentV1<'_>; 0]))
+                                                                                                                                                                                                                               [&'static str; 1])
+                                                                                                                                                                                                                             as
+                                                                                                                                                                                                                             &'static [&'static str; 1]);
+                                                                                                                                                                                                                     (__STATIC_FMTSTR
+                                                                                                                                                                                                                         as
+                                                                                                                                                                                                                         &'static [&'static str])
+                                                                                                                                                                                                                 }
+                                                                                                                                                                                                                    as
+                                                                                                                                                                                                                    &[&str]),
+                                                                                                                                                                                                                (&(match (()
+                                                                                                                                                                                                                             as
+                                                                                                                                                                                                                             ())
+                                                                                                                                                                                                                       {
+                                                                                                                                                                                                                       ()
+                                                                                                                                                                                                                       =>
+                                                                                                                                                                                                                       ([]
+                                                                                                                                                                                                                           as
+                                                                                                                                                                                                                           [core::fmt::ArgumentV1<'_>; 0]),
+                                                                                                                                                                                                                   }
+                                                                                                                                                                                                                      as
+                                                                                                                                                                                                                      [core::fmt::ArgumentV1<'_>; 0])
+                                                                                                                                                                                                                    as
+                                                                                                                                                                                                                    &[core::fmt::ArgumentV1<'_>; 0]))
                                                                                                      as
                                                                                                      core::fmt::Arguments<'_>))
         as collections::string::String);
diff --git a/src/test/run-make/a-b-a-linker-guard/Makefile b/src/test/run-make/a-b-a-linker-guard/Makefile
new file mode 100644
index 00000000000..0962ebfbff5
--- /dev/null
+++ b/src/test/run-make/a-b-a-linker-guard/Makefile
@@ -0,0 +1,12 @@
+-include ../tools.mk
+
+# Test that if we build `b` against a version of `a` that has one set
+# of types, it will not run with a dylib that has a different set of
+# types.
+
+all:
+	$(RUSTC) a.rs --cfg x -C prefer-dynamic
+	$(RUSTC) b.rs -C prefer-dynamic
+	$(call RUN,b)
+	$(RUSTC) a.rs --cfg y -C prefer-dynamic
+	$(call FAIL,b)
diff --git a/src/test/run-make/a-b-a-linker-guard/a.rs b/src/test/run-make/a-b-a-linker-guard/a.rs
new file mode 100644
index 00000000000..e6cbe2e64d0
--- /dev/null
+++ b/src/test/run-make/a-b-a-linker-guard/a.rs
@@ -0,0 +1,20 @@
+// Copyright 2014 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_name = "a"]
+#![crate_type = "dylib"]
+
+#[cfg(x)]
+pub fn foo(x: u32) { }
+
+#[cfg(y)]
+pub fn foo(x: i32) { }
+
+
diff --git a/src/test/run-make/a-b-a-linker-guard/b.rs b/src/test/run-make/a-b-a-linker-guard/b.rs
new file mode 100644
index 00000000000..89fd48de5bb
--- /dev/null
+++ b/src/test/run-make/a-b-a-linker-guard/b.rs
@@ -0,0 +1,17 @@
+// Copyright 2014 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_name = "b"]
+
+extern crate a;
+
+fn main() {
+    a::foo(22_u32);
+}
diff --git a/src/test/run-make/extern-overrides-distribution/Makefile b/src/test/run-make/extern-overrides-distribution/Makefile
index 110db9f068d..7d063a4c83c 100644
--- a/src/test/run-make/extern-overrides-distribution/Makefile
+++ b/src/test/run-make/extern-overrides-distribution/Makefile
@@ -1,5 +1,5 @@
 -include ../tools.mk
 
 all:
-	$(RUSTC) libc.rs
+	$(RUSTC) libc.rs -Cmetadata=foo
 	$(RUSTC) main.rs --extern libc=$(TMPDIR)/liblibc.rlib
diff --git a/src/test/run-make/issue-26006/Makefile b/src/test/run-make/issue-26006/Makefile
index de89a6f6ad6..66aa78d5386 100644
--- a/src/test/run-make/issue-26006/Makefile
+++ b/src/test/run-make/issue-26006/Makefile
@@ -12,7 +12,7 @@ time: libc
 
 libc:
 	mkdir -p $(OUT)/libc
-	$(RUSTC) in/libc/lib.rs --crate-name=libc -o $(OUT)/libc/liblibc.rlib
+	$(RUSTC) in/libc/lib.rs --crate-name=libc -Cmetadata=foo -o $(OUT)/libc/liblibc.rlib
 else
 all:
 endif
diff --git a/src/test/run-make/relocation-model/Makefile b/src/test/run-make/relocation-model/Makefile
index b22f34fa35b..485ecbb4b5a 100644
--- a/src/test/run-make/relocation-model/Makefile
+++ b/src/test/run-make/relocation-model/Makefile
@@ -7,8 +7,7 @@ all: others
 	$(RUSTC) -C relocation-model=default foo.rs
 	$(call RUN,foo)
 
-	$(RUSTC) -C relocation-model=default --crate-type=dylib foo.rs
-	$(RUSTC) -C relocation-model=dynamic-no-pic --crate-type=dylib foo.rs
+	$(RUSTC) -C relocation-model=dynamic-no-pic --crate-type=dylib foo.rs --emit=link,obj
 
 ifdef IS_MSVC
 # FIXME(#28026)
@@ -17,5 +16,4 @@ else
 others:
 	$(RUSTC) -C relocation-model=static foo.rs
 	$(call RUN,foo)
-	$(RUSTC) -C relocation-model=static --crate-type=dylib foo.rs
 endif
diff --git a/src/test/run-make/reproducible-build/Makefile b/src/test/run-make/reproducible-build/Makefile
new file mode 100644
index 00000000000..8e799ca1a43
--- /dev/null
+++ b/src/test/run-make/reproducible-build/Makefile
@@ -0,0 +1,20 @@
+-include ../tools.mk
+all:
+	$(RUSTC) reproducible-build-aux.rs
+	$(RUSTC) reproducible-build.rs -o"$(TMPDIR)/reproducible-build1"
+	$(RUSTC) reproducible-build.rs -o"$(TMPDIR)/reproducible-build2"
+	nm "$(TMPDIR)/reproducible-build1" | sort > "$(TMPDIR)/reproducible-build1.nm"
+	nm "$(TMPDIR)/reproducible-build2" | sort > "$(TMPDIR)/reproducible-build2.nm"
+	cmp "$(TMPDIR)/reproducible-build1.nm" "$(TMPDIR)/reproducible-build2.nm" || exit 1
+	$(RUSTC) reproducible-build-aux.rs -g
+	$(RUSTC) reproducible-build.rs -g -o"$(TMPDIR)/reproducible-build1-debug"
+	$(RUSTC) reproducible-build.rs -g -o"$(TMPDIR)/reproducible-build2-debug"
+	nm "$(TMPDIR)/reproducible-build1-debug" | sort > "$(TMPDIR)/reproducible-build1-debug.nm"
+	nm "$(TMPDIR)/reproducible-build2-debug" | sort > "$(TMPDIR)/reproducible-build2-debug.nm"
+	cmp "$(TMPDIR)/reproducible-build1-debug.nm" "$(TMPDIR)/reproducible-build2-debug.nm" || exit 1
+	$(RUSTC) reproducible-build-aux.rs -O
+	$(RUSTC) reproducible-build.rs -O -o"$(TMPDIR)/reproducible-build1-opt"
+	$(RUSTC) reproducible-build.rs -O -o"$(TMPDIR)/reproducible-build2-opt"
+	nm "$(TMPDIR)/reproducible-build1-opt" | sort > "$(TMPDIR)/reproducible-build1-opt.nm"
+	nm "$(TMPDIR)/reproducible-build2-opt" | sort > "$(TMPDIR)/reproducible-build2-opt.nm"
+	cmp "$(TMPDIR)/reproducible-build1-opt.nm" "$(TMPDIR)/reproducible-build2-opt.nm" || exit 1
diff --git a/src/test/run-make/reproducible-build/reproducible-build-aux.rs b/src/test/run-make/reproducible-build/reproducible-build-aux.rs
new file mode 100644
index 00000000000..9ef853e7996
--- /dev/null
+++ b/src/test/run-make/reproducible-build/reproducible-build-aux.rs
@@ -0,0 +1,38 @@
+// Copyright 2016 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="lib"]
+
+pub static STATIC: i32 = 1234;
+
+pub struct Struct<T1, T2> {
+    _t1: std::marker::PhantomData<T1>,
+    _t2: std::marker::PhantomData<T2>,
+}
+
+pub fn regular_fn(_: i32) {}
+
+pub fn generic_fn<T1, T2>() {}
+
+impl<T1, T2> Drop for Struct<T1, T2> {
+    fn drop(&mut self) {}
+}
+
+pub enum Enum {
+    Variant1,
+    Variant2(u32),
+    Variant3 { x: u32 }
+}
+
+pub struct TupleStruct(pub i8, pub i16, pub i32, pub i64);
+
+pub trait Trait<T1, T2> {
+    fn foo(&self);
+}
diff --git a/src/test/run-make/reproducible-build/reproducible-build.rs b/src/test/run-make/reproducible-build/reproducible-build.rs
new file mode 100644
index 00000000000..dc7c702e5cc
--- /dev/null
+++ b/src/test/run-make/reproducible-build/reproducible-build.rs
@@ -0,0 +1,128 @@
+// Copyright 2016 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.
+
+// This test case makes sure that two identical invocations of the compiler
+// (i.e. same code base, same compile-flags, same compiler-versions, etc.)
+// produce the same output. In the past, symbol names of monomorphized functions
+// were not deterministic (which we want to avoid).
+//
+// The test tries to exercise as many different paths into symbol name
+// generation as possible:
+//
+// - regular functions
+// - generic functions
+// - methods
+// - statics
+// - closures
+// - enum variant constructors
+// - tuple struct constructors
+// - drop glue
+// - FnOnce adapters
+// - Trait object shims
+// - Fn Pointer shims
+
+#![allow(dead_code)]
+
+extern crate reproducible_build_aux;
+
+static STATIC: i32 = 1234;
+
+pub struct Struct<T1, T2> {
+    x: T1,
+    y: T2,
+}
+
+fn regular_fn(_: i32) {}
+
+fn generic_fn<T1, T2>() {}
+
+impl<T1, T2> Drop for Struct<T1, T2> {
+    fn drop(&mut self) {}
+}
+
+pub enum Enum {
+    Variant1,
+    Variant2(u32),
+    Variant3 { x: u32 }
+}
+
+struct TupleStruct(i8, i16, i32, i64);
+
+impl TupleStruct {
+    pub fn bar(&self) {}
+}
+
+trait Trait<T1, T2> {
+    fn foo(&self);
+}
+
+impl Trait<i32, u64> for u64 {
+    fn foo(&self) {}
+}
+
+impl reproducible_build_aux::Trait<char, String> for TupleStruct {
+    fn foo(&self) {}
+}
+
+fn main() {
+    regular_fn(STATIC);
+    generic_fn::<u32, char>();
+    generic_fn::<char, Struct<u32, u64>>();
+    generic_fn::<Struct<u64, u32>, reproducible_build_aux::Struct<u32, u64>>();
+
+    let dropped = Struct {
+        x: "",
+        y: 'a',
+    };
+
+    let _ = Enum::Variant1;
+    let _ = Enum::Variant2(0);
+    let _ = Enum::Variant3 { x: 0 };
+    let _ = TupleStruct(1, 2, 3, 4);
+
+    let closure  = |x| {
+        x + 1i32
+    };
+
+    fn inner<F: Fn(i32) -> i32>(f: F) -> i32 {
+        f(STATIC)
+    }
+
+    println!("{}", inner(closure));
+
+    let object_shim: &Trait<i32, u64> = &0u64;
+    object_shim.foo();
+
+    fn with_fn_once_adapter<F: FnOnce(i32)>(f: F) {
+        f(0);
+    }
+
+    with_fn_once_adapter(|_:i32| { });
+
+    reproducible_build_aux::regular_fn(STATIC);
+    reproducible_build_aux::generic_fn::<u32, char>();
+    reproducible_build_aux::generic_fn::<char, Struct<u32, u64>>();
+    reproducible_build_aux::generic_fn::<Struct<u64, u32>,
+                                         reproducible_build_aux::Struct<u32, u64>>();
+
+    let _ = reproducible_build_aux::Enum::Variant1;
+    let _ = reproducible_build_aux::Enum::Variant2(0);
+    let _ = reproducible_build_aux::Enum::Variant3 { x: 0 };
+    let _ = reproducible_build_aux::TupleStruct(1, 2, 3, 4);
+
+    let object_shim: &reproducible_build_aux::Trait<char, String> = &TupleStruct(0, 1, 2, 3);
+    object_shim.foo();
+
+    let pointer_shim: &Fn(i32) = &regular_fn;
+
+    TupleStruct(1, 2, 3, 4).bar();
+}
+
+
diff --git a/src/test/run-pass/backtrace.rs b/src/test/run-pass/backtrace.rs
index 2a98706351a..36cac8f50a8 100644
--- a/src/test/run-pass/backtrace.rs
+++ b/src/test/run-pass/backtrace.rs
@@ -51,13 +51,29 @@ fn template(me: &str) -> Command {
     return m;
 }
 
+fn expected(fn_name: &str) -> String {
+    // FIXME(#32481)
+    //
+    // On windows, we read the function name from debuginfo using some
+    // system APIs. For whatever reason, these APIs seem to use the
+    // "name" field, which is only the "relative" name, not the full
+    // name with namespace info, so we just see `foo` and not
+    // `backtrace::foo` as we see on linux (which uses the linkage
+    // name).
+    if cfg!(windows) && cfg!(target_env = "msvc") {
+        format!(" - {}", fn_name)
+    } else {
+        format!(" - backtrace::{}", fn_name)
+    }
+}
+
 fn runtest(me: &str) {
     // Make sure that the stack trace is printed
     let p = template(me).arg("fail").env("RUST_BACKTRACE", "1").spawn().unwrap();
     let out = p.wait_with_output().unwrap();
     assert!(!out.status.success());
     let s = str::from_utf8(&out.stderr).unwrap();
-    assert!(s.contains("stack backtrace") && s.contains(" - foo"),
+    assert!(s.contains("stack backtrace") && s.contains(&expected("foo")),
             "bad output: {}", s);
 
     // Make sure the stack trace is *not* printed
@@ -67,7 +83,7 @@ fn runtest(me: &str) {
     let out = p.wait_with_output().unwrap();
     assert!(!out.status.success());
     let s = str::from_utf8(&out.stderr).unwrap();
-    assert!(!s.contains("stack backtrace") && !s.contains(" - foo"),
+    assert!(!s.contains("stack backtrace") && !s.contains(&expected("foo")),
             "bad output2: {}", s);
 
     // Make sure a stack trace is printed
@@ -77,7 +93,7 @@ fn runtest(me: &str) {
     let s = str::from_utf8(&out.stderr).unwrap();
     // loosened the following from double::h to double:: due to
     // spurious failures on mac, 32bit, optimized
-    assert!(s.contains("stack backtrace") && s.contains(" - double"),
+    assert!(s.contains("stack backtrace") && s.contains(&expected("double")),
             "bad output3: {}", s);
 
     // Make sure a stack trace isn't printed too many times
diff --git a/src/test/run-pass/command-before-exec.rs b/src/test/run-pass/command-before-exec.rs
index 7c5a21911db..16560637b69 100644
--- a/src/test/run-pass/command-before-exec.rs
+++ b/src/test/run-pass/command-before-exec.rs
@@ -9,8 +9,6 @@
 // except according to those terms.
 
 // ignore-windows - this is a unix-specific test
-// no-prefer-dynamic - this test breaks with dynamic linking as
-// some LD_LIBRARY_PATH entries are relative and it cd's to /.
 
 #![feature(process_exec, libc)]
 
diff --git a/src/test/run-pass/issue-17718.rs b/src/test/run-pass/issue-17718.rs
index 2bb69d105ff..744e63f159b 100644
--- a/src/test/run-pass/issue-17718.rs
+++ b/src/test/run-pass/issue-17718.rs
@@ -8,13 +8,13 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// aux-build:issue-17718.rs
+// aux-build:issue-17718-aux.rs
 
 
 #![feature(core)]
 #![feature(const_fn)]
 
-extern crate issue_17718 as other;
+extern crate issue_17718_aux as other;
 
 use std::sync::atomic::{AtomicUsize, Ordering};
 
diff --git a/src/test/run-pass/typeid-intrinsic.rs b/src/test/run-pass/typeid-intrinsic.rs
index db53fa855f1..4bd82baafeb 100644
--- a/src/test/run-pass/typeid-intrinsic.rs
+++ b/src/test/run-pass/typeid-intrinsic.rs
@@ -8,13 +8,13 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// aux-build:typeid-intrinsic.rs
-// aux-build:typeid-intrinsic2.rs
+// aux-build:typeid-intrinsic-aux1.rs
+// aux-build:typeid-intrinsic-aux2.rs
 
 #![feature(core_intrinsics)]
 
-extern crate typeid_intrinsic as other1;
-extern crate typeid_intrinsic2 as other2;
+extern crate typeid_intrinsic_aux1 as other1;
+extern crate typeid_intrinsic_aux2 as other2;
 
 use std::hash::{SipHasher, Hasher, Hash};
 use std::any::TypeId;