about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules3
-rw-r--r--src/bootstrap/doc.rs1
m---------src/doc/edition-guide0
m---------src/doc/nomicon0
-rw-r--r--src/doc/unstable-book/src/language-features/self-struct-ctor.md33
-rw-r--r--src/liballoc/string.rs2
-rw-r--r--src/libarena/lib.rs2
-rw-r--r--src/libcore/Cargo.toml4
-rw-r--r--src/libcore/array.rs9
-rw-r--r--src/libcore/num/mod.rs79
-rw-r--r--src/libcore/panicking.rs24
-rw-r--r--src/librustc/hir/lowering.rs16
-rw-r--r--src/librustc/hir/print.rs61
-rw-r--r--src/librustc/infer/at.rs1
-rw-r--r--src/librustc/infer/nll_relate/mod.rs322
-rw-r--r--src/librustc/infer/resolve.rs1
-rw-r--r--src/librustc/middle/reachable.rs5
-rw-r--r--src/librustc/middle/resolve_lifetime.rs2
-rw-r--r--src/librustc/session/config.rs2
-rw-r--r--src/librustc/traits/mod.rs1
-rw-r--r--src/librustc/traits/object_safety.rs2
-rw-r--r--src/librustc/ty/context.rs224
-rw-r--r--src/librustc/ty/fold.rs45
-rw-r--r--src/librustc/ty/mod.rs5
-rw-r--r--src/librustc/ty/relate.rs295
-rw-r--r--src/librustc/ty/sty.rs8
-rw-r--r--src/librustc_codegen_llvm/back/write.rs10
-rw-r--r--src/librustc_codegen_llvm/debuginfo/metadata.rs4
-rw-r--r--src/librustc_codegen_llvm/llvm/archive_ro.rs16
-rw-r--r--src/librustc_codegen_llvm/metadata.rs4
-rw-r--r--src/librustc_codegen_ssa/back/link.rs6
-rw-r--r--src/librustc_data_structures/interner.rs68
-rw-r--r--src/librustc_data_structures/lib.rs2
-rw-r--r--src/librustc_driver/pretty.rs2
-rw-r--r--src/librustc_fs_util/lib.rs6
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs17
-rw-r--r--src/librustc_mir/monomorphize/partitioning.rs2
-rw-r--r--src/librustc_mir/transform/qualify_consts.rs259
-rw-r--r--src/librustc_privacy/lib.rs2
-rw-r--r--src/librustc_traits/chalk_context/mod.rs119
-rw-r--r--src/librustc_traits/chalk_context/resolvent_ops.rs241
-rw-r--r--src/librustc_traits/chalk_context/unify.rs98
-rw-r--r--src/librustc_typeck/check_unused.rs2
-rw-r--r--src/libserialize/collection_impls.rs2
-rw-r--r--src/libstd/Cargo.toml3
-rw-r--r--src/libstd/collections/hash/map.rs7
-rw-r--r--src/libstd/collections/hash/table.rs4
-rw-r--r--src/libstd/panicking.rs19
-rw-r--r--src/libsyntax/ext/expand.rs84
-rw-r--r--src/libsyntax/feature_gate.rs7
-rw-r--r--src/libsyntax/parse/parser.rs2
-rw-r--r--src/libsyntax/print/pp.rs309
-rw-r--r--src/libsyntax/print/pprust.rs106
-rw-r--r--src/libsyntax/ptr.rs28
-rw-r--r--src/libsyntax_pos/lib.rs1
-rw-r--r--src/libsyntax_pos/symbol.rs4
-rw-r--r--src/test/run-make/llvm-outputs/Makefile5
-rw-r--r--src/test/run-pass/rfcs/rfc-2302-self-struct-ctor.rs2
-rw-r--r--src/test/ui/consts/issue-56164.rs13
-rw-r--r--src/test/ui/consts/issue-56164.stderr15
-rw-r--r--src/test/ui/feature-gates/feature-gate-custom_test_frameworks.rs (renamed from src/test/ui/feature-gate-custom_test_frameworks.rs)0
-rw-r--r--src/test/ui/feature-gates/feature-gate-custom_test_frameworks.stderr (renamed from src/test/ui/feature-gate-custom_test_frameworks.stderr)0
-rw-r--r--src/test/ui/feature-gates/feature-gate-doc_cfg-cfg-rustdoc.rs (renamed from src/test/ui/feature-gate-doc_cfg-cfg-rustdoc.rs)0
-rw-r--r--src/test/ui/feature-gates/feature-gate-doc_cfg-cfg-rustdoc.stderr (renamed from src/test/ui/feature-gate-doc_cfg-cfg-rustdoc.stderr)0
-rw-r--r--src/test/ui/feature-gates/feature-gate-self-struct-ctor.rs22
-rw-r--r--src/test/ui/feature-gates/feature-gate-self-struct-ctor.stderr19
-rw-r--r--src/test/ui/feature-gates/feature-gate-underscore_const_names.rs (renamed from src/test/ui/feature-gate-underscore_const_names.rs)0
-rw-r--r--src/test/ui/feature-gates/feature-gate-underscore_const_names.stderr (renamed from src/test/ui/feature-gate-underscore_const_names.stderr)0
-rw-r--r--src/test/ui/feature-gates/feature-gate-unsized_locals.rs (renamed from src/test/ui/feature-gate-unsized_locals.rs)0
-rw-r--r--src/test/ui/feature-gates/feature-gate-unsized_locals.stderr (renamed from src/test/ui/feature-gate-unsized_locals.stderr)0
-rw-r--r--src/test/ui/issues/issue-56202.rs17
-rw-r--r--src/test/ui/keyword/keyword-self-as-identifier.rs1
-rw-r--r--src/test/ui/keyword/keyword-self-as-identifier.stderr13
-rw-r--r--src/test/ui/self/self_type_keyword-2.rs3
-rw-r--r--src/test/ui/self/self_type_keyword-2.stderr32
-rw-r--r--src/tools/linkchecker/main.rs38
76 files changed, 1906 insertions, 855 deletions
diff --git a/.gitmodules b/.gitmodules
index 6b2714edd4d..4a136cff1cd 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -65,3 +65,6 @@
 [submodule "src/doc/rustc-guide"]
 	path = src/doc/rustc-guide
 	url = https://github.com/rust-lang/rustc-guide.git
