about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-02-24 07:55:34 +0000
committerbors <bors@rust-lang.org>2018-02-24 07:55:34 +0000
commit6070d3e47e5e9f15575a3bd33583358b52bc6eda (patch)
tree1277310a9582461e3b0dbe8db1ceb6f5f173f3c9
parentb0a8620ed639d5085d7e1cca3626681a6e4e328e (diff)
parentb26442a3cb12e988f70d9805b8bbfae52fd20d7d (diff)
downloadrust-6070d3e47e5e9f15575a3bd33583358b52bc6eda.tar.gz
rust-6070d3e47e5e9f15575a3bd33583358b52bc6eda.zip
Auto merge of #48476 - Manishearth:rollup, r=Manishearth
Rollup of 12 pull requests

- Successful merges: #47933, #48072, #48083, #48123, #48157, #48219, #48221, #48245, #48429, #48436, #48438, #48472
- Failed merges:
-rw-r--r--src/Cargo.lock2
-rw-r--r--src/liballoc/linked_list.rs4
-rw-r--r--src/liballoc/string.rs2
-rw-r--r--src/liballoc/vec.rs4
-rw-r--r--src/libcore/iter/iterator.rs48
-rw-r--r--src/libproc_macro/lib.rs6
-rw-r--r--src/librustc/Cargo.toml2
-rw-r--r--src/librustc/lib.rs3
-rw-r--r--src/librustc/middle/resolve_lifetime.rs82
-rw-r--r--src/librustc/traits/error_reporting.rs42
-rw-r--r--src/librustc/traits/mod.rs2
-rw-r--r--src/librustc/util/common.rs21
-rw-r--r--src/librustc_data_structures/bitvec.rs278
-rw-r--r--src/librustc_data_structures/indexed_vec.rs15
-rw-r--r--src/librustc_driver/driver.rs4
-rw-r--r--src/librustc_lint/types.rs240
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/values.rs37
-rw-r--r--src/librustc_trans/back/lto.rs9
-rw-r--r--src/librustc_trans/back/write.rs25
-rw-r--r--src/librustc_typeck/check/closure.rs182
-rw-r--r--src/libsyntax/feature_gate.rs2
-rw-r--r--src/libsyntax/parse/parser.rs8
-rw-r--r--src/test/compile-fail/issue-14309.rs10
-rw-r--r--src/test/compile-fail/issue-16250.rs2
-rw-r--r--src/test/compile-fail/lint-ctypes-enum.rs18
-rw-r--r--src/test/compile-fail/union/union-repr-c.rs2
-rw-r--r--src/test/run-pass/hygiene/issue-47312.rs30
-rw-r--r--src/test/run-pass/impl-trait/lifetimes.rs8
-rw-r--r--src/test/ui-fulldeps/proc-macro/load-panic.rs1
-rw-r--r--src/test/ui-fulldeps/proc-macro/load-panic.stderr4
-rw-r--r--src/test/ui/lint-ctypes.rs (renamed from src/test/compile-fail/lint-ctypes.rs)40
-rw-r--r--src/test/ui/lint-ctypes.stderr170
-rw-r--r--src/test/ui/mismatched_types/closure-arg-count-expected-type-issue-47244.rs29
-rw-r--r--src/test/ui/mismatched_types/closure-arg-count-expected-type-issue-47244.stderr14
m---------src/tools/clippy27
35 files changed, 1040 insertions, 333 deletions
diff --git a/src/Cargo.lock b/src/Cargo.lock
index 7dc8374e3e8..8f25820d3a5 100644
--- a/src/Cargo.lock
+++ b/src/Cargo.lock
@@ -1623,7 +1623,9 @@ dependencies = [
  "fmt_macros 0.0.0",
  "graphviz 0.0.0",
  "jobserver 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc_macro 0.0.0",
  "rustc_apfloat 0.0.0",
  "rustc_back 0.0.0",
  "rustc_const_math 0.0.0",
diff --git a/src/liballoc/linked_list.rs b/src/liballoc/linked_list.rs
index 65be087b35e..ec579e3fd68 100644
--- a/src/liballoc/linked_list.rs
+++ b/src/liballoc/linked_list.rs
@@ -747,8 +747,8 @@ impl<T> LinkedList<T> {
     /// Creates an iterator which uses a closure to determine if an element should be removed.
     ///
     /// If the closure returns true, then the element is removed and yielded.
-    /// If the closure returns false, it will try again, and call the closure on the next element,
-    /// seeing if it passes the test.
+    /// If the closure returns false, the element will remain in the list and will not be yielded
+    /// by the iterator.
     ///
     /// Note that `drain_filter` lets you mutate every element in the filter closure, regardless of
     /// whether you choose to keep or remove it.
diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs
index 8d99d0bc8f4..409d2ab287e 100644
--- a/src/liballoc/string.rs
+++ b/src/liballoc/string.rs
@@ -364,7 +364,7 @@ impl String {
     ///
     /// Given that the `String` is empty, this will not allocate any initial
     /// buffer. While that means that this initial operation is very
-    /// inexpensive, but may cause excessive allocation later, when you add
+    /// inexpensive, it may cause excessive allocation later when you add
     /// data. If you have an idea of how much data the `String` will hold,
     /// consider the [`with_capacity`] method to prevent excessive
     /// re-allocation.
diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs
index 39a4d271bd6..3c9b6b94b44 100644
--- a/src/liballoc/vec.rs
+++ b/src/liballoc/vec.rs
@@ -1966,8 +1966,8 @@ impl<T> Vec<T> {
     /// Creates an iterator which uses a closure to determine if an element should be removed.
     ///
     /// If the closure returns true, then the element is removed and yielded.
-    /// If the closure returns false, it will try again, and call the closure
-    /// on the next element, seeing if it passes the test.
+    /// If the closure returns false, the element will remain in the vector and will not be yielded
+    /// by the iterator.
     ///
     /// Using this method is equivalent to the following code:
     ///
diff --git a/src/libcore/iter/iterator.rs b/src/libcore/iter/iterator.rs
index c1a0518cd22..877793cb3c5 100644
--- a/src/libcore/iter/iterator.rs
+++ b/src/libcore/iter/iterator.rs
@@ -1366,9 +1366,9 @@ pub trait Iterator {
     ///
     /// In particular, try to have this call `try_fold()` on the internal parts
     /// from which this iterator is composed.  If multiple calls are needed,
-    /// the `?` operator be convenient for chaining the accumulator value along,
-    /// but beware any invariants that need to be upheld before those early
-    /// returns.  This is a `&mut self` method, so iteration needs to be
+    /// the `?` operator may be convenient for chaining the accumulator value
+    /// along, but beware any invariants that need to be upheld before those
+    /// early returns.  This is a `&mut self` method, so iteration needs to be
     /// resumable after hitting an error here.
     ///
     /// # Examples
@@ -1414,6 +1414,42 @@ pub trait Iterator {
         Try::from_ok(accum)
     }
 
+    /// An iterator method that applies a fallible function to each item in the
+    /// iterator, stopping at the first error and returning that error.
+    ///
+    /// This can also be thought of as the fallible form of [`for_each()`]
+    /// or as the stateless version of [`try_fold()`].
+    ///
+    /// [`for_each()`]: #method.for_each
+    /// [`try_fold()`]: #method.try_fold
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(iterator_try_fold)]
+    /// use std::fs::rename;
+    /// use std::io::{stdout, Write};
+    /// use std::path::Path;
+    ///
+    /// let data = ["no_tea.txt", "stale_bread.json", "torrential_rain.png"];
+    ///
+    /// let res = data.iter().try_for_each(|x| writeln!(stdout(), "{}", x));
+    /// assert!(res.is_ok());
+    ///
+    /// let mut it = data.iter().cloned();
+    /// let res = it.try_for_each(|x| rename(x, Path::new(x).with_extension("old")));
+    /// assert!(res.is_err());
+    /// // It short-circuited, so the remaining items are still in the iterator:
+    /// assert_eq!(it.next(), Some("stale_bread.json"));
+    /// ```
+    #[inline]
+    #[unstable(feature = "iterator_try_fold", issue = "45594")]
+    fn try_for_each<F, R>(&mut self, mut f: F) -> R where
+        Self: Sized, F: FnMut(Self::Item) -> R, R: Try<Ok=()>
+    {
+        self.try_fold((), move |(), x| f(x))
+    }
+
     /// An iterator method that applies a function, producing a single, final value.
     ///
     /// `fold()` takes two arguments: an initial value, and a closure with two
@@ -1532,7 +1568,7 @@ pub trait Iterator {
     fn all<F>(&mut self, mut f: F) -> bool where
         Self: Sized, F: FnMut(Self::Item) -> bool
     {
-        self.try_fold((), move |(), x| {
+        self.try_for_each(move |x| {
             if f(x) { LoopState::Continue(()) }
             else { LoopState::Break(()) }
         }) == LoopState::Continue(())
@@ -1581,7 +1617,7 @@ pub trait Iterator {
         Self: Sized,
         F: FnMut(Self::Item) -> bool
     {
-        self.try_fold((), move |(), x| {
+        self.try_for_each(move |x| {
             if f(x) { LoopState::Break(()) }
             else { LoopState::Continue(()) }
         }) == LoopState::Break(())
@@ -1635,7 +1671,7 @@ pub trait Iterator {
         Self: Sized,
         P: FnMut(&Self::Item) -> bool,
     {
-        self.try_fold((), move |(), x| {
+        self.try_for_each(move |x| {
             if predicate(&x) { LoopState::Break(x) }
             else { LoopState::Continue(()) }
         }).break_value()
diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs
index 6768e0ade43..878a536836d 100644
--- a/src/libproc_macro/lib.rs
+++ b/src/libproc_macro/lib.rs
@@ -844,6 +844,12 @@ pub mod __internal {
         })
     }
 
+    pub fn in_sess() -> bool
+    {
+        let p = CURRENT_SESS.with(|p| p.get());
+        !p.0.is_null()
+    }
+
     pub fn with_sess<F, R>(f: F) -> R
         where F: FnOnce((&ParseSess, Mark)) -> R
     {
diff --git a/src/librustc/Cargo.toml b/src/librustc/Cargo.toml
index 2c4898cb2c0..7e84a69dd79 100644
--- a/src/librustc/Cargo.toml
+++ b/src/librustc/Cargo.toml
@@ -14,7 +14,9 @@ bitflags = "1.0"
 fmt_macros = { path = "../libfmt_macros" }
 graphviz = { path = "../libgraphviz" }
 jobserver = "0.1"
+lazy_static = "1.0.0"
 log = { version = "0.4", features = ["release_max_level_info", "std"] }
+proc_macro = { path = "../libproc_macro" }
 rustc_apfloat = { path = "../librustc_apfloat" }
 rustc_back = { path = "../librustc_back" }
 rustc_const_math = { path = "../librustc_const_math" }
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index 8c76e0182df..2e7bf4d001d 100644
--- a/src/librustc/lib.rs
+++ b/src/librustc/lib.rs
@@ -60,6 +60,7 @@
 #![feature(never_type)]
 #![feature(non_exhaustive)]
 #![feature(nonzero)]
+#![feature(proc_macro_internals)]
 #![feature(quote)]
 #![feature(refcell_replace_swap)]
 #![feature(rustc_diagnostic_macros)]
@@ -81,6 +82,7 @@ extern crate core;
 extern crate fmt_macros;
 extern crate getopts;
 extern crate graphviz;
+#[macro_use] extern crate lazy_static;
 #[cfg(windows)]
 extern crate libc;
 extern crate rustc_back;
@@ -92,6 +94,7 @@ extern crate rustc_errors as errors;
 #[macro_use] extern crate syntax;
 extern crate syntax_pos;
 extern crate jobserver;
+extern crate proc_macro;
 
 extern crate serialize as rustc_serialize; // used by deriving
 
diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs
index 59460141166..5201df2119d 100644
--- a/src/librustc/middle/resolve_lifetime.rs
+++ b/src/librustc/middle/resolve_lifetime.rs
@@ -270,6 +270,19 @@ enum Scope<'a> {
         /// we should use for an early-bound region?
         next_early_index: u32,
 
+        /// Whether or not this binder would serve as the parent
+        /// binder for abstract types introduced within. For example:
+        ///
+        ///     fn foo<'a>() -> impl for<'b> Trait<Item = impl Trait2<'a>>
+        ///
+        /// Here, the abstract types we create for the `impl Trait`
+        /// and `impl Trait2` references will both have the `foo` item
+        /// as their parent. When we get to `impl Trait2`, we find
+        /// that it is nested within the `for<>` binder -- this flag
+        /// allows us to skip that when looking for the parent binder
+        /// of the resulting abstract type.
+        abstract_type_parent: bool,
+
         s: ScopeRef<'a>,
     },
 
@@ -498,6 +511,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                 let scope = Scope::Binder {
                     lifetimes,
                     next_early_index,
+                    abstract_type_parent: true,
                     s: ROOT_SCOPE,
                 };
                 self.with(scope, |old_scope, this| {
@@ -541,6 +555,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                         .collect(),
                     s: self.scope,
                     next_early_index,
+                    abstract_type_parent: false,
                 };
                 self.with(scope, |old_scope, this| {
                     // a bare fn has no bounds, so everything
@@ -614,7 +629,10 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     ref generics,
                     ref bounds,
                 } = *exist_ty;
-                let mut index = self.next_early_index();
+
+                // We want to start our early-bound indices at the end of the parent scope,
+                // not including any parent `impl Trait`s.
+                let mut index = self.next_early_index_for_abstract_type();
                 debug!("visit_ty: index = {}", index);
 
                 let mut elision = None;
@@ -638,7 +656,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                         s: self.scope
                     };
                     self.with(scope, |_old_scope, this| {
-                        let scope = Scope::Binder { lifetimes, next_early_index, s: this.scope };
+                        let scope = Scope::Binder {
+                            lifetimes,
+                            next_early_index,
+                            s: this.scope,
+                            abstract_type_parent: false,
+                        };
                         this.with(scope, |_old_scope, this| {
                             this.visit_generics(generics);
                             for bound in bounds {
@@ -647,7 +670,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                         });
                     });
                 } else {
-                    let scope = Scope::Binder { lifetimes, next_early_index, s: self.scope };
+                    let scope = Scope::Binder {
+                        lifetimes,
+                        next_early_index,
+                        s: self.scope,
+                        abstract_type_parent: false,
+                    };
                     self.with(scope, |_old_scope, this| {
                         this.visit_generics(generics);
                         for bound in bounds {
@@ -681,7 +709,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     .collect();
 
                 let next_early_index = index + generics.ty_params().count() as u32;
-                let scope = Scope::Binder { lifetimes, next_early_index, s: self.scope };
+                let scope = Scope::Binder {
+                    lifetimes,
+                    next_early_index,
+                    s: self.scope,
+                    abstract_type_parent: true,
+                };
                 self.with(scope, |_old_scope, this| {
                     this.visit_generics(generics);
                     for bound in bounds {
@@ -721,7 +754,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     .collect();
 
                 let next_early_index = index + generics.ty_params().count() as u32;
-                let scope = Scope::Binder { lifetimes, next_early_index, s: self.scope };
+                let scope = Scope::Binder {
+                    lifetimes,
+                    next_early_index,
+                    s: self.scope,
+                    abstract_type_parent: true,
+                };
                 self.with(scope, |_old_scope, this| {
                     this.visit_generics(generics);
                     this.visit_ty(ty);
@@ -792,6 +830,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                                 .collect(),
                             s: self.scope,
                             next_early_index,
+                            abstract_type_parent: false,
                         };
                         let result = self.with(scope, |old_scope, this| {
                             this.check_lifetime_params(old_scope, &bound_generic_params);
@@ -853,6 +892,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     .collect(),
                 s: self.scope,
                 next_early_index,
+                abstract_type_parent: false,
             };
             self.with(scope, |old_scope, this| {
                 this.check_lifetime_params(old_scope, &trait_ref.bound_generic_params);
@@ -1046,6 +1086,7 @@ fn extract_labels(ctxt: &mut LifetimeContext<'_, '_>, body: &hir::Body) {
                     ref lifetimes,
                     s,
                     next_early_index: _,
+                    abstract_type_parent: _,
                 } => {
                     // FIXME (#24278): non-hygienic comparison
                     if let Some(def) = lifetimes.get(&hir::LifetimeName::Name(label)) {
@@ -1303,6 +1344,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
             lifetimes,
             next_early_index,
             s: self.scope,
+            abstract_type_parent: true,
         };
         self.with(scope, move |old_scope, this| {
             this.check_lifetime_params(old_scope, &generics.params);
@@ -1310,25 +1352,41 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
         });
     }
 
-    /// Returns the next index one would use for an early-bound-region
-    /// if extending the current scope.
-    fn next_early_index(&self) -> u32 {
+    fn next_early_index_helper(&self, only_abstract_type_parent: bool) -> u32 {
         let mut scope = self.scope;
         loop {
             match *scope {
                 Scope::Root => return 0,
 
                 Scope::Binder {
-                    next_early_index, ..
-                } => return next_early_index,
+                    next_early_index,
+                    abstract_type_parent,
+                    ..
+                } if (!only_abstract_type_parent || abstract_type_parent)
+                => return next_early_index,
 
-                Scope::Body { s, .. }
+                Scope::Binder { s, .. }
+                | Scope::Body { s, .. }
                 | Scope::Elision { s, .. }
                 | Scope::ObjectLifetimeDefault { s, .. } => scope = s,
             }
         }
     }
 
+    /// Returns the next index one would use for an early-bound-region
+    /// if extending the current scope.
+    fn next_early_index(&self) -> u32 {
+        self.next_early_index_helper(true)
+    }
+
+    /// Returns the next index one would use for an `impl Trait` that
+    /// is being converted into an `abstract type`. This will be the
+    /// next early index from the enclosing item, for the most
+    /// part. See the `abstract_type_parent` field for more info.
+    fn next_early_index_for_abstract_type(&self) -> u32 {
+        self.next_early_index_helper(false)
+    }
+
     fn resolve_lifetime_ref(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
         debug!("resolve_lifetime_ref(lifetime_ref={:?})", lifetime_ref);
         // Walk up the scope chain, tracking the number of fn scopes
@@ -1353,6 +1411,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                     ref lifetimes,
                     s,
                     next_early_index: _,
+                    abstract_type_parent: _,
                 } => {
                     if let Some(&def) = lifetimes.get(&lifetime_ref.name) {
                         break Some(def.shifted(late_depth));
@@ -2102,6 +2161,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                     ref lifetimes,
                     s,
                     next_early_index: _,
+                    abstract_type_parent: _,
                 } => {
                     if let Some(&def) = lifetimes.get(&lifetime.name) {
                         let node_id = self.tcx.hir.as_local_node_id(def.id().unwrap()).unwrap();
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index 214d8ec325f..12d8d6f3d74 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -747,7 +747,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                     ty::TyTuple(ref tys, _) => tys.iter()
                         .map(|t| match t.sty {
                             ty::TypeVariants::TyTuple(ref tys, _) => ArgKind::Tuple(
-                                span,
+                                Some(span),
                                 tys.iter()
                                     .map(|ty| ("_".to_owned(), format!("{}", ty.sty)))
                                     .collect::<Vec<_>>()
@@ -815,7 +815,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
-    fn get_fn_like_arguments(&self, node: hir::map::Node) -> (Span, Vec<ArgKind>) {
+    /// Given some node representing a fn-like thing in the HIR map,
+    /// returns a span and `ArgKind` information that describes the
+    /// arguments it expects. This can be supplied to
+    /// `report_arg_count_mismatch`.
+    pub fn get_fn_like_arguments(&self, node: hir::map::Node) -> (Span, Vec<ArgKind>) {
         match node {
             hir::map::NodeExpr(&hir::Expr {
                 node: hir::ExprClosure(_, ref _decl, id, span, _),
@@ -829,7 +833,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                             ..
                         } = arg.pat.clone().into_inner() {
                             ArgKind::Tuple(
-                                span,
+                                Some(span),
                                 args.iter().map(|pat| {
                                     let snippet = self.tcx.sess.codemap()
                                         .span_to_snippet(pat.span).unwrap();
@@ -862,7 +866,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                 (self.tcx.sess.codemap().def_span(span), decl.inputs.iter()
                         .map(|arg| match arg.clone().into_inner().node {
                     hir::TyTup(ref tys) => ArgKind::Tuple(
-                        arg.span,
+                        Some(arg.span),
                         tys.iter()
                             .map(|_| ("_".to_owned(), "_".to_owned()))
                             .collect::<Vec<_>>(),
@@ -874,7 +878,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
-    fn report_arg_count_mismatch(
+    /// Reports an error when the number of arguments needed by a
+    /// trait match doesn't match the number that the expression
+    /// provides.
+    pub fn report_arg_count_mismatch(
         &self,
         span: Span,
         found_span: Option<Span>,
@@ -1385,13 +1392,34 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     }
 }
 
-enum ArgKind {
+/// Summarizes information
+pub enum ArgKind {
+    /// An argument of non-tuple type. Parameters are (name, ty)
     Arg(String, String),
-    Tuple(Span, Vec<(String, String)>),
+
+    /// An argument of tuple type. For a "found" argument, the span is
+    /// the locationo in the source of the pattern. For a "expected"
+    /// argument, it will be None. The vector is a list of (name, ty)
+    /// strings for the components of the tuple.
+    Tuple(Option<Span>, Vec<(String, String)>),
 }
 
 impl ArgKind {
     fn empty() -> ArgKind {
         ArgKind::Arg("_".to_owned(), "_".to_owned())
     }
+
+    /// Creates an `ArgKind` from the expected type of an
+    /// argument. This has no name (`_`) and no source spans..
+    pub fn from_expected_ty(t: Ty<'_>) -> ArgKind {
+        match t.sty {
+            ty::TyTuple(ref tys, _) => ArgKind::Tuple(
+                None,
+                tys.iter()
+                   .map(|ty| ("_".to_owned(), format!("{}", ty.sty)))
+                   .collect::<Vec<_>>()
+            ),
+            _ => ArgKind::Arg("_".to_owned(), format!("{}", t.sty)),
+        }
+    }
 }
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index 31836f7e3c5..520b997882e 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -49,7 +49,7 @@ pub use self::util::SupertraitDefIds;
 pub use self::util::transitive_bounds;
 
 mod coherence;
-mod error_reporting;
+pub mod error_reporting;
 mod fulfill;
 mod project;
 mod object_safety;
diff --git a/src/librustc/util/common.rs b/src/librustc/util/common.rs
index 2971f3e853a..55e9a98e7ef 100644
--- a/src/librustc/util/common.rs
+++ b/src/librustc/util/common.rs
@@ -16,6 +16,7 @@ use std::ffi::CString;
 use std::fmt::Debug;
 use std::hash::{Hash, BuildHasher};
 use std::iter::repeat;
+use std::panic;
 use std::path::Path;
 use std::time::{Duration, Instant};
 
@@ -23,6 +24,8 @@ use std::sync::mpsc::{Sender};
 use syntax_pos::{SpanData};
 use ty::maps::{QueryMsg};
 use dep_graph::{DepNode};
+use proc_macro;
+use lazy_static;
 
 // The name of the associated type for `Fn` return types
 pub const FN_OUTPUT_NAME: &'static str = "Output";
@@ -34,6 +37,24 @@ pub struct ErrorReported;
 
 thread_local!(static TIME_DEPTH: Cell<usize> = Cell::new(0));
 
+lazy_static! {
+    static ref DEFAULT_HOOK: Box<Fn(&panic::PanicInfo) + Sync + Send + 'static> = {
+        let hook = panic::take_hook();
+        panic::set_hook(Box::new(panic_hook));
+        hook
+    };
+}
+
+fn panic_hook(info: &panic::PanicInfo) {
+    if !proc_macro::__internal::in_sess() {
+        (*DEFAULT_HOOK)(info)
+    }
+}
+
+pub fn install_panic_hook() {
+    lazy_static::initialize(&DEFAULT_HOOK);
+}
+
 /// Initialized for -Z profile-queries
 thread_local!(static PROFQ_CHAN: RefCell<Option<Sender<ProfileQueriesMsg>>> = RefCell::new(None));
 
diff --git a/src/librustc_data_structures/bitvec.rs b/src/librustc_data_structures/bitvec.rs
index 80cdb0e4417..54565afa4c6 100644
--- a/src/librustc_data_structures/bitvec.rs
+++ b/src/librustc_data_structures/bitvec.rs
@@ -8,19 +8,28 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use std::collections::BTreeMap;
+use std::collections::btree_map::Entry;
+use std::marker::PhantomData;
 use std::iter::FromIterator;
+use indexed_vec::{Idx, IndexVec};
+
+type Word = u128;
+const WORD_BITS: usize = 128;
 
 /// A very simple BitVector type.
 #[derive(Clone, Debug, PartialEq)]
 pub struct BitVector {
-    data: Vec<u64>,
+    data: Vec<Word>,
 }
 
 impl BitVector {
     #[inline]
     pub fn new(num_bits: usize) -> BitVector {
-        let num_words = u64s(num_bits);
-        BitVector { data: vec![0; num_words] }
+        let num_words = words(num_bits);
+        BitVector {
+            data: vec![0; num_words],
+        }
     }
 
     #[inline]
@@ -78,7 +87,7 @@ impl BitVector {
 
     #[inline]
     pub fn grow(&mut self, num_bits: usize) {
-        let num_words = u64s(num_bits);
+        let num_words = words(num_bits);
         if self.data.len() < num_words {
             self.data.resize(num_words, 0)
         }
@@ -96,8 +105,8 @@ impl BitVector {
 }
 
 pub struct BitVectorIter<'a> {
-    iter: ::std::slice::Iter<'a, u64>,
-    current: u64,
+    iter: ::std::slice::Iter<'a, Word>,
+    current: Word,
     idx: usize,
 }
 
@@ -107,10 +116,10 @@ impl<'a> Iterator for BitVectorIter<'a> {
         while self.current == 0 {
             self.current = if let Some(&i) = self.iter.next() {
                 if i == 0 {
-                    self.idx += 64;
+                    self.idx += WORD_BITS;
                     continue;
                 } else {
-                    self.idx = u64s(self.idx) * 64;
+                    self.idx = words(self.idx) * WORD_BITS;
                     i
                 }
             } else {
@@ -126,12 +135,15 @@ impl<'a> Iterator for BitVectorIter<'a> {
 }
 
 impl FromIterator<bool> for BitVector {
-    fn from_iter<I>(iter: I) -> BitVector where I: IntoIterator<Item=bool> {
+    fn from_iter<I>(iter: I) -> BitVector
+    where
+        I: IntoIterator<Item = bool>,
+    {
         let iter = iter.into_iter();
         let (len, _) = iter.size_hint();
-        // Make the minimum length for the bitvector 64 bits since that's
+        // Make the minimum length for the bitvector WORD_BITS bits since that's
         // the smallest non-zero size anyway.
-        let len = if len < 64 { 64 } else { len };
+        let len = if len < WORD_BITS { WORD_BITS } else { len };
         let mut bv = BitVector::new(len);
         for (idx, val) in iter.enumerate() {
             if idx > len {
@@ -152,32 +164,32 @@ impl FromIterator<bool> for BitVector {
 #[derive(Clone, Debug)]
 pub struct BitMatrix {
     columns: usize,
-    vector: Vec<u64>,
+    vector: Vec<Word>,
 }
 
 impl BitMatrix {
     /// Create a new `rows x columns` matrix, initially empty.
     pub fn new(rows: usize, columns: usize) -> BitMatrix {
         // For every element, we need one bit for every other
-        // element. Round up to an even number of u64s.
-        let u64s_per_row = u64s(columns);
+        // element. Round up to an even number of words.
+        let words_per_row = words(columns);
         BitMatrix {
             columns,
-            vector: vec![0; rows * u64s_per_row],
+            vector: vec![0; rows * words_per_row],
         }
     }
 
     /// The range of bits for a given row.
     fn range(&self, row: usize) -> (usize, usize) {
-        let u64s_per_row = u64s(self.columns);
-        let start = row * u64s_per_row;
-        (start, start + u64s_per_row)
+        let words_per_row = words(self.columns);
+        let start = row * words_per_row;
+        (start, start + words_per_row)
     }
 
     /// Sets the cell at `(row, column)` to true. Put another way, add
     /// `column` to the bitset for `row`.
     ///
-    /// Returns true if this changed the matrix, and false otherwies.
+    /// Returns true if this changed the matrix, and false otherwise.
     pub fn add(&mut self, row: usize, column: usize) -> bool {
         let (start, _) = self.range(row);
         let (word, mask) = word_mask(column);
@@ -208,12 +220,12 @@ impl BitMatrix {
         let mut result = Vec::with_capacity(self.columns);
         for (base, (i, j)) in (a_start..a_end).zip(b_start..b_end).enumerate() {
             let mut v = self.vector[i] & self.vector[j];
-            for bit in 0..64 {
+            for bit in 0..WORD_BITS {
                 if v == 0 {
                     break;
                 }
                 if v & 0x1 != 0 {
-                    result.push(base * 64 + bit);
+                    result.push(base * WORD_BITS + bit);
                 }
                 v >>= 1;
             }
@@ -254,15 +266,214 @@ impl BitMatrix {
     }
 }
 
+#[derive(Clone, Debug)]
+pub struct SparseBitMatrix<R, C>
+where
+    R: Idx,
+    C: Idx,
+{
+    vector: IndexVec<R, SparseBitSet<C>>,
+}
+
+impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
+    /// Create a new `rows x columns` matrix, initially empty.
+    pub fn new(rows: R, _columns: C) -> SparseBitMatrix<R, C> {
+        SparseBitMatrix {
+            vector: IndexVec::from_elem_n(SparseBitSet::new(), rows.index()),
+        }
+    }
+
+    /// Sets the cell at `(row, column)` to true. Put another way, insert
+    /// `column` to the bitset for `row`.
+    ///
+    /// Returns true if this changed the matrix, and false otherwise.
+    pub fn add(&mut self, row: R, column: C) -> bool {
+        self.vector[row].insert(column)
+    }
+
+    /// Do the bits from `row` contain `column`? Put another way, is
+    /// the matrix cell at `(row, column)` true?  Put yet another way,
+    /// if the matrix represents (transitive) reachability, can
+    /// `row` reach `column`?
+    pub fn contains(&self, row: R, column: C) -> bool {
+        self.vector[row].contains(column)
+    }
+
+    /// Add the bits from row `read` to the bits from row `write`,
+    /// return true if anything changed.
+    ///
+    /// This is used when computing transitive reachability because if
+    /// you have an edge `write -> read`, because in that case
+    /// `write` can reach everything that `read` can (and
+    /// potentially more).
+    pub fn merge(&mut self, read: R, write: R) -> bool {
+        let mut changed = false;
+
+        if read != write {
+            let (bit_set_read, bit_set_write) = self.vector.pick2_mut(read, write);
+
+            for read_val in bit_set_read.iter() {
+                changed = changed | bit_set_write.insert(read_val);
+            }
+        }
+
+        changed
+    }
+
+    /// Iterates through all the columns set to true in a given row of
+    /// the matrix.
+    pub fn iter<'a>(&'a self, row: R) -> impl Iterator<Item = C> + 'a {
+        self.vector[row].iter()
+    }
+}
+
+#[derive(Clone, Debug)]
+pub struct SparseBitSet<I: Idx> {
+    chunk_bits: BTreeMap<u32, Word>,
+    _marker: PhantomData<I>,
+}
+
+#[derive(Copy, Clone)]
+pub struct SparseChunk<I> {
+    key: u32,
+    bits: Word,
+    _marker: PhantomData<I>,
+}
+
+impl<I: Idx> SparseChunk<I> {
+    pub fn one(index: I) -> Self {
+        let index = index.index();
+        let key_usize = index / 128;
+        let key = key_usize as u32;
+        assert_eq!(key as usize, key_usize);
+        SparseChunk {
+            key,
+            bits: 1 << (index % 128),
+            _marker: PhantomData,
+        }
+    }
+
+    pub fn any(&self) -> bool {
+        self.bits != 0
+    }
+
+    pub fn iter(&self) -> impl Iterator<Item = I> {
+        let base = self.key as usize * 128;
+        let mut bits = self.bits;
+        (0..128)
+            .map(move |i| {
+                let current_bits = bits;
+                bits >>= 1;
+                (i, current_bits)
+            })
+            .take_while(|&(_, bits)| bits != 0)
+            .filter_map(move |(i, bits)| {
+                if (bits & 1) != 0 {
+                    Some(I::new(base + i))
+                } else {
+                    None
+                }
+            })
+    }
+}
+
+impl<I: Idx> SparseBitSet<I> {
+    pub fn new() -> Self {
+        SparseBitSet {
+            chunk_bits: BTreeMap::new(),
+            _marker: PhantomData,
+        }
+    }
+
+    pub fn capacity(&self) -> usize {
+        self.chunk_bits.len() * 128
+    }
+
+    pub fn contains_chunk(&self, chunk: SparseChunk<I>) -> SparseChunk<I> {
+        SparseChunk {
+            bits: self.chunk_bits
+                .get(&chunk.key)
+                .map_or(0, |bits| bits & chunk.bits),
+            ..chunk
+        }
+    }
+
+    pub fn insert_chunk(&mut self, chunk: SparseChunk<I>) -> SparseChunk<I> {
+        if chunk.bits == 0 {
+            return chunk;
+        }
+        let bits = self.chunk_bits.entry(chunk.key).or_insert(0);
+        let old_bits = *bits;
+        let new_bits = old_bits | chunk.bits;
+        *bits = new_bits;
+        let changed = new_bits ^ old_bits;
+        SparseChunk {
+            bits: changed,
+            ..chunk
+        }
+    }
+
+    pub fn remove_chunk(&mut self, chunk: SparseChunk<I>) -> SparseChunk<I> {
+        if chunk.bits == 0 {
+            return chunk;
+        }
+        let changed = match self.chunk_bits.entry(chunk.key) {
+            Entry::Occupied(mut bits) => {
+                let old_bits = *bits.get();
+                let new_bits = old_bits & !chunk.bits;
+                if new_bits == 0 {
+                    bits.remove();
+                } else {
+                    bits.insert(new_bits);
+                }
+                new_bits ^ old_bits
+            }
+            Entry::Vacant(_) => 0,
+        };
+        SparseChunk {
+            bits: changed,
+            ..chunk
+        }
+    }
+
+    pub fn clear(&mut self) {
+        self.chunk_bits.clear();
+    }
+
+    pub fn chunks<'a>(&'a self) -> impl Iterator<Item = SparseChunk<I>> + 'a {
+        self.chunk_bits.iter().map(|(&key, &bits)| SparseChunk {
+            key,
+            bits,
+            _marker: PhantomData,
+        })
+    }
+
+    pub fn contains(&self, index: I) -> bool {
+        self.contains_chunk(SparseChunk::one(index)).any()
+    }
+
+    pub fn insert(&mut self, index: I) -> bool {
+        self.insert_chunk(SparseChunk::one(index)).any()
+    }
+
+    pub fn remove(&mut self, index: I) -> bool {
+        self.remove_chunk(SparseChunk::one(index)).any()
+    }
+
+    pub fn iter<'a>(&'a self) -> impl Iterator<Item = I> + 'a {
+        self.chunks().flat_map(|chunk| chunk.iter())
+    }
+}
+
 #[inline]
-fn u64s(elements: usize) -> usize {
-    (elements + 63) / 64
+fn words(elements: usize) -> usize {
+    (elements + WORD_BITS - 1) / WORD_BITS
 }
 
 #[inline]
-fn word_mask(index: usize) -> (usize, u64) {
-    let word = index / 64;
-    let mask = 1 << (index % 64);
+fn word_mask(index: usize) -> (usize, Word) {
+    let word = index / WORD_BITS;
+    let mask = 1 << (index % WORD_BITS);
     (word, mask)
 }
 
@@ -278,11 +489,12 @@ fn bitvec_iter_works() {
     bitvec.insert(65);
     bitvec.insert(66);
     bitvec.insert(99);
-    assert_eq!(bitvec.iter().collect::<Vec<_>>(),
-               [1, 10, 19, 62, 63, 64, 65, 66, 99]);
+    assert_eq!(
+        bitvec.iter().collect::<Vec<_>>(),
+        [1, 10, 19, 62, 63, 64, 65, 66, 99]
+    );
 }
 
-
 #[test]
 fn bitvec_iter_works_2() {
     let mut bitvec = BitVector::new(319);
@@ -314,24 +526,24 @@ fn union_two_vecs() {
 #[test]
 fn grow() {
     let mut vec1 = BitVector::new(65);
-    for index in 0 .. 65 {
+    for index in 0..65 {
         assert!(vec1.insert(index));
         assert!(!vec1.insert(index));
     }
     vec1.grow(128);
 
     // Check if the bits set before growing are still set
-    for index in 0 .. 65 {
+    for index in 0..65 {
         assert!(vec1.contains(index));
     }
 
     // Check if the new bits are all un-set
-    for index in 65 .. 128 {
+    for index in 65..128 {
         assert!(!vec1.contains(index));
     }
 
     // Check that we can set all new bits without running out of bounds
-    for index in 65 .. 128 {
+    for index in 65..128 {
         assert!(vec1.insert(index));
         assert!(!vec1.insert(index));
     }
diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_data_structures/indexed_vec.rs
index b11ca107af7..3e94b3f4d30 100644
--- a/src/librustc_data_structures/indexed_vec.rs
+++ b/src/librustc_data_structures/indexed_vec.rs
@@ -482,6 +482,21 @@ impl<I: Idx, T> IndexVec<I, T> {
     pub fn get_mut(&mut self, index: I) -> Option<&mut T> {
         self.raw.get_mut(index.index())
     }
+
+    /// Return mutable references to two distinct elements, a and b. Panics if a == b.
+    #[inline]
+    pub fn pick2_mut(&mut self, a: I, b: I) -> (&mut T, &mut T) {
+        let (ai, bi) = (a.index(), b.index());
+        assert!(ai != bi);
+
+        if ai < bi {
+            let (c1, c2) = self.raw.split_at_mut(bi);
+            (&mut c1[ai], &mut c2[0])
+        } else {
+            let (c2, c1) = self.pick2_mut(b, a);
+            (c1, c2)
+        }
+    }
 }
 
 impl<I: Idx, T: Clone> IndexVec<I, T> {
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index b8a1fe99105..eb67c9ce4b7 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -24,7 +24,7 @@ use rustc::middle::cstore::CrateStore;
 use rustc::middle::privacy::AccessLevels;
 use rustc::ty::{self, TyCtxt, Resolutions, AllArenas};
 use rustc::traits;
-use rustc::util::common::{ErrorReported, time};
+use rustc::util::common::{ErrorReported, time, install_panic_hook};
 use rustc_allocator as allocator;
 use rustc_borrowck as borrowck;
 use rustc_incremental;
@@ -123,6 +123,8 @@ pub fn compile_input(trans: Box<TransCrate>,
         let outputs = build_output_filenames(input, outdir, output, &krate.attrs, sess);
         let crate_name =
             ::rustc_trans_utils::link::find_crate_name(Some(sess), &krate.attrs, input);
+        install_panic_hook();
+
         let ExpansionResult { expanded_crate, defs, analysis, resolutions, mut hir_forest } = {
             phase_2_configure_and_expand(
                 sess,
diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs
index f734f3182a9..ef9b3d38c63 100644
--- a/src/librustc_lint/types.rs
+++ b/src/librustc_lint/types.rs
@@ -10,7 +10,6 @@
 
 #![allow(non_snake_case)]
 
-use rustc::hir::def_id::DefId;
 use rustc::hir::map as hir_map;
 use rustc::ty::subst::Substs;
 use rustc::ty::{self, AdtKind, Ty, TyCtxt};
@@ -26,7 +25,6 @@ use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64};
 
 use syntax::ast;
 use syntax::abi::Abi;
-use syntax::attr;
 use syntax_pos::Span;
 use syntax::codemap;
 
@@ -353,13 +351,14 @@ struct ImproperCTypesVisitor<'a, 'tcx: 'a> {
     cx: &'a LateContext<'a, 'tcx>,
 }
 
-enum FfiResult {
+enum FfiResult<'tcx> {
     FfiSafe,
-    FfiPhantom,
-    FfiUnsafe(&'static str),
-    FfiBadStruct(DefId, &'static str),
-    FfiBadUnion(DefId, &'static str),
-    FfiBadEnum(DefId, &'static str),
+    FfiPhantom(Ty<'tcx>),
+    FfiUnsafe {
+        ty: Ty<'tcx>,
+        reason: &'static str,
+        help: Option<&'static str>,
+    },
 }
 
 /// Check if this enum can be safely exported based on the
@@ -397,23 +396,12 @@ fn is_repr_nullable_ptr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     false
 }
 
-fn is_ffi_safe(ty: attr::IntType) -> bool {
-    match ty {
-        attr::SignedInt(ast::IntTy::I8) | attr::UnsignedInt(ast::UintTy::U8) |
-        attr::SignedInt(ast::IntTy::I16) | attr::UnsignedInt(ast::UintTy::U16) |
-        attr::SignedInt(ast::IntTy::I32) | attr::UnsignedInt(ast::UintTy::U32) |
-        attr::SignedInt(ast::IntTy::I64) | attr::UnsignedInt(ast::UintTy::U64) |
-        attr::SignedInt(ast::IntTy::I128) | attr::UnsignedInt(ast::UintTy::U128) => true,
-        attr::SignedInt(ast::IntTy::Isize) | attr::UnsignedInt(ast::UintTy::Usize) => false
-    }
-}
-
 impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
     /// Check if the given type is "ffi-safe" (has a stable, well-defined
     /// representation which can be exported to C code).
     fn check_type_for_ffi(&self,
                           cache: &mut FxHashSet<Ty<'tcx>>,
-                          ty: Ty<'tcx>) -> FfiResult {
+                          ty: Ty<'tcx>) -> FfiResult<'tcx> {
         use self::FfiResult::*;
 
         let cx = self.cx.tcx;
@@ -429,19 +417,25 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
         match ty.sty {
             ty::TyAdt(def, substs) => {
                 if def.is_phantom_data() {
-                    return FfiPhantom;
+                    return FfiPhantom(ty);
                 }
                 match def.adt_kind() {
                     AdtKind::Struct => {
                         if !def.repr.c() && !def.repr.transparent() {
-                            return FfiUnsafe("found struct without foreign-function-safe \
-                                              representation annotation in foreign module, \
-                                              consider adding a #[repr(C)] attribute to the type");
+                            return FfiUnsafe {
+                                ty: ty,
+                                reason: "this struct has unspecified layout",
+                                help: Some("consider adding a #[repr(C)] or #[repr(transparent)] \
+                                            attribute to this struct"),
+                            };
                         }
 
                         if def.non_enum_variant().fields.is_empty() {
-                            return FfiUnsafe("found zero-size struct in foreign module, consider \
-                                              adding a member to this struct");
+                            return FfiUnsafe {
+                                ty: ty,
+                                reason: "this struct has no fields",
+                                help: Some("consider adding a member to this struct"),
+                            };
                         }
 
                         // We can't completely trust repr(C) and repr(transparent) markings;
@@ -467,28 +461,30 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                                 FfiSafe => {
                                     all_phantom = false;
                                 }
-                                FfiPhantom => {}
-                                FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => {
+                                FfiPhantom(..) => {}
+                                FfiUnsafe { .. } => {
                                     return r;
                                 }
-                                FfiUnsafe(s) => {
-                                    return FfiBadStruct(def.did, s);
-                                }
                             }
                         }
 
-                        if all_phantom { FfiPhantom } else { FfiSafe }
+                        if all_phantom { FfiPhantom(ty) } else { FfiSafe }
                     }
                     AdtKind::Union => {
                         if !def.repr.c() {
-                            return FfiUnsafe("found union without foreign-function-safe \
-                                              representation annotation in foreign module, \
-                                              consider adding a #[repr(C)] attribute to the type");
+                            return FfiUnsafe {
+                                ty: ty,
+                                reason: "this union has unspecified layout",
+                                help: Some("consider adding a #[repr(C)] attribute to this union"),
+                            };
                         }
 
                         if def.non_enum_variant().fields.is_empty() {
-                            return FfiUnsafe("found zero-size union in foreign module, consider \
-                                              adding a member to this union");
+                            return FfiUnsafe {
+                                ty: ty,
+                                reason: "this union has no fields",
+                                help: Some("consider adding a field to this union"),
+                            };
                         }
 
                         let mut all_phantom = true;
@@ -501,17 +497,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                                 FfiSafe => {
                                     all_phantom = false;
                                 }
-                                FfiPhantom => {}
-                                FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => {
+                                FfiPhantom(..) => {}
+                                FfiUnsafe { .. } => {
                                     return r;
                                 }
-                                FfiUnsafe(s) => {
-                                    return FfiBadUnion(def.did, s);
-                                }
                             }
                         }
 
-                        if all_phantom { FfiPhantom } else { FfiSafe }
+                        if all_phantom { FfiPhantom(ty) } else { FfiSafe }
                     }
                     AdtKind::Enum => {
                         if def.variants.is_empty() {
@@ -524,25 +517,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                         if !def.repr.c() && def.repr.int.is_none() {
                             // Special-case types like `Option<extern fn()>`.
                             if !is_repr_nullable_ptr(cx, def, substs) {
-                                return FfiUnsafe("found enum without foreign-function-safe \
-                                                  representation annotation in foreign \
-                                                  module, consider adding a #[repr(...)] \
-                                                  attribute to the type");
-                            }
-                        }
-
-                        if let Some(int_ty) = def.repr.int {
-                            if !is_ffi_safe(int_ty) {
-                                // FIXME: This shouldn't be reachable: we should check
-                                // this earlier.
-                                return FfiUnsafe("enum has unexpected #[repr(...)] attribute");
+                                return FfiUnsafe {
+                                    ty: ty,
+                                    reason: "enum has no representation hint",
+                                    help: Some("consider adding a #[repr(...)] attribute \
+                                                to this enum"),
+                                };
                             }
-
-                            // Enum with an explicitly sized discriminant; either
-                            // a C-style enum or a discriminated union.
-
-                            // The layout of enum variants is implicitly repr(C).
-                            // FIXME: Is that correct?
                         }
 
                         // Check the contained variants.
@@ -554,15 +535,15 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                                 let r = self.check_type_for_ffi(cache, arg);
                                 match r {
                                     FfiSafe => {}
-                                    FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => {
+                                    FfiUnsafe { .. } => {
                                         return r;
                                     }
-                                    FfiPhantom => {
-                                        return FfiBadEnum(def.did,
-                                                          "Found phantom data in enum variant");
-                                    }
-                                    FfiUnsafe(s) => {
-                                        return FfiBadEnum(def.did, s);
+                                    FfiPhantom(..) => {
+                                        return FfiUnsafe {
+                                            ty: ty,
+                                            reason: "this enum contains a PhantomData field",
+                                            help: None,
+                                        };
                                     }
                                 }
                             }
@@ -572,45 +553,44 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                 }
             }
 
-            ty::TyChar => {
-                FfiUnsafe("found Rust type `char` in foreign module, while \
-                           `u32` or `libc::wchar_t` should be used")
-            }
+            ty::TyChar => FfiUnsafe {
+                ty: ty,
+                reason: "the `char` type has no C equivalent",
+                help: Some("consider using `u32` or `libc::wchar_t` instead"),
+            },
 
-            ty::TyInt(ast::IntTy::I128) => {
-                FfiUnsafe("found Rust type `i128` in foreign module, but \
-                           128-bit integers don't currently have a known \
-                           stable ABI")
-            }
-
-            ty::TyUint(ast::UintTy::U128) => {
-                FfiUnsafe("found Rust type `u128` in foreign module, but \
-                           128-bit integers don't currently have a known \
-                           stable ABI")
-            }
+            ty::TyInt(ast::IntTy::I128) | ty::TyUint(ast::UintTy::U128) => FfiUnsafe {
+                ty: ty,
+                reason: "128-bit integers don't currently have a known stable ABI",
+                help: None,
+            },
 
             // Primitive types with a stable representation.
             ty::TyBool | ty::TyInt(..) | ty::TyUint(..) | ty::TyFloat(..) | ty::TyNever => FfiSafe,
 
-            ty::TySlice(_) => {
-                FfiUnsafe("found Rust slice type in foreign module, \
-                           consider using a raw pointer instead")
-            }
-
-            ty::TyDynamic(..) => {
-                FfiUnsafe("found Rust trait type in foreign module, \
-                           consider using a raw pointer instead")
-            }
-
-            ty::TyStr => {
-                FfiUnsafe("found Rust type `str` in foreign module; \
-                           consider using a `*const libc::c_char`")
-            }
-
-            ty::TyTuple(..) => {
-                FfiUnsafe("found Rust tuple type in foreign module; \
-                           consider using a struct instead")
-            }
+            ty::TySlice(_) => FfiUnsafe {
+                ty: ty,
+                reason: "slices have no C equivalent",
+                help: Some("consider using a raw pointer instead"),
+            },
+
+            ty::TyDynamic(..) => FfiUnsafe {
+                ty: ty,
+                reason: "trait objects have no C equivalent",
+                help: None,
+            },
+
+            ty::TyStr => FfiUnsafe {
+                ty: ty,
+                reason: "string slices have no C equivalent",
+                help: Some("consider using `*const u8` and a length instead"),
+            },
+
+            ty::TyTuple(..) => FfiUnsafe {
+                ty: ty,
+                reason: "tuples have unspecified layout",
+                help: Some("consider using a struct instead"),
+            },
 
             ty::TyRawPtr(ref m) |
             ty::TyRef(_, ref m) => self.check_type_for_ffi(cache, m.ty),
@@ -620,9 +600,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
             ty::TyFnPtr(sig) => {
                 match sig.abi() {
                     Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic | Abi::RustCall => {
-                        return FfiUnsafe("found function pointer with Rust calling convention in \
-                                          foreign module; consider using an `extern` function \
-                                          pointer")
+                        return FfiUnsafe {
+                            ty: ty,
+                            reason: "this function pointer has Rust-specific calling convention",
+                            help: Some("consider using an `fn \"extern\"(...) -> ...` \
+                                        function pointer instead"),
+                        }
                     }
                     _ => {}
                 }
@@ -670,40 +653,25 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
 
         match self.check_type_for_ffi(&mut FxHashSet(), ty) {
             FfiResult::FfiSafe => {}
-            FfiResult::FfiPhantom => {
-                self.cx.span_lint(IMPROPER_CTYPES,
-                                  sp,
-                                  &format!("found zero-sized type composed only \
-                                            of phantom-data in a foreign-function."));
-            }
-            FfiResult::FfiUnsafe(s) => {
-                self.cx.span_lint(IMPROPER_CTYPES, sp, s);
-            }
-            FfiResult::FfiBadStruct(_, s) => {
-                // FIXME: This diagnostic is difficult to read, and doesn't
-                // point at the relevant field.
+            FfiResult::FfiPhantom(ty) => {
                 self.cx.span_lint(IMPROPER_CTYPES,
                                   sp,
-                                  &format!("found non-foreign-function-safe member in struct \
-                                            marked #[repr(C)]: {}",
-                                           s));
+                                  &format!("`extern` block uses type `{}` which is not FFI-safe: \
+                                            composed only of PhantomData", ty));
             }
-            FfiResult::FfiBadUnion(_, s) => {
-                // FIXME: This diagnostic is difficult to read, and doesn't
-                // point at the relevant field.
-                self.cx.span_lint(IMPROPER_CTYPES,
-                                  sp,
-                                  &format!("found non-foreign-function-safe member in union \
-                                            marked #[repr(C)]: {}",
-                                           s));
-            }
-            FfiResult::FfiBadEnum(_, s) => {
-                // FIXME: This diagnostic is difficult to read, and doesn't
-                // point at the relevant variant.
-                self.cx.span_lint(IMPROPER_CTYPES,
-                                  sp,
-                                  &format!("found non-foreign-function-safe member in enum: {}",
-                                           s));
+            FfiResult::FfiUnsafe { ty: unsafe_ty, reason, help } => {
+                let msg = format!("`extern` block uses type `{}` which is not FFI-safe: {}",
+                                  unsafe_ty, reason);
+                let mut diag = self.cx.struct_span_lint(IMPROPER_CTYPES, sp, &msg);
+                if let Some(s) = help {
+                    diag.help(s);
+                }
+                if let ty::TyAdt(def, _) = unsafe_ty.sty {
+                    if let Some(sp) = self.cx.tcx.hir.span_if_local(def.did) {
+                        diag.span_note(sp, "type defined here");
+                    }
+                }
+                diag.emit();
             }
         }
     }
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/values.rs b/src/librustc_mir/borrow_check/nll/region_infer/values.rs
index 45236bbc4aa..e6f2a43bfc8 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/values.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/values.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use std::rc::Rc;
-use rustc_data_structures::bitvec::BitMatrix;
+use rustc_data_structures::bitvec::SparseBitMatrix;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::indexed_vec::Idx;
 use rustc_data_structures::indexed_vec::IndexVec;
@@ -69,9 +69,7 @@ impl RegionValueElements {
 
     /// Iterates over the `RegionElementIndex` for all points in the CFG.
     pub(super) fn all_point_indices<'a>(&'a self) -> impl Iterator<Item = RegionElementIndex> + 'a {
-        (0..self.num_points).map(move |i| {
-            RegionElementIndex::new(i + self.num_universal_regions)
-        })
+        (0..self.num_points).map(move |i| RegionElementIndex::new(i + self.num_universal_regions))
     }
 
     /// Iterates over the `RegionElementIndex` for all points in the CFG.
@@ -132,7 +130,7 @@ impl RegionValueElements {
 }
 
 /// A newtype for the integers that represent one of the possible
-/// elements in a region. These are the rows in the `BitMatrix` that
+/// elements in a region. These are the rows in the `SparseBitMatrix` that
 /// is used to store the values of all regions. They have the following
 /// convention:
 ///
@@ -154,7 +152,6 @@ pub(super) enum RegionElement {
     UniversalRegion(RegionVid),
 }
 
-
 pub(super) trait ToElementIndex {
     fn to_element_index(self, elements: &RegionValueElements) -> RegionElementIndex;
 }
@@ -184,18 +181,18 @@ impl ToElementIndex for RegionElementIndex {
 }
 
 /// Stores the values for a set of regions. These are stored in a
-/// compact `BitMatrix` representation, with one row per region
+/// compact `SparseBitMatrix` representation, with one row per region
 /// variable. The columns consist of either universal regions or
 /// points in the CFG.
 #[derive(Clone)]
 pub(super) struct RegionValues {
     elements: Rc<RegionValueElements>,
-    matrix: BitMatrix,
+    matrix: SparseBitMatrix<RegionVid, RegionElementIndex>,
 
     /// If cause tracking is enabled, maps from a pair (r, e)
     /// consisting of a region `r` that contains some element `e` to
     /// the reason that the element is contained. There should be an
-    /// entry for every bit set to 1 in `BitMatrix`.
+    /// entry for every bit set to 1 in `SparseBitMatrix`.
     causes: Option<CauseMap>,
 }
 
@@ -214,7 +211,10 @@ impl RegionValues {
 
         Self {
             elements: elements.clone(),
-            matrix: BitMatrix::new(num_region_variables, elements.num_elements()),
+            matrix: SparseBitMatrix::new(
+                RegionVid::new(num_region_variables),
+                RegionElementIndex::new(elements.num_elements()),
+            ),
             causes: if track_causes.0 {
                 Some(CauseMap::default())
             } else {
@@ -238,7 +238,7 @@ impl RegionValues {
     where
         F: FnOnce(&CauseMap) -> Cause,
     {
-        if self.matrix.add(r.index(), i.index()) {
+        if self.matrix.add(r, i) {
             debug!("add(r={:?}, i={:?})", r, self.elements.to_element(i));
 
             if let Some(causes) = &mut self.causes {
@@ -289,13 +289,12 @@ impl RegionValues {
         constraint_location: Location,
         constraint_span: Span,
     ) -> bool {
-        // We could optimize this by improving `BitMatrix::merge` so
+        // We could optimize this by improving `SparseBitMatrix::merge` so
         // it does not always merge an entire row. That would
         // complicate causal tracking though.
         debug!(
             "add_universal_regions_outlived_by(from_region={:?}, to_region={:?})",
-            from_region,
-            to_region
+            from_region, to_region
         );
         let mut changed = false;
         for elem in self.elements.all_universal_region_indices() {
@@ -315,7 +314,7 @@ impl RegionValues {
     /// True if the region `r` contains the given element.
     pub(super) fn contains<E: ToElementIndex>(&self, r: RegionVid, elem: E) -> bool {
         let i = self.elements.index(elem);
-        self.matrix.contains(r.index(), i.index())
+        self.matrix.contains(r, i)
     }
 
     /// Iterate over the value of the region `r`, yielding up element
@@ -325,9 +324,7 @@ impl RegionValues {
         &'a self,
         r: RegionVid,
     ) -> impl Iterator<Item = RegionElementIndex> + 'a {
-        self.matrix
-            .iter(r.index())
-            .map(move |i| RegionElementIndex::new(i))
+        self.matrix.iter(r).map(move |i| i)
     }
 
     /// Returns just the universal regions that are contained in a given region's value.
@@ -415,9 +412,7 @@ impl RegionValues {
             assert_eq!(location1.block, location2.block);
             str.push_str(&format!(
                 "{:?}[{}..={}]",
-                location1.block,
-                location1.statement_index,
-                location2.statement_index
+                location1.block, location1.statement_index, location2.statement_index
             ));
         }
     }
diff --git a/src/librustc_trans/back/lto.rs b/src/librustc_trans/back/lto.rs
index ab354a30d41..3f9e9191cf0 100644
--- a/src/librustc_trans/back/lto.rs
+++ b/src/librustc_trans/back/lto.rs
@@ -122,8 +122,9 @@ pub(crate) fn run(cgcx: &CodegenContext,
             None
         }
     };
-
-    let mut symbol_white_list = cgcx.exported_symbols[&LOCAL_CRATE]
+    let exported_symbols = cgcx.exported_symbols
+        .as_ref().expect("needs exported symbols for LTO");
+    let mut symbol_white_list = exported_symbols[&LOCAL_CRATE]
         .iter()
         .filter_map(symbol_filter)
         .collect::<Vec<CString>>();
@@ -156,8 +157,10 @@ pub(crate) fn run(cgcx: &CodegenContext,
         }
 
         for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() {
+            let exported_symbols = cgcx.exported_symbols
+                .as_ref().expect("needs exported symbols for LTO");
             symbol_white_list.extend(
-                cgcx.exported_symbols[&cnum]
+                exported_symbols[&cnum]
                     .iter()
                     .filter_map(symbol_filter));
 
diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs
index ded9a296817..af5178eb565 100644
--- a/src/librustc_trans/back/write.rs
+++ b/src/librustc_trans/back/write.rs
@@ -333,7 +333,7 @@ pub struct CodegenContext {
     pub no_landing_pads: bool,
     pub save_temps: bool,
     pub fewer_names: bool,
-    pub exported_symbols: Arc<ExportedSymbols>,
+    pub exported_symbols: Option<Arc<ExportedSymbols>>,
     pub opts: Arc<config::Options>,
     pub crate_types: Vec<config::CrateType>,
     pub each_linked_rlib_for_lto: Vec<(CrateNum, PathBuf)>,
@@ -1394,14 +1394,25 @@ fn start_executing_work(tcx: TyCtxt,
                         allocator_config: Arc<ModuleConfig>)
                         -> thread::JoinHandle<Result<CompiledModules, ()>> {
     let coordinator_send = tcx.tx_to_llvm_workers.clone();
-    let mut exported_symbols = FxHashMap();
-    exported_symbols.insert(LOCAL_CRATE, tcx.exported_symbols(LOCAL_CRATE));
-    for &cnum in tcx.crates().iter() {
-        exported_symbols.insert(cnum, tcx.exported_symbols(cnum));
-    }
-    let exported_symbols = Arc::new(exported_symbols);
     let sess = tcx.sess;
 
+    let exported_symbols = match sess.lto() {
+        Lto::No => None,
+        Lto::ThinLocal => {
+            let mut exported_symbols = FxHashMap();
+            exported_symbols.insert(LOCAL_CRATE, tcx.exported_symbols(LOCAL_CRATE));
+            Some(Arc::new(exported_symbols))
+        }
+        Lto::Yes | Lto::Fat | Lto::Thin => {
+            let mut exported_symbols = FxHashMap();
+            exported_symbols.insert(LOCAL_CRATE, tcx.exported_symbols(LOCAL_CRATE));
+            for &cnum in tcx.crates().iter() {
+                exported_symbols.insert(cnum, tcx.exported_symbols(cnum));
+            }
+            Some(Arc::new(exported_symbols))
+        }
+    };
+
     // First up, convert our jobserver into a helper thread so we can use normal
     // mpsc channels to manage our messages and such.
     // After we've requested tokens then we'll, when we can,
diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs
index df15f781ae8..794d466ee7c 100644
--- a/src/librustc_typeck/check/closure.rs
+++ b/src/librustc_typeck/check/closure.rs
@@ -17,14 +17,24 @@ use rustc::hir::def_id::DefId;
 use rustc::infer::{InferOk, InferResult};
 use rustc::infer::LateBoundRegionConversionTime;
 use rustc::infer::type_variable::TypeVariableOrigin;
+use rustc::traits::error_reporting::ArgKind;
 use rustc::ty::{self, ToPolyTraitRef, Ty};
 use rustc::ty::subst::Substs;
 use rustc::ty::TypeFoldable;
 use std::cmp;
 use std::iter;
 use syntax::abi::Abi;
+use syntax::codemap::Span;
 use rustc::hir;
 
+/// What signature do we *expect* the closure to have from context?
+#[derive(Debug)]
+struct ExpectedSig<'tcx> {
+    /// Span that gave us this expectation, if we know that.
+    cause_span: Option<Span>,
+    sig: ty::FnSig<'tcx>,
+}
+
 struct ClosureSignatures<'tcx> {
     bound_sig: ty::PolyFnSig<'tcx>,
     liberated_sig: ty::FnSig<'tcx>,
@@ -42,8 +52,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     ) -> Ty<'tcx> {
         debug!(
             "check_expr_closure(expr={:?},expected={:?})",
-            expr,
-            expected
+            expr, expected
         );
 
         // It's always helpful for inference if we know the kind of
@@ -64,12 +73,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         decl: &'gcx hir::FnDecl,
         body: &'gcx hir::Body,
         gen: Option<hir::GeneratorMovability>,
-        expected_sig: Option<ty::FnSig<'tcx>>,
+        expected_sig: Option<ExpectedSig<'tcx>>,
     ) -> Ty<'tcx> {
         debug!(
             "check_closure(opt_kind={:?}, expected_sig={:?})",
-            opt_kind,
-            expected_sig
+            opt_kind, expected_sig
         );
 
         let expr_def_id = self.tcx.hir.local_def_id(expr.id);
@@ -109,19 +117,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         let closure_type = self.tcx.mk_closure(expr_def_id, substs);
 
         if let Some(GeneratorTypes { yield_ty, interior }) = generator_types {
-            self.demand_eqtype(expr.span,
-                               yield_ty,
-                               substs.generator_yield_ty(expr_def_id, self.tcx));
-            self.demand_eqtype(expr.span,
-                               liberated_sig.output(),
-                               substs.generator_return_ty(expr_def_id, self.tcx));
+            self.demand_eqtype(
+                expr.span,
+                yield_ty,
+                substs.generator_yield_ty(expr_def_id, self.tcx),
+            );
+            self.demand_eqtype(
+                expr.span,
+                liberated_sig.output(),
+                substs.generator_return_ty(expr_def_id, self.tcx),
+            );
             return self.tcx.mk_generator(expr_def_id, substs, interior);
         }
 
         debug!(
             "check_closure: expr.id={:?} closure_type={:?}",
-            expr.id,
-            closure_type
+            expr.id, closure_type
         );
 
         // Tuple up the arguments and insert the resulting function type into
@@ -138,29 +149,33 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
         debug!(
             "check_closure: expr_def_id={:?}, sig={:?}, opt_kind={:?}",
-            expr_def_id,
-            sig,
-            opt_kind
+            expr_def_id, sig, opt_kind
         );
 
         let sig_fn_ptr_ty = self.tcx.mk_fn_ptr(sig);
-        self.demand_eqtype(expr.span,
-                           sig_fn_ptr_ty,
-                           substs.closure_sig_ty(expr_def_id, self.tcx));
+        self.demand_eqtype(
+            expr.span,
+            sig_fn_ptr_ty,
+            substs.closure_sig_ty(expr_def_id, self.tcx),
+        );
 
         if let Some(kind) = opt_kind {
-            self.demand_eqtype(expr.span,
-                               kind.to_ty(self.tcx),
-                               substs.closure_kind_ty(expr_def_id, self.tcx));
+            self.demand_eqtype(
+                expr.span,
+                kind.to_ty(self.tcx),
+                substs.closure_kind_ty(expr_def_id, self.tcx),
+            );
         }
 
         closure_type
     }
 
+    /// Given the expected type, figures out what it can about this closure we
+    /// are about to type check:
     fn deduce_expectations_from_expected_type(
         &self,
         expected_ty: Ty<'tcx>,
-    ) -> (Option<ty::FnSig<'tcx>>, Option<ty::ClosureKind>) {
+    ) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
         debug!(
             "deduce_expectations_from_expected_type(expected_ty={:?})",
             expected_ty
@@ -172,7 +187,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     .projection_bounds()
                     .filter_map(|pb| {
                         let pb = pb.with_self_ty(self.tcx, self.tcx.types.err);
-                        self.deduce_sig_from_projection(&pb)
+                        self.deduce_sig_from_projection(None, &pb)
                     })
                     .next();
                 let kind = object_type
@@ -181,7 +196,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 (sig, kind)
             }
             ty::TyInfer(ty::TyVar(vid)) => self.deduce_expectations_from_obligations(vid),
-            ty::TyFnPtr(sig) => (Some(sig.skip_binder().clone()), Some(ty::ClosureKind::Fn)),
+            ty::TyFnPtr(sig) => {
+                let expected_sig = ExpectedSig {
+                    cause_span: None,
+                    sig: sig.skip_binder().clone(),
+                };
+                (Some(expected_sig), Some(ty::ClosureKind::Fn))
+            }
             _ => (None, None),
         }
     }
@@ -189,7 +210,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     fn deduce_expectations_from_obligations(
         &self,
         expected_vid: ty::TyVid,
-    ) -> (Option<ty::FnSig<'tcx>>, Option<ty::ClosureKind>) {
+    ) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
         let fulfillment_cx = self.fulfillment_cx.borrow();
         // Here `expected_ty` is known to be a type inference variable.
 
@@ -209,7 +230,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     ty::Predicate::Projection(ref proj_predicate) => {
                         let trait_ref = proj_predicate.to_poly_trait_ref(self.tcx);
                         self.self_type_matches_expected_vid(trait_ref, expected_vid)
-                            .and_then(|_| self.deduce_sig_from_projection(proj_predicate))
+                            .and_then(|_| {
+                                self.deduce_sig_from_projection(
+                                    Some(obligation.cause.span),
+                                    proj_predicate,
+                                )
+                            })
                     }
                     _ => None,
                 }
@@ -259,10 +285,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
     /// Given a projection like "<F as Fn(X)>::Result == Y", we can deduce
     /// everything we need to know about a closure.
+    ///
+    /// The `cause_span` should be the span that caused us to
+    /// have this expected signature, or `None` if we can't readily
+    /// know that.
     fn deduce_sig_from_projection(
         &self,
+        cause_span: Option<Span>,
         projection: &ty::PolyProjectionPredicate<'tcx>,
-    ) -> Option<ty::FnSig<'tcx>> {
+    ) -> Option<ExpectedSig<'tcx>> {
         let tcx = self.tcx;
 
         debug!("deduce_sig_from_projection({:?})", projection);
@@ -294,16 +325,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             ret_param_ty
         );
 
-        let fn_sig = self.tcx.mk_fn_sig(
+        let sig = self.tcx.mk_fn_sig(
             input_tys.cloned(),
             ret_param_ty,
             false,
             hir::Unsafety::Normal,
             Abi::Rust,
         );
-        debug!("deduce_sig_from_projection: fn_sig {:?}", fn_sig);
+        debug!("deduce_sig_from_projection: sig {:?}", sig);
 
-        Some(fn_sig)
+        Some(ExpectedSig { cause_span, sig })
     }
 
     fn self_type_matches_expected_vid(
@@ -314,8 +345,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         let self_ty = self.shallow_resolve(trait_ref.self_ty());
         debug!(
             "self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?})",
-            trait_ref,
-            self_ty
+            trait_ref, self_ty
         );
         match self_ty.sty {
             ty::TyInfer(ty::TyVar(v)) if expected_vid == v => Some(trait_ref),
@@ -328,7 +358,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         expr_def_id: DefId,
         decl: &hir::FnDecl,
         body: &hir::Body,
-        expected_sig: Option<ty::FnSig<'tcx>>,
+        expected_sig: Option<ExpectedSig<'tcx>>,
     ) -> ClosureSignatures<'tcx> {
         if let Some(e) = expected_sig {
             self.sig_of_closure_with_expectation(expr_def_id, decl, body, e)
@@ -404,7 +434,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         expr_def_id: DefId,
         decl: &hir::FnDecl,
         body: &hir::Body,
-        expected_sig: ty::FnSig<'tcx>,
+        expected_sig: ExpectedSig<'tcx>,
     ) -> ClosureSignatures<'tcx> {
         debug!(
             "sig_of_closure_with_expectation(expected_sig={:?})",
@@ -414,20 +444,24 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // Watch out for some surprises and just ignore the
         // expectation if things don't see to match up with what we
         // expect.
-        if expected_sig.variadic != decl.variadic {
-            return self.sig_of_closure_no_expectation(expr_def_id, decl, body);
-        } else if expected_sig.inputs_and_output.len() != decl.inputs.len() + 1 {
-            // we could probably handle this case more gracefully
+        if expected_sig.sig.variadic != decl.variadic {
             return self.sig_of_closure_no_expectation(expr_def_id, decl, body);
+        } else if expected_sig.sig.inputs_and_output.len() != decl.inputs.len() + 1 {
+            return self.sig_of_closure_with_mismatched_number_of_arguments(
+                expr_def_id,
+                decl,
+                body,
+                expected_sig,
+            );
         }
 
         // Create a `PolyFnSig`. Note the oddity that late bound
         // regions appearing free in `expected_sig` are now bound up
         // in this binder we are creating.
-        assert!(!expected_sig.has_regions_escaping_depth(1));
+        assert!(!expected_sig.sig.has_regions_escaping_depth(1));
         let bound_sig = ty::Binder(self.tcx.mk_fn_sig(
-            expected_sig.inputs().iter().cloned(),
-            expected_sig.output(),
+            expected_sig.sig.inputs().iter().cloned(),
+            expected_sig.sig.output(),
             decl.variadic,
             hir::Unsafety::Normal,
             Abi::RustCall,
@@ -453,6 +487,35 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         closure_sigs
     }
 
+    fn sig_of_closure_with_mismatched_number_of_arguments(
+        &self,
+        expr_def_id: DefId,
+        decl: &hir::FnDecl,
+        body: &hir::Body,
+        expected_sig: ExpectedSig<'tcx>,
+    ) -> ClosureSignatures<'tcx> {
+        let expr_map_node = self.tcx.hir.get_if_local(expr_def_id).unwrap();
+        let expected_args: Vec<_> = expected_sig
+            .sig
+            .inputs()
+            .iter()
+            .map(|ty| ArgKind::from_expected_ty(ty))
+            .collect();
+        let (closure_span, found_args) = self.get_fn_like_arguments(expr_map_node);
+        let expected_span = expected_sig.cause_span.unwrap_or(closure_span);
+        self.report_arg_count_mismatch(
+            expected_span,
+            Some(closure_span),
+            expected_args,
+            found_args,
+            true,
+        ).emit();
+
+        let error_sig = self.error_sig_of_closure(decl);
+
+        self.closure_sigs(expr_def_id, body, error_sig)
+    }
+
     /// Enforce the user's types against the expectation.  See
     /// `sig_of_closure_with_expectation` for details on the overall
     /// strategy.
@@ -558,13 +621,46 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         result
     }
 
+    /// Converts the types that the user supplied, in case that doing
+    /// so should yield an error, but returns back a signature where
+    /// all parameters are of type `TyErr`.
+    fn error_sig_of_closure(&self, decl: &hir::FnDecl) -> ty::PolyFnSig<'tcx> {
+        let astconv: &AstConv = self;
+
+        let supplied_arguments = decl.inputs.iter().map(|a| {
+            // Convert the types that the user supplied (if any), but ignore them.
+            astconv.ast_ty_to_ty(a);
+            self.tcx.types.err
+        });
+
+        match decl.output {
+            hir::Return(ref output) => {
+                astconv.ast_ty_to_ty(&output);
+            }
+            hir::DefaultReturn(_) => {}
+        }
+
+        let result = ty::Binder(self.tcx.mk_fn_sig(
+            supplied_arguments,
+            self.tcx.types.err,
+            decl.variadic,
+            hir::Unsafety::Normal,
+            Abi::RustCall,
+        ));
+
+        debug!("supplied_sig_of_closure: result={:?}", result);
+
+        result
+    }
+
     fn closure_sigs(
         &self,
         expr_def_id: DefId,
         body: &hir::Body,
         bound_sig: ty::PolyFnSig<'tcx>,
     ) -> ClosureSignatures<'tcx> {
-        let liberated_sig = self.tcx().liberate_late_bound_regions(expr_def_id, &bound_sig);
+        let liberated_sig = self.tcx()
+            .liberate_late_bound_regions(expr_def_id, &bound_sig);
         let liberated_sig = self.inh.normalize_associated_types_in(
             body.value.span,
             body.value.id,
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index c0fde71d086..f7cebed5f62 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -186,7 +186,7 @@ declare_features! (
     (active, rustc_attrs, "1.0.0", Some(29642)),
 
     // Allows the use of non lexical lifetimes; RFC 2094
-    (active, nll, "1.0.0", Some(44928)),
+    (active, nll, "1.0.0", Some(43234)),
 
     // Allows the use of #[allow_internal_unstable]. This is an
     // attribute on macro_rules! and can't use the attribute handling
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 74daa5179d3..1a33de84429 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -2630,8 +2630,7 @@ impl<'a> Parser<'a> {
                     // A tuple index may not have a suffix
                     self.expect_no_suffix(sp, "tuple index", suf);
 
-                    let dot_span = self.prev_span;
-                    hi = self.span;
+                    let idx_span = self.span;
                     self.bump();
 
                     let invalid_msg = "invalid tuple or struct index";
@@ -2646,9 +2645,8 @@ impl<'a> Parser<'a> {
                                                     n.to_string());
                                 err.emit();
                             }
-                            let id = respan(dot_span.to(hi), n);
-                            let field = self.mk_tup_field(e, id);
-                            e = self.mk_expr(lo.to(hi), field, ThinVec::new());
+                            let field = self.mk_tup_field(e, respan(idx_span, n));
+                            e = self.mk_expr(lo.to(idx_span), field, ThinVec::new());
                         }
                         None => {
                             let prev_span = self.prev_span;
diff --git a/src/test/compile-fail/issue-14309.rs b/src/test/compile-fail/issue-14309.rs
index 56261c34a03..f76fa3e4a8e 100644
--- a/src/test/compile-fail/issue-14309.rs
+++ b/src/test/compile-fail/issue-14309.rs
@@ -37,13 +37,13 @@ struct D {
 }
 
 extern "C" {
-    fn foo(x: A); //~ ERROR found struct without foreign-function-safe
-    fn bar(x: B); //~ ERROR foreign-function-safe
+    fn foo(x: A); //~ ERROR type `A` which is not FFI-safe
+    fn bar(x: B); //~ ERROR type `A`
     fn baz(x: C);
-    fn qux(x: A2); //~ ERROR foreign-function-safe
-    fn quux(x: B2); //~ ERROR foreign-function-safe
+    fn qux(x: A2); //~ ERROR type `A`
+    fn quux(x: B2); //~ ERROR type `A`
     fn corge(x: C2);
-    fn fred(x: D); //~ ERROR foreign-function-safe
+    fn fred(x: D); //~ ERROR type `A`
 }
 
 fn main() { }
diff --git a/src/test/compile-fail/issue-16250.rs b/src/test/compile-fail/issue-16250.rs
index 288fe4a9abb..f9d01003005 100644
--- a/src/test/compile-fail/issue-16250.rs
+++ b/src/test/compile-fail/issue-16250.rs
@@ -13,7 +13,7 @@
 pub struct Foo;
 
 extern {
-    pub fn foo(x: (Foo)); //~ ERROR found struct without
+    pub fn foo(x: (Foo)); //~ ERROR unspecified layout
 }
 
 fn main() {
diff --git a/src/test/compile-fail/lint-ctypes-enum.rs b/src/test/compile-fail/lint-ctypes-enum.rs
index e35dadbea9d..7b7ffd8fc10 100644
--- a/src/test/compile-fail/lint-ctypes-enum.rs
+++ b/src/test/compile-fail/lint-ctypes-enum.rs
@@ -16,11 +16,23 @@ enum U { A }
 enum B { C, D }
 enum T { E, F, G }
 
+#[repr(C)]
+enum ReprC { A, B, C }
+
+#[repr(u8)]
+enum U8 { A, B, C }
+
+#[repr(isize)]
+enum Isize { A, B, C }
+
 extern {
    fn zf(x: Z);
-   fn uf(x: U); //~ ERROR found enum without foreign-function-safe
-   fn bf(x: B); //~ ERROR found enum without foreign-function-safe
-   fn tf(x: T); //~ ERROR found enum without foreign-function-safe
+   fn uf(x: U); //~ ERROR enum has no representation hint
+   fn bf(x: B); //~ ERROR enum has no representation hint
+   fn tf(x: T); //~ ERROR enum has no representation hint
+   fn reprc(x: ReprC);
+   fn u8(x: U8);
+   fn isize(x: Isize);
 }
 
 pub fn main() { }
diff --git a/src/test/compile-fail/union/union-repr-c.rs b/src/test/compile-fail/union/union-repr-c.rs
index 15a4197fe94..36c42ce1104 100644
--- a/src/test/compile-fail/union/union-repr-c.rs
+++ b/src/test/compile-fail/union/union-repr-c.rs
@@ -22,7 +22,7 @@ union W {
 
 extern "C" {
     static FOREIGN1: U; // OK
-    static FOREIGN2: W; //~ ERROR found union without foreign-function-safe representation
+    static FOREIGN2: W; //~ ERROR union has unspecified layout
 }
 
 fn main() {}
diff --git a/src/test/run-pass/hygiene/issue-47312.rs b/src/test/run-pass/hygiene/issue-47312.rs
new file mode 100644
index 00000000000..5e83f3808d8
--- /dev/null
+++ b/src/test/run-pass/hygiene/issue-47312.rs
@@ -0,0 +1,30 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-pretty pretty-printing is unhygienic
+
+#![feature(decl_macro)]
+#![allow(unused)]
+
+mod foo {
+    pub macro m($s:tt, $i:tt) {
+        $s.$i
+    }
+}
+
+mod bar {
+    struct S(i32);
+    fn f() {
+        let s = S(0);
+        ::foo::m!(s, 0);
+    }
+}
+
+fn main() {}
diff --git a/src/test/run-pass/impl-trait/lifetimes.rs b/src/test/run-pass/impl-trait/lifetimes.rs
index 1f2d76f2894..213a46ded8e 100644
--- a/src/test/run-pass/impl-trait/lifetimes.rs
+++ b/src/test/run-pass/impl-trait/lifetimes.rs
@@ -50,6 +50,14 @@ fn closure_hr_elided_return() -> impl Fn(&u32) -> &u32 { |x| x }
 fn closure_pass_through_elided_return(x: impl Fn(&u32) -> &u32) -> impl Fn(&u32) -> &u32 { x }
 fn closure_pass_through_reference_elided(x: &impl Fn(&u32) -> &u32) -> &impl Fn(&u32) -> &u32 { x }
 
+fn nested_lifetime<'a>(input: &'a str)
+    -> impl Iterator<Item = impl Iterator<Item = i32> + 'a> + 'a
+{
+    input.lines().map(|line| {
+        line.split_whitespace().map(|cell| cell.parse().unwrap())
+    })
+}
+
 fn pass_through_elision(x: &u32) -> impl Into<&u32> { x }
 fn pass_through_elision_with_fn_ptr(x: &fn(&u32) -> &u32) -> impl Into<&fn(&u32) -> &u32> { x }
 
diff --git a/src/test/ui-fulldeps/proc-macro/load-panic.rs b/src/test/ui-fulldeps/proc-macro/load-panic.rs
index 328f398efd5..462eaf03417 100644
--- a/src/test/ui-fulldeps/proc-macro/load-panic.rs
+++ b/src/test/ui-fulldeps/proc-macro/load-panic.rs
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 // aux-build:derive-panic.rs
+// compile-flags:--error-format human
 
 #[macro_use]
 extern crate derive_panic;
diff --git a/src/test/ui-fulldeps/proc-macro/load-panic.stderr b/src/test/ui-fulldeps/proc-macro/load-panic.stderr
index 1be1609d45b..ab2ab84315a 100644
--- a/src/test/ui-fulldeps/proc-macro/load-panic.stderr
+++ b/src/test/ui-fulldeps/proc-macro/load-panic.stderr
@@ -1,7 +1,7 @@
 error: proc-macro derive panicked
-  --> $DIR/load-panic.rs:16:10
+  --> $DIR/load-panic.rs:17:10
    |
-16 | #[derive(A)]
+17 | #[derive(A)]
    |          ^
    |
    = help: message: nope!
diff --git a/src/test/compile-fail/lint-ctypes.rs b/src/test/ui/lint-ctypes.rs
index c22239dee0a..77cb1ef0f51 100644
--- a/src/test/compile-fail/lint-ctypes.rs
+++ b/src/test/ui/lint-ctypes.rs
@@ -51,27 +51,27 @@ pub struct TransparentCustomZst(i32, ZeroSize);
 pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData<i32>);
 
 extern {
-    pub fn ptr_type1(size: *const Foo); //~ ERROR: found struct without
-    pub fn ptr_type2(size: *const Foo); //~ ERROR: found struct without
-    pub fn slice_type(p: &[u32]); //~ ERROR: found Rust slice type
-    pub fn str_type(p: &str); //~ ERROR: found Rust type
-    pub fn box_type(p: Box<u32>); //~ ERROR found struct without
-    pub fn char_type(p: char); //~ ERROR found Rust type
-    pub fn i128_type(p: i128); //~ ERROR found Rust type
-    pub fn u128_type(p: u128); //~ ERROR found Rust type
-    pub fn trait_type(p: &Clone); //~ ERROR found Rust trait type
-    pub fn tuple_type(p: (i32, i32)); //~ ERROR found Rust tuple type
-    pub fn tuple_type2(p: I32Pair); //~ ERROR found Rust tuple type
-    pub fn zero_size(p: ZeroSize); //~ ERROR found zero-size struct
-    pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); //~ ERROR found zero-sized type
+    pub fn ptr_type1(size: *const Foo); //~ ERROR: uses type `Foo`
+    pub fn ptr_type2(size: *const Foo); //~ ERROR: uses type `Foo`
+    pub fn slice_type(p: &[u32]); //~ ERROR: uses type `[u32]`
+    pub fn str_type(p: &str); //~ ERROR: uses type `str`
+    pub fn box_type(p: Box<u32>); //~ ERROR uses type `std::boxed::Box<u32>`
+    pub fn char_type(p: char); //~ ERROR uses type `char`
+    pub fn i128_type(p: i128); //~ ERROR uses type `i128`
+    pub fn u128_type(p: u128); //~ ERROR uses type `u128`
+    pub fn trait_type(p: &Clone); //~ ERROR uses type `std::clone::Clone`
+    pub fn tuple_type(p: (i32, i32)); //~ ERROR uses type `(i32, i32)`
+    pub fn tuple_type2(p: I32Pair); //~ ERROR uses type `(i32, i32)`
+    pub fn zero_size(p: ZeroSize); //~ ERROR struct has no fields
+    pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); //~ ERROR composed only of PhantomData
     pub fn zero_size_phantom_toplevel()
-        -> ::std::marker::PhantomData<bool>; //~ ERROR: found zero-sized type
-    pub fn fn_type(p: RustFn); //~ ERROR found function pointer with Rust
-    pub fn fn_type2(p: fn()); //~ ERROR found function pointer with Rust
-    pub fn fn_contained(p: RustBadRet); //~ ERROR: found struct without
-    pub fn transparent_i128(p: TransparentI128); //~ ERROR: found Rust type `i128`
-    pub fn transparent_str(p: TransparentStr); //~ ERROR: found Rust type `str`
-    pub fn transparent_fn(p: TransparentBadFn); //~ ERROR: found struct without
+        -> ::std::marker::PhantomData<bool>; //~ ERROR: composed only of PhantomData
+    pub fn fn_type(p: RustFn); //~ ERROR function pointer has Rust-specific
+    pub fn fn_type2(p: fn()); //~ ERROR function pointer has Rust-specific
+    pub fn fn_contained(p: RustBadRet); //~ ERROR: uses type `std::boxed::Box<u32>`
+    pub fn transparent_i128(p: TransparentI128); //~ ERROR: uses type `i128`
+    pub fn transparent_str(p: TransparentStr); //~ ERROR: uses type `str`
+    pub fn transparent_fn(p: TransparentBadFn); //~ ERROR: uses type `std::boxed::Box<u32>`
 
     pub fn good3(fptr: Option<extern fn()>);
     pub fn good4(aptr: &[u8; 4 as usize]);
diff --git a/src/test/ui/lint-ctypes.stderr b/src/test/ui/lint-ctypes.stderr
new file mode 100644
index 00000000000..748c311055f
--- /dev/null
+++ b/src/test/ui/lint-ctypes.stderr
@@ -0,0 +1,170 @@
+error: `extern` block uses type `Foo` which is not FFI-safe: this struct has unspecified layout
+  --> $DIR/lint-ctypes.rs:54:28
+   |
+54 |     pub fn ptr_type1(size: *const Foo); //~ ERROR: uses type `Foo`
+   |                            ^^^^^^^^^^
+   |
+note: lint level defined here
+  --> $DIR/lint-ctypes.rs:11:9
+   |
+11 | #![deny(improper_ctypes)]
+   |         ^^^^^^^^^^^^^^^
+   = help: consider adding a #[repr(C)] or #[repr(transparent)] attribute to this struct
+note: type defined here
+  --> $DIR/lint-ctypes.rs:32:1
+   |
+32 | pub struct Foo;
+   | ^^^^^^^^^^^^^^^
+
+error: `extern` block uses type `Foo` which is not FFI-safe: this struct has unspecified layout
+  --> $DIR/lint-ctypes.rs:55:28
+   |
+55 |     pub fn ptr_type2(size: *const Foo); //~ ERROR: uses type `Foo`
+   |                            ^^^^^^^^^^
+   |
+   = help: consider adding a #[repr(C)] or #[repr(transparent)] attribute to this struct
+note: type defined here
+  --> $DIR/lint-ctypes.rs:32:1
+   |
+32 | pub struct Foo;
+   | ^^^^^^^^^^^^^^^
+
+error: `extern` block uses type `[u32]` which is not FFI-safe: slices have no C equivalent
+  --> $DIR/lint-ctypes.rs:56:26
+   |
+56 |     pub fn slice_type(p: &[u32]); //~ ERROR: uses type `[u32]`
+   |                          ^^^^^^
+   |
+   = help: consider using a raw pointer instead
+
+error: `extern` block uses type `str` which is not FFI-safe: string slices have no C equivalent
+  --> $DIR/lint-ctypes.rs:57:24
+   |
+57 |     pub fn str_type(p: &str); //~ ERROR: uses type `str`
+   |                        ^^^^
+   |
+   = help: consider using `*const u8` and a length instead
+
+error: `extern` block uses type `std::boxed::Box<u32>` which is not FFI-safe: this struct has unspecified layout
+  --> $DIR/lint-ctypes.rs:58:24
+   |
+58 |     pub fn box_type(p: Box<u32>); //~ ERROR uses type `std::boxed::Box<u32>`
+   |                        ^^^^^^^^
+   |
+   = help: consider adding a #[repr(C)] or #[repr(transparent)] attribute to this struct
+
+error: `extern` block uses type `char` which is not FFI-safe: the `char` type has no C equivalent
+  --> $DIR/lint-ctypes.rs:59:25
+   |
+59 |     pub fn char_type(p: char); //~ ERROR uses type `char`
+   |                         ^^^^
+   |
+   = help: consider using `u32` or `libc::wchar_t` instead
+
+error: `extern` block uses type `i128` which is not FFI-safe: 128-bit integers don't currently have a known stable ABI
+  --> $DIR/lint-ctypes.rs:60:25
+   |
+60 |     pub fn i128_type(p: i128); //~ ERROR uses type `i128`
+   |                         ^^^^
+
+error: `extern` block uses type `u128` which is not FFI-safe: 128-bit integers don't currently have a known stable ABI
+  --> $DIR/lint-ctypes.rs:61:25
+   |
+61 |     pub fn u128_type(p: u128); //~ ERROR uses type `u128`
+   |                         ^^^^
+
+error: `extern` block uses type `std::clone::Clone` which is not FFI-safe: trait objects have no C equivalent
+  --> $DIR/lint-ctypes.rs:62:26
+   |
+62 |     pub fn trait_type(p: &Clone); //~ ERROR uses type `std::clone::Clone`
+   |                          ^^^^^^
+
+error: `extern` block uses type `(i32, i32)` which is not FFI-safe: tuples have unspecified layout
+  --> $DIR/lint-ctypes.rs:63:26
+   |
+63 |     pub fn tuple_type(p: (i32, i32)); //~ ERROR uses type `(i32, i32)`
+   |                          ^^^^^^^^^^
+   |
+   = help: consider using a struct instead
+
+error: `extern` block uses type `(i32, i32)` which is not FFI-safe: tuples have unspecified layout
+  --> $DIR/lint-ctypes.rs:64:27
+   |
+64 |     pub fn tuple_type2(p: I32Pair); //~ ERROR uses type `(i32, i32)`
+   |                           ^^^^^^^
+   |
+   = help: consider using a struct instead
+
+error: `extern` block uses type `ZeroSize` which is not FFI-safe: this struct has no fields
+  --> $DIR/lint-ctypes.rs:65:25
+   |
+65 |     pub fn zero_size(p: ZeroSize); //~ ERROR struct has no fields
+   |                         ^^^^^^^^
+   |
+   = help: consider adding a member to this struct
+note: type defined here
+  --> $DIR/lint-ctypes.rs:28:1
+   |
+28 | pub struct ZeroSize;
+   | ^^^^^^^^^^^^^^^^^^^^
+
+error: `extern` block uses type `ZeroSizeWithPhantomData` which is not FFI-safe: composed only of PhantomData
+  --> $DIR/lint-ctypes.rs:66:33
+   |
+66 |     pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); //~ ERROR composed only of PhantomData
+   |                                 ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `extern` block uses type `std::marker::PhantomData<bool>` which is not FFI-safe: composed only of PhantomData
+  --> $DIR/lint-ctypes.rs:68:12
+   |
+68 |         -> ::std::marker::PhantomData<bool>; //~ ERROR: composed only of PhantomData
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `extern` block uses type `fn()` which is not FFI-safe: this function pointer has Rust-specific calling convention
+  --> $DIR/lint-ctypes.rs:69:23
+   |
+69 |     pub fn fn_type(p: RustFn); //~ ERROR function pointer has Rust-specific
+   |                       ^^^^^^
+   |
+   = help: consider using an `fn "extern"(...) -> ...` function pointer instead
+
+error: `extern` block uses type `fn()` which is not FFI-safe: this function pointer has Rust-specific calling convention
+  --> $DIR/lint-ctypes.rs:70:24
+   |
+70 |     pub fn fn_type2(p: fn()); //~ ERROR function pointer has Rust-specific
+   |                        ^^^^
+   |
+   = help: consider using an `fn "extern"(...) -> ...` function pointer instead
+
+error: `extern` block uses type `std::boxed::Box<u32>` which is not FFI-safe: this struct has unspecified layout
+  --> $DIR/lint-ctypes.rs:71:28
+   |
+71 |     pub fn fn_contained(p: RustBadRet); //~ ERROR: uses type `std::boxed::Box<u32>`
+   |                            ^^^^^^^^^^
+   |
+   = help: consider adding a #[repr(C)] or #[repr(transparent)] attribute to this struct
+
+error: `extern` block uses type `i128` which is not FFI-safe: 128-bit integers don't currently have a known stable ABI
+  --> $DIR/lint-ctypes.rs:72:32
+   |
+72 |     pub fn transparent_i128(p: TransparentI128); //~ ERROR: uses type `i128`
+   |                                ^^^^^^^^^^^^^^^
+
+error: `extern` block uses type `str` which is not FFI-safe: string slices have no C equivalent
+  --> $DIR/lint-ctypes.rs:73:31
+   |
+73 |     pub fn transparent_str(p: TransparentStr); //~ ERROR: uses type `str`
+   |                               ^^^^^^^^^^^^^^
+   |
+   = help: consider using `*const u8` and a length instead
+
+error: `extern` block uses type `std::boxed::Box<u32>` which is not FFI-safe: this struct has unspecified layout
+  --> $DIR/lint-ctypes.rs:74:30
+   |
+74 |     pub fn transparent_fn(p: TransparentBadFn); //~ ERROR: uses type `std::boxed::Box<u32>`
+   |                              ^^^^^^^^^^^^^^^^
+   |
+   = help: consider adding a #[repr(C)] or #[repr(transparent)] attribute to this struct
+
+error: aborting due to 20 previous errors
+
diff --git a/src/test/ui/mismatched_types/closure-arg-count-expected-type-issue-47244.rs b/src/test/ui/mismatched_types/closure-arg-count-expected-type-issue-47244.rs
new file mode 100644
index 00000000000..b6463ca067b
--- /dev/null
+++ b/src/test/ui/mismatched_types/closure-arg-count-expected-type-issue-47244.rs
@@ -0,0 +1,29 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Regression test for #47244: in this specific scenario, when the
+// expected type indicated 1 argument but the closure takes two, we
+// would (early on) create type variables for the type of `b`. If the
+// user then attempts to invoke a method on `b`, we would get an error
+// saying that the type of `b` must be known, which was not very
+// helpful.
+
+use std::collections::HashMap;
+fn main() {
+
+    let m = HashMap::new();
+    m.insert( "foo", "bar" );
+
+    m.iter().map( |_, b| {
+        //~^ ERROR closure is expected to take a single 2-tuple
+
+        b.to_string()
+    });
+}
diff --git a/src/test/ui/mismatched_types/closure-arg-count-expected-type-issue-47244.stderr b/src/test/ui/mismatched_types/closure-arg-count-expected-type-issue-47244.stderr
new file mode 100644
index 00000000000..34934b8d195
--- /dev/null
+++ b/src/test/ui/mismatched_types/closure-arg-count-expected-type-issue-47244.stderr
@@ -0,0 +1,14 @@
+error[E0593]: closure is expected to take a single 2-tuple as argument, but it takes 2 distinct arguments
+  --> $DIR/closure-arg-count-expected-type-issue-47244.rs:24:14
+   |
+24 |     m.iter().map( |_, b| {
+   |              ^^^  ------ takes 2 distinct arguments
+   |              |
+   |              expected closure that takes a single 2-tuple as argument
+help: change the closure to accept a tuple instead of individual arguments
+   |
+24 |     m.iter().map( |(_, b)| {
+   |                   ^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy b/src/tools/clippy
-Subproject ce47e529d29f0bf19b31ae80b37b467e42fb97e
+Subproject d5e233a720495c52af25d8f6dcc9e55e1193beb