+[submodule "src/doc/edition-guide"]
+	path = src/doc/edition-guide
+	url = https://github.com/rust-lang-nursery/edition-guide
diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs
index f9b19ffb10d..18a2b950e59 100644
--- a/src/bootstrap/doc.rs
+++ b/src/bootstrap/doc.rs
@@ -70,6 +70,7 @@ macro_rules! book {
 book!(
     Nomicon, "src/doc/nomicon", "nomicon";
     Reference, "src/doc/reference", "reference";
+    EditionGuide, "src/doc/edition-guide", "edition-guide";
     RustdocBook, "src/doc/rustdoc", "rustdoc";
     RustcBook, "src/doc/rustc", "rustc";
     RustByExample, "src/doc/rust-by-example", "rust-by-example";
diff --git a/src/doc/edition-guide b/src/doc/edition-guide
new file mode 160000
+Subproject ad895867b675199a7f597ce7045a56875a7e516
diff --git a/src/doc/nomicon b/src/doc/nomicon
-Subproject f8a4e96feb2e5a6ed1ef170ad40e3509a7755cb
+Subproject b7eb4a087207af2405c0669fa577f8545b894c6
diff --git a/src/doc/unstable-book/src/language-features/self-struct-ctor.md b/src/doc/unstable-book/src/language-features/self-struct-ctor.md
deleted file mode 100644
index b4742c48a32..00000000000
--- a/src/doc/unstable-book/src/language-features/self-struct-ctor.md
+++ /dev/null
@@ -1,33 +0,0 @@
-# `self_struct_ctor`
-
-The tracking issue for this feature is: [#51994]
-[#51994]: https://github.com/rust-lang/rust/issues/51994
-
-------------------------
-
-The `self_struct_ctor` feature gate lets you use the special `Self`
-identifier as a constructor and a pattern.
-
-A simple example is:
-
-```rust
-#![feature(self_struct_ctor)]
-
-struct ST(i32, i32);
-
-impl ST {
-    fn new() -> Self {
-        ST(0, 1)
-    }
-
-    fn ctor() -> Self {
-        Self(1,2)           // constructed by `Self`, it is the same as `ST(1, 2)`
-    }
-
-    fn pattern(self) {
-        match self {
-            Self(x, y) => println!("{} {}", x, y), // used as a pattern
-        }
-    }
-}
-```
diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs
index 8d009101ce7..0b25d911a29 100644
--- a/src/liballoc/string.rs
+++ b/src/liballoc/string.rs
@@ -618,6 +618,8 @@ impl String {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn from_utf16(v: &[u16]) -> Result<String, FromUtf16Error> {
+        // This isn't done via collect::<Result<_, _>>() for performance reasons.
+        // FIXME: the function can be simplified again when #48994 is closed.
         let mut ret = String::with_capacity(v.len());
         for c in decode_utf16(v.iter().cloned()) {
             if let Ok(c) = c {
diff --git a/src/libarena/lib.rs b/src/libarena/lib.rs
index dae05a368fa..955cab1d93f 100644
--- a/src/libarena/lib.rs
+++ b/src/libarena/lib.rs
@@ -298,6 +298,7 @@ pub struct DroplessArena {
 unsafe impl Send for DroplessArena {}
 
 impl Default for DroplessArena {
+    #[inline]
     fn default() -> DroplessArena {
         DroplessArena {
             ptr: Cell::new(0 as *mut u8),
@@ -319,6 +320,7 @@ impl DroplessArena {
         false
     }
 
+    #[inline]
     fn align(&self, align: usize) {
         let final_address = ((self.ptr.get() as usize) + align - 1) & !(align - 1);
         self.ptr.set(final_address as *mut u8);
diff --git a/src/libcore/Cargo.toml b/src/libcore/Cargo.toml
index 0b01cfc488b..7fd61f07d5e 100644
--- a/src/libcore/Cargo.toml
+++ b/src/libcore/Cargo.toml
@@ -21,3 +21,7 @@ path = "../libcore/benches/lib.rs"
 
 [dev-dependencies]
 rand = "0.5"
+
+[features]
+# Make panics and failed asserts immediately abort without formatting any message
+panic_immediate_abort = []
diff --git a/src/libcore/array.rs b/src/libcore/array.rs
index 3d24f8902bd..26e7a79d35d 100644
--- a/src/libcore/array.rs
+++ b/src/libcore/array.rs
@@ -149,6 +149,15 @@ macro_rules! array_impls {
             }
 
             #[unstable(feature = "try_from", issue = "33417")]
+            impl<'a, T> TryFrom<&'a [T]> for [T; $N] where T: Copy {
+                type Error = TryFromSliceError;
+
+                fn try_from(slice: &[T]) -> Result<[T; $N], TryFromSliceError> {
+                    <&Self>::try_from(slice).map(|r| *r)
+                }
+            }
+
+            #[unstable(feature = "try_from", issue = "33417")]
             impl<'a, T> TryFrom<&'a [T]> for &'a [T; $N] {
                 type Error = TryFromSliceError;
 
diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs
index 57b5903c9d3..805be431328 100644
--- a/src/libcore/num/mod.rs
+++ b/src/libcore/num/mod.rs
@@ -1989,6 +1989,19 @@ big endian.
 ```
 let value = ", stringify!($SelfT), "::from_be_bytes(", $be_bytes, ");
 assert_eq!(value, ", $swap_op, ");
+```
+
+When starting from a slice rather than an array, fallible conversion APIs can be used:
+
+```
+#![feature(try_from)]
+use std::convert::TryInto;
+
+fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {
+    let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());
+    *input = rest;
+    ", stringify!($SelfT), "::from_be_bytes(int_bytes.try_into().unwrap())
+}
 ```"),
             #[stable(feature = "int_to_from_bytes", since = "1.32.0")]
             #[rustc_const_unstable(feature = "const_int_conversion")]
@@ -2008,6 +2021,19 @@ little endian.
 ```
 let value = ", stringify!($SelfT), "::from_le_bytes(", $le_bytes, ");
 assert_eq!(value, ", $swap_op, ");
+```
+
+When starting from a slice rather than an array, fallible conversion APIs can be used:
+
+```
+#![feature(try_from)]
+use std::convert::TryInto;
+
+fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {
+    let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());
+    *input = rest;
+    ", stringify!($SelfT), "::from_be_bytes(int_bytes.try_into().unwrap())
+}
 ```"),
             #[stable(feature = "int_to_from_bytes", since = "1.32.0")]
             #[rustc_const_unstable(feature = "const_int_conversion")]
@@ -2037,6 +2063,19 @@ let value = ", stringify!($SelfT), "::from_ne_bytes(if cfg!(target_endian = \"bi
         ", $le_bytes, "
     });
 assert_eq!(value, ", $swap_op, ");
+```
+
+When starting from a slice rather than an array, fallible conversion APIs can be used:
+
+```
+#![feature(try_from)]
+use std::convert::TryInto;
+
+fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {
+    let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());
+    *input = rest;
+    ", stringify!($SelfT), "::from_be_bytes(int_bytes.try_into().unwrap())
+}
 ```"),
             #[stable(feature = "int_to_from_bytes", since = "1.32.0")]
             #[rustc_const_unstable(feature = "const_int_conversion")]
@@ -3614,6 +3653,7 @@ assert_eq!(3", stringify!($SelfT), ".checked_next_power_of_two(), Some(4));
 assert_eq!(", stringify!($SelfT), "::max_value().checked_next_power_of_two(), None);",
 $EndFeature, "
 ```"),
+            #[inline]
             #[stable(feature = "rust1", since = "1.0.0")]
             pub fn checked_next_power_of_two(self) -> Option<Self> {
                 self.one_less_than_next_power_of_two().checked_add(1)
@@ -3719,6 +3759,19 @@ big endian.
 ```
 let value = ", stringify!($SelfT), "::from_be_bytes(", $be_bytes, ");
 assert_eq!(value, ", $swap_op, ");
+```
+
+When starting from a slice rather than an array, fallible conversion APIs can be used:
+
+```
+#![feature(try_from)]
+use std::convert::TryInto;
+
+fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {
+    let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());
+    *input = rest;
+    ", stringify!($SelfT), "::from_be_bytes(int_bytes.try_into().unwrap())
+}
 ```"),
             #[stable(feature = "int_to_from_bytes", since = "1.32.0")]
             #[rustc_const_unstable(feature = "const_int_conversion")]
@@ -3738,6 +3791,19 @@ little endian.
 ```
 let value = ", stringify!($SelfT), "::from_le_bytes(", $le_bytes, ");
 assert_eq!(value, ", $swap_op, ");
+```
+
+When starting from a slice rather than an array, fallible conversion APIs can be used:
+
+```
+#![feature(try_from)]
+use std::convert::TryInto;
+
+fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {
+    let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());
+    *input = rest;
+    ", stringify!($SelfT), "::from_be_bytes(int_bytes.try_into().unwrap())
+}
 ```"),
             #[stable(feature = "int_to_from_bytes", since = "1.32.0")]
             #[rustc_const_unstable(feature = "const_int_conversion")]
@@ -3767,6 +3833,19 @@ let value = ", stringify!($SelfT), "::from_ne_bytes(if cfg!(target_endian = \"bi
         ", $le_bytes, "
     });
 assert_eq!(value, ", $swap_op, ");
+```
+
+When starting from a slice rather than an array, fallible conversion APIs can be used:
+
+```
+#![feature(try_from)]
+use std::convert::TryInto;
+
+fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {
+    let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());
+    *input = rest;
+    ", stringify!($SelfT), "::from_be_bytes(int_bytes.try_into().unwrap())
+}
 ```"),
             #[stable(feature = "int_to_from_bytes", since = "1.32.0")]
             #[rustc_const_unstable(feature = "const_int_conversion")]
diff --git a/src/libcore/panicking.rs b/src/libcore/panicking.rs
index 58407de9566..aa18a60fc0f 100644
--- a/src/libcore/panicking.rs
+++ b/src/libcore/panicking.rs
@@ -39,9 +39,16 @@
 use fmt;
 use panic::{Location, PanicInfo};
 
-#[cold] #[inline(never)] // this is the slow path, always
+#[cold]
+// never inline unless panic_immediate_abort to avoid code
+// bloat at the call sites as much as possible
+#[cfg_attr(not(feature="panic_immediate_abort"),inline(never))]
 #[lang = "panic"]
 pub fn panic(expr_file_line_col: &(&'static str, &'static str, u32, u32)) -> ! {
+    if cfg!(feature = "panic_immediate_abort") {
+        unsafe { super::intrinsics::abort() }
+    }
+
     // Use Arguments::new_v1 instead of format_args!("{}", expr) to potentially
     // reduce size overhead. The format_args! macro uses str's Display trait to
     // write expr, which calls Formatter::pad, which must accommodate string
@@ -52,16 +59,27 @@ pub fn panic(expr_file_line_col: &(&'static str, &'static str, u32, u32)) -> ! {
     panic_fmt(fmt::Arguments::new_v1(&[expr], &[]), &(file, line, col))
 }
 
-#[cold] #[inline(never)]
+#[cold]
+#[cfg_attr(not(feature="panic_immediate_abort"),inline(never))]
 #[lang = "panic_bounds_check"]
 fn panic_bounds_check(file_line_col: &(&'static str, u32, u32),
                      index: usize, len: usize) -> ! {
+    if cfg!(feature = "panic_immediate_abort") {
+        unsafe { super::intrinsics::abort() }
+    }
+
     panic_fmt(format_args!("index out of bounds: the len is {} but the index is {}",
                            len, index), file_line_col)
 }
 
-#[cold] #[inline(never)]
+#[cold]
+#[cfg_attr(not(feature="panic_immediate_abort"),inline(never))]
+#[cfg_attr(    feature="panic_immediate_abort" ,inline)]
 pub fn panic_fmt(fmt: fmt::Arguments, file_line_col: &(&'static str, u32, u32)) -> ! {
+    if cfg!(feature = "panic_immediate_abort") {
+        unsafe { super::intrinsics::abort() }
+    }
+
     // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call
     #[allow(improper_ctypes)] // PanicInfo contains a trait object which is not FFI safe
     extern "Rust" {
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index b3ba2968c9f..dc8baa112bb 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -67,7 +67,6 @@ use syntax::ast;
 use syntax::ast::*;
 use syntax::errors;
 use syntax::ext::hygiene::{Mark, SyntaxContext};
-use syntax::feature_gate::{emit_feature_err, GateIssue};
 use syntax::print::pprust;
 use syntax::ptr::P;
 use syntax::source_map::{self, respan, CompilerDesugaringKind, Spanned};
@@ -3628,7 +3627,6 @@ impl<'a> LoweringContext<'a> {
                     ParamMode::Optional,
                     ImplTraitContext::disallowed(),
                 );
-                self.check_self_struct_ctor_feature(&qpath);
                 hir::PatKind::TupleStruct(
                     qpath,
                     pats.iter().map(|x| self.lower_pat(x)).collect(),
@@ -3643,7 +3641,6 @@ impl<'a> LoweringContext<'a> {
                     ParamMode::Optional,
                     ImplTraitContext::disallowed(),
                 );
-                self.check_self_struct_ctor_feature(&qpath);
                 hir::PatKind::Path(qpath)
             }
             PatKind::Struct(ref path, ref fields, etc) => {
@@ -4039,7 +4036,6 @@ impl<'a> LoweringContext<'a> {
                     ParamMode::Optional,
                     ImplTraitContext::disallowed(),
                 );
-                self.check_self_struct_ctor_feature(&qpath);
                 hir::ExprKind::Path(qpath)
             }
             ExprKind::Break(opt_label, ref opt_expr) => {
@@ -5102,18 +5098,6 @@ impl<'a> LoweringContext<'a> {
                                             ThinVec::new()));
         P(self.expr_call(e.span, from_err, hir_vec![e]))
     }
-
-    fn check_self_struct_ctor_feature(&self, qp: &hir::QPath) {
-        if let hir::QPath::Resolved(_, ref p) = qp {
-            if p.segments.len() == 1 &&
-               p.segments[0].ident.name == keywords::SelfType.name() &&
-               !self.sess.features_untracked().self_struct_ctor {
-                emit_feature_err(&self.sess.parse_sess, "self_struct_ctor",
-                                 p.span, GateIssue::Language,
-                                 "`Self` struct constructors are unstable");
-            }
-        }
-    }
 }
 
 fn body_ids(bodies: &BTreeMap<hir::BodyId, hir::Body>) -> Vec<hir::BodyId> {
diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs
index e69d32ad1de..9a0ceddcf1b 100644
--- a/src/librustc/hir/print.rs
+++ b/src/librustc/hir/print.rs
@@ -25,6 +25,7 @@ use hir;
 use hir::{PatKind, GenericBound, TraitBoundModifier, RangeEnd};
 use hir::{GenericParam, GenericParamKind, GenericArg};
 
+use std::borrow::Cow;
 use std::cell::Cell;
 use std::io::{self, Write, Read};
 use std::iter::Peekable;
@@ -209,7 +210,7 @@ pub fn to_string<F>(ann: &dyn PpAnn, f: F) -> String
     String::from_utf8(wr).unwrap()
 }
 
-pub fn visibility_qualified(vis: &hir::Visibility, w: &str) -> String {
+pub fn visibility_qualified<S: Into<Cow<'static, str>>>(vis: &hir::Visibility, w: S) -> String {
     to_string(NO_ANN, |s| {
         s.print_visibility(vis)?;
         s.s.word(w)
@@ -226,12 +227,13 @@ impl<'a> State<'a> {
         self.s.word(" ")
     }
 
-    pub fn word_nbsp(&mut self, w: &str) -> io::Result<()> {
+    pub fn word_nbsp<S: Into<Cow<'static, str>>>(&mut self, w: S) -> io::Result<()> {
         self.s.word(w)?;
         self.nbsp()
     }
 
-    pub fn head(&mut self, w: &str) -> io::Result<()> {
+    pub fn head<S: Into<Cow<'static, str>>>(&mut self, w: S) -> io::Result<()> {
+        let w = w.into();
         // outer-box is consistent
         self.cbox(indent_unit)?;
         // head-box is inconsistent
@@ -303,7 +305,7 @@ impl<'a> State<'a> {
     pub fn synth_comment(&mut self, text: String) -> io::Result<()> {
         self.s.word("/*")?;
         self.s.space()?;
-        self.s.word(&text[..])?;
+        self.s.word(text)?;
         self.s.space()?;
         self.s.word("*/")
     }
@@ -468,7 +470,7 @@ impl<'a> State<'a> {
                 self.end() // end the outer fn box
             }
             hir::ForeignItemKind::Static(ref t, m) => {
-                self.head(&visibility_qualified(&item.vis, "static"))?;
+                self.head(visibility_qualified(&item.vis, "static"))?;
                 if m {
                     self.word_space("mut")?;
                 }
@@ -480,7 +482,7 @@ impl<'a> State<'a> {
                 self.end() // end the outer cbox
             }
             hir::ForeignItemKind::Type => {
-                self.head(&visibility_qualified(&item.vis, "type"))?;
+                self.head(visibility_qualified(&item.vis, "type"))?;
                 self.print_name(item.name)?;
                 self.s.word(";")?;
                 self.end()?; // end the head-ibox
@@ -495,7 +497,7 @@ impl<'a> State<'a> {
                               default: Option<hir::BodyId>,
                               vis: &hir::Visibility)
                               -> io::Result<()> {
-        self.s.word(&visibility_qualified(vis, ""))?;
+        self.s.word(visibility_qualified(vis, ""))?;
         self.word_space("const")?;
         self.print_ident(ident)?;
         self.word_space(":")?;
@@ -534,7 +536,7 @@ impl<'a> State<'a> {
         self.ann.pre(self, AnnNode::Item(item))?;
         match item.node {
             hir::ItemKind::ExternCrate(orig_name) => {
-                self.head(&visibility_qualified(&item.vis, "extern crate"))?;
+                self.head(visibility_qualified(&item.vis, "extern crate"))?;
                 if let Some(orig_name) = orig_name {
                     self.print_name(orig_name)?;
                     self.s.space()?;
@@ -547,7 +549,7 @@ impl<'a> State<'a> {
                 self.end()?; // end outer head-block
             }
             hir::ItemKind::Use(ref path, kind) => {
-                self.head(&visibility_qualified(&item.vis, "use"))?;
+                self.head(visibility_qualified(&item.vis, "use"))?;
                 self.print_path(path, false)?;
 
                 match kind {
@@ -566,7 +568,7 @@ impl<'a> State<'a> {
                 self.end()?; // end outer head-block
             }
             hir::ItemKind::Static(ref ty, m, expr) => {
-                self.head(&visibility_qualified(&item.vis, "static"))?;
+                self.head(visibility_qualified(&item.vis, "static"))?;
                 if m == hir::MutMutable {
                     self.word_space("mut")?;
                 }
@@ -582,7 +584,7 @@ impl<'a> State<'a> {
                 self.end()?; // end the outer cbox
             }
             hir::ItemKind::Const(ref ty, expr) => {
-                self.head(&visibility_qualified(&item.vis, "const"))?;
+                self.head(visibility_qualified(&item.vis, "const"))?;
                 self.print_name(item.name)?;
                 self.word_space(":")?;
                 self.print_type(&ty)?;
@@ -609,7 +611,7 @@ impl<'a> State<'a> {
                 self.ann.nested(self, Nested::Body(body))?;
             }
             hir::ItemKind::Mod(ref _mod) => {
-                self.head(&visibility_qualified(&item.vis, "mod"))?;
+                self.head(visibility_qualified(&item.vis, "mod"))?;
                 self.print_name(item.name)?;
                 self.nbsp()?;
                 self.bopen()?;
@@ -618,18 +620,18 @@ impl<'a> State<'a> {
             }
             hir::ItemKind::ForeignMod(ref nmod) => {
                 self.head("extern")?;
-                self.word_nbsp(&nmod.abi.to_string())?;
+                self.word_nbsp(nmod.abi.to_string())?;
                 self.bopen()?;
                 self.print_foreign_mod(nmod, &item.attrs)?;
                 self.bclose(item.span)?;
             }
             hir::ItemKind::GlobalAsm(ref ga) => {
-                self.head(&visibility_qualified(&item.vis, "global asm"))?;
-                self.s.word(&ga.asm.as_str())?;
+                self.head(visibility_qualified(&item.vis, "global asm"))?;
+                self.s.word(ga.asm.as_str().get())?;
                 self.end()?
             }
             hir::ItemKind::Ty(ref ty, ref generics) => {
-                self.head(&visibility_qualified(&item.vis, "type"))?;
+                self.head(visibility_qualified(&item.vis, "type"))?;
                 self.print_name(item.name)?;
                 self.print_generic_params(&generics.params)?;
                 self.end()?; // end the inner ibox
@@ -642,7 +644,7 @@ impl<'a> State<'a> {
                 self.end()?; // end the outer ibox
             }
             hir::ItemKind::Existential(ref exist) => {
-                self.head(&visibility_qualified(&item.vis, "existential type"))?;
+                self.head(visibility_qualified(&item.vis, "existential type"))?;
                 self.print_name(item.name)?;
                 self.print_generic_params(&exist.generics.params)?;
                 self.end()?; // end the inner ibox
@@ -668,11 +670,11 @@ impl<'a> State<'a> {
                 self.print_enum_def(enum_definition, params, item.name, item.span, &item.vis)?;
             }
             hir::ItemKind::Struct(ref struct_def, ref generics) => {
-                self.head(&visibility_qualified(&item.vis, "struct"))?;
+                self.head(visibility_qualified(&item.vis, "struct"))?;
                 self.print_struct(struct_def, generics, item.name, item.span, true)?;
             }
             hir::ItemKind::Union(ref struct_def, ref generics) => {
-                self.head(&visibility_qualified(&item.vis, "union"))?;
+                self.head(visibility_qualified(&item.vis, "union"))?;
                 self.print_struct(struct_def, generics, item.name, item.span, true)?;
             }
             hir::ItemKind::Impl(unsafety,
@@ -795,7 +797,7 @@ impl<'a> State<'a> {
                           span: syntax_pos::Span,
                           visibility: &hir::Visibility)
                           -> io::Result<()> {
-        self.head(&visibility_qualified(visibility, "enum"))?;
+        self.head(visibility_qualified(visibility, "enum"))?;
         self.print_name(name)?;
         self.print_generic_params(&generics.params)?;
         self.print_where_clause(&generics.where_clause)?;
@@ -1587,14 +1589,14 @@ impl<'a> State<'a> {
     }
 
     pub fn print_usize(&mut self, i: usize) -> io::Result<()> {
-        self.s.word(&i.to_string())
+        self.s.word(i.to_string())
     }
 
     pub fn print_ident(&mut self, ident: ast::Ident) -> io::Result<()> {
         if ident.is_raw_guess() {
-            self.s.word(&format!("r#{}", ident.name))?;
+            self.s.word(format!("r#{}", ident.name))?;
         } else {
-            self.s.word(&ident.as_str())?;
+            self.s.word(ident.as_str().get())?;
         }
         self.ann.post(self, AnnNode::Name(&ident.name))
     }
@@ -2010,7 +2012,7 @@ impl<'a> State<'a> {
         self.commasep(Inconsistent, &decl.inputs, |s, ty| {
             s.ibox(indent_unit)?;
             if let Some(arg_name) = arg_names.get(i) {
-                s.s.word(&arg_name.as_str())?;
+                s.s.word(arg_name.as_str().get())?;
                 s.s.word(":")?;
                 s.s.space()?;
             } else if let Some(body_id) = body_id {
@@ -2073,7 +2075,8 @@ impl<'a> State<'a> {
         }
     }
 
-    pub fn print_bounds(&mut self, prefix: &str, bounds: &[hir::GenericBound]) -> io::Result<()> {
+    pub fn print_bounds(&mut self, prefix: &'static str, bounds: &[hir::GenericBound])
+                        -> io::Result<()> {
         if !bounds.is_empty() {
             self.s.word(prefix)?;
             let mut first = true;
@@ -2322,7 +2325,7 @@ impl<'a> State<'a> {
             Some(Abi::Rust) => Ok(()),
             Some(abi) => {
                 self.word_nbsp("extern")?;
-                self.word_nbsp(&abi.to_string())
+                self.word_nbsp(abi.to_string())
             }
             None => Ok(()),
         }
@@ -2332,7 +2335,7 @@ impl<'a> State<'a> {
         match opt_abi {
             Some(abi) => {
                 self.word_nbsp("extern")?;
-                self.word_nbsp(&abi.to_string())
+                self.word_nbsp(abi.to_string())
             }
             None => Ok(()),
         }
@@ -2342,7 +2345,7 @@ impl<'a> State<'a> {
                                 header: hir::FnHeader,
                                 vis: &hir::Visibility)
                                 -> io::Result<()> {
-        self.s.word(&visibility_qualified(vis, ""))?;
+        self.s.word(visibility_qualified(vis, ""))?;
 
         match header.constness {
             hir::Constness::NotConst => {}
@@ -2358,7 +2361,7 @@ impl<'a> State<'a> {
 
         if header.abi != Abi::Rust {
             self.word_nbsp("extern")?;
-            self.word_nbsp(&header.abi.to_string())?;
+            self.word_nbsp(header.abi.to_string())?;
         }
 
         self.s.word("fn")
diff --git a/src/librustc/infer/at.rs b/src/librustc/infer/at.rs
index 0e4c94aaaf3..70e922c6676 100644
--- a/src/librustc/infer/at.rs
+++ b/src/librustc/infer/at.rs
@@ -52,6 +52,7 @@ pub struct Trace<'a, 'gcx: 'tcx, 'tcx: 'a> {
 }
 
 impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
+    #[inline]
     pub fn at(&'a self,
               cause: &'a ObligationCause<'tcx>,
               param_env: ty::ParamEnv<'tcx>)
diff --git a/src/librustc/infer/nll_relate/mod.rs b/src/librustc/infer/nll_relate/mod.rs
index 9bdbf77fee0..972ba16f7e2 100644
--- a/src/librustc/infer/nll_relate/mod.rs
+++ b/src/librustc/infer/nll_relate/mod.rs
@@ -11,30 +11,41 @@
 //! This code is kind of an alternate way of doing subtyping,
 //! supertyping, and type equating, distinct from the `combine.rs`
 //! code but very similar in its effect and design. Eventually the two
-//! ought to be merged. This code is intended for use in NLL.
+//! ought to be merged. This code is intended for use in NLL and chalk.
 //!
 //! Here are the key differences:
 //!
-//! - This code generally assumes that there are no unbound type
-//!   inferences variables, because at NLL
-//!   time types are fully inferred up-to regions.
-//!   - Actually, to support user-given type annotations like
-//!     `Vec<_>`, we do have some measure of support for type
-//!     inference variables, but we impose some simplifying
-//!     assumptions on them that would not be suitable for the infer
-//!     code more generally. This could be fixed.
+//! - This code may choose to bypass some checks (e.g. the occurs check)
+//!   in the case where we know that there are no unbound type inference
+//!   variables. This is the case for NLL, because at NLL time types are fully
+//!   inferred up-to regions.
 //! - This code uses "universes" to handle higher-ranked regions and
 //!   not the leak-check. This is "more correct" than what rustc does
 //!   and we are generally migrating in this direction, but NLL had to
 //!   get there first.
+//!
+//! Also, this code assumes that there are no bound types at all, not even
+//! free ones. This is ok because:
+//! - we are not relating anything quantified over some type variable
+//! - we will have instantiated all the bound type vars already (the one
+//!   thing we relate in chalk are basically domain goals and their
+//!   constituents)
 
 use crate::infer::InferCtxt;
 use crate::ty::fold::{TypeFoldable, TypeVisitor};
 use crate::ty::relate::{self, Relate, RelateResult, TypeRelation};
 use crate::ty::subst::Kind;
 use crate::ty::{self, Ty, TyCtxt};
+use crate::ty::error::TypeError;
+use crate::traits::DomainGoal;
 use rustc_data_structures::fx::FxHashMap;
 
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub enum NormalizationStrategy {
+    Lazy,
+    Eager,
+}
+
 pub struct TypeRelating<'me, 'gcx: 'tcx, 'tcx: 'me, D>
 where
     D: TypeRelatingDelegate<'tcx>,
@@ -75,6 +86,10 @@ pub trait TypeRelatingDelegate<'tcx> {
     /// delegate.
     fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>);
 
+    /// Push a domain goal that will need to be proved for the two types to
+    /// be related. Used for lazy normalization.
+    fn push_domain_goal(&mut self, domain_goal: DomainGoal<'tcx>);
+
     /// Creates a new universe index. Used when instantiating placeholders.
     fn create_next_universe(&mut self) -> ty::UniverseIndex;
 
@@ -105,6 +120,13 @@ pub trait TypeRelatingDelegate<'tcx> {
     /// relate `Foo<'?0>` with `Foo<'a>` (and probably add an outlives
     /// relation stating that `'?0: 'a`).
     fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>;
+
+    /// Define the normalization strategy to use, eager or lazy.
+    fn normalization() -> NormalizationStrategy;
+
+    /// Enable some optimizations if we do not expect inference variables
+    /// in the RHS of the relation.
+    fn forbid_inference_vars() -> bool;
 }
 
 #[derive(Clone, Debug)]
@@ -242,15 +264,79 @@ where
         self.delegate.push_outlives(sup, sub);
     }
 
-    /// When we encounter a canonical variable `var` in the output,
-    /// equate it with `kind`. If the variable has been previously
-    /// equated, then equate it again.
-    fn relate_var(&mut self, var_ty: Ty<'tcx>, value_ty: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
-        debug!("equate_var(var_ty={:?}, value_ty={:?})", var_ty, value_ty);
+    /// Relate a projection type and some value type lazily. This will always
+    /// succeed, but we push an additional `ProjectionEq` goal depending
+    /// on the value type:
+    /// - if the value type is any type `T` which is not a projection, we push
+    ///   `ProjectionEq(projection = T)`.
+    /// - if the value type is another projection `other_projection`, we create
+    ///   a new inference variable `?U` and push the two goals
+    ///   `ProjectionEq(projection = ?U)`, `ProjectionEq(other_projection = ?U)`.
+    fn relate_projection_ty(
+        &mut self,
+        projection_ty: ty::ProjectionTy<'tcx>,
+        value_ty: ty::Ty<'tcx>
+    ) -> Ty<'tcx> {
+        use crate::infer::type_variable::TypeVariableOrigin;
+        use crate::traits::WhereClause;
+        use syntax_pos::DUMMY_SP;
+
+        match value_ty.sty {
+            ty::Projection(other_projection_ty) => {
+                let var = self.infcx.next_ty_var(TypeVariableOrigin::MiscVariable(DUMMY_SP));
+                self.relate_projection_ty(projection_ty, var);
+                self.relate_projection_ty(other_projection_ty, var);
+                var
+            }
+
+            _ => {
+                let projection = ty::ProjectionPredicate {
+                    projection_ty,
+                    ty: value_ty,
+                };
+                self.delegate.push_domain_goal(
+                    DomainGoal::Holds(WhereClause::ProjectionEq(projection))
+                );
+                value_ty
+            }
+        }
+    }
+
+    /// Relate a type inference variable with a value type.
+    fn relate_ty_var(
+        &mut self,
+        vid: ty::TyVid,
+        value_ty: Ty<'tcx>
+    ) -> RelateResult<'tcx, Ty<'tcx>> {
+        debug!("relate_ty_var(vid={:?}, value_ty={:?})", vid, value_ty);
+
+        match value_ty.sty {
+            ty::Infer(ty::TyVar(value_vid)) => {
+                // Two type variables: just equate them.
+                self.infcx.type_variables.borrow_mut().equate(vid, value_vid);
+                return Ok(value_ty);
+            }
+
+            ty::Projection(projection_ty)
+                if D::normalization() == NormalizationStrategy::Lazy =>
+            {
+                return Ok(self.relate_projection_ty(projection_ty, self.infcx.tcx.mk_var(vid)));
+            }
+
+            _ => (),
+        }
+
+        let generalized_ty = self.generalize_value(value_ty, vid)?;
+        debug!("relate_ty_var: generalized_ty = {:?}", generalized_ty);
+
+        if D::forbid_inference_vars() {
+            // In NLL, we don't have type inference variables
+            // floating around, so we can do this rather imprecise
+            // variant of the occurs-check.
+            assert!(!generalized_ty.has_infer_types());
+        }
 
-        let generalized_ty = self.generalize_value(value_ty);
-        self.infcx
-            .force_instantiate_unchecked(var_ty, generalized_ty);
+        self.infcx.type_variables.borrow_mut().instantiate(vid, generalized_ty);
 
         // The generalized values we extract from `canonical_var_values` have
         // been fully instantiated and hence the set of scopes we have
@@ -264,22 +350,27 @@ where
         // Restore the old scopes now.
         self.a_scopes = old_a_scopes;
 
-        debug!("equate_var: complete, result = {:?}", result);
+        debug!("relate_ty_var: complete, result = {:?}", result);
         result
     }
 
-    fn generalize_value<T: Relate<'tcx>>(&mut self, value: T) -> T {
-        TypeGeneralizer {
-            tcx: self.infcx.tcx,
+    fn generalize_value<T: Relate<'tcx>>(
+        &mut self,
+        value: T,
+        for_vid: ty::TyVid
+    ) -> RelateResult<'tcx, T> {
+        let universe = self.infcx.probe_ty_var(for_vid).unwrap_err();
+
+        let mut generalizer = TypeGeneralizer {
+            infcx: self.infcx,
             delegate: &mut self.delegate,
             first_free_index: ty::INNERMOST,
             ambient_variance: self.ambient_variance,
+            for_vid_sub_root: self.infcx.type_variables.borrow_mut().sub_root_var(for_vid),
+            universe,
+        };
 
-            // These always correspond to an `_` or `'_` written by
-            // user, and those are always in the root universe.
-            universe: ty::UniverseIndex::ROOT,
-        }.relate(&value, &value)
-            .unwrap()
+        generalizer.relate(&value, &value)
     }
 }
 
@@ -327,11 +418,35 @@ where
         Ok(r)
     }
 
-    fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
+    fn tys(&mut self, a: Ty<'tcx>, mut b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
         let a = self.infcx.shallow_resolve(a);
-        match a.sty {
-            ty::Infer(ty::TyVar(_)) | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) => {
-                self.relate_var(a.into(), b.into())
+
+        if !D::forbid_inference_vars() {
+            b = self.infcx.shallow_resolve(b);
+        }
+
+        match (&a.sty, &b.sty) {
+            (_, &ty::Infer(ty::TyVar(vid))) => {
+                if D::forbid_inference_vars() {
+                    // Forbid inference variables in the RHS.
+                    bug!("unexpected inference var {:?}", b)
+                } else {
+                    self.relate_ty_var(vid, a)
+                }
+            }
+
+            (&ty::Infer(ty::TyVar(vid)), _) => self.relate_ty_var(vid, b),
+
+            (&ty::Projection(projection_ty), _)
+                if D::normalization() == NormalizationStrategy::Lazy =>
+            {
+                Ok(self.relate_projection_ty(projection_ty, b))
+            }
+
+            (_, &ty::Projection(projection_ty))
+                if D::normalization() == NormalizationStrategy::Lazy =>
+            {
+                Ok(self.relate_projection_ty(projection_ty, a))
             }
 
             _ => {
@@ -340,7 +455,8 @@ where
                     a, b, self.ambient_variance
                 );
 
-                relate::super_relate_tys(self, a, b)
+                // Will also handle unification of `IntVar` and `FloatVar`.
+                self.infcx.super_combine_tys(self, a, b)
             }
         }
     }
@@ -551,7 +667,7 @@ struct TypeGeneralizer<'me, 'gcx: 'tcx, 'tcx: 'me, D>
 where
     D: TypeRelatingDelegate<'tcx> + 'me,
 {
-    tcx: TyCtxt<'me, 'gcx, 'tcx>,
+    infcx: &'me InferCtxt<'me, 'gcx, 'tcx>,
 
     delegate: &'me mut D,
 
@@ -561,6 +677,14 @@ where
 
     first_free_index: ty::DebruijnIndex,
 
+    /// The vid of the type variable that is in the process of being
+    /// instantiated. If we find this within the value we are folding,
+    /// that means we would have created a cyclic value.
+    for_vid_sub_root: ty::TyVid,
+
+    /// The universe of the type variable that is in the process of being
+    /// instantiated. If we find anything that this universe cannot name,
+    /// we reject the relation.
     universe: ty::UniverseIndex,
 }
 
@@ -569,7 +693,7 @@ where
     D: TypeRelatingDelegate<'tcx>,
 {
     fn tcx(&self) -> TyCtxt<'me, 'gcx, 'tcx> {
-        self.tcx
+        self.infcx.tcx
     }
 
     fn tag(&self) -> &'static str {
@@ -609,17 +733,84 @@ where
     }
 
     fn tys(&mut self, a: Ty<'tcx>, _: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
+        use crate::infer::type_variable::TypeVariableValue;
+
         debug!("TypeGeneralizer::tys(a={:?})", a,);
 
         match a.sty {
-            ty::Infer(ty::TyVar(_)) | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) => {
+            ty::Infer(ty::TyVar(_)) | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_))
+                if D::forbid_inference_vars() =>
+            {
                 bug!(
                     "unexpected inference variable encountered in NLL generalization: {:?}",
                     a
                 );
             }
 
-            _ => relate::super_relate_tys(self, a, a),
+            ty::Infer(ty::TyVar(vid)) => {
+                let mut variables = self.infcx.type_variables.borrow_mut();
+                let vid = variables.root_var(vid);
+                let sub_vid = variables.sub_root_var(vid);
+                if sub_vid == self.for_vid_sub_root {
+                    // If sub-roots are equal, then `for_vid` and
+                    // `vid` are related via subtyping.
+                    debug!("TypeGeneralizer::tys: occurs check failed");
+                    return Err(TypeError::Mismatch);
+                } else {
+                    match variables.probe(vid) {
+                        TypeVariableValue::Known { value: u } => {
+                            drop(variables);
+                            self.relate(&u, &u)
+                        }
+                        TypeVariableValue::Unknown { universe: _universe } => {
+                            if self.ambient_variance == ty::Bivariant {
+                                // FIXME: we may need a WF predicate (related to #54105).
+                            }
+
+                            let origin = *variables.var_origin(vid);
+
+                            // Replacing with a new variable in the universe `self.universe`,
+                            // it will be unified later with the original type variable in
+                            // the universe `_universe`.
+                            let new_var_id = variables.new_var(self.universe, false, origin);
+
+                            let u = self.tcx().mk_var(new_var_id);
+                            debug!(
+                                "generalize: replacing original vid={:?} with new={:?}",
+                                vid,
+                                u
+                            );
+                            return Ok(u);
+                        }
+                    }
+                }
+            }
+
+            ty::Infer(ty::IntVar(_)) |
+            ty::Infer(ty::FloatVar(_)) => {
+                // No matter what mode we are in,
+                // integer/floating-point types must be equal to be
+                // relatable.
+                Ok(a)
+            }
+
+            ty::Placeholder(placeholder) => {
+                if self.universe.cannot_name(placeholder.universe) {
+                    debug!(
+                        "TypeGeneralizer::tys: root universe {:?} cannot name\
+                        placeholder in universe {:?}",
+                        self.universe,
+                        placeholder.universe
+                    );
+                    Err(TypeError::Mismatch)
+                } else {
+                    Ok(a)
+                }
+            }
+
+            _ => {
+                relate::super_relate_tys(self, a, a)
+            }
         }
     }
 
@@ -673,64 +864,3 @@ where
         Ok(ty::Binder::bind(result))
     }
 }
-
-impl InferCtxt<'_, '_, 'tcx> {
-    /// A hacky sort of method used by the NLL type-relating code:
-    ///
-    /// - `var` must be some unbound type variable.
-    /// - `value` must be a suitable type to use as its value.
-    ///
-    /// `var` will then be equated with `value`. Note that this
-    /// sidesteps a number of important checks, such as the "occurs
-    /// check" that prevents cyclic types, so it is important not to
-    /// use this method during regular type-check.
-    fn force_instantiate_unchecked(&self, var: Ty<'tcx>, value: Ty<'tcx>) {
-        match (&var.sty, &value.sty) {
-            (&ty::Infer(ty::TyVar(vid)), _) => {
-                let mut type_variables = self.type_variables.borrow_mut();
-
-                // In NLL, we don't have type inference variables
-                // floating around, so we can do this rather imprecise
-                // variant of the occurs-check.
-                assert!(!value.has_infer_types());
-
-                type_variables.instantiate(vid, value);
-            }
-
-            (&ty::Infer(ty::IntVar(vid)), &ty::Int(value)) => {
-                let mut int_unification_table = self.int_unification_table.borrow_mut();
-                int_unification_table
-                    .unify_var_value(vid, Some(ty::IntVarValue::IntType(value)))
-                    .unwrap_or_else(|_| {
-                        bug!("failed to unify int var `{:?}` with `{:?}`", vid, value);
-                    });
-            }
-
-            (&ty::Infer(ty::IntVar(vid)), &ty::Uint(value)) => {
-                let mut int_unification_table = self.int_unification_table.borrow_mut();
-                int_unification_table
-                    .unify_var_value(vid, Some(ty::IntVarValue::UintType(value)))
-                    .unwrap_or_else(|_| {
-                        bug!("failed to unify int var `{:?}` with `{:?}`", vid, value);
-                    });
-            }
-
-            (&ty::Infer(ty::FloatVar(vid)), &ty::Float(value)) => {
-                let mut float_unification_table = self.float_unification_table.borrow_mut();
-                float_unification_table
-                    .unify_var_value(vid, Some(ty::FloatVarValue(value)))
-                    .unwrap_or_else(|_| {
-                        bug!("failed to unify float var `{:?}` with `{:?}`", vid, value)
-                    });
-            }
-
-            _ => {
-                bug!(
-                    "force_instantiate_unchecked invoked with bad combination: var={:?} value={:?}",
-                    var,
-                    value,
-                );
-            }
-        }
-    }
-}
diff --git a/src/librustc/infer/resolve.rs b/src/librustc/infer/resolve.rs
index a0c310ac276..7a1ee85acd4 100644
--- a/src/librustc/infer/resolve.rs
+++ b/src/librustc/infer/resolve.rs
@@ -25,6 +25,7 @@ pub struct OpportunisticTypeResolver<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
 }
 
 impl<'a, 'gcx, 'tcx> OpportunisticTypeResolver<'a, 'gcx, 'tcx> {
+    #[inline]
     pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self {
         OpportunisticTypeResolver { infcx }
     }
diff --git a/src/librustc/middle/reachable.rs b/src/librustc/middle/reachable.rs
index 0009a517dd1..ab0094df0e2 100644
--- a/src/librustc/middle/reachable.rs
+++ b/src/librustc/middle/reachable.rs
@@ -117,8 +117,9 @@ impl<'a, 'tcx> Visitor<'tcx> for ReachableContext<'a, 'tcx> {
                 self.reachable_symbols.insert(node_id);
             }
             Some(def) => {
-                let def_id = def.def_id();
-                if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
+                if let Some((node_id, def_id)) = def.opt_def_id().and_then(|def_id| {
+                    self.tcx.hir.as_local_node_id(def_id).map(|node_id| (node_id, def_id))
+                }) {
                     if self.def_id_represents_local_inlined_item(def_id) {
                         self.worklist.push(node_id);
                     } else {
diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs
index 6ff450508d1..07054ee99af 100644
--- a/src/librustc/middle/resolve_lifetime.rs
+++ b/src/librustc/middle/resolve_lifetime.rs
@@ -1573,7 +1573,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
             .collect();
 
         // ensure that we issue lints in a repeatable order
-        def_ids.sort_by_key(|&def_id| self.tcx.def_path_hash(def_id));
+        def_ids.sort_by_cached_key(|&def_id| self.tcx.def_path_hash(def_id));
 
         for def_id in def_ids {
             debug!(
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index 3fd22793a08..480d4a8e48f 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -1286,8 +1286,6 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
         "print some performance-related statistics"),
     hir_stats: bool = (false, parse_bool, [UNTRACKED],
         "print some statistics about AST and HIR"),
-    mir_stats: bool = (false, parse_bool, [UNTRACKED],
-        "print some statistics about MIR"),
     always_encode_mir: bool = (false, parse_bool, [TRACKED],
         "encode MIR of all functions into the crate metadata"),
     osx_rpath_install_name: bool = (false, parse_bool, [TRACKED],
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index e582a902046..ab2fa68ab5f 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -1052,6 +1052,7 @@ impl<'tcx,O> Obligation<'tcx,O> {
 }
 
 impl<'tcx> ObligationCause<'tcx> {
+    #[inline]
     pub fn new(span: Span,
                body_id: ast::NodeId,
                code: ObligationCauseCode<'tcx>)
diff --git a/src/librustc/traits/object_safety.rs b/src/librustc/traits/object_safety.rs
index c79fa386123..2909daf22b3 100644
--- a/src/librustc/traits/object_safety.rs
+++ b/src/librustc/traits/object_safety.rs
@@ -409,7 +409,7 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
             .collect::<Vec<_>>();
 
         // existential predicates need to be in a specific order
-        associated_types.sort_by_key(|item| self.def_path_hash(item.def_id));
+        associated_types.sort_by_cached_key(|item| self.def_path_hash(item.def_id));
 
         let projection_predicates = associated_types.into_iter().map(|item| {
             ty::ExistentialPredicate::Projection(ty::ExistentialProjection {
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 79a4638d6f7..42a4de1682c 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -53,6 +53,7 @@ use ty::CanonicalTy;
 use ty::CanonicalPolyFnSig;
 use util::nodemap::{DefIdMap, DefIdSet, ItemLocalMap};
 use util::nodemap::{FxHashMap, FxHashSet};
+use rustc_data_structures::interner::HashInterner;
 use smallvec::SmallVec;
 use rustc_data_structures::stable_hasher::{HashStable, hash_stable_hashmap,
                                            StableHasher, StableHasherResult,
@@ -113,7 +114,7 @@ pub struct GlobalArenas<'tcx> {
     const_allocs: TypedArena<interpret::Allocation>,
 }
 
-type InternedSet<'tcx, T> = Lock<FxHashSet<Interned<'tcx, T>>>;
+type InternedSet<'tcx, T> = Lock<FxHashMap<Interned<'tcx, T>, ()>>;
 
 pub struct CtxtInterners<'tcx> {
     /// The arena that types, regions, etc are allocated from
@@ -155,6 +156,7 @@ impl<'gcx: 'tcx, 'tcx> CtxtInterners<'tcx> {
     }
 
     /// Intern a type
+    #[inline(never)]
     fn intern_ty(
         local: &CtxtInterners<'tcx>,
         global: &CtxtInterners<'gcx>,
@@ -166,56 +168,45 @@ impl<'gcx: 'tcx, 'tcx> CtxtInterners<'tcx> {
         // determine that all contents are in the global tcx.
         // See comments on Lift for why we can't use that.
         if flags.flags.intersects(ty::TypeFlags::KEEP_IN_LOCAL_TCX) {
-            let mut interner = local.type_.borrow_mut();
-            if let Some(&Interned(ty)) = interner.get(&st) {
-                return ty;
-            }
-
-            let ty_struct = TyS {
-                sty: st,
-                flags: flags.flags,
-                outer_exclusive_binder: flags.outer_exclusive_binder,
-            };
+            local.type_.borrow_mut().intern(st, |st| {
+                let ty_struct = TyS {
+                    sty: st,
+                    flags: flags.flags,
+                    outer_exclusive_binder: flags.outer_exclusive_binder,
+                };
 
-            // Make sure we don't end up with inference
-            // types/regions in the global interner
-            if local as *const _ as usize == global as *const _ as usize {
-                bug!("Attempted to intern `{:?}` which contains \
-                      inference types/regions in the global type context",
-                     &ty_struct);
-            }
+                // Make sure we don't end up with inference
+                // types/regions in the global interner
+                if local as *const _ as usize == global as *const _ as usize {
+                    bug!("Attempted to intern `{:?}` which contains \
+                        inference types/regions in the global type context",
+                        &ty_struct);
+                }
 
-            // Don't be &mut TyS.
-            let ty: Ty<'tcx> = local.arena.alloc(ty_struct);
-            interner.insert(Interned(ty));
-            ty
+                Interned(local.arena.alloc(ty_struct))
+            }).0
         } else {
-            let mut interner = global.type_.borrow_mut();
-            if let Some(&Interned(ty)) = interner.get(&st) {
-                return ty;
-            }
-
-            let ty_struct = TyS {
-                sty: st,
-                flags: flags.flags,
-                outer_exclusive_binder: flags.outer_exclusive_binder,
-            };
+            global.type_.borrow_mut().intern(st, |st| {
+                let ty_struct = TyS {
+                    sty: st,
+                    flags: flags.flags,
+                    outer_exclusive_binder: flags.outer_exclusive_binder,
+                };
 
-            // This is safe because all the types the ty_struct can point to
-            // already is in the global arena
-            let ty_struct: TyS<'gcx> = unsafe {
-                mem::transmute(ty_struct)
-            };
+                // This is safe because all the types the ty_struct can point to
+                // already is in the global arena
+                let ty_struct: TyS<'gcx> = unsafe {
+                    mem::transmute(ty_struct)
+                };
 
-            // Don't be &mut TyS.
-            let ty: Ty<'gcx> = global.arena.alloc(ty_struct);
-            interner.insert(Interned(ty));
-            ty
+                Interned(global.arena.alloc(ty_struct))
+            }).0
         }
     }
 }
 
 pub struct CommonTypes<'tcx> {
+    pub unit: Ty<'tcx>,
     pub bool: Ty<'tcx>,
     pub char: Ty<'tcx>,
     pub isize: Ty<'tcx>,
@@ -825,14 +816,13 @@ impl<'tcx> CommonTypes<'tcx> {
     fn new(interners: &CtxtInterners<'tcx>) -> CommonTypes<'tcx> {
         let mk = |sty| CtxtInterners::intern_ty(interners, interners, sty);
         let mk_region = |r| {
-            if let Some(r) = interners.region.borrow().get(&r) {
-                return r.0;
-            }
-            let r = interners.arena.alloc(r);
-            interners.region.borrow_mut().insert(Interned(r));
-            &*r
+            interners.region.borrow_mut().intern(r, |r| {
+                Interned(interners.arena.alloc(r))
+            }).0
         };
+
         CommonTypes {
+            unit: mk(Tuple(List::empty())),
             bool: mk(Bool),
             char: mk(Char),
             never: mk(Never),
@@ -885,6 +875,7 @@ pub struct TyCtxt<'a, 'gcx: 'tcx, 'tcx: 'a> {
 
 impl<'a, 'gcx, 'tcx> Deref for TyCtxt<'a, 'gcx, 'tcx> {
     type Target = &'a GlobalCtxt<'gcx>;
+    #[inline(always)]
     fn deref(&self) -> &Self::Target {
         &self.gcx
     }
@@ -950,14 +941,14 @@ pub struct GlobalCtxt<'tcx> {
     /// Data layout specification for the current target.
     pub data_layout: TargetDataLayout,
 
-    stability_interner: Lock<FxHashSet<&'tcx attr::Stability>>,
+    stability_interner: Lock<FxHashMap<&'tcx attr::Stability, ()>>,
 
     /// Stores the value of constants (and deduplicates the actual memory)
-    allocation_interner: Lock<FxHashSet<&'tcx Allocation>>,
+    allocation_interner: Lock<FxHashMap<&'tcx Allocation, ()>>,
 
     pub alloc_map: Lock<interpret::AllocMap<'tcx, &'tcx Allocation>>,
 
-    layout_interner: Lock<FxHashSet<&'tcx LayoutDetails>>,
+    layout_interner: Lock<FxHashMap<&'tcx LayoutDetails, ()>>,
 
     /// A general purpose channel to throw data out the back towards LLVM worker
     /// threads.
@@ -1040,16 +1031,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         self,
         alloc: Allocation,
     ) -> &'gcx Allocation {
-        let allocs = &mut self.allocation_interner.borrow_mut();
-        if let Some(alloc) = allocs.get(&alloc) {
-            return alloc;
-        }
-
-        let interned = self.global_arenas.const_allocs.alloc(alloc);
-        if let Some(prev) = allocs.replace(interned) { // insert into interner
-            bug!("Tried to overwrite interned Allocation: {:#?}", prev)
-        }
-        interned
+        self.allocation_interner.borrow_mut().intern(alloc, |alloc| {
+            self.global_arenas.const_allocs.alloc(alloc)
+        })
     }
 
     /// Allocates a byte or string literal for `mir::interpret`, read-only
@@ -1061,29 +1045,15 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
     }
 
     pub fn intern_stability(self, stab: attr::Stability) -> &'gcx attr::Stability {
-        let mut stability_interner = self.stability_interner.borrow_mut();
-        if let Some(st) = stability_interner.get(&stab) {
-            return st;
-        }
-
-        let interned = self.global_interners.arena.alloc(stab);
-        if let Some(prev) = stability_interner.replace(interned) {
-            bug!("Tried to overwrite interned Stability: {:?}", prev)
-        }
-        interned
+        self.stability_interner.borrow_mut().intern(stab, |stab| {
+            self.global_interners.arena.alloc(stab)
+        })
     }
 
     pub fn intern_layout(self, layout: LayoutDetails) -> &'gcx LayoutDetails {
-        let mut layout_interner = self.layout_interner.borrow_mut();
-        if let Some(layout) = layout_interner.get(&layout) {
-            return layout;
-        }
-
-        let interned = self.global_arenas.layout.alloc(layout);
-        if let Some(prev) = layout_interner.replace(interned) {
-            bug!("Tried to overwrite interned Layout: {:?}", prev)
-        }
-        interned
+        self.layout_interner.borrow_mut().intern(layout, |layout| {
+            self.global_arenas.layout.alloc(layout)
+        })
     }
 
     /// Returns a range of the start/end indices specified with the
@@ -2193,7 +2163,7 @@ macro_rules! sty_debug_print {
                 };
                 $(let mut $variant = total;)*
 
-                for &Interned(t) in tcx.interners.type_.borrow().iter() {
+                for &Interned(t) in tcx.interners.type_.borrow().keys() {
                     let variant = match t.sty {
                         ty::Bool | ty::Char | ty::Int(..) | ty::Uint(..) |
                             ty::Float(..) | ty::Str | ty::Never => continue,
@@ -2252,6 +2222,13 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
 /// An entry in an interner.
 struct Interned<'tcx, T: 'tcx+?Sized>(&'tcx T);
 
+impl<'tcx, T: 'tcx+?Sized> Clone for Interned<'tcx, T> {
+    fn clone(&self) -> Self {
+        Interned(self.0)
+    }
+}
+impl<'tcx, T: 'tcx+?Sized> Copy for Interned<'tcx, T> {}
+
 // NB: An Interned<Ty> compares and hashes as a sty.
 impl<'tcx> PartialEq for Interned<'tcx, TyS<'tcx>> {
     fn eq(&self, other: &Interned<'tcx, TyS<'tcx>>) -> bool {
@@ -2372,37 +2349,28 @@ macro_rules! intern_method {
                 // determine that all contents are in the global tcx.
                 // See comments on Lift for why we can't use that.
                 if ($keep_in_local_tcx)(&v) {
-                    let mut interner = self.interners.$name.borrow_mut();
-                    if let Some(&Interned(v)) = interner.get(key) {
-                        return v;
-                    }
-
-                    // Make sure we don't end up with inference
-                    // types/regions in the global tcx.
-                    if self.is_global() {
-                        bug!("Attempted to intern `{:?}` which contains \
-                              inference types/regions in the global type context",
-                             v);
-                    }
-
-                    let i = $alloc_method(&self.interners.arena, v);
-                    interner.insert(Interned(i));
-                    i
+                    self.interners.$name.borrow_mut().intern_ref(key, || {
+                        // Make sure we don't end up with inference
+                        // types/regions in the global tcx.
+                        if self.is_global() {
+                            bug!("Attempted to intern `{:?}` which contains \
+                                inference types/regions in the global type context",
+                                v);
+                        }
+
+                        Interned($alloc_method(&self.interners.arena, v))
+                    }).0
                 } else {
-                    let mut interner = self.global_interners.$name.borrow_mut();
-                    if let Some(&Interned(v)) = interner.get(key) {
-                        return v;
-                    }
-
-                    // This transmutes $alloc<'tcx> to $alloc<'gcx>
-                    let v = unsafe {
-                        mem::transmute(v)
-                    };
-                    let i: &$lt_tcx $ty = $alloc_method(&self.global_interners.arena, v);
-                    // Cast to 'gcx
-                    let i = unsafe { mem::transmute(i) };
-                    interner.insert(Interned(i));
-                    i
+                    self.global_interners.$name.borrow_mut().intern_ref(key, || {
+                        // This transmutes $alloc<'tcx> to $alloc<'gcx>
+                        let v = unsafe {
+                            mem::transmute(v)
+                        };
+                        let i: &$lt_tcx $ty = $alloc_method(&self.global_interners.arena, v);
+                        // Cast to 'gcx
+                        let i = unsafe { mem::transmute(i) };
+                        Interned(i)
+                    }).0
                 }
             }
         }
@@ -2515,6 +2483,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         self.mk_fn_ptr(converted_sig)
     }
 
+    #[inline]
     pub fn mk_ty(&self, st: TyKind<'tcx>) -> Ty<'tcx> {
         CtxtInterners::intern_ty(&self.interners, &self.global_interners, st)
     }
@@ -2548,19 +2517,23 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
+    #[inline]
     pub fn mk_str(self) -> Ty<'tcx> {
         self.mk_ty(Str)
     }
 
+    #[inline]
     pub fn mk_static_str(self) -> Ty<'tcx> {
         self.mk_imm_ref(self.types.re_static, self.mk_str())
     }
 
+    #[inline]
     pub fn mk_adt(self, def: &'tcx AdtDef, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> {
         // take a copy of substs so that we own the vectors inside
         self.mk_ty(Adt(def, substs))
     }
 
+    #[inline]
     pub fn mk_foreign(self, def_id: DefId) -> Ty<'tcx> {
         self.mk_ty(Foreign(def_id))
     }
@@ -2584,42 +2557,52 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         self.mk_ty(Adt(adt_def, substs))
     }
 
+    #[inline]
     pub fn mk_ptr(self, tm: TypeAndMut<'tcx>) -> Ty<'tcx> {
         self.mk_ty(RawPtr(tm))
     }
 
+    #[inline]
     pub fn mk_ref(self, r: Region<'tcx>, tm: TypeAndMut<'tcx>) -> Ty<'tcx> {
         self.mk_ty(Ref(r, tm.ty, tm.mutbl))
     }
 
+    #[inline]
     pub fn mk_mut_ref(self, r: Region<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
         self.mk_ref(r, TypeAndMut {ty: ty, mutbl: hir::MutMutable})
     }
 
+    #[inline]
     pub fn mk_imm_ref(self, r: Region<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
         self.mk_ref(r, TypeAndMut {ty: ty, mutbl: hir::MutImmutable})
     }
 
+    #[inline]
     pub fn mk_mut_ptr(self, ty: Ty<'tcx>) -> Ty<'tcx> {
         self.mk_ptr(TypeAndMut {ty: ty, mutbl: hir::MutMutable})
     }
 
+    #[inline]
     pub fn mk_imm_ptr(self, ty: Ty<'tcx>) -> Ty<'tcx> {
         self.mk_ptr(TypeAndMut {ty: ty, mutbl: hir::MutImmutable})
     }
 
+    #[inline]
     pub fn mk_nil_ptr(self) -> Ty<'tcx> {
         self.mk_imm_ptr(self.mk_unit())
     }
 
+    #[inline]
     pub fn mk_array(self, ty: Ty<'tcx>, n: u64) -> Ty<'tcx> {
         self.mk_ty(Array(ty, ty::Const::from_usize(self, n)))
     }
 
+    #[inline]
     pub fn mk_slice(self, ty: Ty<'tcx>) -> Ty<'tcx> {
         self.mk_ty(Slice(ty))
     }
 
+    #[inline]
     pub fn intern_tup(self, ts: &[Ty<'tcx>]) -> Ty<'tcx> {
         self.mk_ty(Tuple(self.intern_type_list(ts)))
     }
@@ -2628,10 +2611,12 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         iter.intern_with(|ts| self.mk_ty(Tuple(self.intern_type_list(ts))))
     }
 
+    #[inline]
     pub fn mk_unit(self) -> Ty<'tcx> {
-        self.intern_tup(&[])
+        self.types.unit
     }
 
+    #[inline]
     pub fn mk_diverging_default(self) -> Ty<'tcx> {
         if self.features().never_type {
             self.types.never
@@ -2640,19 +2625,23 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
+    #[inline]
     pub fn mk_bool(self) -> Ty<'tcx> {
         self.mk_ty(Bool)
     }
 
+    #[inline]
     pub fn mk_fn_def(self, def_id: DefId,
                      substs: &'tcx Substs<'tcx>) -> Ty<'tcx> {
         self.mk_ty(FnDef(def_id, substs))
     }
 
+    #[inline]
     pub fn mk_fn_ptr(self, fty: PolyFnSig<'tcx>) -> Ty<'tcx> {
         self.mk_ty(FnPtr(fty))
     }
 
+    #[inline]
     pub fn mk_dynamic(
         self,
         obj: ty::Binder<&'tcx List<ExistentialPredicate<'tcx>>>,
@@ -2661,6 +2650,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         self.mk_ty(Dynamic(obj, reg))
     }
 
+    #[inline]
     pub fn mk_projection(self,
                          item_def_id: DefId,
                          substs: &'tcx Substs<'tcx>)
@@ -2671,11 +2661,13 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
             }))
         }
 
+    #[inline]
     pub fn mk_closure(self, closure_id: DefId, closure_substs: ClosureSubsts<'tcx>)
                       -> Ty<'tcx> {
         self.mk_ty(Closure(closure_id, closure_substs))
     }
 
+    #[inline]
     pub fn mk_generator(self,
                         id: DefId,
                         generator_substs: GeneratorSubsts<'tcx>,
@@ -2684,32 +2676,39 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         self.mk_ty(Generator(id, generator_substs, movability))
     }
 
+    #[inline]
     pub fn mk_generator_witness(self, types: ty::Binder<&'tcx List<Ty<'tcx>>>) -> Ty<'tcx> {
         self.mk_ty(GeneratorWitness(types))
     }
 
+    #[inline]
     pub fn mk_var(self, v: TyVid) -> Ty<'tcx> {
         self.mk_infer(TyVar(v))
     }
 
+    #[inline]
     pub fn mk_int_var(self, v: IntVid) -> Ty<'tcx> {
         self.mk_infer(IntVar(v))
     }
 
+    #[inline]
     pub fn mk_float_var(self, v: FloatVid) -> Ty<'tcx> {
         self.mk_infer(FloatVar(v))
     }
 
+    #[inline]
     pub fn mk_infer(self, it: InferTy) -> Ty<'tcx> {
         self.mk_ty(Infer(it))
     }
 
+    #[inline]
     pub fn mk_ty_param(self,
                        index: u32,
                        name: InternedString) -> Ty<'tcx> {
         self.mk_ty(Param(ParamTy { idx: index, name: name }))
     }
 
+    #[inline]
     pub fn mk_self_type(self) -> Ty<'tcx> {
         self.mk_ty_param(0, keywords::SelfType.name().as_interned_str())
     }
@@ -2723,6 +2722,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
+    #[inline]
     pub fn mk_opaque(self, def_id: DefId, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> {
         self.mk_ty(Opaque(def_id, substs))
     }
diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs
index 6f0e8d4f026..20f64597b78 100644
--- a/src/librustc/ty/fold.rs
+++ b/src/librustc/ty/fold.rs
@@ -374,6 +374,7 @@ pub struct RegionFolder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
 }
 
 impl<'a, 'gcx, 'tcx> RegionFolder<'a, 'gcx, 'tcx> {
+    #[inline]
     pub fn new(
         tcx: TyCtxt<'a, 'gcx, 'tcx>,
         skipped_regions: &'a mut bool,
@@ -679,24 +680,31 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
 // vars. See comment on `shift_vars_through_binders` method in
 // `subst.rs` for more details.
 
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+enum Direction {
+    In,
+    Out,
+}
+
 struct Shifter<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     tcx: TyCtxt<'a, 'gcx, 'tcx>,
-
     current_index: ty::DebruijnIndex,
     amount: u32,
+    direction: Direction,
 }
 
 impl Shifter<'a, 'gcx, 'tcx> {
-    pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, amount: u32) -> Self {
+    pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, amount: u32, direction: Direction) -> Self {
         Shifter {
             tcx,
             current_index: ty::INNERMOST,
             amount,
+            direction,
         }
     }
 }
 
-impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Shifter<'a, 'gcx, 'tcx> {
+impl TypeFolder<'gcx, 'tcx> for Shifter<'a, 'gcx, 'tcx> {
     fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'tcx> { self.tcx }
 
     fn fold_binder<T: TypeFoldable<'tcx>>(&mut self, t: &ty::Binder<T>) -> ty::Binder<T> {
@@ -712,7 +720,14 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Shifter<'a, 'gcx, 'tcx> {
                 if self.amount == 0 || debruijn < self.current_index {
                     r
                 } else {
-                    let shifted = ty::ReLateBound(debruijn.shifted_in(self.amount), br);
+                    let debruijn = match self.direction {
+                        Direction::In => debruijn.shifted_in(self.amount),
+                        Direction::Out => {
+                            assert!(debruijn.as_u32() >= self.amount);
+                            debruijn.shifted_out(self.amount)
+                        }
+                    };
+                    let shifted = ty::ReLateBound(debruijn, br);
                     self.tcx.mk_region(shifted)
                 }
             }
@@ -726,8 +741,15 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Shifter<'a, 'gcx, 'tcx> {
                 if self.amount == 0 || debruijn < self.current_index {
                     ty
                 } else {
+                    let debruijn = match self.direction {
+                        Direction::In => debruijn.shifted_in(self.amount),
+                        Direction::Out => {
+                            assert!(debruijn.as_u32() >= self.amount);
+                            debruijn.shifted_out(self.amount)
+                        }
+                    };
                     self.tcx.mk_ty(
-                        ty::Bound(debruijn.shifted_in(self.amount), bound_ty)
+                        ty::Bound(debruijn, bound_ty)
                     )
                 }
             }
@@ -760,7 +782,18 @@ pub fn shift_vars<'a, 'gcx, 'tcx, T>(
     debug!("shift_vars(value={:?}, amount={})",
            value, amount);
 
-    value.fold_with(&mut Shifter::new(tcx, amount))
+    value.fold_with(&mut Shifter::new(tcx, amount, Direction::In))
+}
+
+pub fn shift_out_vars<'a, 'gcx, 'tcx, T>(
+    tcx: TyCtxt<'a, 'gcx, 'tcx>,
+    value: &T,
+    amount: u32
+) -> T where T: TypeFoldable<'tcx> {
+    debug!("shift_out_vars(value={:?}, amount={})",
+           value, amount);
+
+    value.fold_with(&mut Shifter::new(tcx, amount, Direction::Out))
 }
 
 /// An "escaping var" is a bound var whose binder is not part of `t`. A bound var can be a
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index b371f4532e5..4633ab11663 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -1275,6 +1275,7 @@ impl<'tcx> PolyProjectionPredicate<'tcx> {
         self.skip_binder().projection_ty.item_def_id
     }
 
+    #[inline]
     pub fn to_poly_trait_ref(&self, tcx: TyCtxt<'_, '_, '_>) -> PolyTraitRef<'tcx> {
         // Note: unlike with `TraitRef::to_poly_trait_ref()`,
         // `self.0.trait_ref` is permitted to have escaping regions.
@@ -1633,6 +1634,7 @@ impl<'tcx> ParamEnv<'tcx> {
     /// there are no where clauses in scope. Hidden types (like `impl
     /// Trait`) are left hidden, so this is suitable for ordinary
     /// type-checking.
+    #[inline]
     pub fn empty() -> Self {
         Self::new(List::empty(), Reveal::UserFacing)
     }
@@ -1644,11 +1646,13 @@ impl<'tcx> ParamEnv<'tcx> {
     ///
     /// N.B. If you want to have predicates in scope, use `ParamEnv::new`,
     /// or invoke `param_env.with_reveal_all()`.
+    #[inline]
     pub fn reveal_all() -> Self {
         Self::new(List::empty(), Reveal::All)
     }
 
     /// Construct a trait environment with the given set of predicates.
+    #[inline]
     pub fn new(caller_bounds: &'tcx List<ty::Predicate<'tcx>>,
                reveal: Reveal)
                -> Self {
@@ -2148,6 +2152,7 @@ impl<'a, 'gcx, 'tcx> AdtDef {
         }
     }
 
+    #[inline]
     pub fn variant_descr(&self) -> &'static str {
         match self.adt_kind() {
             AdtKind::Struct => "struct",
diff --git a/src/librustc/ty/relate.rs b/src/librustc/ty/relate.rs
index 082c1bd5fea..1b64a686794 100644
--- a/src/librustc/ty/relate.rs
+++ b/src/librustc/ty/relate.rs
@@ -25,6 +25,7 @@ use std::rc::Rc;
 use std::iter;
 use rustc_target::spec::abi;
 use hir as ast;
+use traits;
 
 pub type RelateResult<'tcx, T> = Result<T, TypeError<'tcx>>;
 
@@ -371,6 +372,10 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R,
             bug!("var types encountered in super_relate_tys")
         }
 
+        (ty::Bound(..), _) | (_, ty::Bound(..)) => {
+            bug!("bound types encountered in super_relate_tys")
+        }
+
         (&ty::Error, _) | (_, &ty::Error) =>
         {
             Ok(tcx.types.err)
@@ -394,6 +399,10 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R,
             Ok(a)
         }
 
+        (ty::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => {
+            Ok(a)
+        }
+
         (&ty::Adt(a_def, a_substs), &ty::Adt(b_def, b_substs))
             if a_def == b_def =>
         {
@@ -556,8 +565,13 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R,
             Ok(tcx.mk_fn_ptr(fty))
         }
 
-        (&ty::Projection(ref a_data), &ty::Projection(ref b_data)) =>
-        {
+        (ty::UnnormalizedProjection(a_data), ty::UnnormalizedProjection(b_data)) => {
+            let projection_ty = relation.relate(a_data, b_data)?;
+            Ok(tcx.mk_ty(ty::UnnormalizedProjection(projection_ty)))
+        }
+
+        // these two are already handled downstream in case of lazy normalization
+        (ty::Projection(a_data), ty::Projection(b_data)) => {
             let projection_ty = relation.relate(a_data, b_data)?;
             Ok(tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs))
         }
@@ -710,6 +724,283 @@ impl<'tcx> Relate<'tcx> for Kind<'tcx> {
     }
 }
 
+impl<'tcx> Relate<'tcx> for ty::TraitPredicate<'tcx> {
+    fn relate<'a, 'gcx, R>(
+        relation: &mut R,
+        a: &ty::TraitPredicate<'tcx>,
+        b: &ty::TraitPredicate<'tcx>
+    ) -> RelateResult<'tcx, ty::TraitPredicate<'tcx>>
+        where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'tcx, 'tcx: 'a
+    {
+        Ok(ty::TraitPredicate {
+            trait_ref: relation.relate(&a.trait_ref, &b.trait_ref)?,
+        })
+    }
+}
+
+impl<'tcx> Relate<'tcx> for ty::ProjectionPredicate<'tcx> {
+    fn relate<'a, 'gcx, R>(
+        relation: &mut R,
+        a: &ty::ProjectionPredicate<'tcx>,
+        b: &ty::ProjectionPredicate<'tcx>,
+    ) -> RelateResult<'tcx, ty::ProjectionPredicate<'tcx>>
+        where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'tcx, 'tcx: 'a
+    {
+        Ok(ty::ProjectionPredicate {
+            projection_ty: relation.relate(&a.projection_ty, &b.projection_ty)?,
+            ty: relation.relate(&a.ty, &b.ty)?,
+        })
+    }
+}
+
+impl<'tcx> Relate<'tcx> for traits::WhereClause<'tcx> {
+    fn relate<'a, 'gcx, R>(
+        relation: &mut R,
+        a: &traits::WhereClause<'tcx>,
+        b: &traits::WhereClause<'tcx>
+    ) -> RelateResult<'tcx, traits::WhereClause<'tcx>>
+        where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'tcx, 'tcx: 'a
+    {
+        use traits::WhereClause::*;
+        match (a, b) {
+            (Implemented(a_pred), Implemented(b_pred)) => {
+                Ok(Implemented(relation.relate(a_pred, b_pred)?))
+            }
+
+            (ProjectionEq(a_pred), ProjectionEq(b_pred)) => {
+                Ok(ProjectionEq(relation.relate(a_pred, b_pred)?))
+            }
+
+            (RegionOutlives(a_pred), RegionOutlives(b_pred)) => {
+                Ok(RegionOutlives(ty::OutlivesPredicate(
+                    relation.relate(&a_pred.0, &b_pred.0)?,
+                    relation.relate(&a_pred.1, &b_pred.1)?,
+                )))
+            }
+
+            (TypeOutlives(a_pred), TypeOutlives(b_pred)) => {
+                Ok(TypeOutlives(ty::OutlivesPredicate(
+                    relation.relate(&a_pred.0, &b_pred.0)?,
+                    relation.relate(&a_pred.1, &b_pred.1)?,
+                )))
+            }
+
+            _ =>  Err(TypeError::Mismatch),
+        }
+    }
+}
+
+impl<'tcx> Relate<'tcx> for traits::WellFormed<'tcx> {
+    fn relate<'a, 'gcx, R>(
+        relation: &mut R,
+        a: &traits::WellFormed<'tcx>,
+        b: &traits::WellFormed<'tcx>
+    ) -> RelateResult<'tcx, traits::WellFormed<'tcx>>
+        where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'tcx, 'tcx: 'a
+    {
+        use traits::WellFormed::*;
+        match (a, b) {
+            (Trait(a_pred), Trait(b_pred)) => Ok(Trait(relation.relate(a_pred, b_pred)?)),
+            (Ty(a_ty), Ty(b_ty)) => Ok(Ty(relation.relate(a_ty, b_ty)?)),
+            _ =>  Err(TypeError::Mismatch),
+        }
+    }
+}
+
+impl<'tcx> Relate<'tcx> for traits::FromEnv<'tcx> {
+    fn relate<'a, 'gcx, R>(
+        relation: &mut R,
+        a: &traits::FromEnv<'tcx>,
+        b: &traits::FromEnv<'tcx>
+    ) -> RelateResult<'tcx, traits::FromEnv<'tcx>>
+        where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'tcx, 'tcx: 'a
+    {
+        use traits::FromEnv::*;
+        match (a, b) {
+            (Trait(a_pred), Trait(b_pred)) => Ok(Trait(relation.relate(a_pred, b_pred)?)),
+            (Ty(a_ty), Ty(b_ty)) => Ok(Ty(relation.relate(a_ty, b_ty)?)),
+            _ =>  Err(TypeError::Mismatch),
+        }
+    }
+}
+
+impl<'tcx> Relate<'tcx> for traits::DomainGoal<'tcx> {
+    fn relate<'a, 'gcx, R>(
+        relation: &mut R,
+        a: &traits::DomainGoal<'tcx>,
+        b: &traits::DomainGoal<'tcx>
+    ) -> RelateResult<'tcx, traits::DomainGoal<'tcx>>
+        where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'tcx, 'tcx: 'a
+    {
+        use traits::DomainGoal::*;
+        match (a, b) {
+            (Holds(a_wc), Holds(b_wc)) => Ok(Holds(relation.relate(a_wc, b_wc)?)),
+            (WellFormed(a_wf), WellFormed(b_wf)) => Ok(WellFormed(relation.relate(a_wf, b_wf)?)),
+            (FromEnv(a_fe), FromEnv(b_fe)) => Ok(FromEnv(relation.relate(a_fe, b_fe)?)),
+
+            (Normalize(a_pred), Normalize(b_pred)) => {
+                Ok(Normalize(relation.relate(a_pred, b_pred)?))
+            }
+
+            _ =>  Err(TypeError::Mismatch),
+        }
+    }
+}
+
+impl<'tcx> Relate<'tcx> for traits::Goal<'tcx> {
+    fn relate<'a, 'gcx, R>(
+        relation: &mut R,
+        a: &traits::Goal<'tcx>,
+        b: &traits::Goal<'tcx>
+    ) -> RelateResult<'tcx, traits::Goal<'tcx>>
+        where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'tcx, 'tcx: 'a
+    {
+        use traits::GoalKind::*;
+        match (a, b) {
+            (Implies(a_clauses, a_goal), Implies(b_clauses, b_goal)) => {
+                let clauses = relation.relate(a_clauses, b_clauses)?;
+                let goal = relation.relate(a_goal, b_goal)?;
+                Ok(relation.tcx().mk_goal(Implies(clauses, goal)))
+            }
+
+            (And(a_left, a_right), And(b_left, b_right)) => {
+                let left = relation.relate(a_left, b_left)?;
+                let right = relation.relate(a_right, b_right)?;
+                Ok(relation.tcx().mk_goal(And(left, right)))
+            }
+
+            (Not(a_goal), Not(b_goal)) => {
+                let goal = relation.relate(a_goal, b_goal)?;
+                Ok(relation.tcx().mk_goal(Not(goal)))
+            }
+
+            (DomainGoal(a_goal), DomainGoal(b_goal)) => {
+                let goal = relation.relate(a_goal, b_goal)?;
+                Ok(relation.tcx().mk_goal(DomainGoal(goal)))
+            }
+
+            (Quantified(a_qkind, a_goal), Quantified(b_qkind, b_goal))
+                if a_qkind == b_qkind =>
+            {
+                let goal = relation.relate(a_goal, b_goal)?;
+                Ok(relation.tcx().mk_goal(Quantified(*a_qkind, goal)))
+            }
+
+            (CannotProve, CannotProve) => Ok(*a),
+
+            _ => Err(TypeError::Mismatch),
+        }
+    }
+}
+
+impl<'tcx> Relate<'tcx> for traits::Goals<'tcx> {
+    fn relate<'a, 'gcx, R>(
+        relation: &mut R,
+        a: &traits::Goals<'tcx>,
+        b: &traits::Goals<'tcx>
+    ) -> RelateResult<'tcx, traits::Goals<'tcx>>
+        where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'tcx, 'tcx: 'a
+    {
+        if a.len() != b.len() {
+            return Err(TypeError::Mismatch);
+        }
+
+        let tcx = relation.tcx();
+        let goals = a.iter().zip(b.iter()).map(|(a, b)| relation.relate(a, b));
+        Ok(tcx.mk_goals(goals)?)
+    }
+}
+
+impl<'tcx> Relate<'tcx> for traits::Clause<'tcx> {
+    fn relate<'a, 'gcx, R>(
+        relation: &mut R,
+        a: &traits::Clause<'tcx>,
+        b: &traits::Clause<'tcx>
+    ) -> RelateResult<'tcx, traits::Clause<'tcx>>
+        where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'tcx, 'tcx: 'a
+    {
+        use traits::Clause::*;
+        match (a, b) {
+            (Implies(a_clause), Implies(b_clause)) => {
+                let clause = relation.relate(a_clause, b_clause)?;
+                Ok(Implies(clause))
+            }
+
+            (ForAll(a_clause), ForAll(b_clause)) => {
+                let clause = relation.relate(a_clause, b_clause)?;
+                Ok(ForAll(clause))
+            }
+
+            _ => Err(TypeError::Mismatch),
+        }
+    }
+}
+
+impl<'tcx> Relate<'tcx> for traits::Clauses<'tcx> {
+    fn relate<'a, 'gcx, R>(
+        relation: &mut R,
+        a: &traits::Clauses<'tcx>,
+        b: &traits::Clauses<'tcx>
+    ) -> RelateResult<'tcx, traits::Clauses<'tcx>>
+        where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'tcx, 'tcx: 'a
+    {
+        if a.len() != b.len() {
+            return Err(TypeError::Mismatch);
+        }
+
+        let tcx = relation.tcx();
+        let clauses = a.iter().zip(b.iter()).map(|(a, b)| relation.relate(a, b));
+        Ok(tcx.mk_clauses(clauses)?)
+    }
+}
+
+impl<'tcx> Relate<'tcx> for traits::ProgramClause<'tcx> {
+    fn relate<'a, 'gcx, R>(
+        relation: &mut R,
+        a: &traits::ProgramClause<'tcx>,
+        b: &traits::ProgramClause<'tcx>
+    ) -> RelateResult<'tcx, traits::ProgramClause<'tcx>>
+        where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'tcx, 'tcx: 'a
+    {
+        Ok(traits::ProgramClause {
+            goal: relation.relate(&a.goal, &b.goal)?,
+            hypotheses: relation.relate(&a.hypotheses, &b.hypotheses)?,
+            category: traits::ProgramClauseCategory::Other,
+        })
+    }
+}
+
+impl<'tcx> Relate<'tcx> for traits::Environment<'tcx> {
+    fn relate<'a, 'gcx, R>(
+        relation: &mut R,
+        a: &traits::Environment<'tcx>,
+        b: &traits::Environment<'tcx>
+    ) -> RelateResult<'tcx, traits::Environment<'tcx>>
+        where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'tcx, 'tcx: 'a
+    {
+        Ok(traits::Environment {
+            clauses: relation.relate(&a.clauses, &b.clauses)?,
+        })
+    }
+}
+
+impl<'tcx, G> Relate<'tcx> for traits::InEnvironment<'tcx, G>
+    where G: Relate<'tcx>
+{
+    fn relate<'a, 'gcx, R>(
+        relation: &mut R,
+        a: &traits::InEnvironment<'tcx, G>,
+        b: &traits::InEnvironment<'tcx, G>
+    ) -> RelateResult<'tcx, traits::InEnvironment<'tcx, G>>
+        where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'tcx, 'tcx: 'a
+    {
+        Ok(traits::InEnvironment {
+            environment: relation.relate(&a.environment, &b.environment)?,
+            goal: relation.relate(&a.goal, &b.goal)?,
+        })
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // Error handling
 
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index a18e3a27546..1416cb17fea 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -667,6 +667,7 @@ impl<'tcx> TraitRef<'tcx> {
         }
     }
 
+    #[inline]
     pub fn self_ty(&self) -> Ty<'tcx> {
         self.substs.type_at(0)
     }
@@ -978,15 +979,18 @@ impl<'tcx> FnSig<'tcx> {
 pub type PolyFnSig<'tcx> = Binder<FnSig<'tcx>>;
 
 impl<'tcx> PolyFnSig<'tcx> {
+    #[inline]
     pub fn inputs(&self) -> Binder<&'tcx [Ty<'tcx>]> {
         self.map_bound_ref(|fn_sig| fn_sig.inputs())
     }
+    #[inline]
     pub fn input(&self, index: usize) -> ty::Binder<Ty<'tcx>> {
         self.map_bound_ref(|fn_sig| fn_sig.inputs()[index])
     }
     pub fn inputs_and_output(&self) -> ty::Binder<&'tcx List<Ty<'tcx>>> {
         self.map_bound_ref(|fn_sig| fn_sig.inputs_and_output)
     }
+    #[inline]
     pub fn output(&self) -> ty::Binder<Ty<'tcx>> {
         self.map_bound_ref(|fn_sig| fn_sig.output())
     }
@@ -1548,6 +1552,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
         }
     }
 
+    #[inline]
     pub fn is_ty_var(&self) -> bool {
         match self.sty {
             Infer(TyVar(_)) => true,
@@ -1732,6 +1737,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
         }
     }
 
+    #[inline]
     pub fn is_integral(&self) -> bool {
         match self.sty {
             Infer(IntVar(_)) | Int(_) | Uint(_) => true,
@@ -1762,6 +1768,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
         }
     }
 
+    #[inline]
     pub fn is_fp(&self) -> bool {
         match self.sty {
             Infer(FloatVar(_)) | Float(_) => true,
@@ -1845,6 +1852,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
         }
     }
 
+    #[inline]
     pub fn ty_adt_def(&self) -> Option<&'tcx AdtDef> {
         match self.sty {
             Adt(adt, _) => Some(adt),
diff --git a/src/librustc_codegen_llvm/back/write.rs b/src/librustc_codegen_llvm/back/write.rs
index 7945d381760..4eb920324bd 100644
--- a/src/librustc_codegen_llvm/back/write.rs
+++ b/src/librustc_codegen_llvm/back/write.rs
@@ -23,7 +23,7 @@ use llvm_util;
 use ModuleLlvm;
 use rustc_codegen_ssa::{ModuleCodegen, CompiledModule};
 use rustc::util::common::time_ext;
-use rustc_fs_util::{path2cstr, link_or_copy};
+use rustc_fs_util::{path_to_c_string, link_or_copy};
 use rustc_data_structures::small_c_str::SmallCStr;
 use errors::{self, Handler, FatalError};
 use type_::Type;
@@ -80,7 +80,7 @@ pub fn write_output_file(
         output: &Path,
         file_type: llvm::FileType) -> Result<(), FatalError> {
     unsafe {
-        let output_c = path2cstr(output);
+        let output_c = path_to_c_string(output);
         let result = llvm::LLVMRustWriteOutputFile(target, pm, m, output_c.as_ptr(), file_type);
         if result.into_result().is_err() {
             let msg = format!("could not write output to {}", output.display());
@@ -211,7 +211,7 @@ pub(crate) fn save_temp_bitcode(
         let ext = format!("{}.bc", name);
         let cgu = Some(&module.name[..]);
         let path = cgcx.output_filenames.temp_path_ext(&ext, cgu);
-        let cstr = path2cstr(&path);
+        let cstr = path_to_c_string(&path);
         let llmod = module.module_llvm.llmod();
         llvm::LLVMWriteBitcodeToFile(llmod, cstr.as_ptr());
     }
@@ -324,7 +324,7 @@ pub(crate) unsafe fn optimize(cgcx: &CodegenContext<LlvmCodegenBackend>,
 
     if config.emit_no_opt_bc {
         let out = cgcx.output_filenames.temp_path_ext("no-opt.bc", module_name);
-        let out = path2cstr(&out);
+        let out = path_to_c_string(&out);
         llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr());
     }
 
@@ -530,7 +530,7 @@ pub(crate) unsafe fn codegen(cgcx: &CodegenContext<LlvmCodegenBackend>,
             || -> Result<(), FatalError> {
             if config.emit_ir {
                 let out = cgcx.output_filenames.temp_path(OutputType::LlvmAssembly, module_name);
-                let out = path2cstr(&out);
+                let out = path_to_c_string(&out);
 
                 extern "C" fn demangle_callback(input_ptr: *const c_char,
                                                 input_len: size_t,
diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs
index 81f2769800d..47e92fbe87d 100644
--- a/src/librustc_codegen_llvm/debuginfo/metadata.rs
+++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs
@@ -39,7 +39,7 @@ use rustc::ty::layout::{self, Align, HasDataLayout, Integer, IntegerExt, LayoutO
                         PrimitiveExt, Size, TyLayout};
 use rustc::session::config;
 use rustc::util::nodemap::FxHashMap;
-use rustc_fs_util::path2cstr;
+use rustc_fs_util::path_to_c_string;
 use rustc_data_structures::small_c_str::SmallCStr;
 
 use libc::{c_uint, c_longlong};
@@ -892,7 +892,7 @@ pub fn compile_unit_metadata(tcx: TyCtxt,
     };
 
     fn path_to_mdstring(llcx: &'ll llvm::Context, path: &Path) -> &'ll Value {
-        let path_str = path2cstr(path);
+        let path_str = path_to_c_string(path);
         unsafe {
             llvm::LLVMMDStringInContext(llcx,
                                         path_str.as_ptr(),
diff --git a/src/librustc_codegen_llvm/llvm/archive_ro.rs b/src/librustc_codegen_llvm/llvm/archive_ro.rs
index 2a77f256e3a..d5c73fecf81 100644
--- a/src/librustc_codegen_llvm/llvm/archive_ro.rs
+++ b/src/librustc_codegen_llvm/llvm/archive_ro.rs
@@ -10,10 +10,10 @@
 
 //! A wrapper around LLVM's archive (.a) code
 
-use std::ffi::CString;
 use std::path::Path;
 use std::slice;
 use std::str;
+use rustc_fs_util::path_to_c_string;
 
 pub struct ArchiveRO {
     pub raw: &'static mut super::Archive,
@@ -38,24 +38,12 @@ impl ArchiveRO {
     /// raised.
     pub fn open(dst: &Path) -> Result<ArchiveRO, String> {
         return unsafe {
-            let s = path2cstr(dst);
+            let s = path_to_c_string(dst);
             let ar = super::LLVMRustOpenArchive(s.as_ptr()).ok_or_else(|| {
                 super::last_error().unwrap_or_else(|| "failed to open archive".to_owned())
             })?;
             Ok(ArchiveRO { raw: ar })
         };
-
-        #[cfg(unix)]
-        fn path2cstr(p: &Path) -> CString {
-            use std::os::unix::prelude::*;
-            use std::ffi::OsStr;
-            let p: &OsStr = p.as_ref();
-            CString::new(p.as_bytes()).unwrap()
-        }
-        #[cfg(windows)]
-        fn path2cstr(p: &Path) -> CString {
-            CString::new(p.to_str().unwrap()).unwrap()
-        }
     }
 
     pub fn iter(&self) -> Iter {
diff --git a/src/librustc_codegen_llvm/metadata.rs b/src/librustc_codegen_llvm/metadata.rs
index 7752465d885..5605f64c2e7 100644
--- a/src/librustc_codegen_llvm/metadata.rs
+++ b/src/librustc_codegen_llvm/metadata.rs
@@ -18,7 +18,7 @@ use rustc_data_structures::owning_ref::OwningRef;
 use std::path::Path;
 use std::ptr;
 use std::slice;
-use rustc_fs_util::path2cstr;
+use rustc_fs_util::path_to_c_string;
 
 pub use rustc_data_structures::sync::MetadataRef;
 
@@ -57,7 +57,7 @@ impl MetadataLoader for LlvmMetadataLoader {
                           filename: &Path)
                           -> Result<MetadataRef, String> {
         unsafe {
-            let buf = path2cstr(filename);
+            let buf = path_to_c_string(filename);
             let mb = llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf.as_ptr())
                 .ok_or_else(|| format!("error reading library: '{}'", filename.display()))?;
             let of = ObjectFile::new(mb)
diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs
index b0575b841d5..24a70dc7977 100644
--- a/src/librustc_codegen_ssa/back/link.rs
+++ b/src/librustc_codegen_ssa/back/link.rs
@@ -161,7 +161,11 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
                 LinkerFlavor::Lld(_) => "lld",
             }), flavor)),
             (Some(linker), None) => {
-                let stem = linker.file_stem().and_then(|stem| stem.to_str()).unwrap_or_else(|| {
+                let stem = if linker.extension().and_then(|ext| ext.to_str()) == Some("exe") {
+                    linker.file_stem().and_then(|stem| stem.to_str())
+                } else {
+                    linker.to_str()
+                }.unwrap_or_else(|| {
                     sess.fatal("couldn't extract file stem from specified linker");
                 }).to_owned();
 
diff --git a/src/librustc_data_structures/interner.rs b/src/librustc_data_structures/interner.rs
new file mode 100644
index 00000000000..29e5aefee7f
--- /dev/null
+++ b/src/librustc_data_structures/interner.rs
@@ -0,0 +1,68 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::hash::Hash;
+use std::hash::BuildHasher;
+use std::hash::Hasher;
+use std::collections::HashMap;
+use std::collections::hash_map::RawEntryMut;
+use std::borrow::Borrow;
+
+pub trait HashInterner<K: Eq + Hash> {
+    fn intern_ref<Q: ?Sized, F: FnOnce() -> K>(&mut self, value: &Q, make: F) -> K
+        where K: Borrow<Q>,
+              Q: Hash + Eq;
+
+    fn intern<Q, F: FnOnce(Q) -> K>(&mut self, value: Q, make: F) -> K
+        where K: Borrow<Q>,
+              Q: Hash + Eq;
+}
+
+impl<K: Eq + Hash + Copy, S: BuildHasher> HashInterner<K> for HashMap<K, (), S> {
+    #[inline]
+    fn intern_ref<Q: ?Sized, F: FnOnce() -> K>(&mut self, value: &Q, make: F) -> K
+        where K: Borrow<Q>,
+              Q: Hash + Eq
+    {
+        let mut hasher = self.hasher().build_hasher();
+        value.hash(&mut hasher);
+        let hash = hasher.finish();
+        let entry = self.raw_entry_mut().from_key_hashed_nocheck(hash, value);
+
+        match entry {
+            RawEntryMut::Occupied(e) => *e.key(),
+            RawEntryMut::Vacant(e) => {
+                let v = make();
+                e.insert_hashed_nocheck(hash, v, ());
+                v
+            }
+        }
+    }
+
+    #[inline]
+    fn intern<Q, F: FnOnce(Q) -> K>(&mut self, value: Q, make: F) -> K
+        where K: Borrow<Q>,
+              Q: Hash + Eq
+    {
+        let mut hasher = self.hasher().build_hasher();
+        value.hash(&mut hasher);
+        let hash = hasher.finish();
+        let entry = self.raw_entry_mut().from_key_hashed_nocheck(hash, &value);
+
+        match entry {
+            RawEntryMut::Occupied(e) => *e.key(),
+            RawEntryMut::Vacant(e) => {
+                let v = make(value);
+                e.insert_hashed_nocheck(hash, v, ());
+                v
+            }
+        }
+    }
+}
diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs
index 135abebdacb..96cb235a933 100644
--- a/src/librustc_data_structures/lib.rs
+++ b/src/librustc_data_structures/lib.rs
@@ -29,6 +29,7 @@
 #![feature(nll)]
 #![feature(allow_internal_unstable)]
 #![feature(vec_resize_with)]
+#![feature(hash_raw_entry)]
 
 #![cfg_attr(unix, feature(libc))]
 #![cfg_attr(test, feature(test))]
@@ -66,6 +67,7 @@ pub mod flock;
 pub mod fx;
 pub mod graph;
 pub mod indexed_vec;
+pub mod interner;
 pub mod obligation_forest;
 pub mod owning_ref;
 pub mod ptr_key;
diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs
index c7ba31e3395..fb8093d1d77 100644
--- a/src/librustc_driver/pretty.rs
+++ b/src/librustc_driver/pretty.rs
@@ -530,7 +530,7 @@ impl<'a, 'tcx> pprust_hir::PpAnn for TypedAnnotation<'a, 'tcx> {
                 s.s.space()?;
                 s.s.word("as")?;
                 s.s.space()?;
-                s.s.word(&self.tables.get().expr_ty(expr).to_string())?;
+                s.s.word(self.tables.get().expr_ty(expr).to_string())?;
                 s.pclose()
             }
             _ => Ok(()),
diff --git a/src/librustc_fs_util/lib.rs b/src/librustc_fs_util/lib.rs
index ffe420b109d..1b0ff4f861c 100644
--- a/src/librustc_fs_util/lib.rs
+++ b/src/librustc_fs_util/lib.rs
@@ -116,13 +116,13 @@ pub fn rename_or_copy_remove<P: AsRef<Path>, Q: AsRef<Path>>(p: P,
 }
 
 #[cfg(unix)]
-pub fn path2cstr(p: &Path) -> CString {
-    use std::os::unix::prelude::*;
+pub fn path_to_c_string(p: &Path) -> CString {
+    use std::os::unix::ffi::OsStrExt;
     use std::ffi::OsStr;
     let p: &OsStr = p.as_ref();
     CString::new(p.as_bytes()).unwrap()
 }
 #[cfg(windows)]
-pub fn path2cstr(p: &Path) -> CString {
+pub fn path_to_c_string(p: &Path) -> CString {
     CString::new(p.to_str().unwrap()).unwrap()
 }
diff --git a/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs b/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs
index cf4f9130807..225e2841fb0 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs
@@ -10,10 +10,11 @@
 
 use borrow_check::nll::constraints::OutlivesConstraint;
 use borrow_check::nll::type_check::{BorrowCheckContext, Locations};
-use rustc::infer::nll_relate::{TypeRelating, TypeRelatingDelegate};
+use rustc::infer::nll_relate::{TypeRelating, TypeRelatingDelegate, NormalizationStrategy};
 use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
 use rustc::mir::ConstraintCategory;
 use rustc::traits::query::Fallible;
+use rustc::traits::DomainGoal;
 use rustc::ty::relate::TypeRelation;
 use rustc::ty::{self, Ty};
 
@@ -38,7 +39,7 @@ pub(super) fn relate_types<'tcx>(
     TypeRelating::new(
         infcx,
         NllTypeRelatingDelegate::new(infcx, borrowck_context, locations, category),
-        v,
+        v
     ).relate(&a, &b)?;
     Ok(())
 }
@@ -115,4 +116,16 @@ impl TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, '_, 'tcx> {
                 });
         }
     }
+
+    fn push_domain_goal(&mut self, _: DomainGoal<'tcx>) {
+        bug!("should never be invoked with eager normalization")
+    }
+
+    fn normalization() -> NormalizationStrategy {
+        NormalizationStrategy::Eager
+    }
+
+    fn forbid_inference_vars() -> bool {
+        true
+    }
 }
diff --git a/src/librustc_mir/monomorphize/partitioning.rs b/src/librustc_mir/monomorphize/partitioning.rs
index 6dba020120f..3a6ee6da422 100644
--- a/src/librustc_mir/monomorphize/partitioning.rs
+++ b/src/librustc_mir/monomorphize/partitioning.rs
@@ -985,7 +985,7 @@ fn collect_and_partition_mono_items<'a, 'tcx>(
                 output.push_str(" @@");
                 let mut empty = Vec::new();
                 let cgus = item_to_cgus.get_mut(i).unwrap_or(&mut empty);
-                cgus.as_mut_slice().sort_by_key(|&(ref name, _)| name.clone());
+                cgus.as_mut_slice().sort_by_cached_key(|&(ref name, _)| name.clone());
                 cgus.dedup();
                 for &(ref cgu_name, (linkage, _)) in cgus.iter() {
                     output.push_str(" ");
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index 09fe7b14c79..bcee6d75b5a 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -895,145 +895,160 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
             let mut is_shuffle = false;
             let mut is_const_fn = false;
             let mut is_promotable_const_fn = false;
-            if let ty::FnDef(def_id, _) = fn_ty.sty {
-                callee_def_id = Some(def_id);
-                match self.tcx.fn_sig(def_id).abi() {
-                    Abi::RustIntrinsic |
-                    Abi::PlatformIntrinsic => {
-                        assert!(!self.tcx.is_const_fn(def_id));
-                        match &self.tcx.item_name(def_id).as_str()[..] {
-                            | "size_of"
-                            | "min_align_of"
-                            | "needs_drop"
-                            | "type_id"
-                            | "bswap"
-                            | "bitreverse"
-                            | "ctpop"
-                            | "cttz"
-                            | "cttz_nonzero"
-                            | "ctlz"
-                            | "ctlz_nonzero"
-                            | "overflowing_add"
-                            | "overflowing_sub"
-                            | "overflowing_mul"
-                            | "unchecked_shl"
-                            | "unchecked_shr"
-                            | "rotate_left"
-                            | "rotate_right"
-                            | "add_with_overflow"
-                            | "sub_with_overflow"
-                            | "mul_with_overflow"
-                            // no need to check feature gates, intrinsics are only callable from the
-                            // libstd or with forever unstable feature gates
-                            => is_const_fn = true,
-                            // special intrinsic that can be called diretly without an intrinsic
-                            // feature gate needs a language feature gate
-                            "transmute" => {
-                                // never promote transmute calls
-                                if self.mode != Mode::Fn {
-                                    is_const_fn = true;
-                                    // const eval transmute calls only with the feature gate
-                                    if !self.tcx.features().const_transmute {
-                                        emit_feature_err(
-                                            &self.tcx.sess.parse_sess, "const_transmute",
-                                            self.span, GateIssue::Language,
-                                            &format!("The use of std::mem::transmute() \
-                                            is gated in {}s", self.mode));
+            match fn_ty.sty {
+                ty::FnDef(def_id, _) => {
+                    callee_def_id = Some(def_id);
+                    match self.tcx.fn_sig(def_id).abi() {
+                        Abi::RustIntrinsic |
+                        Abi::PlatformIntrinsic => {
+                            assert!(!self.tcx.is_const_fn(def_id));
+                            match &self.tcx.item_name(def_id).as_str()[..] {
+                                | "size_of"
+                                | "min_align_of"
+                                | "needs_drop"
+                                | "type_id"
+                                | "bswap"
+                                | "bitreverse"
+                                | "ctpop"
+                                | "cttz"
+                                | "cttz_nonzero"
+                                | "ctlz"
+                                | "ctlz_nonzero"
+                                | "overflowing_add"
+                                | "overflowing_sub"
+                                | "overflowing_mul"
+                                | "unchecked_shl"
+                                | "unchecked_shr"
+                                | "rotate_left"
+                                | "rotate_right"
+                                | "add_with_overflow"
+                                | "sub_with_overflow"
+                                | "mul_with_overflow"
+                                // no need to check feature gates, intrinsics are only callable
+                                // from the libstd or with forever unstable feature gates
+                                => is_const_fn = true,
+                                // special intrinsic that can be called diretly without an intrinsic
+                                // feature gate needs a language feature gate
+                                "transmute" => {
+                                    // never promote transmute calls
+                                    if self.mode != Mode::Fn {
+                                        is_const_fn = true;
+                                        // const eval transmute calls only with the feature gate
+                                        if !self.tcx.features().const_transmute {
+                                            emit_feature_err(
+                                                &self.tcx.sess.parse_sess, "const_transmute",
+                                                self.span, GateIssue::Language,
+                                                &format!("The use of std::mem::transmute() \
+                                                is gated in {}s", self.mode));
+                                        }
                                     }
                                 }
-                            }
 
-                            name if name.starts_with("simd_shuffle") => {
-                                is_shuffle = true;
-                            }
+                                name if name.starts_with("simd_shuffle") => {
+                                    is_shuffle = true;
+                                }
 
-                            _ => {}
-                        }
-                    }
-                    _ => {
-                        // in normal functions we only care about promotion
-                        if self.mode == Mode::Fn {
-                            // never promote const fn calls of
-                            // functions without #[rustc_promotable]
-                            if self.tcx.is_promotable_const_fn(def_id) {
-                                is_const_fn = true;
-                                is_promotable_const_fn = true;
-                            } else if self.tcx.is_const_fn(def_id) {
-                                is_const_fn = true;
+                                _ => {}
                             }
-                        } else {
-                            // stable const fn or unstable const fns with their feature gate
-                            // active
-                            if self.tcx.is_const_fn(def_id) {
-                                is_const_fn = true;
-                            } else if self.is_const_panic_fn(def_id) {
-                                // check the const_panic feature gate
-                                // FIXME: cannot allow this inside `allow_internal_unstable` because
-                                // that would make `panic!` insta stable in constants, since the
-                                // macro is marked with the attr
-                                if self.tcx.features().const_panic {
+                        }
+                        _ => {
+                            // in normal functions we only care about promotion
+                            if self.mode == Mode::Fn {
+                                // never promote const fn calls of
+                                // functions without #[rustc_promotable]
+                                if self.tcx.is_promotable_const_fn(def_id) {
                                     is_const_fn = true;
-                                } else {
-                                    // don't allow panics in constants without the feature gate
-                                    emit_feature_err(
-                                        &self.tcx.sess.parse_sess,
-                                        "const_panic",
-                                        self.span,
-                                        GateIssue::Language,
-                                        &format!("panicking in {}s is unstable", self.mode),
-                                    );
-                                }
-                            } else if let Some(feature) = self.tcx.is_unstable_const_fn(def_id) {
-                                // check `#[unstable]` const fns or `#[rustc_const_unstable]`
-                                // functions without the feature gate active in this crate to report
-                                // a better error message than the one below
-                                if self.span.allows_unstable() {
-                                    // `allow_internal_unstable` can make such calls stable
+                                    is_promotable_const_fn = true;
+                                } else if self.tcx.is_const_fn(def_id) {
                                     is_const_fn = true;
-                                } else {
-                                    let mut err = self.tcx.sess.struct_span_err(self.span,
-                                        &format!("`{}` is not yet stable as a const fn",
-                                                self.tcx.item_path_str(def_id)));
-                                    help!(&mut err,
-                                        "in Nightly builds, add `#![feature({})]` \
-                                        to the crate attributes to enable",
-                                        feature);
-                                    err.emit();
                                 }
                             } else {
-                                // FIXME(#24111) Remove this check when const fn stabilizes
-                                let (msg, note) = if let UnstableFeatures::Disallow =
-                                        self.tcx.sess.opts.unstable_features {
-                                    (format!("calls in {}s are limited to \
-                                            tuple structs and tuple variants",
-                                            self.mode),
-                                    Some("a limited form of compile-time function \
-                                        evaluation is available on a nightly \
-                                        compiler via `const fn`"))
+                                // stable const fn or unstable const fns with their feature gate
+                                // active
+                                if self.tcx.is_const_fn(def_id) {
+                                    is_const_fn = true;
+                                } else if self.is_const_panic_fn(def_id) {
+                                    // check the const_panic feature gate
+                                    // FIXME: cannot allow this inside `allow_internal_unstable`
+                                    // because that would make `panic!` insta stable in constants,
+                                    // since the macro is marked with the attr
+                                    if self.tcx.features().const_panic {
+                                        is_const_fn = true;
+                                    } else {
+                                        // don't allow panics in constants without the feature gate
+                                        emit_feature_err(
+                                            &self.tcx.sess.parse_sess,
+                                            "const_panic",
+                                            self.span,
+                                            GateIssue::Language,
+                                            &format!("panicking in {}s is unstable", self.mode),
+                                        );
+                                    }
+                                } else if let Some(feat) = self.tcx.is_unstable_const_fn(def_id) {
+                                    // check `#[unstable]` const fns or `#[rustc_const_unstable]`
+                                    // functions without the feature gate active in this crate to
+                                    // report a better error message than the one below
+                                    if self.span.allows_unstable() {
+                                        // `allow_internal_unstable` can make such calls stable
+                                        is_const_fn = true;
+                                    } else {
+                                        let mut err = self.tcx.sess.struct_span_err(self.span,
+                                            &format!("`{}` is not yet stable as a const fn",
+                                                    self.tcx.item_path_str(def_id)));
+                                        help!(&mut err,
+                                            "in Nightly builds, add `#![feature({})]` \
+                                            to the crate attributes to enable",
+                                            feat);
+                                        err.emit();
+                                    }
                                 } else {
-                                    (format!("calls in {}s are limited \
-                                            to constant functions, \
-                                            tuple structs and tuple variants",
-                                            self.mode),
-                                    None)
-                                };
-                                let mut err = struct_span_err!(
-                                    self.tcx.sess,
-                                    self.span,
-                                    E0015,
-                                    "{}",
-                                    msg,
-                                );
-                                if let Some(note) = note {
-                                    err.span_note(self.span, note);
+                                    // FIXME(#24111) Remove this check when const fn stabilizes
+                                    let (msg, note) = if let UnstableFeatures::Disallow =
+                                            self.tcx.sess.opts.unstable_features {
+                                        (format!("calls in {}s are limited to \
+                                                tuple structs and tuple variants",
+                                                self.mode),
+                                        Some("a limited form of compile-time function \
+                                            evaluation is available on a nightly \
+                                            compiler via `const fn`"))
+                                    } else {
+                                        (format!("calls in {}s are limited \
+                                                to constant functions, \
+                                                tuple structs and tuple variants",
+                                                self.mode),
+                                        None)
+                                    };
+                                    let mut err = struct_span_err!(
+                                        self.tcx.sess,
+                                        self.span,
+                                        E0015,
+                                        "{}",
+                                        msg,
+                                    );
+                                    if let Some(note) = note {
+                                        err.span_note(self.span, note);
+                                    }
+                                    err.emit();
                                 }
-                                err.emit();
                             }
                         }
                     }
+                },
+                ty::FnPtr(_) => {
+                    if self.mode != Mode::Fn {
+                        let mut err = self.tcx.sess.struct_span_err(
+                            self.span,
+                            &format!("function pointers are not allowed in const fn"));
+                        err.emit();
+                    }
+                },
+                _ => {
+                    self.not_const();
+                    return
                 }
             }
 
+
             let constant_arguments = callee_def_id.and_then(|id| {
                 args_required_const(self.tcx, id)
             });
diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs
index fbd6f6edd31..6d9abbf5af2 100644
--- a/src/librustc_privacy/lib.rs
+++ b/src/librustc_privacy/lib.rs
@@ -969,7 +969,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
                             Some(poly_projection_predicate.skip_binder()
                                                           .projection_ty.trait_ref(self.tcx))
                         }
-                        ty::Predicate::TypeOutlives(..) => None,
+                        ty::Predicate::TypeOutlives(..) | ty::Predicate::RegionOutlives(..) => None,
                         _ => bug!("unexpected predicate: {:?}", predicate),
                     };
                     if let Some(trait_ref) = trait_ref {
diff --git a/src/librustc_traits/chalk_context/mod.rs b/src/librustc_traits/chalk_context/mod.rs
index 25a6af284b5..58a8d2abd99 100644
--- a/src/librustc_traits/chalk_context/mod.rs
+++ b/src/librustc_traits/chalk_context/mod.rs
@@ -9,13 +9,25 @@
 // except according to those terms.
 
 mod program_clauses;
-
-use chalk_engine::fallible::Fallible as ChalkEngineFallible;
-use chalk_engine::{context, hh::HhGoal, DelayedLiteral, Literal, ExClause};
+mod resolvent_ops;
+mod unify;
+
+use chalk_engine::fallible::{Fallible, NoSolution};
+use chalk_engine::{
+    context,
+    hh::HhGoal,
+    DelayedLiteral,
+    Literal,
+    ExClause
+};
+use rustc::infer::{InferCtxt, LateBoundRegionConversionTime};
 use rustc::infer::canonical::{
-    Canonical, CanonicalVarValues, OriginalQueryValues, QueryRegionConstraint, QueryResponse,
+    Canonical,
+    CanonicalVarValues,
+    OriginalQueryValues,
+    QueryResponse,
+    Certainty,
 };
-use rustc::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime};
 use rustc::traits::{
     DomainGoal,
     ExClauseFold,
@@ -27,14 +39,15 @@ use rustc::traits::{
     Environment,
     InEnvironment,
 };
+use rustc::ty::{self, TyCtxt};
 use rustc::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
 use rustc::ty::subst::{Kind, UnpackedKind};
-use rustc::ty::{self, TyCtxt};
+use syntax_pos::DUMMY_SP;
 
 use std::fmt::{self, Debug};
 use std::marker::PhantomData;
 
-use syntax_pos::DUMMY_SP;
+use self::unify::*;
 
 #[derive(Copy, Clone, Debug)]
 crate struct ChalkArenas<'gcx> {
@@ -55,10 +68,12 @@ crate struct ChalkInferenceContext<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
 #[derive(Copy, Clone, Debug)]
 crate struct UniverseMap;
 
+crate type RegionConstraint<'tcx> = ty::OutlivesPredicate<Kind<'tcx>, ty::Region<'tcx>>;
+
 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
 crate struct ConstrainedSubst<'tcx> {
     subst: CanonicalVarValues<'tcx>,
-    constraints: Vec<QueryRegionConstraint<'tcx>>,
+    constraints: Vec<RegionConstraint<'tcx>>,
 }
 
 BraceStructTypeFoldableImpl! {
@@ -86,7 +101,7 @@ impl context::Context for ChalkArenas<'tcx> {
 
     type GoalInEnvironment = InEnvironment<'tcx, Goal<'tcx>>;
 
-    type RegionConstraint = QueryRegionConstraint<'tcx>;
+    type RegionConstraint = RegionConstraint<'tcx>;
 
     type Substitution = CanonicalVarValues<'tcx>;
 
@@ -104,7 +119,7 @@ impl context::Context for ChalkArenas<'tcx> {
 
     type ProgramClauses = Vec<Clause<'tcx>>;
 
-    type UnificationResult = InferOk<'tcx, ()>;
+    type UnificationResult = UnificationResult<'tcx>;
 
     fn goal_in_environment(
         env: &Environment<'tcx>,
@@ -118,9 +133,34 @@ impl context::AggregateOps<ChalkArenas<'gcx>> for ChalkContext<'cx, 'gcx> {
     fn make_solution(
         &self,
         _root_goal: &Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>>,
-        _simplified_answers: impl context::AnswerStream<ChalkArenas<'gcx>>,
+        mut simplified_answers: impl context::AnswerStream<ChalkArenas<'gcx>>,
     ) -> Option<Canonical<'gcx, QueryResponse<'gcx, ()>>> {
-        unimplemented!()
+        use chalk_engine::SimplifiedAnswer;
+
+        if simplified_answers.peek_answer().is_none() {
+            return None;
+        }
+
+        let SimplifiedAnswer { subst, ambiguous } = simplified_answers
+            .next_answer()
+            .unwrap();
+
+        let ambiguous = simplified_answers.peek_answer().is_some() || ambiguous;
+
+        Some(subst.unchecked_map(|subst| {
+            QueryResponse {
+                var_values: subst.subst,
+                region_constraints: subst.constraints
+                    .into_iter()
+                    .map(|c| ty::Binder::bind(c))
+                    .collect(),
+                certainty: match ambiguous {
+                    true => Certainty::Ambiguous,
+                    false => Certainty::Proven,
+                },
+                value: (),
+            }
+        }))
     }
 }
 
@@ -197,7 +237,7 @@ impl context::ContextOps<ChalkArenas<'gcx>> for ChalkContext<'cx, 'gcx> {
 
     fn is_trivial_substitution(
         u_canon: &Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>>,
-        canonical_subst: &Canonical<'tcx, ConstrainedSubst<'tcx>>,
+        canonical_subst: &Canonical<'gcx, ConstrainedSubst<'gcx>>,
     ) -> bool {
         let subst = &canonical_subst.value.subst;
         assert_eq!(u_canon.variables.len(), subst.var_values.len());
@@ -282,30 +322,6 @@ impl context::InferenceTable<ChalkArenas<'gcx>, ChalkArenas<'tcx>>
     }
 }
 
-impl context::ResolventOps<ChalkArenas<'gcx>, ChalkArenas<'tcx>>
-    for ChalkInferenceContext<'cx, 'gcx, 'tcx>
-{
-    fn resolvent_clause(
-        &mut self,
-        _environment: &Environment<'tcx>,
-        _goal: &DomainGoal<'tcx>,
-        _subst: &CanonicalVarValues<'tcx>,
-        _clause: &Clause<'tcx>,
-    ) -> chalk_engine::fallible::Fallible<Canonical<'gcx, ChalkExClause<'gcx>>> {
-        panic!()
-    }
-
-    fn apply_answer_subst(
-        &mut self,
-        _ex_clause: ChalkExClause<'tcx>,
-        _selected_goal: &InEnvironment<'tcx, Goal<'tcx>>,
-        _answer_table_goal: &Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>>,
-        _canonical_answer_subst: &Canonical<'gcx, ConstrainedSubst<'gcx>>,
-    ) -> chalk_engine::fallible::Fallible<ChalkExClause<'tcx>> {
-        panic!()
-    }
-}
-
 impl context::TruncateOps<ChalkArenas<'gcx>, ChalkArenas<'tcx>>
     for ChalkInferenceContext<'cx, 'gcx, 'tcx>
 {
@@ -376,7 +392,7 @@ impl context::UnificationOps<ChalkArenas<'gcx>, ChalkArenas<'tcx>>
     fn canonicalize_constrained_subst(
         &mut self,
         subst: CanonicalVarValues<'tcx>,
-        constraints: Vec<QueryRegionConstraint<'tcx>>,
+        constraints: Vec<RegionConstraint<'tcx>>,
     ) -> Canonical<'gcx, ConstrainedSubst<'gcx>> {
         self.infcx.canonicalize_response(&ConstrainedSubst { subst, constraints })
     }
@@ -400,11 +416,13 @@ impl context::UnificationOps<ChalkArenas<'gcx>, ChalkArenas<'tcx>>
 
     fn unify_parameters(
         &mut self,
-        _environment: &Environment<'tcx>,
-        _a: &Kind<'tcx>,
-        _b: &Kind<'tcx>,
-    ) -> ChalkEngineFallible<InferOk<'tcx, ()>> {
-        panic!()
+        environment: &Environment<'tcx>,
+        a: &Kind<'tcx>,
+        b: &Kind<'tcx>,
+    ) -> Fallible<UnificationResult<'tcx>> {
+        self.infcx.commit_if_ok(|_| {
+            unify(self.infcx, *environment, a, b).map_err(|_| NoSolution)
+        })
     }
 
     fn sink_answer_subset(
@@ -421,11 +439,22 @@ impl context::UnificationOps<ChalkArenas<'gcx>, ChalkArenas<'tcx>>
         panic!("lift")
     }
 
-    fn into_ex_clause(&mut self, _result: InferOk<'tcx, ()>, _ex_clause: &mut ChalkExClause<'tcx>) {
-        panic!("TBD")
+    fn into_ex_clause(
+        &mut self,
+        result: UnificationResult<'tcx>,
+        ex_clause: &mut ChalkExClause<'tcx>
+    ) {
+        into_ex_clause(result, ex_clause);
     }
 }
 
+crate fn into_ex_clause(result: UnificationResult<'tcx>, ex_clause: &mut ChalkExClause<'tcx>) {
+    ex_clause.subgoals.extend(
+        result.goals.into_iter().map(Literal::Positive)
+    );
+    ex_clause.constraints.extend(result.constraints);
+}
+
 type ChalkHhGoal<'tcx> = HhGoal<ChalkArenas<'tcx>>;
 
 type ChalkExClause<'tcx> = ExClause<ChalkArenas<'tcx>>;
diff --git a/src/librustc_traits/chalk_context/resolvent_ops.rs b/src/librustc_traits/chalk_context/resolvent_ops.rs
new file mode 100644
index 00000000000..df6458a766d
--- /dev/null
+++ b/src/librustc_traits/chalk_context/resolvent_ops.rs
@@ -0,0 +1,241 @@
+use chalk_engine::fallible::{Fallible, NoSolution};
+use chalk_engine::{
+    context,
+    Literal,
+    ExClause
+};
+use rustc::infer::{InferCtxt, LateBoundRegionConversionTime};
+use rustc::infer::canonical::{Canonical, CanonicalVarValues};
+use rustc::traits::{
+    DomainGoal,
+    Goal,
+    GoalKind,
+    Clause,
+    ProgramClause,
+    Environment,
+    InEnvironment,
+};
+use rustc::ty::{self, Ty};
+use rustc::ty::subst::Kind;
+use rustc::ty::relate::{Relate, RelateResult, TypeRelation};
+use syntax_pos::DUMMY_SP;
+
+use super::{ChalkInferenceContext, ChalkArenas, ChalkExClause, ConstrainedSubst};
+use super::unify::*;
+
+impl context::ResolventOps<ChalkArenas<'gcx>, ChalkArenas<'tcx>>
+    for ChalkInferenceContext<'cx, 'gcx, 'tcx>
+{
+    fn resolvent_clause(
+        &mut self,
+        environment: &Environment<'tcx>,
+        goal: &DomainGoal<'tcx>,
+        subst: &CanonicalVarValues<'tcx>,
+        clause: &Clause<'tcx>,
+    ) -> Fallible<Canonical<'gcx, ChalkExClause<'gcx>>> {
+        use chalk_engine::context::UnificationOps;
+
+        self.infcx.probe(|_| {
+            let ProgramClause {
+                goal: consequence,
+                hypotheses,
+                ..
+            } = match clause {
+                Clause::Implies(program_clause) => *program_clause,
+                Clause::ForAll(program_clause) => self.infcx.replace_bound_vars_with_fresh_vars(
+                    DUMMY_SP,
+                    LateBoundRegionConversionTime::HigherRankedType,
+                    program_clause
+                ).0,
+            };
+
+            let result = unify(self.infcx, *environment, goal, &consequence)
+                .map_err(|_| NoSolution)?;
+
+            let mut ex_clause = ExClause {
+                subst: subst.clone(),
+                delayed_literals: vec![],
+                constraints: vec![],
+                subgoals: vec![],
+            };
+
+            self.into_ex_clause(result, &mut ex_clause);
+
+            ex_clause.subgoals.extend(
+                hypotheses.iter().map(|g| match g {
+                    GoalKind::Not(g) => Literal::Negative(environment.with(*g)),
+                    g => Literal::Positive(environment.with(*g)),
+                })
+            );
+
+            let canonical_ex_clause = self.canonicalize_ex_clause(&ex_clause);
+            Ok(canonical_ex_clause)
+        })
+    }
+
+    fn apply_answer_subst(
+        &mut self,
+        ex_clause: ChalkExClause<'tcx>,
+        selected_goal: &InEnvironment<'tcx, Goal<'tcx>>,
+        answer_table_goal: &Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>>,
+        canonical_answer_subst: &Canonical<'gcx, ConstrainedSubst<'gcx>>,
+    ) -> Fallible<ChalkExClause<'tcx>> {
+        let (answer_subst, _) = self.infcx.instantiate_canonical_with_fresh_inference_vars(
+            DUMMY_SP,
+            canonical_answer_subst
+        );
+
+        let mut substitutor = AnswerSubstitutor {
+            infcx: self.infcx,
+            environment: selected_goal.environment,
+            answer_subst: answer_subst.subst,
+            binder_index: ty::INNERMOST,
+            ex_clause,
+        };
+
+        substitutor.relate(&answer_table_goal.value, &selected_goal)
+            .map_err(|_| NoSolution)?;
+
+        let mut ex_clause = substitutor.ex_clause;
+        ex_clause.constraints.extend(answer_subst.constraints);
+        Ok(ex_clause)
+    }
+}
+
+struct AnswerSubstitutor<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
+    infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
+    environment: Environment<'tcx>,
+    answer_subst: CanonicalVarValues<'tcx>,
+    binder_index: ty::DebruijnIndex,
+    ex_clause: ChalkExClause<'tcx>,
+}
+
+impl AnswerSubstitutor<'cx, 'gcx, 'tcx> {
+    fn unify_free_answer_var(
+        &mut self,
+        answer_var: ty::BoundVar,
+        pending: Kind<'tcx>
+    ) -> RelateResult<'tcx, ()> {
+        let answer_param = &self.answer_subst.var_values[answer_var];
+        let pending = &ty::fold::shift_out_vars(
+            self.infcx.tcx,
+            &pending,
+            self.binder_index.as_u32()
+        );
+
+        super::into_ex_clause(
+            unify(self.infcx, self.environment, answer_param, pending)?,
+            &mut self.ex_clause
+        );
+
+        Ok(())
+    }
+}
+
+impl TypeRelation<'cx, 'gcx, 'tcx> for AnswerSubstitutor<'cx, 'gcx, 'tcx> {
+    fn tcx(&self) -> ty::TyCtxt<'cx, 'gcx, 'tcx> {
+        self.infcx.tcx
+    }
+
+    fn tag(&self) -> &'static str {
+        "chalk_context::answer_substitutor"
+    }
+
+    fn a_is_expected(&self) -> bool {
+        true
+    }
+
+    fn relate_with_variance<T: Relate<'tcx>>(
+        &mut self,
+        _variance: ty::Variance,
+        a: &T,
+        b: &T,
+    ) -> RelateResult<'tcx, T> {
+        // We don't care about variance.
+        self.relate(a, b)
+    }
+
+    fn binders<T: Relate<'tcx>>(
+        &mut self,
+        a: &ty::Binder<T>,
+        b: &ty::Binder<T>,
+    ) -> RelateResult<'tcx, ty::Binder<T>> {
+        self.binder_index.shift_in(1);
+        let result = self.relate(a.skip_binder(), b.skip_binder())?;
+        self.binder_index.shift_out(1);
+        Ok(ty::Binder::bind(result))
+    }
+
+    fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
+        let b = self.infcx.shallow_resolve(b);
+
+        if let &ty::Bound(debruijn, bound_ty) = &a.sty {
+            // Free bound var
+            if debruijn == self.binder_index {
+                self.unify_free_answer_var(bound_ty.var, b.into())?;
+                return Ok(b);
+            }
+        }
+
+        match (&a.sty, &b.sty) {
+            (&ty::Bound(a_debruijn, a_bound), &ty::Bound(b_debruijn, b_bound)) => {
+                assert_eq!(a_debruijn, b_debruijn);
+                assert_eq!(a_bound.var, b_bound.var);
+                Ok(a)
+            }
+
+            // Those should have been canonicalized away.
+            (ty::Placeholder(..), _) => {
+                bug!("unexpected placeholder ty in `AnswerSubstitutor`: {:?} ", a);
+            }
+
+            // Everything else should just be a perfect match as well,
+            // and we forbid inference variables.
+            _ => match ty::relate::super_relate_tys(self, a, b) {
+                Ok(ty) => Ok(ty),
+                Err(err) => bug!("type mismatch in `AnswerSubstitutor`: {}", err),
+            }
+        }
+    }
+
+    fn regions(
+        &mut self,
+        a: ty::Region<'tcx>,
+        b: ty::Region<'tcx>,
+    ) -> RelateResult<'tcx, ty::Region<'tcx>> {
+        let b = match b {
+            &ty::ReVar(vid) => self.infcx
+                .borrow_region_constraints()
+                .opportunistic_resolve_var(self.infcx.tcx, vid),
+
+            other => other,
+        };
+
+        if let &ty::ReLateBound(debruijn, bound) = a {
+            // Free bound region
+            if debruijn == self.binder_index {
+                self.unify_free_answer_var(bound.assert_bound_var(), b.into())?;
+                return Ok(b);
+            }
+        }
+
+        match (a, b) {
+            (&ty::ReLateBound(a_debruijn, a_bound), &ty::ReLateBound(b_debruijn, b_bound)) => {
+                assert_eq!(a_debruijn, b_debruijn);
+                assert_eq!(a_bound.assert_bound_var(), b_bound.assert_bound_var());
+            }
+
+            (ty::ReStatic, ty::ReStatic) |
+            (ty::ReErased, ty::ReErased) |
+            (ty::ReEmpty, ty::ReEmpty) => (),
+
+            (&ty::ReFree(a_free), &ty::ReFree(b_free)) => {
+                assert_eq!(a_free, b_free);
+            }
+
+            _ => bug!("unexpected regions in `AnswerSubstitutor`: {:?}, {:?}", a, b),
+        }
+
+        Ok(a)
+    }
+}
diff --git a/src/librustc_traits/chalk_context/unify.rs b/src/librustc_traits/chalk_context/unify.rs
new file mode 100644
index 00000000000..3a9c3918d13
--- /dev/null
+++ b/src/librustc_traits/chalk_context/unify.rs
@@ -0,0 +1,98 @@
+use rustc::infer::nll_relate::{TypeRelating, TypeRelatingDelegate, NormalizationStrategy};
+use rustc::infer::{InferCtxt, RegionVariableOrigin};
+use rustc::traits::{DomainGoal, Goal, Environment, InEnvironment};
+use rustc::ty::relate::{Relate, TypeRelation, RelateResult};
+use rustc::ty;
+use syntax_pos::DUMMY_SP;
+
+crate struct UnificationResult<'tcx> {
+    crate goals: Vec<InEnvironment<'tcx, Goal<'tcx>>>,
+    crate constraints: Vec<super::RegionConstraint<'tcx>>,
+}
+
+crate fn unify<'me, 'gcx, 'tcx, T: Relate<'tcx>>(
+    infcx: &'me InferCtxt<'me, 'gcx, 'tcx>,
+    environment: Environment<'tcx>,
+    a: &T,
+    b: &T
+) -> RelateResult<'tcx, UnificationResult<'tcx>> {
+    let mut delegate = ChalkTypeRelatingDelegate::new(
+        infcx,
+        environment
+    );
+
+    TypeRelating::new(
+        infcx,
+        &mut delegate,
+        ty::Variance::Invariant
+    ).relate(a, b)?;
+
+    Ok(UnificationResult {
+        goals: delegate.goals,
+        constraints: delegate.constraints,
+    })
+}
+
+struct ChalkTypeRelatingDelegate<'me, 'gcx: 'tcx, 'tcx: 'me> {
+    infcx: &'me InferCtxt<'me, 'gcx, 'tcx>,
+    environment: Environment<'tcx>,
+    goals: Vec<InEnvironment<'tcx, Goal<'tcx>>>,
+    constraints: Vec<super::RegionConstraint<'tcx>>,
+}
+
+impl ChalkTypeRelatingDelegate<'me, 'gcx, 'tcx> {
+    fn new(
+        infcx: &'me InferCtxt<'me, 'gcx, 'tcx>,
+        environment: Environment<'tcx>,
+    ) -> Self {
+        Self {
+            infcx,
+            environment,
+            goals: Vec::new(),
+            constraints: Vec::new(),
+        }
+    }
+}
+
+impl TypeRelatingDelegate<'tcx> for &mut ChalkTypeRelatingDelegate<'_, '_, 'tcx> {
+    fn create_next_universe(&mut self) -> ty::UniverseIndex {
+        self.infcx.create_next_universe()
+    }
+
+    fn next_existential_region_var(&mut self) -> ty::Region<'tcx> {
+        self.infcx.next_region_var(RegionVariableOrigin::MiscVariable(DUMMY_SP))
+    }
+
+    fn next_placeholder_region(
+        &mut self,
+        placeholder: ty::PlaceholderRegion
+    ) -> ty::Region<'tcx> {
+        self.infcx.tcx.mk_region(ty::RePlaceholder(placeholder))
+    }
+
+    fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> {
+        self.infcx.next_region_var_in_universe(
+            RegionVariableOrigin::MiscVariable(DUMMY_SP),
+            universe
+        )
+    }
+
+    fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>) {
+        self.constraints.push(ty::OutlivesPredicate(sup.into(), sub));
+    }
+
+    fn push_domain_goal(&mut self, domain_goal: DomainGoal<'tcx>) {
+        let goal = self.environment.with(
+            self.infcx.tcx.mk_goal(domain_goal.into_goal())
+        );
+        self.goals.push(goal);
+    }
+
+    fn normalization() -> NormalizationStrategy {
+        NormalizationStrategy::Lazy
+    }
+
+    fn forbid_inference_vars() -> bool {
+        false
+    }
+}
diff --git a/src/librustc_typeck/check_unused.rs b/src/librustc_typeck/check_unused.rs
index 22a96d4e908..103331894ff 100644
--- a/src/librustc_typeck/check_unused.rs
+++ b/src/librustc_typeck/check_unused.rs
@@ -185,7 +185,7 @@ fn unused_crates_lint<'tcx>(tcx: TyCtxt<'_, 'tcx, 'tcx>) {
             Some(orig_name) => format!("use {} as {};", orig_name, item.name),
             None => format!("use {};", item.name),
         };
-        let replacement = visibility_qualified(&item.vis, &base_replacement);
+        let replacement = visibility_qualified(&item.vis, base_replacement);
         tcx.struct_span_lint_node(lint, id, extern_crate.span, msg)
             .span_suggestion_short_with_applicability(
                 extern_crate.span,
diff --git a/src/libserialize/collection_impls.rs b/src/libserialize/collection_impls.rs
index 3e028d755c6..cbd642dd6ad 100644
--- a/src/libserialize/collection_impls.rs
+++ b/src/libserialize/collection_impls.rs
@@ -86,7 +86,7 @@ impl<T: Encodable> Encodable for VecDeque<T> {
 impl<T:Decodable> Decodable for VecDeque<T> {
     fn decode<D: Decoder>(d: &mut D) -> Result<VecDeque<T>, D::Error> {
         d.read_seq(|d, len| {
-            let mut deque: VecDeque<T> = VecDeque::new();
+            let mut deque: VecDeque<T> = VecDeque::with_capacity(len);
             for i in 0..len {
                 deque.push_back(d.read_seq_elt(i, |d| Decodable::decode(d))?);
             }
diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml
index 2b1d515c83b..c1446218367 100644
--- a/src/libstd/Cargo.toml
+++ b/src/libstd/Cargo.toml
@@ -47,6 +47,9 @@ backtrace = []
 panic-unwind = ["panic_unwind"]
 profiler = ["profiler_builtins"]
 
+# Make panics and failed asserts immediately abort without formatting any message
+panic_immediate_abort = ["core/panic_immediate_abort"]
+
 # An off-by-default feature which enables a linux-syscall-like ABI for libstd to
 # interoperate with the host environment. Currently not well documented and
 # requires rebuilding the standard library to use it.
diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs
index 7c717d832fa..349aa029ba8 100644
--- a/src/libstd/collections/hash/map.rs
+++ b/src/libstd/collections/hash/map.rs
@@ -849,6 +849,7 @@ impl<K, V, S> HashMap<K, V, S>
     /// let mut map: HashMap<&str, i32> = HashMap::new();
     /// map.reserve(10);
     /// ```
+    #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn reserve(&mut self, additional: usize) {
         match self.reserve_internal(additional, Infallible) {
@@ -880,6 +881,7 @@ impl<K, V, S> HashMap<K, V, S>
         self.reserve_internal(additional, Fallible)
     }
 
+    #[inline]
     fn reserve_internal(&mut self, additional: usize, fallibility: Fallibility)
         -> Result<(), CollectionAllocErr> {
 
@@ -1571,6 +1573,7 @@ impl<K, V, S> HashMap<K, V, S>
     /// so that the map now contains keys which compare equal, search may start
     /// acting erratically, with two keys randomly masking each other. Implementations
     /// are free to assume this doesn't happen (within the limits of memory-safety).
+    #[inline(always)]
     #[unstable(feature = "hash_raw_entry", issue = "56167")]
     pub fn raw_entry_mut(&mut self) -> RawEntryBuilderMut<K, V, S> {
         self.reserve(1);
@@ -1911,6 +1914,7 @@ impl<'a, K, V, S> RawEntryBuilderMut<'a, K, V, S>
     }
 
     /// Create a `RawEntryMut` from the given key and its hash.
+    #[inline]
     #[unstable(feature = "hash_raw_entry", issue = "56167")]
     pub fn from_key_hashed_nocheck<Q: ?Sized>(self, hash: u64, k: &Q) -> RawEntryMut<'a, K, V, S>
         where K: Borrow<Q>,
@@ -1919,6 +1923,7 @@ impl<'a, K, V, S> RawEntryBuilderMut<'a, K, V, S>
         self.from_hash(hash, |q| q.borrow().eq(k))
     }
 
+    #[inline]
     fn search<F>(self, hash: u64, is_match: F, compare_hashes: bool)  -> RawEntryMut<'a, K, V, S>
         where for<'b> F: FnMut(&'b K) -> bool,
     {
@@ -1941,6 +1946,7 @@ impl<'a, K, V, S> RawEntryBuilderMut<'a, K, V, S>
         }
     }
     /// Create a `RawEntryMut` from the given hash.
+    #[inline]
     #[unstable(feature = "hash_raw_entry", issue = "56167")]
     pub fn from_hash<F>(self, hash: u64, is_match: F) -> RawEntryMut<'a, K, V, S>
         where for<'b> F: FnMut(&'b K) -> bool,
@@ -2215,6 +2221,7 @@ impl<'a, K, V, S> RawVacantEntryMut<'a, K, V, S> {
 
     /// Sets the value of the entry with the VacantEntry's key,
     /// and returns a mutable reference to it.
+    #[inline]
     #[unstable(feature = "hash_raw_entry", issue = "56167")]
     pub fn insert_hashed_nocheck(self, hash: u64, key: K, value: V) -> (&'a mut K, &'a mut V) {
         let hash = SafeHash::new(hash);
diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs
index 547f97cc8ac..479e6dccb90 100644
--- a/src/libstd/collections/hash/table.rs
+++ b/src/libstd/collections/hash/table.rs
@@ -329,6 +329,7 @@ impl<K, V, M> Put<K, V> for FullBucket<K, V, M>
 }
 
 impl<K, V, M: Deref<Target = RawTable<K, V>>> Bucket<K, V, M> {
+    #[inline]
     pub fn new(table: M, hash: SafeHash) -> Bucket<K, V, M> {
         Bucket::at_index(table, hash.inspect() as usize)
     }
@@ -342,6 +343,7 @@ impl<K, V, M: Deref<Target = RawTable<K, V>>> Bucket<K, V, M> {
         }
     }
 
+    #[inline]
     pub fn at_index(table: M, ib_index: usize) -> Bucket<K, V, M> {
         // if capacity is 0, then the RawBucket will be populated with bogus pointers.
         // This is an uncommon case though, so avoid it in release builds.
@@ -654,6 +656,7 @@ impl<K, V, M> GapThenFull<K, V, M>
 
 // Returns a Layout which describes the allocation required for a hash table,
 // and the offset of the array of (key, value) pairs in the allocation.
+#[inline(always)]
 fn calculate_layout<K, V>(capacity: usize) -> Result<(Layout, usize), LayoutErr> {
     let hashes = Layout::array::<HashUint>(capacity)?;
     let pairs = Layout::array::<(K, V)>(capacity)?;
@@ -722,6 +725,7 @@ impl<K, V> RawTable<K, V> {
         }
     }
 
+    #[inline(always)]
     fn raw_bucket_at(&self, index: usize) -> RawBucket<K, V> {
         let (_, pairs_offset) = calculate_layout::<K, V>(self.capacity())
             .unwrap_or_else(|_| unsafe { hint::unreachable_unchecked() });
diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs
index a0590a800d3..4930d356608 100644
--- a/src/libstd/panicking.rs
+++ b/src/libstd/panicking.rs
@@ -334,9 +334,17 @@ pub fn rust_begin_panic(info: &PanicInfo) -> ! {
 #[unstable(feature = "libstd_sys_internals",
            reason = "used by the panic! macro",
            issue = "0")]
-#[inline(never)] #[cold]
+#[cold]
+// If panic_immediate_abort, inline the abort call,
+// otherwise avoid inlining because of it is cold path.
+#[cfg_attr(not(feature="panic_immediate_abort"),inline(never))]
+#[cfg_attr(    feature="panic_immediate_abort" ,inline)]
 pub fn begin_panic_fmt(msg: &fmt::Arguments,
                        file_line_col: &(&'static str, u32, u32)) -> ! {
+    if cfg!(feature = "panic_immediate_abort") {
+        unsafe { intrinsics::abort() }
+    }
+
     let (file, line, col) = *file_line_col;
     let info = PanicInfo::internal_constructor(
         Some(msg),
@@ -398,8 +406,15 @@ fn continue_panic_fmt(info: &PanicInfo) -> ! {
            reason = "used by the panic! macro",
            issue = "0")]
 #[cfg_attr(not(test), lang = "begin_panic")]
-#[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible
+// never inline unless panic_immediate_abort to avoid code
+// bloat at the call sites as much as possible
+#[cfg_attr(not(feature="panic_immediate_abort"),inline(never))]
+#[cold]
 pub fn begin_panic<M: Any + Send>(msg: M, file_line_col: &(&'static str, u32, u32)) -> ! {
+    if cfg!(feature = "panic_immediate_abort") {
+        unsafe { intrinsics::abort() }
+    }
+
     // Note that this should be the only allocation performed in this code path.
     // Currently this means that panic!() on OOM will invoke this code path,
     // but then again we're not really ready for panic on OOM anyway. If
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index cc8af70a050..68a96293891 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -1201,50 +1201,62 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
 
 impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
     fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
-        let mut expr = self.cfg.configure_expr(expr).into_inner();
-        expr.node = self.cfg.configure_expr_kind(expr.node);
-
-        // ignore derives so they remain unused
-        let (attr, expr, after_derive) = self.classify_nonitem(expr);
-
-        if attr.is_some() {
-            // collect the invoc regardless of whether or not attributes are permitted here
-            // expansion will eat the attribute so it won't error later
-            attr.as_ref().map(|a| self.cfg.maybe_emit_expr_attr_err(a));
-
-            // AstFragmentKind::Expr requires the macro to emit an expression
-            return self.collect_attr(attr, vec![], Annotatable::Expr(P(expr)),
-                                     AstFragmentKind::Expr, after_derive).make_expr();
-        }
+        let expr = self.cfg.configure_expr(expr);
+        expr.map(|mut expr| {
+            expr.node = self.cfg.configure_expr_kind(expr.node);
+
+            // ignore derives so they remain unused
+            let (attr, expr, after_derive) = self.classify_nonitem(expr);
+
+            if attr.is_some() {
+                // Collect the invoc regardless of whether or not attributes are permitted here
+                // expansion will eat the attribute so it won't error later.
+                attr.as_ref().map(|a| self.cfg.maybe_emit_expr_attr_err(a));
+
+                // AstFragmentKind::Expr requires the macro to emit an expression.
+                return self.collect_attr(attr, vec![], Annotatable::Expr(P(expr)),
+                                         AstFragmentKind::Expr, after_derive)
+                    .make_expr()
+                    .into_inner()
+            }
 
-        if let ast::ExprKind::Mac(mac) = expr.node {
-            self.check_attributes(&expr.attrs);
-            self.collect_bang(mac, expr.span, AstFragmentKind::Expr).make_expr()
-        } else {
-            P(noop_fold_expr(expr, self))
-        }
+            if let ast::ExprKind::Mac(mac) = expr.node {
+                self.check_attributes(&expr.attrs);
+                self.collect_bang(mac, expr.span, AstFragmentKind::Expr)
+                    .make_expr()
+                    .into_inner()
+            } else {
+                noop_fold_expr(expr, self)
+            }
+        })
     }
 
     fn fold_opt_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
-        let mut expr = configure!(self, expr).into_inner();
-        expr.node = self.cfg.configure_expr_kind(expr.node);
+        let expr = configure!(self, expr);
+        expr.filter_map(|mut expr| {
+            expr.node = self.cfg.configure_expr_kind(expr.node);
 
-        // ignore derives so they remain unused
-        let (attr, expr, after_derive) = self.classify_nonitem(expr);
+            // Ignore derives so they remain unused.
+            let (attr, expr, after_derive) = self.classify_nonitem(expr);
 
-        if attr.is_some() {
-            attr.as_ref().map(|a| self.cfg.maybe_emit_expr_attr_err(a));
+            if attr.is_some() {
+                attr.as_ref().map(|a| self.cfg.maybe_emit_expr_attr_err(a));
 
-            return self.collect_attr(attr, vec![], Annotatable::Expr(P(expr)),
-                                     AstFragmentKind::OptExpr, after_derive).make_opt_expr();
-        }
+                return self.collect_attr(attr, vec![], Annotatable::Expr(P(expr)),
+                                         AstFragmentKind::OptExpr, after_derive)
+                    .make_opt_expr()
+                    .map(|expr| expr.into_inner())
+            }
 
-        if let ast::ExprKind::Mac(mac) = expr.node {
-            self.check_attributes(&expr.attrs);
-            self.collect_bang(mac, expr.span, AstFragmentKind::OptExpr).make_opt_expr()
-        } else {
-            Some(P(noop_fold_expr(expr, self)))
-        }
+            if let ast::ExprKind::Mac(mac) = expr.node {
+                self.check_attributes(&expr.attrs);
+                self.collect_bang(mac, expr.span, AstFragmentKind::OptExpr)
+                    .make_opt_expr()
+                    .map(|expr| expr.into_inner())
+            } else {
+                Some(noop_fold_expr(expr, self))
+            }
+        })
     }
 
     fn fold_pat(&mut self, pat: P<ast::Pat>) -> P<ast::Pat> {
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index c93abc39ff3..2402de5a816 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -475,9 +475,6 @@ declare_features! (
     // Non-builtin attributes in inner attribute position
     (active, custom_inner_attributes, "1.30.0", Some(54726), None),
 
-    // Self struct constructor  (RFC 2302)
-    (active, self_struct_ctor, "1.30.0", Some(51994), None),
-
     // allow mixing of bind-by-move in patterns and references to
     // those identifiers in guards, *if* we are using MIR-borrowck
     // (aka NLL). Essentially this means you need to be on
@@ -688,9 +685,11 @@ declare_features! (
     (accepted, macro_literal_matcher, "1.31.0", Some(35625), None),
     // Use `?` as the Kleene "at most one" operator
     (accepted, macro_at_most_once_rep, "1.32.0", Some(48075), None),
+    // Self struct constructor  (RFC 2302)
+    (accepted, self_struct_ctor, "1.32.0", Some(51994), None),
 );
 
-// If you change this, please modify src/doc/unstable-book as well. You must
+// If you change this, please modify `src/doc/unstable-book` as well. You must
 // move that documentation into the relevant place in the other docs, and
 // remove the chapter on the flag.
 
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 506199b60ad..f13bb7df0b4 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -2791,7 +2791,7 @@ impl<'a> Parser<'a> {
                             s.print_usize(float.trunc() as usize)?;
                             s.pclose()?;
                             s.s.word(".")?;
-                            s.s.word(fstr.splitn(2, ".").last().unwrap())
+                            s.s.word(fstr.splitn(2, ".").last().unwrap().to_string())
                         });
                         err.span_suggestion_with_applicability(
                             lo.to(self.prev_span),
diff --git a/src/libsyntax/print/pp.rs b/src/libsyntax/print/pp.rs
index 086de35d531..aaed56da29d 100644
--- a/src/libsyntax/print/pp.rs
+++ b/src/libsyntax/print/pp.rs
@@ -140,13 +140,14 @@
 //! calculation, SCAN will write "infinity" to the size and let PRINT consume
 //! it.
 //!
-//! In this implementation (following the paper, again) the SCAN process is
-//! the method called `Printer::pretty_print`, and the 'PRINT' process is the method
-//! called `Printer::print`.
+//! In this implementation (following the paper, again) the SCAN process is the
+//! methods called `Printer::pretty_print_*`, and the 'PRINT' process is the
+//! method called `Printer::print`.
 
 use std::collections::VecDeque;
 use std::fmt;
 use std::io;
+use std::borrow::Cow;
 
 /// How to break. Described in more detail in the module docs.
 #[derive(Clone, Copy, PartialEq)]
@@ -169,7 +170,10 @@ pub struct BeginToken {
 
 #[derive(Clone)]
 pub enum Token {
-    String(String, isize),
+    // In practice a string token contains either a `&'static str` or a
+    // `String`. `Cow` is overkill for this because we never modify the data,
+    // but it's more convenient than rolling our own more specialized type.
+    String(Cow<'static, str>, isize),
     Break(BreakToken),
     Begin(BeginToken),
     End,
@@ -309,84 +313,86 @@ impl<'a> Printer<'a> {
     pub fn last_token(&mut self) -> Token {
         self.buf[self.right].token.clone()
     }
-    /// be very careful with this!
+
+    /// Be very careful with this!
     pub fn replace_last_token(&mut self, t: Token) {
         self.buf[self.right].token = t;
     }
-    pub fn pretty_print(&mut self, token: Token) -> io::Result<()> {
-        debug!("pp Vec<{},{}>", self.left, self.right);
-        match token {
-          Token::Eof => {
-            if !self.scan_stack.is_empty() {
-                self.check_stack(0);
-                self.advance_left()?;
-            }
-            self.indent(0);
-            Ok(())
-          }
-          Token::Begin(b) => {
-            if self.scan_stack.is_empty() {
-                self.left_total = 1;
-                self.right_total = 1;
-                self.left = 0;
-                self.right = 0;
-            } else {
-                self.advance_right();
-            }
-            debug!("pp Begin({})/buffer Vec<{},{}>",
-                   b.offset, self.left, self.right);
-            self.buf[self.right] = BufEntry { token: token, size: -self.right_total };
-            let right = self.right;
-            self.scan_push(right);
-            Ok(())
-          }
-          Token::End => {
-            if self.scan_stack.is_empty() {
-                debug!("pp End/print Vec<{},{}>", self.left, self.right);
-                self.print(token, 0)
-            } else {
-                debug!("pp End/buffer Vec<{},{}>", self.left, self.right);
-                self.advance_right();
-                self.buf[self.right] = BufEntry { token: token, size: -1 };
-                let right = self.right;
-                self.scan_push(right);
-                Ok(())
-            }
-          }
-          Token::Break(b) => {
-            if self.scan_stack.is_empty() {
-                self.left_total = 1;
-                self.right_total = 1;
-                self.left = 0;
-                self.right = 0;
-            } else {
-                self.advance_right();
-            }
-            debug!("pp Break({})/buffer Vec<{},{}>",
-                   b.offset, self.left, self.right);
+
+    fn pretty_print_eof(&mut self) -> io::Result<()> {
+        if !self.scan_stack.is_empty() {
             self.check_stack(0);
+            self.advance_left()?;
+        }
+        self.indent(0);
+        Ok(())
+    }
+
+    fn pretty_print_begin(&mut self, b: BeginToken) -> io::Result<()> {
+        if self.scan_stack.is_empty() {
+            self.left_total = 1;
+            self.right_total = 1;
+            self.left = 0;
+            self.right = 0;
+        } else {
+            self.advance_right();
+        }
+        debug!("pp Begin({})/buffer Vec<{},{}>",
+               b.offset, self.left, self.right);
+        self.buf[self.right] = BufEntry { token: Token::Begin(b), size: -self.right_total };
+        let right = self.right;
+        self.scan_push(right);
+        Ok(())
+    }
+
+    fn pretty_print_end(&mut self) -> io::Result<()> {
+        if self.scan_stack.is_empty() {
+            debug!("pp End/print Vec<{},{}>", self.left, self.right);
+            self.print_end()
+        } else {
+            debug!("pp End/buffer Vec<{},{}>", self.left, self.right);
+            self.advance_right();
+            self.buf[self.right] = BufEntry { token: Token::End, size: -1 };
             let right = self.right;
             self.scan_push(right);
-            self.buf[self.right] = BufEntry { token: token, size: -self.right_total };
-            self.right_total += b.blank_space;
             Ok(())
-          }
-          Token::String(s, len) => {
-            if self.scan_stack.is_empty() {
-                debug!("pp String('{}')/print Vec<{},{}>",
-                       s, self.left, self.right);
-                self.print(Token::String(s, len), len)
-            } else {
-                debug!("pp String('{}')/buffer Vec<{},{}>",
-                       s, self.left, self.right);
-                self.advance_right();
-                self.buf[self.right] = BufEntry { token: Token::String(s, len), size: len };
-                self.right_total += len;
-                self.check_stream()
-            }
-          }
         }
     }
+
+    fn pretty_print_break(&mut self, b: BreakToken) -> io::Result<()> {
+        if self.scan_stack.is_empty() {
+            self.left_total = 1;
+            self.right_total = 1;
+            self.left = 0;
+            self.right = 0;
+        } else {
+            self.advance_right();
+        }
+        debug!("pp Break({})/buffer Vec<{},{}>",
+               b.offset, self.left, self.right);
+        self.check_stack(0);
+        let right = self.right;
+        self.scan_push(right);
+        self.buf[self.right] = BufEntry { token: Token::Break(b), size: -self.right_total };
+        self.right_total += b.blank_space;
+        Ok(())
+    }
+
+    fn pretty_print_string(&mut self, s: Cow<'static, str>, len: isize) -> io::Result<()> {
+        if self.scan_stack.is_empty() {
+            debug!("pp String('{}')/print Vec<{},{}>",
+                   s, self.left, self.right);
+            self.print_string(s, len)
+        } else {
+            debug!("pp String('{}')/buffer Vec<{},{}>",
+                   s, self.left, self.right);
+            self.advance_right();
+            self.buf[self.right] = BufEntry { token: Token::String(s, len), size: len };
+            self.right_total += len;
+            self.check_stream()
+        }
+    }
+
     pub fn check_stream(&mut self) -> io::Result<()> {
         debug!("check_stream Vec<{}, {}> with left_total={}, right_total={}",
                self.left, self.right, self.left_total, self.right_total);
@@ -405,19 +411,24 @@ impl<'a> Printer<'a> {
         }
         Ok(())
     }
+
     pub fn scan_push(&mut self, x: usize) {
         debug!("scan_push {}", x);
         self.scan_stack.push_front(x);
     }
+
     pub fn scan_pop(&mut self) -> usize {
         self.scan_stack.pop_front().unwrap()
     }
+
     pub fn scan_top(&mut self) -> usize {
         *self.scan_stack.front().unwrap()
     }
+
     pub fn scan_pop_bottom(&mut self) -> usize {
         self.scan_stack.pop_back().unwrap()
     }
+
     pub fn advance_right(&mut self) {
         self.right += 1;
         self.right %= self.buf_max_len;
@@ -427,6 +438,7 @@ impl<'a> Printer<'a> {
         }
         assert_ne!(self.right, self.left);
     }
+
     pub fn advance_left(&mut self) -> io::Result<()> {
         debug!("advance_left Vec<{},{}>, sizeof({})={}", self.left, self.right,
                self.left, self.buf[self.left].size);
@@ -461,6 +473,7 @@ impl<'a> Printer<'a> {
 
         Ok(())
     }
+
     pub fn check_stack(&mut self, k: isize) {
         if !self.scan_stack.is_empty() {
             let x = self.scan_top();
@@ -488,6 +501,7 @@ impl<'a> Printer<'a> {
             }
         }
     }
+
     pub fn print_newline(&mut self, amount: isize) -> io::Result<()> {
         debug!("NEWLINE {}", amount);
         let ret = write!(self.out, "\n");
@@ -495,10 +509,12 @@ impl<'a> Printer<'a> {
         self.indent(amount);
         ret
     }
+
     pub fn indent(&mut self, amount: isize) {
         debug!("INDENT {}", amount);
         self.pending_indentation += amount;
     }
+
     pub fn get_top(&mut self) -> PrintStackElem {
         match self.print_stack.last() {
             Some(el) => *el,
@@ -508,62 +524,50 @@ impl<'a> Printer<'a> {
             }
         }
     }
-    pub fn print_str(&mut self, s: &str) -> io::Result<()> {
-        while self.pending_indentation > 0 {
-            write!(self.out, " ")?;
-            self.pending_indentation -= 1;
+
+    pub fn print_begin(&mut self, b: BeginToken, l: isize) -> io::Result<()> {
+        if l > self.space {
+            let col = self.margin - self.space + b.offset;
+            debug!("print Begin -> push broken block at col {}", col);
+            self.print_stack.push(PrintStackElem {
+                offset: col,
+                pbreak: PrintStackBreak::Broken(b.breaks)
+            });
+        } else {
+            debug!("print Begin -> push fitting block");
+            self.print_stack.push(PrintStackElem {
+                offset: 0,
+                pbreak: PrintStackBreak::Fits
+            });
         }
-        write!(self.out, "{}", s)
+        Ok(())
     }
-    pub fn print(&mut self, token: Token, l: isize) -> io::Result<()> {
-        debug!("print {} {} (remaining line space={})", token, l,
-               self.space);
-        debug!("{}", buf_str(&self.buf,
-                             self.left,
-                             self.right,
-                             6));
-        match token {
-          Token::Begin(b) => {
-            if l > self.space {
-                let col = self.margin - self.space + b.offset;
-                debug!("print Begin -> push broken block at col {}", col);
-                self.print_stack.push(PrintStackElem {
-                    offset: col,
-                    pbreak: PrintStackBreak::Broken(b.breaks)
-                });
-            } else {
-                debug!("print Begin -> push fitting block");
-                self.print_stack.push(PrintStackElem {
-                    offset: 0,
-                    pbreak: PrintStackBreak::Fits
-                });
-            }
-            Ok(())
-          }
-          Token::End => {
-            debug!("print End -> pop End");
-            let print_stack = &mut self.print_stack;
-            assert!(!print_stack.is_empty());
-            print_stack.pop().unwrap();
-            Ok(())
-          }
-          Token::Break(b) => {
-            let top = self.get_top();
-            match top.pbreak {
-              PrintStackBreak::Fits => {
+
+    pub fn print_end(&mut self) -> io::Result<()> {
+        debug!("print End -> pop End");
+        let print_stack = &mut self.print_stack;
+        assert!(!print_stack.is_empty());
+        print_stack.pop().unwrap();
+        Ok(())
+    }
+
+    pub fn print_break(&mut self, b: BreakToken, l: isize) -> io::Result<()> {
+        let top = self.get_top();
+        match top.pbreak {
+            PrintStackBreak::Fits => {
                 debug!("print Break({}) in fitting block", b.blank_space);
                 self.space -= b.blank_space;
                 self.indent(b.blank_space);
                 Ok(())
-              }
-              PrintStackBreak::Broken(Breaks::Consistent) => {
+            }
+            PrintStackBreak::Broken(Breaks::Consistent) => {
                 debug!("print Break({}+{}) in consistent block",
                        top.offset, b.offset);
                 let ret = self.print_newline(top.offset + b.offset);
                 self.space = self.margin - (top.offset + b.offset);
                 ret
-              }
-              PrintStackBreak::Broken(Breaks::Inconsistent) => {
+            }
+            PrintStackBreak::Broken(Breaks::Inconsistent) => {
                 if l > self.space {
                     debug!("print Break({}+{}) w/ newline in inconsistent",
                            top.offset, b.offset);
@@ -577,20 +581,37 @@ impl<'a> Printer<'a> {
                     self.space -= b.blank_space;
                     Ok(())
                 }
-              }
             }
-          }
-          Token::String(ref s, len) => {
-            debug!("print String({})", s);
-            assert_eq!(l, len);
-            // assert!(l <= space);
-            self.space -= len;
-            self.print_str(s)
-          }
-          Token::Eof => {
-            // Eof should never get here.
-            panic!();
-          }
+        }
+    }
+
+    pub fn print_string(&mut self, s: Cow<'static, str>, len: isize) -> io::Result<()> {
+        debug!("print String({})", s);
+        // assert!(len <= space);
+        self.space -= len;
+        while self.pending_indentation > 0 {
+            write!(self.out, " ")?;
+            self.pending_indentation -= 1;
+        }
+        write!(self.out, "{}", s)
+    }
+
+    pub fn print(&mut self, token: Token, l: isize) -> io::Result<()> {
+        debug!("print {} {} (remaining line space={})", token, l,
+               self.space);
+        debug!("{}", buf_str(&self.buf,
+                             self.left,
+                             self.right,
+                             6));
+        match token {
+            Token::Begin(b) => self.print_begin(b, l),
+            Token::End => self.print_end(),
+            Token::Break(b) => self.print_break(b, l),
+            Token::String(s, len) => {
+                assert_eq!(len, l);
+                self.print_string(s, len)
+            }
+            Token::Eof => panic!(), // Eof should never get here.
         }
     }
 
@@ -598,10 +619,10 @@ impl<'a> Printer<'a> {
 
     /// "raw box"
     pub fn rbox(&mut self, indent: usize, b: Breaks) -> io::Result<()> {
-        self.pretty_print(Token::Begin(BeginToken {
+        self.pretty_print_begin(BeginToken {
             offset: indent as isize,
             breaks: b
-        }))
+        })
     }
 
     /// Inconsistent breaking box
@@ -615,30 +636,24 @@ impl<'a> Printer<'a> {
     }
 
     pub fn break_offset(&mut self, n: usize, off: isize) -> io::Result<()> {
-        self.pretty_print(Token::Break(BreakToken {
+        self.pretty_print_break(BreakToken {
             offset: off,
             blank_space: n as isize
-        }))
+        })
     }
 
     pub fn end(&mut self) -> io::Result<()> {
-        self.pretty_print(Token::End)
+        self.pretty_print_end()
     }
 
     pub fn eof(&mut self) -> io::Result<()> {
-        self.pretty_print(Token::Eof)
-    }
-
-    pub fn word(&mut self, wrd: &str) -> io::Result<()> {
-        self.pretty_print(Token::String(wrd.to_string(), wrd.len() as isize))
-    }
-
-    pub fn huge_word(&mut self, wrd: &str) -> io::Result<()> {
-        self.pretty_print(Token::String(wrd.to_string(), SIZE_INFINITY))
+        self.pretty_print_eof()
     }
 
-    pub fn zero_word(&mut self, wrd: &str) -> io::Result<()> {
-        self.pretty_print(Token::String(wrd.to_string(), 0))
+    pub fn word<S: Into<Cow<'static, str>>>(&mut self, wrd: S) -> io::Result<()> {
+        let s = wrd.into();
+        let len = s.len() as isize;
+        self.pretty_print_string(s, len)
     }
 
     fn spaces(&mut self, n: usize) -> io::Result<()> {
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index ce7708cc42e..14ad4b5c6f8 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -29,6 +29,7 @@ use syntax_pos::{DUMMY_SP, FileName};
 use tokenstream::{self, TokenStream, TokenTree};
 
 use std::ascii;
+use std::borrow::Cow;
 use std::io::{self, Write, Read};
 use std::iter::Peekable;
 use std::vec;
@@ -444,7 +445,7 @@ pub trait PrintState<'a> {
     fn cur_lit(&mut self) -> Option<&comments::Literal>;
     fn bump_lit(&mut self) -> Option<comments::Literal>;
 
-    fn word_space(&mut self, w: &str) -> io::Result<()> {
+    fn word_space<S: Into<Cow<'static, str>>>(&mut self, w: S) -> io::Result<()> {
         self.writer().word(w)?;
         self.writer().space()
     }
@@ -539,7 +540,7 @@ pub trait PrintState<'a> {
             comments::Mixed => {
                 assert_eq!(cmnt.lines.len(), 1);
                 self.writer().zerobreak()?;
-                self.writer().word(&cmnt.lines[0])?;
+                self.writer().word(cmnt.lines[0].clone())?;
                 self.writer().zerobreak()
             }
             comments::Isolated => {
@@ -548,7 +549,7 @@ pub trait PrintState<'a> {
                     // Don't print empty lines because they will end up as trailing
                     // whitespace
                     if !line.is_empty() {
-                        self.writer().word(&line[..])?;
+                        self.writer().word(line.clone())?;
                     }
                     self.writer().hardbreak()?;
                 }
@@ -559,13 +560,13 @@ pub trait PrintState<'a> {
                     self.writer().word(" ")?;
                 }
                 if cmnt.lines.len() == 1 {
-                    self.writer().word(&cmnt.lines[0])?;
+                    self.writer().word(cmnt.lines[0].clone())?;
                     self.writer().hardbreak()
                 } else {
                     self.ibox(0)?;
                     for line in &cmnt.lines {
                         if !line.is_empty() {
-                            self.writer().word(&line[..])?;
+                            self.writer().word(line.clone())?;
                         }
                         self.writer().hardbreak()?;
                     }
@@ -610,7 +611,7 @@ pub trait PrintState<'a> {
     fn print_literal(&mut self, lit: &ast::Lit) -> io::Result<()> {
         self.maybe_print_comment(lit.span.lo())?;
         if let Some(ltrl) = self.next_lit(lit.span.lo()) {
-            return self.writer().word(&ltrl.lit);
+            return self.writer().word(ltrl.lit.clone());
         }
         match lit.node {
             ast::LitKind::Str(st, style) => self.print_string(&st.as_str(), style),
@@ -618,31 +619,31 @@ pub trait PrintState<'a> {
                 let mut res = String::from("b'");
                 res.extend(ascii::escape_default(byte).map(|c| c as char));
                 res.push('\'');
-                self.writer().word(&res[..])
+                self.writer().word(res)
             }
             ast::LitKind::Char(ch) => {
                 let mut res = String::from("'");
                 res.extend(ch.escape_default());
                 res.push('\'');
-                self.writer().word(&res[..])
+                self.writer().word(res)
             }
             ast::LitKind::Int(i, t) => {
                 match t {
                     ast::LitIntType::Signed(st) => {
-                        self.writer().word(&st.val_to_string(i as i128))
+                        self.writer().word(st.val_to_string(i as i128))
                     }
                     ast::LitIntType::Unsigned(ut) => {
-                        self.writer().word(&ut.val_to_string(i))
+                        self.writer().word(ut.val_to_string(i))
                     }
                     ast::LitIntType::Unsuffixed => {
-                        self.writer().word(&i.to_string())
+                        self.writer().word(i.to_string())
                     }
                 }
             }
             ast::LitKind::Float(ref f, t) => {
-                self.writer().word(&format!("{}{}", &f, t.ty_to_string()))
+                self.writer().word(format!("{}{}", &f, t.ty_to_string()))
             }
-            ast::LitKind::FloatUnsuffixed(ref f) => self.writer().word(&f.as_str()),
+            ast::LitKind::FloatUnsuffixed(ref f) => self.writer().word(f.as_str().get()),
             ast::LitKind::Bool(val) => {
                 if val { self.writer().word("true") } else { self.writer().word("false") }
             }
@@ -652,7 +653,7 @@ pub trait PrintState<'a> {
                     escaped.extend(ascii::escape_default(ch)
                                          .map(|c| c as char));
                 }
-                self.writer().word(&format!("b\"{}\"", escaped))
+                self.writer().word(format!("b\"{}\"", escaped))
             }
         }
     }
@@ -669,7 +670,7 @@ pub trait PrintState<'a> {
                          string=st))
             }
         };
-        self.writer().word(&st[..])
+        self.writer().word(st)
     }
 
     fn print_inner_attributes(&mut self,
@@ -727,7 +728,7 @@ pub trait PrintState<'a> {
             if segment.ident.name != keywords::CrateRoot.name() &&
                segment.ident.name != keywords::DollarCrate.name()
             {
-                self.writer().word(&segment.ident.as_str())?;
+                self.writer().word(segment.ident.as_str().get())?;
             } else if segment.ident.name == keywords::DollarCrate.name() {
                 self.print_dollar_crate(segment.ident.span.ctxt())?;
             }
@@ -746,7 +747,7 @@ pub trait PrintState<'a> {
         }
         self.maybe_print_comment(attr.span.lo())?;
         if attr.is_sugared_doc {
-            self.writer().word(&attr.value_str().unwrap().as_str())?;
+            self.writer().word(attr.value_str().unwrap().as_str().get())?;
             self.writer().hardbreak()
         } else {
             match attr.style {
@@ -807,7 +808,7 @@ pub trait PrintState<'a> {
     fn print_tt(&mut self, tt: tokenstream::TokenTree) -> io::Result<()> {
         match tt {
             TokenTree::Token(_, ref tk) => {
-                self.writer().word(&token_to_string(tk))?;
+                self.writer().word(token_to_string(tk))?;
                 match *tk {
                     parse::token::DocComment(..) => {
                         self.writer().hardbreak()
@@ -816,11 +817,11 @@ pub trait PrintState<'a> {
                 }
             }
             TokenTree::Delimited(_, ref delimed) => {
-                self.writer().word(&token_to_string(&delimed.open_token()))?;
+                self.writer().word(token_to_string(&delimed.open_token()))?;
                 self.writer().space()?;
                 self.print_tts(delimed.stream())?;
                 self.writer().space()?;
-                self.writer().word(&token_to_string(&delimed.close_token()))
+                self.writer().word(token_to_string(&delimed.close_token()))
             },
         }
     }
@@ -889,12 +890,13 @@ impl<'a> State<'a> {
         self.s.cbox(u)
     }
 
-    pub fn word_nbsp(&mut self, w: &str) -> io::Result<()> {
+    pub fn word_nbsp<S: Into<Cow<'static, str>>>(&mut self, w: S) -> io::Result<()> {
         self.s.word(w)?;
         self.nbsp()
     }
 
-    pub fn head(&mut self, w: &str) -> io::Result<()> {
+    pub fn head<S: Into<Cow<'static, str>>>(&mut self, w: S) -> io::Result<()> {
+        let w = w.into();
         // outer-box is consistent
         self.cbox(INDENT_UNIT)?;
         // head-box is inconsistent
@@ -956,7 +958,7 @@ impl<'a> State<'a> {
     pub fn synth_comment(&mut self, text: String) -> io::Result<()> {
         self.s.word("/*")?;
         self.s.space()?;
-        self.s.word(&text[..])?;
+        self.s.word(text)?;
         self.s.space()?;
         self.s.word("*/")
     }
@@ -1129,7 +1131,7 @@ impl<'a> State<'a> {
                 self.end() // end the outer fn box
             }
             ast::ForeignItemKind::Static(ref t, m) => {
-                self.head(&visibility_qualified(&item.vis, "static"))?;
+                self.head(visibility_qualified(&item.vis, "static"))?;
                 if m {
                     self.word_space("mut")?;
                 }
@@ -1141,7 +1143,7 @@ impl<'a> State<'a> {
                 self.end() // end the outer cbox
             }
             ast::ForeignItemKind::Ty => {
-                self.head(&visibility_qualified(&item.vis, "type"))?;
+                self.head(visibility_qualified(&item.vis, "type"))?;
                 self.print_ident(item.ident)?;
                 self.s.word(";")?;
                 self.end()?; // end the head-ibox
@@ -1164,7 +1166,7 @@ impl<'a> State<'a> {
                               vis: &ast::Visibility)
                               -> io::Result<()>
     {
-        self.s.word(&visibility_qualified(vis, ""))?;
+        self.s.word(visibility_qualified(vis, ""))?;
         self.word_space("const")?;
         self.print_ident(ident)?;
         self.word_space(":")?;
@@ -1203,7 +1205,7 @@ impl<'a> State<'a> {
         self.ann.pre(self, AnnNode::Item(item))?;
         match item.node {
             ast::ItemKind::ExternCrate(orig_name) => {
-                self.head(&visibility_qualified(&item.vis, "extern crate"))?;
+                self.head(visibility_qualified(&item.vis, "extern crate"))?;
                 if let Some(orig_name) = orig_name {
                     self.print_name(orig_name)?;
                     self.s.space()?;
@@ -1216,14 +1218,14 @@ impl<'a> State<'a> {
                 self.end()?; // end outer head-block
             }
             ast::ItemKind::Use(ref tree) => {
-                self.head(&visibility_qualified(&item.vis, "use"))?;
+                self.head(visibility_qualified(&item.vis, "use"))?;
                 self.print_use_tree(tree)?;
                 self.s.word(";")?;
                 self.end()?; // end inner head-block
                 self.end()?; // end outer head-block
             }
             ast::ItemKind::Static(ref ty, m, ref expr) => {
-                self.head(&visibility_qualified(&item.vis, "static"))?;
+                self.head(visibility_qualified(&item.vis, "static"))?;
                 if m == ast::Mutability::Mutable {
                     self.word_space("mut")?;
                 }
@@ -1239,7 +1241,7 @@ impl<'a> State<'a> {
                 self.end()?; // end the outer cbox
             }
             ast::ItemKind::Const(ref ty, ref expr) => {
-                self.head(&visibility_qualified(&item.vis, "const"))?;
+                self.head(visibility_qualified(&item.vis, "const"))?;
                 self.print_ident(item.ident)?;
                 self.word_space(":")?;
                 self.print_type(ty)?;
@@ -1264,7 +1266,7 @@ impl<'a> State<'a> {
                 self.print_block_with_attrs(body, &item.attrs)?;
             }
             ast::ItemKind::Mod(ref _mod) => {
-                self.head(&visibility_qualified(&item.vis, "mod"))?;
+                self.head(visibility_qualified(&item.vis, "mod"))?;
                 self.print_ident(item.ident)?;
 
                 if _mod.inline || self.is_expanded {
@@ -1281,18 +1283,18 @@ impl<'a> State<'a> {
             }
             ast::ItemKind::ForeignMod(ref nmod) => {
                 self.head("extern")?;
-                self.word_nbsp(&nmod.abi.to_string())?;
+                self.word_nbsp(nmod.abi.to_string())?;
                 self.bopen()?;
                 self.print_foreign_mod(nmod, &item.attrs)?;
                 self.bclose(item.span)?;
             }
             ast::ItemKind::GlobalAsm(ref ga) => {
-                self.head(&visibility_qualified(&item.vis, "global_asm!"))?;
-                self.s.word(&ga.asm.as_str())?;
+                self.head(visibility_qualified(&item.vis, "global_asm!"))?;
+                self.s.word(ga.asm.as_str().get())?;
                 self.end()?;
             }
             ast::ItemKind::Ty(ref ty, ref generics) => {
-                self.head(&visibility_qualified(&item.vis, "type"))?;
+                self.head(visibility_qualified(&item.vis, "type"))?;
                 self.print_ident(item.ident)?;
                 self.print_generic_params(&generics.params)?;
                 self.end()?; // end the inner ibox
@@ -1305,7 +1307,7 @@ impl<'a> State<'a> {
                 self.end()?; // end the outer ibox
             }
             ast::ItemKind::Existential(ref bounds, ref generics) => {
-                self.head(&visibility_qualified(&item.vis, "existential type"))?;
+                self.head(visibility_qualified(&item.vis, "existential type"))?;
                 self.print_ident(item.ident)?;
                 self.print_generic_params(&generics.params)?;
                 self.end()?; // end the inner ibox
@@ -1326,11 +1328,11 @@ impl<'a> State<'a> {
                 )?;
             }
             ast::ItemKind::Struct(ref struct_def, ref generics) => {
-                self.head(&visibility_qualified(&item.vis, "struct"))?;
+                self.head(visibility_qualified(&item.vis, "struct"))?;
                 self.print_struct(struct_def, generics, item.ident, item.span, true)?;
             }
             ast::ItemKind::Union(ref struct_def, ref generics) => {
-                self.head(&visibility_qualified(&item.vis, "union"))?;
+                self.head(visibility_qualified(&item.vis, "union"))?;
                 self.print_struct(struct_def, generics, item.ident, item.span, true)?;
             }
             ast::ItemKind::Impl(unsafety,
@@ -1479,7 +1481,7 @@ impl<'a> State<'a> {
                           generics: &ast::Generics, ident: ast::Ident,
                           span: syntax_pos::Span,
                           visibility: &ast::Visibility) -> io::Result<()> {
-        self.head(&visibility_qualified(visibility, "enum"))?;
+        self.head(visibility_qualified(visibility, "enum"))?;
         self.print_ident(ident)?;
         self.print_generic_params(&generics.params)?;
         self.print_where_clause(&generics.where_clause)?;
@@ -1514,9 +1516,9 @@ impl<'a> State<'a> {
             ast::VisibilityKind::Restricted { ref path, .. } => {
                 let path = to_string(|s| s.print_path(path, false, 0));
                 if path == "self" || path == "super" {
-                    self.word_nbsp(&format!("pub({})", path))
+                    self.word_nbsp(format!("pub({})", path))
                 } else {
-                    self.word_nbsp(&format!("pub(in {})", path))
+                    self.word_nbsp(format!("pub(in {})", path))
                 }
             }
             ast::VisibilityKind::Inherited => Ok(())
@@ -2415,19 +2417,19 @@ impl<'a> State<'a> {
 
     pub fn print_ident(&mut self, ident: ast::Ident) -> io::Result<()> {
         if ident.is_raw_guess() {
-            self.s.word(&format!("r#{}", ident))?;
+            self.s.word(format!("r#{}", ident))?;
         } else {
-            self.s.word(&ident.as_str())?;
+            self.s.word(ident.as_str().get())?;
         }
         self.ann.post(self, AnnNode::Ident(&ident))
     }
 
     pub fn print_usize(&mut self, i: usize) -> io::Result<()> {
-        self.s.word(&i.to_string())
+        self.s.word(i.to_string())
     }
 
     pub fn print_name(&mut self, name: ast::Name) -> io::Result<()> {
-        self.s.word(&name.as_str())?;
+        self.s.word(name.as_str().get())?;
         self.ann.post(self, AnnNode::Name(&name))
     }
 
@@ -2851,10 +2853,8 @@ impl<'a> State<'a> {
         }
     }
 
-    pub fn print_type_bounds(&mut self,
-                        prefix: &str,
-                        bounds: &[ast::GenericBound])
-                        -> io::Result<()> {
+    pub fn print_type_bounds(&mut self, prefix: &'static str, bounds: &[ast::GenericBound])
+                             -> io::Result<()> {
         if !bounds.is_empty() {
             self.s.word(prefix)?;
             let mut first = true;
@@ -3146,7 +3146,7 @@ impl<'a> State<'a> {
             Some(Abi::Rust) => Ok(()),
             Some(abi) => {
                 self.word_nbsp("extern")?;
-                self.word_nbsp(&abi.to_string())
+                self.word_nbsp(abi.to_string())
             }
             None => Ok(())
         }
@@ -3157,7 +3157,7 @@ impl<'a> State<'a> {
         match opt_abi {
             Some(abi) => {
                 self.word_nbsp("extern")?;
-                self.word_nbsp(&abi.to_string())
+                self.word_nbsp(abi.to_string())
             }
             None => Ok(())
         }
@@ -3166,7 +3166,7 @@ impl<'a> State<'a> {
     pub fn print_fn_header_info(&mut self,
                                 header: ast::FnHeader,
                                 vis: &ast::Visibility) -> io::Result<()> {
-        self.s.word(&visibility_qualified(vis, ""))?;
+        self.s.word(visibility_qualified(vis, ""))?;
 
         match header.constness.node {
             ast::Constness::NotConst => {}
@@ -3178,7 +3178,7 @@ impl<'a> State<'a> {
 
         if header.abi != Abi::Rust {
             self.word_nbsp("extern")?;
-            self.word_nbsp(&header.abi.to_string())?;
+            self.word_nbsp(header.abi.to_string())?;
         }
 
         self.s.word("fn")
diff --git a/src/libsyntax/ptr.rs b/src/libsyntax/ptr.rs
index bb47d9b535b..9fbc64758da 100644
--- a/src/libsyntax/ptr.rs
+++ b/src/libsyntax/ptr.rs
@@ -72,7 +72,7 @@ impl<T: 'static> P<T> {
         *self.ptr
     }
 
-    /// Transform the inner value, consuming `self` and producing a new `P<T>`.
+    /// Produce a new `P<T>` from `self` without reallocating.
     pub fn map<F>(mut self, f: F) -> P<T> where
         F: FnOnce(T) -> T,
     {
@@ -88,8 +88,30 @@ impl<T: 'static> P<T> {
             ptr::write(p, f(ptr::read(p)));
 
             // Recreate self from the raw pointer.
-            P {
-                ptr: Box::from_raw(p)
+            P { ptr: Box::from_raw(p) }
+        }
+    }
+
+    /// Optionally produce a new `P<T>` from `self` without reallocating.
+    pub fn filter_map<F>(mut self, f: F) -> Option<P<T>> where
+        F: FnOnce(T) -> Option<T>,
+    {
+        let p: *mut T = &mut *self.ptr;
+
+        // Leak self in case of panic.
+        // FIXME(eddyb) Use some sort of "free guard" that
+        // only deallocates, without dropping the pointee,
+        // in case the call the `f` below ends in a panic.
+        mem::forget(self);
+
+        unsafe {
+            if let Some(v) = f(ptr::read(p)) {
+                ptr::write(p, v);
+
+                // Recreate self from the raw pointer.
+                Some(P { ptr: Box::from_raw(p) })
+            } else {
+                None
             }
         }
     }
diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs
index 65f6d27239b..34fb71e4ddf 100644
--- a/src/libsyntax_pos/lib.rs
+++ b/src/libsyntax_pos/lib.rs
@@ -587,6 +587,7 @@ impl fmt::Debug for SpanData {
 }
 
 impl MultiSpan {
+    #[inline]
     pub fn new() -> MultiSpan {
         MultiSpan {
             primary_spans: vec![],
diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs
index e333a4f2176..741877bb4c8 100644
--- a/src/libsyntax_pos/symbol.rs
+++ b/src/libsyntax_pos/symbol.rs
@@ -492,6 +492,10 @@ impl LocalInternedString {
             symbol: Symbol::intern(self.string)
         }
     }
+
+    pub fn get(&self) -> &'static str {
+        self.string
+    }
 }
 
 impl<U: ?Sized> ::std::convert::AsRef<U> for LocalInternedString
diff --git a/src/test/run-make/llvm-outputs/Makefile b/src/test/run-make/llvm-outputs/Makefile
new file mode 100644
index 00000000000..d7f67577b04
--- /dev/null
+++ b/src/test/run-make/llvm-outputs/Makefile
@@ -0,0 +1,5 @@
+-include ../../run-make-fulldeps/tools.mk
+
+all:
+	echo 'fn main() {}' | $(BARE_RUSTC) - --out-dir=$(TMPDIR)/random_directory_that_does_not_exist_ir/ --emit=llvm-ir
+	echo 'fn main() {}' | $(BARE_RUSTC) - --out-dir=$(TMPDIR)/random_directory_that_does_not_exist_bc/ --emit=llvm-bc
diff --git a/src/test/run-pass/rfcs/rfc-2302-self-struct-ctor.rs b/src/test/run-pass/rfcs/rfc-2302-self-struct-ctor.rs
index 156e240e442..1ec20c50034 100644
--- a/src/test/run-pass/rfcs/rfc-2302-self-struct-ctor.rs
+++ b/src/test/run-pass/rfcs/rfc-2302-self-struct-ctor.rs
@@ -1,7 +1,5 @@
 // run-pass
 
-#![feature(self_struct_ctor)]
-
 #![allow(dead_code)]
 
 use std::fmt::Display;
diff --git a/src/test/ui/consts/issue-56164.rs b/src/test/ui/consts/issue-56164.rs
new file mode 100644
index 00000000000..9d1a8b59463
--- /dev/null
+++ b/src/test/ui/consts/issue-56164.rs
@@ -0,0 +1,13 @@
+#![feature(const_fn)]
+
+const fn foo() { (||{})() }
+//~^ ERROR calls in constant functions are limited to constant functions, tuple structs and tuple
+// variants
+
+const fn bad(input: fn()) {
+    input()
+    //~^ ERROR function pointers are not allowed in const fn
+}
+
+fn main() {
+}
diff --git a/src/test/ui/consts/issue-56164.stderr b/src/test/ui/consts/issue-56164.stderr
new file mode 100644
index 00000000000..d3e9ce379ae
--- /dev/null
+++ b/src/test/ui/consts/issue-56164.stderr
@@ -0,0 +1,15 @@
+error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+  --> $DIR/issue-56164.rs:3:18
+   |
+LL | const fn foo() { (||{})() }
+   |                  ^^^^^^^^
+
+error: function pointers are not allowed in const fn
+  --> $DIR/issue-56164.rs:8:5
+   |
+LL |     input()
+   |     ^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0015`.
diff --git a/src/test/ui/feature-gate-custom_test_frameworks.rs b/src/test/ui/feature-gates/feature-gate-custom_test_frameworks.rs
index 7c9f7dd0402..7c9f7dd0402 100644
--- a/src/test/ui/feature-gate-custom_test_frameworks.rs
+++ b/src/test/ui/feature-gates/feature-gate-custom_test_frameworks.rs
diff --git a/src/test/ui/feature-gate-custom_test_frameworks.stderr b/src/test/ui/feature-gates/feature-gate-custom_test_frameworks.stderr
index bfcbab50067..bfcbab50067 100644
--- a/src/test/ui/feature-gate-custom_test_frameworks.stderr
+++ b/src/test/ui/feature-gates/feature-gate-custom_test_frameworks.stderr
diff --git a/src/test/ui/feature-gate-doc_cfg-cfg-rustdoc.rs b/src/test/ui/feature-gates/feature-gate-doc_cfg-cfg-rustdoc.rs
index 6207d99dc36..6207d99dc36 100644
--- a/src/test/ui/feature-gate-doc_cfg-cfg-rustdoc.rs
+++ b/src/test/ui/feature-gates/feature-gate-doc_cfg-cfg-rustdoc.rs
diff --git a/src/test/ui/feature-gate-doc_cfg-cfg-rustdoc.stderr b/src/test/ui/feature-gates/feature-gate-doc_cfg-cfg-rustdoc.stderr
index be2c263af04..be2c263af04 100644
--- a/src/test/ui/feature-gate-doc_cfg-cfg-rustdoc.stderr
+++ b/src/test/ui/feature-gates/feature-gate-doc_cfg-cfg-rustdoc.stderr
diff --git a/src/test/ui/feature-gates/feature-gate-self-struct-ctor.rs b/src/test/ui/feature-gates/feature-gate-self-struct-ctor.rs
deleted file mode 100644
index 98eab394913..00000000000
--- a/src/test/ui/feature-gates/feature-gate-self-struct-ctor.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-struct ST1(i32, i32);
-
-impl ST1 {
-    fn ctor() -> Self {
-        Self(1,2)
-        //~^ ERROR: `Self` struct constructors are unstable (see issue #51994) [E0658]
-    }
-}
-
-struct ST2;
-
-impl ST2 {
-    fn ctor() -> Self {
-        Self
-        //~^ ERROR: `Self` struct constructors are unstable (see issue #51994) [E0658]
-    }
-}
-
-fn main() {
-    let _ = ST1::ctor();
-    let _ = ST2::ctor();
-}
diff --git a/src/test/ui/feature-gates/feature-gate-self-struct-ctor.stderr b/src/test/ui/feature-gates/feature-gate-self-struct-ctor.stderr
deleted file mode 100644
index 6061a0db76e..00000000000
--- a/src/test/ui/feature-gates/feature-gate-self-struct-ctor.stderr
+++ /dev/null
@@ -1,19 +0,0 @@
-error[E0658]: `Self` struct constructors are unstable (see issue #51994)
-  --> $DIR/feature-gate-self-struct-ctor.rs:5:9
-   |
-LL |         Self(1,2)
-   |         ^^^^
-   |
-   = help: add #![feature(self_struct_ctor)] to the crate attributes to enable
-
-error[E0658]: `Self` struct constructors are unstable (see issue #51994)
-  --> $DIR/feature-gate-self-struct-ctor.rs:14:9
-   |
-LL |         Self
-   |         ^^^^
-   |
-   = help: add #![feature(self_struct_ctor)] to the crate attributes to enable
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/feature-gate-underscore_const_names.rs b/src/test/ui/feature-gates/feature-gate-underscore_const_names.rs
index b283e286514..b283e286514 100644
--- a/src/test/ui/feature-gate-underscore_const_names.rs
+++ b/src/test/ui/feature-gates/feature-gate-underscore_const_names.rs
diff --git a/src/test/ui/feature-gate-underscore_const_names.stderr b/src/test/ui/feature-gates/feature-gate-underscore_const_names.stderr
index ab90ef8f11f..ab90ef8f11f 100644
--- a/src/test/ui/feature-gate-underscore_const_names.stderr
+++ b/src/test/ui/feature-gates/feature-gate-underscore_const_names.stderr
diff --git a/src/test/ui/feature-gate-unsized_locals.rs b/src/test/ui/feature-gates/feature-gate-unsized_locals.rs
index 7f1f22fa38f..7f1f22fa38f 100644
--- a/src/test/ui/feature-gate-unsized_locals.rs
+++ b/src/test/ui/feature-gates/feature-gate-unsized_locals.rs
diff --git a/src/test/ui/feature-gate-unsized_locals.stderr b/src/test/ui/feature-gates/feature-gate-unsized_locals.stderr
index a0440a373d2..a0440a373d2 100644
--- a/src/test/ui/feature-gate-unsized_locals.stderr
+++ b/src/test/ui/feature-gates/feature-gate-unsized_locals.stderr
diff --git a/src/test/ui/issues/issue-56202.rs b/src/test/ui/issues/issue-56202.rs
new file mode 100644
index 00000000000..bd222b7fe98
--- /dev/null
+++ b/src/test/ui/issues/issue-56202.rs
@@ -0,0 +1,17 @@
+// compile-pass
+
+trait FooTrait {}
+
+trait BarTrait {
+    fn foo<T: FooTrait>(_: T) -> Self;
+}
+
+struct FooStruct(u32);
+
+impl BarTrait for FooStruct {
+    fn foo<T: FooTrait>(_: T) -> Self {
+        Self(u32::default())
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/keyword/keyword-self-as-identifier.rs b/src/test/ui/keyword/keyword-self-as-identifier.rs
index ad5b8fb6434..b50fc68bed6 100644
--- a/src/test/ui/keyword/keyword-self-as-identifier.rs
+++ b/src/test/ui/keyword/keyword-self-as-identifier.rs
@@ -10,5 +10,4 @@
 
 fn main() {
     let Self = 22; //~ ERROR cannot find unit struct/variant or constant `Self` in this scope
-        //~^ ERROR `Self` struct constructors are unstable (see issue #51994)
 }
diff --git a/src/test/ui/keyword/keyword-self-as-identifier.stderr b/src/test/ui/keyword/keyword-self-as-identifier.stderr
index 296269819f8..c47f4aeabef 100644
--- a/src/test/ui/keyword/keyword-self-as-identifier.stderr
+++ b/src/test/ui/keyword/keyword-self-as-identifier.stderr
@@ -4,15 +4,6 @@ error[E0531]: cannot find unit struct/variant or constant `Self` in this scope
 LL |     let Self = 22; //~ ERROR cannot find unit struct/variant or constant `Self` in this scope
    |         ^^^^ not found in this scope
 
-error[E0658]: `Self` struct constructors are unstable (see issue #51994)
-  --> $DIR/keyword-self-as-identifier.rs:12:9
-   |
-LL |     let Self = 22; //~ ERROR cannot find unit struct/variant or constant `Self` in this scope
-   |         ^^^^
-   |
-   = help: add #![feature(self_struct_ctor)] to the crate attributes to enable
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
-Some errors occurred: E0531, E0658.
-For more information about an error, try `rustc --explain E0531`.
+For more information about this error, try `rustc --explain E0531`.
diff --git a/src/test/ui/self/self_type_keyword-2.rs b/src/test/ui/self/self_type_keyword-2.rs
index bbaf060ca87..8331ae0b307 100644
--- a/src/test/ui/self/self_type_keyword-2.rs
+++ b/src/test/ui/self/self_type_keyword-2.rs
@@ -13,14 +13,11 @@ use self::Self as Foo; //~ ERROR unresolved import `self::Self`
 pub fn main() {
     let Self = 5;
     //~^ ERROR cannot find unit struct/variant or constant `Self` in this scope
-    //~^^ ERROR `Self` struct constructors are unstable (see issue #51994)
 
     match 15 {
         Self => (),
         //~^ ERROR cannot find unit struct/variant or constant `Self` in this scope
-        //~^^ ERROR `Self` struct constructors are unstable (see issue #51994)
         Foo { x: Self } => (),
         //~^ ERROR cannot find unit struct/variant or constant `Self` in this scope
-        //~^^ ERROR `Self` struct constructors are unstable (see issue #51994)
     }
 }
diff --git a/src/test/ui/self/self_type_keyword-2.stderr b/src/test/ui/self/self_type_keyword-2.stderr
index 82529974d0e..972e5bdddc6 100644
--- a/src/test/ui/self/self_type_keyword-2.stderr
+++ b/src/test/ui/self/self_type_keyword-2.stderr
@@ -11,42 +11,18 @@ LL |     let Self = 5;
    |         ^^^^ not found in this scope
 
 error[E0531]: cannot find unit struct/variant or constant `Self` in this scope
-  --> $DIR/self_type_keyword-2.rs:19:9
+  --> $DIR/self_type_keyword-2.rs:18:9
    |
 LL |         Self => (),
    |         ^^^^ not found in this scope
 
 error[E0531]: cannot find unit struct/variant or constant `Self` in this scope
-  --> $DIR/self_type_keyword-2.rs:22:18
+  --> $DIR/self_type_keyword-2.rs:20:18
    |
 LL |         Foo { x: Self } => (),
    |                  ^^^^ not found in this scope
 
-error[E0658]: `Self` struct constructors are unstable (see issue #51994)
-  --> $DIR/self_type_keyword-2.rs:14:9
-   |
-LL |     let Self = 5;
-   |         ^^^^
-   |
-   = help: add #![feature(self_struct_ctor)] to the crate attributes to enable
-
-error[E0658]: `Self` struct constructors are unstable (see issue #51994)
-  --> $DIR/self_type_keyword-2.rs:19:9
-   |
-LL |         Self => (),
-   |         ^^^^
-   |
-   = help: add #![feature(self_struct_ctor)] to the crate attributes to enable
-
-error[E0658]: `Self` struct constructors are unstable (see issue #51994)
-  --> $DIR/self_type_keyword-2.rs:22:18
-   |
-LL |         Foo { x: Self } => (),
-   |                  ^^^^
-   |
-   = help: add #![feature(self_struct_ctor)] to the crate attributes to enable
-
-error: aborting due to 7 previous errors
+error: aborting due to 4 previous errors
 
-Some errors occurred: E0432, E0531, E0658.
+Some errors occurred: E0432, E0531.
 For more information about an error, try `rustc --explain E0432`.
diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs
index ca7e0224dc3..11c83819eaa 100644
--- a/src/tools/linkchecker/main.rs
+++ b/src/tools/linkchecker/main.rs
@@ -24,12 +24,12 @@
 //! A few whitelisted exceptions are allowed as there's known bugs in rustdoc,
 //! but this should catch the majority of "broken link" cases.
 
+use std::collections::hash_map::Entry;
+use std::collections::{HashMap, HashSet};
 use std::env;
-use std::fs::File;
-use std::io::prelude::*;
+use std::fs;
 use std::path::{Path, PathBuf, Component};
-use std::collections::{HashMap, HashSet};
-use std::collections::hash_map::Entry;
+use std::rc::Rc;
 
 use Redirect::*;
 
@@ -63,7 +63,7 @@ enum Redirect {
 }
 
 struct FileEntry {
-    source: String,
+    source: Rc<String>,
     ids: HashSet<String>,
 }
 
@@ -113,7 +113,7 @@ fn walk(cache: &mut Cache, root: &Path, dir: &Path, errors: &mut bool) {
                 let entry = cache.get_mut(&pretty_path).unwrap();
                 // we don't need the source anymore,
                 // so drop to reduce memory-usage
-                entry.source = String::new();
+                entry.source = Rc::new(String::new());
             }
         }
     }
@@ -287,24 +287,24 @@ fn load_file(cache: &mut Cache,
              root: &Path,
              file: &Path,
              redirect: Redirect)
-             -> Result<(PathBuf, String), LoadError> {
-    let mut contents = String::new();
+             -> Result<(PathBuf, Rc<String>), LoadError> {
     let pretty_file = PathBuf::from(file.strip_prefix(root).unwrap_or(&file));
 
-    let maybe_redirect = match cache.entry(pretty_file.clone()) {
+    let (maybe_redirect, contents) = match cache.entry(pretty_file.clone()) {
         Entry::Occupied(entry) => {
-            contents = entry.get().source.clone();
-            None
+            (None, entry.get().source.clone())
         }
         Entry::Vacant(entry) => {
-            let mut fp = File::open(file).map_err(|err| {
-                if let FromRedirect(true) = redirect {
-                    LoadError::BrokenRedirect(file.to_path_buf(), err)
-                } else {
-                    LoadError::IOError(err)
+            let contents = match fs::read_to_string(file) {
+                Ok(s) => Rc::new(s),
+                Err(err) => {
+                    return Err(if let FromRedirect(true) = redirect {
+                        LoadError::BrokenRedirect(file.to_path_buf(), err)
+                    } else {
+                        LoadError::IOError(err)
+                    })
                 }
-            })?;
-            fp.read_to_string(&mut contents).map_err(|err| LoadError::IOError(err))?;
+            };
 
             let maybe = maybe_redirect(&contents);
             if maybe.is_some() {
@@ -317,7 +317,7 @@ fn load_file(cache: &mut Cache,
                     ids: HashSet::new(),
                 });
             }
-            maybe
+            (maybe, contents)
         }
     };
     match maybe_redirect.map(|url| file.parent().unwrap().join(url)) {