about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2019-12-31 19:19:36 +0100
committerGitHub <noreply@github.com>2019-12-31 19:19:36 +0100
commit50fb8480dbe65b14b48502c937eea66c4b5a6c31 (patch)
treed05faa6e61945514bcf1b774018015ab56479808
parentbc5963d2eb1f64b32dd82014cbc8c6082e463322 (diff)
parent63dc0e41dbf6d939741238a7c71bf5a63ae3ce55 (diff)
downloadrust-50fb8480dbe65b14b48502c937eea66c4b5a6c31.tar.gz
rust-50fb8480dbe65b14b48502c937eea66c4b5a6c31.zip
Rollup merge of #67730 - Centril:typeck-pat-cleanup, r=estebank
Cleanup pattern type checking, fix diagnostics bugs (+ improvements)

r? @estebank
-rw-r--r--src/librustc/infer/error_reporting/mod.rs18
-rw-r--r--src/librustc/traits/error_reporting.rs2
-rw-r--r--src/librustc/traits/mod.rs14
-rw-r--r--src/librustc/traits/structural_impls.rs8
-rw-r--r--src/librustc_typeck/check/_match.rs38
-rw-r--r--src/librustc_typeck/check/demand.rs31
-rw-r--r--src/librustc_typeck/check/mod.rs103
-rw-r--r--src/librustc_typeck/check/pat.rs205
-rw-r--r--src/librustc_typeck/lib.rs1
-rw-r--r--src/test/ui/block-result/issue-13624.stderr2
-rw-r--r--src/test/ui/destructure-trait-ref.stderr4
-rw-r--r--src/test/ui/error-codes/E0308-4.stderr2
-rw-r--r--src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision.stderr2
-rw-r--r--src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision2.stderr2
-rw-r--r--src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision3.stderr6
-rw-r--r--src/test/ui/issues/issue-11844.stderr2
-rw-r--r--src/test/ui/issues/issue-12552.stderr2
-rw-r--r--src/test/ui/issues/issue-13466.stderr4
-rw-r--r--src/test/ui/issues/issue-14541.stderr4
-rw-r--r--src/test/ui/issues/issue-15896.stderr2
-rw-r--r--src/test/ui/issues/issue-16338.stderr4
-rw-r--r--src/test/ui/issues/issue-16401.stderr2
-rw-r--r--src/test/ui/issues/issue-3680.stderr2
-rw-r--r--src/test/ui/issues/issue-37026.stderr4
-rw-r--r--src/test/ui/issues/issue-5100.stderr2
-rw-r--r--src/test/ui/issues/issue-5358-1.stderr2
-rw-r--r--src/test/ui/issues/issue-57741-1.stderr4
-rw-r--r--src/test/ui/issues/issue-57741.stderr8
-rw-r--r--src/test/ui/issues/issue-67037-pat-tup-scrut-ty-diff-less-fields.stderr4
-rw-r--r--src/test/ui/issues/issue-7092.stderr2
-rw-r--r--src/test/ui/match/match-struct.stderr2
-rw-r--r--src/test/ui/match/match-tag-unary.stderr2
-rw-r--r--src/test/ui/mismatched_types/E0409.stderr2
-rw-r--r--src/test/ui/or-patterns/already-bound-name.stderr4
-rw-r--r--src/test/ui/or-patterns/inconsistent-modes.stderr8
-rw-r--r--src/test/ui/or-patterns/or-pattern-mismatch.stderr2
-rw-r--r--src/test/ui/parser/pat-tuple-5.stderr2
-rw-r--r--src/test/ui/parser/recover-range-pats.stderr15
-rw-r--r--src/test/ui/pattern/pat-struct-field-expr-has-type.rs9
-rw-r--r--src/test/ui/pattern/pat-struct-field-expr-has-type.stderr14
-rw-r--r--src/test/ui/pattern/pat-type-err-formal-param.rs8
-rw-r--r--src/test/ui/pattern/pat-type-err-formal-param.stderr11
-rw-r--r--src/test/ui/pattern/pat-type-err-let-stmt.rs16
-rw-r--r--src/test/ui/pattern/pat-type-err-let-stmt.stderr49
-rw-r--r--src/test/ui/pattern/pattern-error-continue.stderr2
-rw-r--r--src/test/ui/pattern/pattern-tyvar.stderr2
-rw-r--r--src/test/ui/resolve/resolve-inconsistent-binding-mode.stderr6
-rw-r--r--src/test/ui/resolve/resolve-inconsistent-names.stderr2
-rw-r--r--src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr14
-rw-r--r--src/test/ui/structs/structure-constructor-type-mismatch.stderr6
50 files changed, 418 insertions, 244 deletions
diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs
index 027143a3d22..b3a79c08833 100644
--- a/src/librustc/infer/error_reporting/mod.rs
+++ b/src/librustc/infer/error_reporting/mod.rs
@@ -581,10 +581,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         exp_found: Option<ty::error::ExpectedFound<Ty<'tcx>>>,
     ) {
         match cause.code {
-            ObligationCauseCode::MatchExpressionArmPattern { span, ty } => {
+            ObligationCauseCode::Pattern { origin_expr: true, span: Some(span), root_ty } => {
+                let ty = self.resolve_vars_if_possible(&root_ty);
                 if ty.is_suggestable() {
                     // don't show type `_`
-                    err.span_label(span, format!("this match expression has type `{}`", ty));
+                    err.span_label(span, format!("this expression has type `{}`", ty));
                 }
                 if let Some(ty::error::ExpectedFound { found, .. }) = exp_found {
                     if ty.is_box() && ty.boxed_ty() == found {
@@ -599,11 +600,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     }
                 }
             }
+            ObligationCauseCode::Pattern { origin_expr: false, span: Some(span), .. } => {
+                err.span_label(span, "expected due to this");
+            }
             ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
                 source,
                 ref prior_arms,
                 last_ty,
-                discrim_hir_id,
+                scrut_hir_id,
                 ..
             }) => match source {
                 hir::MatchSource::IfLetDesugar { .. } => {
@@ -612,16 +616,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 }
                 hir::MatchSource::TryDesugar => {
                     if let Some(ty::error::ExpectedFound { expected, .. }) = exp_found {
-                        let discrim_expr = self.tcx.hir().expect_expr(discrim_hir_id);
-                        let discrim_ty = if let hir::ExprKind::Call(_, args) = &discrim_expr.kind {
+                        let scrut_expr = self.tcx.hir().expect_expr(scrut_hir_id);
+                        let scrut_ty = if let hir::ExprKind::Call(_, args) = &scrut_expr.kind {
                             let arg_expr = args.first().expect("try desugaring call w/out arg");
                             self.in_progress_tables
                                 .and_then(|tables| tables.borrow().expr_ty_opt(arg_expr))
                         } else {
-                            bug!("try desugaring w/out call expr as discriminant");
+                            bug!("try desugaring w/out call expr as scrutinee");
                         };
 
-                        match discrim_ty {
+                        match scrut_ty {
                             Some(ty) if expected == ty => {
                                 let source_map = self.tcx.sess.source_map();
                                 err.span_suggestion(
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index 878675f9812..5819e7aa5c2 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -2580,7 +2580,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         match *cause_code {
             ObligationCauseCode::ExprAssignable
             | ObligationCauseCode::MatchExpressionArm { .. }
-            | ObligationCauseCode::MatchExpressionArmPattern { .. }
+            | ObligationCauseCode::Pattern { .. }
             | ObligationCauseCode::IfExpression { .. }
             | ObligationCauseCode::IfExpressionWithNoElse
             | ObligationCauseCode::MainFunctionType
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index e6ecf1b676e..fe373e02e10 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -249,10 +249,14 @@ pub enum ObligationCauseCode<'tcx> {
     /// Computing common supertype in the arms of a match expression
     MatchExpressionArm(Box<MatchExpressionArmCause<'tcx>>),
 
-    /// Computing common supertype in the pattern guard for the arms of a match expression
-    MatchExpressionArmPattern {
-        span: Span,
-        ty: Ty<'tcx>,
+    /// Type error arising from type checking a pattern against an expected type.
+    Pattern {
+        /// The span of the scrutinee or type expression which caused the `root_ty` type.
+        span: Option<Span>,
+        /// The root expected type induced by a scrutinee or type expression.
+        root_ty: Ty<'tcx>,
+        /// Whether the `Span` came from an expression or a type expression.
+        origin_expr: bool,
     },
 
     /// Constants in patterns must have `Structural` type.
@@ -311,7 +315,7 @@ pub struct MatchExpressionArmCause<'tcx> {
     pub source: hir::MatchSource,
     pub prior_arms: Vec<Span>,
     pub last_ty: Ty<'tcx>,
-    pub discrim_hir_id: hir::HirId,
+    pub scrut_hir_id: hir::HirId,
 }
 
 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs
index ed0842d8098..c439f20d640 100644
--- a/src/librustc/traits/structural_impls.rs
+++ b/src/librustc/traits/structural_impls.rs
@@ -511,18 +511,18 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
                 source,
                 ref prior_arms,
                 last_ty,
-                discrim_hir_id,
+                scrut_hir_id,
             }) => tcx.lift(&last_ty).map(|last_ty| {
                 super::MatchExpressionArm(box super::MatchExpressionArmCause {
                     arm_span,
                     source,
                     prior_arms: prior_arms.clone(),
                     last_ty,
-                    discrim_hir_id,
+                    scrut_hir_id,
                 })
             }),
-            super::MatchExpressionArmPattern { span, ty } => {
-                tcx.lift(&ty).map(|ty| super::MatchExpressionArmPattern { span, ty })
+            super::Pattern { span, root_ty, origin_expr } => {
+                tcx.lift(&root_ty).map(|root_ty| super::Pattern { span, root_ty, origin_expr })
             }
             super::IfExpression(box super::IfExpressionCause { then, outer, semicolon }) => {
                 Some(super::IfExpression(box super::IfExpressionCause { then, outer, semicolon }))
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index 0d7ddd0fc48..841dd226b40 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -11,7 +11,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub fn check_match(
         &self,
         expr: &'tcx hir::Expr<'tcx>,
-        discrim: &'tcx hir::Expr<'tcx>,
+        scrut: &'tcx hir::Expr<'tcx>,
         arms: &'tcx [hir::Arm<'tcx>],
         expected: Expectation<'tcx>,
         match_src: hir::MatchSource,
@@ -27,7 +27,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         };
 
         // Type check the descriminant and get its type.
-        let discrim_ty = if force_scrutinee_bool {
+        let scrut_ty = if force_scrutinee_bool {
             // Here we want to ensure:
             //
             // 1. That default match bindings are *not* accepted in the condition of an
@@ -36,9 +36,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // 2. By expecting `bool` for `expr` we get nice diagnostics for e.g. `if x = y { .. }`.
             //
             // FIXME(60707): Consider removing hack with principled solution.
-            self.check_expr_has_type_or_error(discrim, self.tcx.types.bool, |_| {})
+            self.check_expr_has_type_or_error(scrut, self.tcx.types.bool, |_| {})
         } else {
-            self.demand_discriminant_type(arms, discrim)
+            self.demand_scrutinee_type(arms, scrut)
         };
 
         // If there are no arms, that is a diverging match; a special case.
@@ -51,7 +51,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // Otherwise, we have to union together the types that the
         // arms produce and so forth.
-        let discrim_diverges = self.diverges.get();
+        let scrut_diverges = self.diverges.get();
         self.diverges.set(Diverges::Maybe);
 
         // rust-lang/rust#55810: Typecheck patterns first (via eager
@@ -61,7 +61,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             .map(|arm| {
                 let mut all_pats_diverge = Diverges::WarnedAlways;
                 self.diverges.set(Diverges::Maybe);
-                self.check_pat_top(&arm.pat, discrim_ty, Some(discrim.span));
+                self.check_pat_top(&arm.pat, scrut_ty, Some(scrut.span), true);
                 all_pats_diverge &= self.diverges.get();
 
                 // As discussed with @eddyb, this is for disabling unreachable_code
@@ -157,7 +157,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             source: match_src,
                             prior_arms: other_arms.clone(),
                             last_ty: prior_arm_ty.unwrap(),
-                            discrim_hir_id: discrim.hir_id,
+                            scrut_hir_id: scrut.hir_id,
                         }),
                     ),
                 };
@@ -186,8 +186,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             };
         }
 
-        // We won't diverge unless the discriminant or all arms diverge.
-        self.diverges.set(discrim_diverges | all_arms_diverge);
+        // We won't diverge unless the scrutinee or all arms diverge.
+        self.diverges.set(scrut_diverges | all_arms_diverge);
 
         coercion.complete(self)
     }
@@ -388,14 +388,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         )
     }
 
-    fn demand_discriminant_type(
+    fn demand_scrutinee_type(
         &self,
         arms: &'tcx [hir::Arm<'tcx>],
-        discrim: &'tcx hir::Expr<'tcx>,
+        scrut: &'tcx hir::Expr<'tcx>,
     ) -> Ty<'tcx> {
         // Not entirely obvious: if matches may create ref bindings, we want to
-        // use the *precise* type of the discriminant, *not* some supertype, as
-        // the "discriminant type" (issue #23116).
+        // use the *precise* type of the scrutinee, *not* some supertype, as
+        // the "scrutinee type" (issue #23116).
         //
         // arielb1 [writes here in this comment thread][c] that there
         // is certainly *some* potential danger, e.g., for an example
@@ -454,17 +454,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             });
 
         if let Some(m) = contains_ref_bindings {
-            self.check_expr_with_needs(discrim, Needs::maybe_mut_place(m))
+            self.check_expr_with_needs(scrut, Needs::maybe_mut_place(m))
         } else {
             // ...but otherwise we want to use any supertype of the
-            // discriminant. This is sort of a workaround, see note (*) in
+            // scrutinee. This is sort of a workaround, see note (*) in
             // `check_pat` for some details.
-            let discrim_ty = self.next_ty_var(TypeVariableOrigin {
+            let scrut_ty = self.next_ty_var(TypeVariableOrigin {
                 kind: TypeVariableOriginKind::TypeInference,
-                span: discrim.span,
+                span: scrut.span,
             });
-            self.check_expr_has_type_or_error(discrim, discrim_ty, |_| {});
-            discrim_ty
+            self.check_expr_has_type_or_error(scrut, scrut_ty, |_| {});
+            scrut_ty
         }
     }
 }
diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs
index 6fdf4efc181..48403687aab 100644
--- a/src/librustc_typeck/check/demand.rs
+++ b/src/librustc_typeck/check/demand.rs
@@ -1,6 +1,6 @@
 use crate::check::FnCtxt;
 use rustc::infer::InferOk;
-use rustc::traits::{self, ObligationCause, ObligationCauseCode};
+use rustc::traits::{self, ObligationCause};
 
 use errors::{Applicability, DiagnosticBuilder};
 use rustc::hir::{self, is_range_literal, print, Node};
@@ -79,35 +79,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    pub fn demand_eqtype_pat_diag(
-        &self,
-        cause_span: Span,
-        expected: Ty<'tcx>,
-        actual: Ty<'tcx>,
-        match_expr_span: Option<Span>,
-    ) -> Option<DiagnosticBuilder<'tcx>> {
-        let cause = if let Some(span) = match_expr_span {
-            self.cause(
-                cause_span,
-                ObligationCauseCode::MatchExpressionArmPattern { span, ty: expected },
-            )
-        } else {
-            self.misc(cause_span)
-        };
-        self.demand_eqtype_with_origin(&cause, expected, actual)
-    }
-
-    pub fn demand_eqtype_pat(
-        &self,
-        cause_span: Span,
-        expected: Ty<'tcx>,
-        actual: Ty<'tcx>,
-        match_expr_span: Option<Span>,
-    ) {
-        self.demand_eqtype_pat_diag(cause_span, expected, actual, match_expr_span)
-            .map(|mut err| err.emit());
-    }
-
     pub fn demand_coerce(
         &self,
         expr: &hir::Expr<'_>,
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 67bbc6db495..042c2fbd13a 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -1268,13 +1268,17 @@ fn check_fn<'a, 'tcx>(
     let mut fcx = FnCtxt::new(inherited, param_env, body.value.hir_id);
     *fcx.ps.borrow_mut() = UnsafetyState::function(fn_sig.unsafety, fn_id);
 
+    let tcx = fcx.tcx;
+    let sess = tcx.sess;
+    let hir = tcx.hir();
+
     let declared_ret_ty = fn_sig.output();
     fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType);
     let revealed_ret_ty =
         fcx.instantiate_opaque_types_from_value(fn_id, &declared_ret_ty, decl.output.span());
     debug!("check_fn: declared_ret_ty: {}, revealed_ret_ty: {}", declared_ret_ty, revealed_ret_ty);
     fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(revealed_ret_ty)));
-    fn_sig = fcx.tcx.mk_fn_sig(
+    fn_sig = tcx.mk_fn_sig(
         fn_sig.inputs().iter().cloned(),
         revealed_ret_ty,
         fn_sig.c_variadic,
@@ -1284,7 +1288,7 @@ fn check_fn<'a, 'tcx>(
 
     let span = body.value.span;
 
-    fn_maybe_err(fcx.tcx, span, fn_sig.abi);
+    fn_maybe_err(tcx, span, fn_sig.abi);
 
     if body.generator_kind.is_some() && can_be_generator.is_some() {
         let yield_ty = fcx
@@ -1293,37 +1297,39 @@ fn check_fn<'a, 'tcx>(
         fcx.yield_ty = Some(yield_ty);
     }
 
-    let outer_def_id = fcx.tcx.closure_base_def_id(fcx.tcx.hir().local_def_id(fn_id));
-    let outer_hir_id = fcx.tcx.hir().as_local_hir_id(outer_def_id).unwrap();
+    let outer_def_id = tcx.closure_base_def_id(hir.local_def_id(fn_id));
+    let outer_hir_id = hir.as_local_hir_id(outer_def_id).unwrap();
     GatherLocalsVisitor { fcx: &fcx, parent_id: outer_hir_id }.visit_body(body);
 
     // C-variadic fns also have a `VaList` input that's not listed in `fn_sig`
     // (as it's created inside the body itself, not passed in from outside).
     let maybe_va_list = if fn_sig.c_variadic {
-        let va_list_did = fcx.tcx.require_lang_item(
+        let va_list_did = tcx.require_lang_item(
             lang_items::VaListTypeLangItem,
             Some(body.params.last().unwrap().span),
         );
-        let region = fcx.tcx.mk_region(ty::ReScope(region::Scope {
+        let region = tcx.mk_region(ty::ReScope(region::Scope {
             id: body.value.hir_id.local_id,
             data: region::ScopeData::CallSite,
         }));
 
-        Some(fcx.tcx.type_of(va_list_did).subst(fcx.tcx, &[region.into()]))
+        Some(tcx.type_of(va_list_did).subst(tcx, &[region.into()]))
     } else {
         None
     };
 
     // Add formal parameters.
-    for (param_ty, param) in fn_sig.inputs().iter().copied().chain(maybe_va_list).zip(body.params) {
+    let inputs_hir = hir.fn_decl_by_hir_id(fn_id).map(|decl| &decl.inputs);
+    let inputs_fn = fn_sig.inputs().iter().copied();
+    for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() {
         // Check the pattern.
-        fcx.check_pat_top(&param.pat, param_ty, None);
+        fcx.check_pat_top(&param.pat, param_ty, try { inputs_hir?.get(idx)?.span }, false);
 
         // Check that argument is Sized.
         // The check for a non-trivial pattern is a hack to avoid duplicate warnings
         // for simple cases like `fn foo(x: Trait)`,
         // where we would error once on the parameter as a whole, and once on the binding `x`.
-        if param.pat.simple_ident().is_none() && !fcx.tcx.features().unsized_locals {
+        if param.pat.simple_ident().is_none() && !tcx.features().unsized_locals {
             fcx.require_type_is_sized(param_ty, decl.output.span(), traits::SizedArgumentType);
         }
 
@@ -1384,11 +1390,11 @@ fn check_fn<'a, 'tcx>(
     fcx.demand_suptype(span, revealed_ret_ty, actual_return_ty);
 
     // Check that the main return type implements the termination trait.
-    if let Some(term_id) = fcx.tcx.lang_items().termination() {
-        if let Some((def_id, EntryFnType::Main)) = fcx.tcx.entry_fn(LOCAL_CRATE) {
-            let main_id = fcx.tcx.hir().as_local_hir_id(def_id).unwrap();
+    if let Some(term_id) = tcx.lang_items().termination() {
+        if let Some((def_id, EntryFnType::Main)) = tcx.entry_fn(LOCAL_CRATE) {
+            let main_id = hir.as_local_hir_id(def_id).unwrap();
             if main_id == fn_id {
-                let substs = fcx.tcx.mk_substs_trait(declared_ret_ty, &[]);
+                let substs = tcx.mk_substs_trait(declared_ret_ty, &[]);
                 let trait_ref = ty::TraitRef::new(term_id, substs);
                 let return_ty_span = decl.output.span();
                 let cause = traits::ObligationCause::new(
@@ -1407,15 +1413,15 @@ fn check_fn<'a, 'tcx>(
     }
 
     // Check that a function marked as `#[panic_handler]` has signature `fn(&PanicInfo) -> !`
-    if let Some(panic_impl_did) = fcx.tcx.lang_items().panic_impl() {
-        if panic_impl_did == fcx.tcx.hir().local_def_id(fn_id) {
-            if let Some(panic_info_did) = fcx.tcx.lang_items().panic_info() {
+    if let Some(panic_impl_did) = tcx.lang_items().panic_impl() {
+        if panic_impl_did == hir.local_def_id(fn_id) {
+            if let Some(panic_info_did) = tcx.lang_items().panic_info() {
                 if declared_ret_ty.kind != ty::Never {
-                    fcx.tcx.sess.span_err(decl.output.span(), "return type should be `!`");
+                    sess.span_err(decl.output.span(), "return type should be `!`");
                 }
 
                 let inputs = fn_sig.inputs();
-                let span = fcx.tcx.hir().span(fn_id);
+                let span = hir.span(fn_id);
                 if inputs.len() == 1 {
                     let arg_is_panic_info = match inputs[0].kind {
                         ty::Ref(region, ty, mutbl) => match ty.kind {
@@ -1430,38 +1436,36 @@ fn check_fn<'a, 'tcx>(
                     };
 
                     if !arg_is_panic_info {
-                        fcx.tcx
-                            .sess
-                            .span_err(decl.inputs[0].span, "argument should be `&PanicInfo`");
+                        sess.span_err(decl.inputs[0].span, "argument should be `&PanicInfo`");
                     }
 
-                    if let Node::Item(item) = fcx.tcx.hir().get(fn_id) {
+                    if let Node::Item(item) = hir.get(fn_id) {
                         if let ItemKind::Fn(_, ref generics, _) = item.kind {
                             if !generics.params.is_empty() {
-                                fcx.tcx.sess.span_err(span, "should have no type parameters");
+                                sess.span_err(span, "should have no type parameters");
                             }
                         }
                     }
                 } else {
-                    let span = fcx.tcx.sess.source_map().def_span(span);
-                    fcx.tcx.sess.span_err(span, "function should have one argument");
+                    let span = sess.source_map().def_span(span);
+                    sess.span_err(span, "function should have one argument");
                 }
             } else {
-                fcx.tcx.sess.err("language item required, but not found: `panic_info`");
+                sess.err("language item required, but not found: `panic_info`");
             }
         }
     }
 
     // Check that a function marked as `#[alloc_error_handler]` has signature `fn(Layout) -> !`
-    if let Some(alloc_error_handler_did) = fcx.tcx.lang_items().oom() {
-        if alloc_error_handler_did == fcx.tcx.hir().local_def_id(fn_id) {
-            if let Some(alloc_layout_did) = fcx.tcx.lang_items().alloc_layout() {
+    if let Some(alloc_error_handler_did) = tcx.lang_items().oom() {
+        if alloc_error_handler_did == hir.local_def_id(fn_id) {
+            if let Some(alloc_layout_did) = tcx.lang_items().alloc_layout() {
                 if declared_ret_ty.kind != ty::Never {
-                    fcx.tcx.sess.span_err(decl.output.span(), "return type should be `!`");
+                    sess.span_err(decl.output.span(), "return type should be `!`");
                 }
 
                 let inputs = fn_sig.inputs();
-                let span = fcx.tcx.hir().span(fn_id);
+                let span = hir.span(fn_id);
                 if inputs.len() == 1 {
                     let arg_is_alloc_layout = match inputs[0].kind {
                         ty::Adt(ref adt, _) => adt.did == alloc_layout_did,
@@ -1469,13 +1473,13 @@ fn check_fn<'a, 'tcx>(
                     };
 
                     if !arg_is_alloc_layout {
-                        fcx.tcx.sess.span_err(decl.inputs[0].span, "argument should be `Layout`");
+                        sess.span_err(decl.inputs[0].span, "argument should be `Layout`");
                     }
 
-                    if let Node::Item(item) = fcx.tcx.hir().get(fn_id) {
+                    if let Node::Item(item) = hir.get(fn_id) {
                         if let ItemKind::Fn(_, ref generics, _) = item.kind {
                             if !generics.params.is_empty() {
-                                fcx.tcx.sess.span_err(
+                                sess.span_err(
                                     span,
                                     "`#[alloc_error_handler]` function should have no type \
                                      parameters",
@@ -1484,11 +1488,11 @@ fn check_fn<'a, 'tcx>(
                         }
                     }
                 } else {
-                    let span = fcx.tcx.sess.source_map().def_span(span);
-                    fcx.tcx.sess.span_err(span, "function should have one argument");
+                    let span = sess.source_map().def_span(span);
+                    sess.span_err(span, "function should have one argument");
                 }
             } else {
-                fcx.tcx.sess.err("language item required, but not found: `alloc_layout`");
+                sess.err("language item required, but not found: `alloc_layout`");
             }
         }
     }
@@ -4304,18 +4308,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
+    /// Type check a `let` statement.
     pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) {
-        let t = self.local_ty(local.span, local.hir_id).decl_ty;
-        self.write_ty(local.hir_id, t);
+        // Determine and write the type which we'll check the pattern against.
+        let ty = self.local_ty(local.span, local.hir_id).decl_ty;
+        self.write_ty(local.hir_id, ty);
 
+        // Type check the initializer.
         if let Some(ref init) = local.init {
             let init_ty = self.check_decl_initializer(local, &init);
-            self.overwrite_local_ty_if_err(local, t, init_ty);
+            self.overwrite_local_ty_if_err(local, ty, init_ty);
         }
 
-        self.check_pat_top(&local.pat, t, None);
+        // Does the expected pattern type originate from an expression and what is the span?
+        let (origin_expr, ty_span) = match (local.ty, local.init) {
+            (Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type.
+            (_, Some(init)) => (true, Some(init.span)), // No explicit type; so use the scrutinee.
+            _ => (false, None), // We have `let $pat;`, so the expected type is unconstrained.
+        };
+
+        // Type check the pattern. Override if necessary to avoid knock-on errors.
+        self.check_pat_top(&local.pat, ty, ty_span, origin_expr);
         let pat_ty = self.node_ty(local.pat.hir_id);
-        self.overwrite_local_ty_if_err(local, t, pat_ty);
+        self.overwrite_local_ty_if_err(local, ty, pat_ty);
     }
 
     fn overwrite_local_ty_if_err(
@@ -4325,7 +4340,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         ty: Ty<'tcx>,
     ) {
         if ty.references_error() {
-            // Override the types everywhere with `types.err` to avoid knock down errors.
+            // Override the types everywhere with `types.err` to avoid knock on errors.
             self.write_ty(local.hir_id, ty);
             self.write_ty(local.pat.hir_id, ty);
             let local_ty = LocalTy { decl_ty, revealed_ty: ty };
diff --git a/src/librustc_typeck/check/pat.rs b/src/librustc_typeck/check/pat.rs
index 2f53378303b..75911893396 100644
--- a/src/librustc_typeck/check/pat.rs
+++ b/src/librustc_typeck/check/pat.rs
@@ -6,6 +6,7 @@ use rustc::hir::pat_util::EnumerateAndAdjustIterator;
 use rustc::hir::{self, HirId, Pat, PatKind};
 use rustc::infer;
 use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc::traits::Pattern;
 use rustc::ty::subst::GenericArg;
 use rustc::ty::{self, BindingMode, Ty, TypeFoldable};
 use syntax::ast;
@@ -29,39 +30,97 @@ pointers. If you encounter this error you should try to avoid dereferencing the
 You can read more about trait objects in the Trait Objects section of the Reference: \
 https://doc.rust-lang.org/reference/types.html#trait-objects";
 
-impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
-    pub fn check_pat_top(
-        &self,
-        pat: &'tcx Pat<'tcx>,
-        expected: Ty<'tcx>,
-        discrim_span: Option<Span>,
-    ) {
-        let def_bm = BindingMode::BindByValue(hir::Mutability::Not);
-        self.check_pat(pat, expected, def_bm, discrim_span);
-    }
-
-    /// `discrim_span` argument having a `Span` indicates that this pattern is part of a match
-    /// expression arm guard, and it points to the match discriminant to add context in type errors.
-    /// In the following example, `discrim_span` corresponds to the `a + b` expression:
+/// Information about the expected type at the top level of type checking a pattern.
+///
+/// **NOTE:** This is only for use by diagnostics. Do NOT use for type checking logic!
+#[derive(Copy, Clone)]
+struct TopInfo<'tcx> {
+    /// The `expected` type at the top level of type checking a pattern.
+    expected: Ty<'tcx>,
+    /// Was the origin of the `span` from a scrutinee expression?
+    ///
+    /// Otherwise there is no scrutinee and it could be e.g. from the type of a formal parameter.
+    origin_expr: bool,
+    /// The span giving rise to the `expected` type, if one could be provided.
+    ///
+    /// If `origin_expr` is `true`, then this is the span of the scrutinee as in:
+    ///
+    /// - `match scrutinee { ... }`
+    /// - `let _ = scrutinee;`
+    ///
+    /// This is used to point to add context in type errors.
+    /// In the following example, `span` corresponds to the `a + b` expression:
     ///
     /// ```text
     /// error[E0308]: mismatched types
-    ///  --> src/main.rs:5:9
+    ///  --> src/main.rs:L:C
     ///   |
-    /// 4 |    let temp: usize = match a + b {
+    /// L |    let temp: usize = match a + b {
     ///   |                            ----- this expression has type `usize`
-    /// 5 |         Ok(num) => num,
+    /// L |         Ok(num) => num,
     ///   |         ^^^^^^^ expected `usize`, found enum `std::result::Result`
     ///   |
     ///   = note: expected type `usize`
     ///              found type `std::result::Result<_, _>`
     /// ```
+    span: Option<Span>,
+}
+
+impl<'tcx> FnCtxt<'_, 'tcx> {
+    fn demand_eqtype_pat_diag(
+        &self,
+        cause_span: Span,
+        expected: Ty<'tcx>,
+        actual: Ty<'tcx>,
+        ti: TopInfo<'tcx>,
+    ) -> Option<DiagnosticBuilder<'tcx>> {
+        let code = Pattern { span: ti.span, root_ty: ti.expected, origin_expr: ti.origin_expr };
+        let cause = self.cause(cause_span, code);
+        self.demand_eqtype_with_origin(&cause, expected, actual)
+    }
+
+    fn demand_eqtype_pat(
+        &self,
+        cause_span: Span,
+        expected: Ty<'tcx>,
+        actual: Ty<'tcx>,
+        ti: TopInfo<'tcx>,
+    ) {
+        self.demand_eqtype_pat_diag(cause_span, expected, actual, ti).map(|mut err| err.emit());
+    }
+}
+
+impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
+    /// Type check the given top level pattern against the `expected` type.
+    ///
+    /// If a `Some(span)` is provided and `origin_expr` holds,
+    /// then the `span` represents the scrutinee's span.
+    /// The scrutinee is found in e.g. `match scrutinee { ... }` and `let pat = scrutinee;`.
+    ///
+    /// Otherwise, `Some(span)` represents the span of a type expression
+    /// which originated the `expected` type.
+    pub fn check_pat_top(
+        &self,
+        pat: &'tcx Pat<'tcx>,
+        expected: Ty<'tcx>,
+        span: Option<Span>,
+        origin_expr: bool,
+    ) {
+        let def_bm = BindingMode::BindByValue(hir::Mutability::Not);
+        self.check_pat(pat, expected, def_bm, TopInfo { expected, origin_expr, span });
+    }
+
+    /// Type check the given `pat` against the `expected` type
+    /// with the provided `def_bm` (default binding mode).
+    ///
+    /// Outside of this module, `check_pat_top` should always be used.
+    /// Conversely, inside this module, `check_pat_top` should never be used.
     fn check_pat(
         &self,
         pat: &'tcx Pat<'tcx>,
         expected: Ty<'tcx>,
         def_bm: BindingMode,
-        discrim_span: Option<Span>,
+        ti: TopInfo<'tcx>,
     ) {
         debug!("check_pat(pat={:?},expected={:?},def_bm={:?})", pat, expected, def_bm);
 
@@ -72,60 +131,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let is_nrp = self.is_non_ref_pat(pat, path_resolution.map(|(res, ..)| res));
         let (expected, def_bm) = self.calc_default_binding_mode(pat, expected, def_bm, is_nrp);
 
-        let ty = match &pat.kind {
+        let ty = match pat.kind {
             PatKind::Wild => expected,
-            PatKind::Lit(lt) => self.check_pat_lit(pat.span, lt, expected, discrim_span),
+            PatKind::Lit(lt) => self.check_pat_lit(pat.span, lt, expected, ti),
             PatKind::Range(begin, end, _) => {
-                match self.check_pat_range(pat.span, begin, end, expected, discrim_span) {
+                match self.check_pat_range(pat.span, begin, end, expected, ti) {
                     None => return,
                     Some(ty) => ty,
                 }
             }
             PatKind::Binding(ba, var_id, _, sub) => {
-                let sub = sub.as_deref();
-                self.check_pat_ident(pat, *ba, *var_id, sub, expected, def_bm, discrim_span)
+                self.check_pat_ident(pat, ba, var_id, sub, expected, def_bm, ti)
             }
-            PatKind::TupleStruct(qpath, subpats, ddpos) => self.check_pat_tuple_struct(
-                pat,
-                qpath,
-                subpats,
-                *ddpos,
-                expected,
-                def_bm,
-                discrim_span,
-            ),
-            PatKind::Path(qpath) => {
+            PatKind::TupleStruct(ref qpath, subpats, ddpos) => {
+                self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, def_bm, ti)
+            }
+            PatKind::Path(ref qpath) => {
                 self.check_pat_path(pat, path_resolution.unwrap(), qpath, expected)
             }
-            PatKind::Struct(qpath, fields, etc) => {
-                self.check_pat_struct(pat, qpath, fields, *etc, expected, def_bm, discrim_span)
+            PatKind::Struct(ref qpath, fields, etc) => {
+                self.check_pat_struct(pat, qpath, fields, etc, expected, def_bm, ti)
             }
             PatKind::Or(pats) => {
-                for pat in *pats {
-                    self.check_pat(pat, expected, def_bm, discrim_span);
+                for pat in pats {
+                    self.check_pat(pat, expected, def_bm, ti);
                 }
                 expected
             }
             PatKind::Tuple(elements, ddpos) => {
-                self.check_pat_tuple(pat.span, *elements, *ddpos, expected, def_bm, discrim_span)
-            }
-            PatKind::Box(inner) => {
-                self.check_pat_box(pat.span, inner, expected, def_bm, discrim_span)
+                self.check_pat_tuple(pat.span, elements, ddpos, expected, def_bm, ti)
             }
+            PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, def_bm, ti),
             PatKind::Ref(inner, mutbl) => {
-                self.check_pat_ref(pat, inner, *mutbl, expected, def_bm, discrim_span)
+                self.check_pat_ref(pat, inner, mutbl, expected, def_bm, ti)
             }
             PatKind::Slice(before, slice, after) => {
-                let slice = slice.as_deref();
-                self.check_pat_slice(
-                    pat.span,
-                    *before,
-                    slice,
-                    *after,
-                    expected,
-                    def_bm,
-                    discrim_span,
-                )
+                self.check_pat_slice(pat.span, before, slice, after, expected, def_bm, ti)
             }
         };
 
@@ -302,7 +343,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         span: Span,
         lt: &hir::Expr<'tcx>,
         expected: Ty<'tcx>,
-        discrim_span: Option<Span>,
+        ti: TopInfo<'tcx>,
     ) -> Ty<'tcx> {
         // We've already computed the type above (when checking for a non-ref pat),
         // so avoid computing it again.
@@ -336,7 +377,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // then that's equivalent to there existing a LUB.
         if let Some(mut err) = self.demand_suptype_diag(span, expected, pat_ty) {
             err.emit_unless(
-                discrim_span
+                ti.span
                     .filter(|&s| {
                         // In the case of `if`- and `while`-expressions we've already checked
                         // that `scrutinee: bool`. We know that the pattern is `true`,
@@ -356,7 +397,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         lhs: &'tcx hir::Expr<'tcx>,
         rhs: &'tcx hir::Expr<'tcx>,
         expected: Ty<'tcx>,
-        discrim_span: Option<Span>,
+        ti: TopInfo<'tcx>,
     ) -> Option<Ty<'tcx>> {
         let lhs_ty = self.check_expr(lhs);
         let rhs_ty = self.check_expr(rhs);
@@ -377,7 +418,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // Subtyping doesn't matter here, as the value is some kind of scalar.
         let demand_eqtype = |x_span, y_span, x_ty, y_ty| {
-            self.demand_eqtype_pat_diag(x_span, expected, x_ty, discrim_span).map(|mut err| {
+            self.demand_eqtype_pat_diag(x_span, expected, x_ty, ti).map(|mut err| {
                 self.endpoint_has_type(&mut err, y_span, y_ty);
                 err.emit();
             });
@@ -451,7 +492,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         sub: Option<&'tcx Pat<'tcx>>,
         expected: Ty<'tcx>,
         def_bm: BindingMode,
-        discrim_span: Option<Span>,
+        ti: TopInfo<'tcx>,
     ) -> Ty<'tcx> {
         // Determine the binding mode...
         let bm = match ba {
@@ -481,17 +522,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 expected
             }
         };
-        self.demand_eqtype_pat(pat.span, eq_ty, local_ty, discrim_span);
+        self.demand_eqtype_pat(pat.span, eq_ty, local_ty, ti);
 
         // If there are multiple arms, make sure they all agree on
         // what the type of the binding `x` ought to be.
         if var_id != pat.hir_id {
             let vt = self.local_ty(pat.span, var_id).decl_ty;
-            self.demand_eqtype_pat(pat.span, vt, local_ty, discrim_span);
+            self.demand_eqtype_pat(pat.span, vt, local_ty, ti);
         }
 
         if let Some(p) = sub {
-            self.check_pat(&p, expected, def_bm, discrim_span);
+            self.check_pat(&p, expected, def_bm, ti);
         }
 
         local_ty
@@ -570,7 +611,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         etc: bool,
         expected: Ty<'tcx>,
         def_bm: BindingMode,
-        discrim_span: Option<Span>,
+        ti: TopInfo<'tcx>,
     ) -> Ty<'tcx> {
         // Resolve the path and check the definition for errors.
         let (variant, pat_ty) = if let Some(variant_ty) = self.check_struct_path(qpath, pat.hir_id)
@@ -578,16 +619,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             variant_ty
         } else {
             for field in fields {
-                self.check_pat(&field.pat, self.tcx.types.err, def_bm, discrim_span);
+                self.check_pat(&field.pat, self.tcx.types.err, def_bm, ti);
             }
             return self.tcx.types.err;
         };
 
         // Type-check the path.
-        self.demand_eqtype_pat(pat.span, expected, pat_ty, discrim_span);
+        self.demand_eqtype_pat(pat.span, expected, pat_ty, ti);
 
         // Type-check subpatterns.
-        if self.check_struct_pat_fields(pat_ty, pat.hir_id, pat.span, variant, fields, etc, def_bm)
+        if self
+            .check_struct_pat_fields(pat_ty, pat.hir_id, pat.span, variant, fields, etc, def_bm, ti)
         {
             pat_ty
         } else {
@@ -638,12 +680,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         ddpos: Option<usize>,
         expected: Ty<'tcx>,
         def_bm: BindingMode,
-        match_arm_pat_span: Option<Span>,
+        ti: TopInfo<'tcx>,
     ) -> Ty<'tcx> {
         let tcx = self.tcx;
         let on_error = || {
             for pat in subpats {
-                self.check_pat(&pat, tcx.types.err, def_bm, match_arm_pat_span);
+                self.check_pat(&pat, tcx.types.err, def_bm, ti);
             }
         };
         let report_unexpected_res = |res: Res| {
@@ -704,7 +746,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let pat_ty = pat_ty.no_bound_vars().expect("expected fn type");
 
         // Type-check the tuple struct pattern against the expected type.
-        let diag = self.demand_eqtype_pat_diag(pat.span, expected, pat_ty, match_arm_pat_span);
+        let diag = self.demand_eqtype_pat_diag(pat.span, expected, pat_ty, ti);
         let had_err = diag.is_some();
         diag.map(|mut err| err.emit());
 
@@ -718,7 +760,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             };
             for (i, subpat) in subpats.iter().enumerate_and_adjust(variant.fields.len(), ddpos) {
                 let field_ty = self.field_ty(subpat.span, &variant.fields[i], substs);
-                self.check_pat(&subpat, field_ty, def_bm, match_arm_pat_span);
+                self.check_pat(&subpat, field_ty, def_bm, ti);
 
                 self.tcx.check_stability(variant.fields[i].did, Some(pat.hir_id), subpat.span);
             }
@@ -822,7 +864,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         ddpos: Option<usize>,
         expected: Ty<'tcx>,
         def_bm: BindingMode,
-        discrim_span: Option<Span>,
+        ti: TopInfo<'tcx>,
     ) -> Ty<'tcx> {
         let tcx = self.tcx;
         let mut expected_len = elements.len();
@@ -849,12 +891,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // further errors being emitted when using the bindings. #50333
             let element_tys_iter = (0..max_len).map(|_| tcx.types.err);
             for (_, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) {
-                self.check_pat(elem, &tcx.types.err, def_bm, discrim_span);
+                self.check_pat(elem, &tcx.types.err, def_bm, ti);
             }
             tcx.mk_tup(element_tys_iter)
         } else {
             for (i, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) {
-                self.check_pat(elem, &element_tys[i].expect_ty(), def_bm, discrim_span);
+                self.check_pat(elem, &element_tys[i].expect_ty(), def_bm, ti);
             }
             pat_ty
         }
@@ -869,6 +911,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         fields: &'tcx [hir::FieldPat<'tcx>],
         etc: bool,
         def_bm: BindingMode,
+        ti: TopInfo<'tcx>,
     ) -> bool {
         let tcx = self.tcx;
 
@@ -918,7 +961,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
             };
 
-            self.check_pat(&field.pat, field_ty, def_bm, None);
+            self.check_pat(&field.pat, field_ty, def_bm, ti);
         }
 
         let mut unmentioned_fields = variant
@@ -1095,7 +1138,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         inner: &'tcx Pat<'tcx>,
         expected: Ty<'tcx>,
         def_bm: BindingMode,
-        discrim_span: Option<Span>,
+        ti: TopInfo<'tcx>,
     ) -> Ty<'tcx> {
         let tcx = self.tcx;
         let (box_ty, inner_ty) = if self.check_dereferenceable(span, expected, &inner) {
@@ -1106,12 +1149,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 span: inner.span,
             });
             let box_ty = tcx.mk_box(inner_ty);
-            self.demand_eqtype_pat(span, expected, box_ty, discrim_span);
+            self.demand_eqtype_pat(span, expected, box_ty, ti);
             (box_ty, inner_ty)
         } else {
             (tcx.types.err, tcx.types.err)
         };
-        self.check_pat(&inner, inner_ty, def_bm, discrim_span);
+        self.check_pat(&inner, inner_ty, def_bm, ti);
         box_ty
     }
 
@@ -1122,7 +1165,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         mutbl: hir::Mutability,
         expected: Ty<'tcx>,
         def_bm: BindingMode,
-        discrim_span: Option<Span>,
+        ti: TopInfo<'tcx>,
     ) -> Ty<'tcx> {
         let tcx = self.tcx;
         let expected = self.shallow_resolve(expected);
@@ -1157,7 +1200,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         } else {
             (tcx.types.err, tcx.types.err)
         };
-        self.check_pat(&inner, inner_ty, def_bm, discrim_span);
+        self.check_pat(&inner, inner_ty, def_bm, ti);
         rptr_ty
     }
 
@@ -1186,7 +1229,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         after: &'tcx [&'tcx Pat<'tcx>],
         expected: Ty<'tcx>,
         def_bm: BindingMode,
-        discrim_span: Option<Span>,
+        ti: TopInfo<'tcx>,
     ) -> Ty<'tcx> {
         let err = self.tcx.types.err;
         let expected = self.structurally_resolved_type(span, expected);
@@ -1211,15 +1254,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // Type check all the patterns before `slice`.
         for elt in before {
-            self.check_pat(&elt, inner_ty, def_bm, discrim_span);
+            self.check_pat(&elt, inner_ty, def_bm, ti);
         }
         // Type check the `slice`, if present, against its expected type.
         if let Some(slice) = slice {
-            self.check_pat(&slice, slice_ty, def_bm, discrim_span);
+            self.check_pat(&slice, slice_ty, def_bm, ti);
         }
         // Type check the elements after `slice`, if present.
         for elt in after {
-            self.check_pat(&elt, inner_ty, def_bm, discrim_span);
+            self.check_pat(&elt, inner_ty, def_bm, ti);
         }
         expected
     }
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index d9b7e98ea75..f06fe1e4bf2 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -65,6 +65,7 @@ This API is completely unstable and subject to change.
 #![feature(in_band_lifetimes)]
 #![feature(nll)]
 #![feature(slice_patterns)]
+#![feature(try_blocks)]
 #![feature(never_type)]
 #![recursion_limit = "256"]
 
diff --git a/src/test/ui/block-result/issue-13624.stderr b/src/test/ui/block-result/issue-13624.stderr
index 90ffb4b2e52..416f055251b 100644
--- a/src/test/ui/block-result/issue-13624.stderr
+++ b/src/test/ui/block-result/issue-13624.stderr
@@ -10,7 +10,7 @@ error[E0308]: mismatched types
   --> $DIR/issue-13624.rs:20:9
    |
 LL |       match enum_struct_variant {
-   |             ------------------- this match expression has type `()`
+   |             ------------------- this expression has type `()`
 LL |         a::Enum::EnumStructVariant { x, y, z } => {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found enum `a::Enum`
 
diff --git a/src/test/ui/destructure-trait-ref.stderr b/src/test/ui/destructure-trait-ref.stderr
index f77291969d2..c78166f411d 100644
--- a/src/test/ui/destructure-trait-ref.stderr
+++ b/src/test/ui/destructure-trait-ref.stderr
@@ -44,7 +44,9 @@ error[E0308]: mismatched types
   --> $DIR/destructure-trait-ref.rs:42:13
    |
 LL |     let box box x = box 1isize as Box<dyn T>;
-   |             ^^^^^ expected trait `T`, found struct `std::boxed::Box`
+   |             ^^^^^   ------------------------ this expression has type `std::boxed::Box<dyn T>`
+   |             |
+   |             expected trait `T`, found struct `std::boxed::Box`
    |
    = note: expected trait object `dyn T`
                     found struct `std::boxed::Box<_>`
diff --git a/src/test/ui/error-codes/E0308-4.stderr b/src/test/ui/error-codes/E0308-4.stderr
index 46805d6e13b..39c06763737 100644
--- a/src/test/ui/error-codes/E0308-4.stderr
+++ b/src/test/ui/error-codes/E0308-4.stderr
@@ -2,7 +2,7 @@ error[E0308]: mismatched types
   --> $DIR/E0308-4.rs:4:15
    |
 LL |     match x {
-   |           - this match expression has type `u8`
+   |           - this expression has type `u8`
 LL |         0u8..=3i8 => (),
    |         ---   ^^^ expected `u8`, found `i8`
    |         |
diff --git a/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision.stderr b/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision.stderr
index 04538cd74b1..76ae7241ff2 100644
--- a/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision.stderr
+++ b/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision.stderr
@@ -8,7 +8,7 @@ error[E0308]: mismatched types
   --> $DIR/exclusive_range_pattern_syntax_collision.rs:5:13
    |
 LL |     match [5..4, 99..105, 43..44] {
-   |           ----------------------- this match expression has type `std::ops::Range<{integer}>`
+   |           ----------------------- this expression has type `[std::ops::Range<{integer}>; 3]`
 LL |         [_, 99.., _] => {},
    |             ^^ expected struct `std::ops::Range`, found integer
    |
diff --git a/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision2.stderr b/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision2.stderr
index c918d0a385c..5c96f8041fe 100644
--- a/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision2.stderr
+++ b/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision2.stderr
@@ -14,7 +14,7 @@ error[E0308]: mismatched types
   --> $DIR/exclusive_range_pattern_syntax_collision2.rs:5:13
    |
 LL |     match [5..4, 99..105, 43..44] {
-   |           ----------------------- this match expression has type `std::ops::Range<{integer}>`
+   |           ----------------------- this expression has type `[std::ops::Range<{integer}>; 3]`
 LL |         [_, 99..] => {},
    |             ^^ expected struct `std::ops::Range`, found integer
    |
diff --git a/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision3.stderr b/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision3.stderr
index e6ee3817b35..17e10324db1 100644
--- a/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision3.stderr
+++ b/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision3.stderr
@@ -8,7 +8,7 @@ error[E0308]: mismatched types
   --> $DIR/exclusive_range_pattern_syntax_collision3.rs:5:12
    |
 LL |     match [5..4, 99..105, 43..44] {
-   |           ----------------------- this match expression has type `std::ops::Range<{integer}>`
+   |           ----------------------- this expression has type `[std::ops::Range<{integer}>; 3]`
 LL |         [..9, 99..100, _] => {},
    |            ^ expected struct `std::ops::Range`, found integer
    |
@@ -19,7 +19,7 @@ error[E0308]: mismatched types
   --> $DIR/exclusive_range_pattern_syntax_collision3.rs:5:15
    |
 LL |     match [5..4, 99..105, 43..44] {
-   |           ----------------------- this match expression has type `std::ops::Range<{integer}>`
+   |           ----------------------- this expression has type `[std::ops::Range<{integer}>; 3]`
 LL |         [..9, 99..100, _] => {},
    |               ^^  --- this is of type `{integer}`
    |               |
@@ -32,7 +32,7 @@ error[E0308]: mismatched types
   --> $DIR/exclusive_range_pattern_syntax_collision3.rs:5:19
    |
 LL |     match [5..4, 99..105, 43..44] {
-   |           ----------------------- this match expression has type `std::ops::Range<{integer}>`
+   |           ----------------------- this expression has type `[std::ops::Range<{integer}>; 3]`
 LL |         [..9, 99..100, _] => {},
    |               --  ^^^ expected struct `std::ops::Range`, found integer
    |               |
diff --git a/src/test/ui/issues/issue-11844.stderr b/src/test/ui/issues/issue-11844.stderr
index 1b22d6f45cf..57533ba5e37 100644
--- a/src/test/ui/issues/issue-11844.stderr
+++ b/src/test/ui/issues/issue-11844.stderr
@@ -2,7 +2,7 @@ error[E0308]: mismatched types
   --> $DIR/issue-11844.rs:6:9
    |
 LL |     match a {
-   |           - this match expression has type `std::option::Option<std::boxed::Box<{integer}>>`
+   |           - this expression has type `std::option::Option<std::boxed::Box<{integer}>>`
 LL |         Ok(a) =>
    |         ^^^^^ expected enum `std::option::Option`, found enum `std::result::Result`
    |
diff --git a/src/test/ui/issues/issue-12552.stderr b/src/test/ui/issues/issue-12552.stderr
index ecafef259d3..60c4cceac51 100644
--- a/src/test/ui/issues/issue-12552.stderr
+++ b/src/test/ui/issues/issue-12552.stderr
@@ -2,7 +2,7 @@ error[E0308]: mismatched types
   --> $DIR/issue-12552.rs:6:5
    |
 LL |   match t {
-   |         - this match expression has type `std::result::Result<_, {integer}>`
+   |         - this expression has type `std::result::Result<_, {integer}>`
 LL |     Some(k) => match k {
    |     ^^^^^^^ expected enum `std::result::Result`, found enum `std::option::Option`
    |
diff --git a/src/test/ui/issues/issue-13466.stderr b/src/test/ui/issues/issue-13466.stderr
index fc20615757a..52d9e2a91b9 100644
--- a/src/test/ui/issues/issue-13466.stderr
+++ b/src/test/ui/issues/issue-13466.stderr
@@ -2,7 +2,7 @@ error[E0308]: mismatched types
   --> $DIR/issue-13466.rs:8:9
    |
 LL |     let _x: usize = match Some(1) {
-   |                           ------- this match expression has type `std::option::Option<{integer}>`
+   |                           ------- this expression has type `std::option::Option<{integer}>`
 LL |         Ok(u) => u,
    |         ^^^^^ expected enum `std::option::Option`, found enum `std::result::Result`
    |
@@ -13,7 +13,7 @@ error[E0308]: mismatched types
   --> $DIR/issue-13466.rs:14:9
    |
 LL |     let _x: usize = match Some(1) {
-   |                           ------- this match expression has type `std::option::Option<{integer}>`
+   |                           ------- this expression has type `std::option::Option<{integer}>`
 ...
 LL |         Err(e) => panic!(e)
    |         ^^^^^^ expected enum `std::option::Option`, found enum `std::result::Result`
diff --git a/src/test/ui/issues/issue-14541.stderr b/src/test/ui/issues/issue-14541.stderr
index c5512e03007..cf155f428c6 100644
--- a/src/test/ui/issues/issue-14541.stderr
+++ b/src/test/ui/issues/issue-14541.stderr
@@ -2,7 +2,9 @@ error[E0308]: mismatched types
   --> $DIR/issue-14541.rs:5:9
    |
 LL |     let Vec3 { y: _, z: _ } = v;
-   |         ^^^^^^^^^^^^^^^^^^^ expected struct `Vec2`, found struct `Vec3`
+   |         ^^^^^^^^^^^^^^^^^^^   - this expression has type `Vec2`
+   |         |
+   |         expected struct `Vec2`, found struct `Vec3`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-15896.stderr b/src/test/ui/issues/issue-15896.stderr
index f553be9df55..b3f0907b81d 100644
--- a/src/test/ui/issues/issue-15896.stderr
+++ b/src/test/ui/issues/issue-15896.stderr
@@ -2,7 +2,7 @@ error[E0308]: mismatched types
   --> $DIR/issue-15896.rs:11:11
    |
 LL |     let u = match e {
-   |                   - this match expression has type `main::R`
+   |                   - this expression has type `main::E`
 LL |         E::B(
 LL |           Tau{t: x},
    |           ^^^^^^^^^ expected enum `main::R`, found struct `main::Tau`
diff --git a/src/test/ui/issues/issue-16338.stderr b/src/test/ui/issues/issue-16338.stderr
index c35edb0c8c0..6878600b029 100644
--- a/src/test/ui/issues/issue-16338.stderr
+++ b/src/test/ui/issues/issue-16338.stderr
@@ -2,7 +2,9 @@ error[E0308]: mismatched types
   --> $DIR/issue-16338.rs:7:9
    |
 LL |     let Slice { data: data, len: len } = "foo";
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `str`, found struct `Slice`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^   ----- this expression has type `&str`
+   |         |
+   |         expected `str`, found struct `Slice`
    |
    = note: expected type `str`
             found struct `Slice<_>`
diff --git a/src/test/ui/issues/issue-16401.stderr b/src/test/ui/issues/issue-16401.stderr
index d3d6108be9d..f8ea0907099 100644
--- a/src/test/ui/issues/issue-16401.stderr
+++ b/src/test/ui/issues/issue-16401.stderr
@@ -2,7 +2,7 @@ error[E0308]: mismatched types
   --> $DIR/issue-16401.rs:8:9
    |
 LL |     match () {
-   |           -- this match expression has type `()`
+   |           -- this expression has type `()`
 LL |         Slice { data: data, len: len } => (),
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found struct `Slice`
    |
diff --git a/src/test/ui/issues/issue-3680.stderr b/src/test/ui/issues/issue-3680.stderr
index 8856f0e3a48..713e4b5ccd5 100644
--- a/src/test/ui/issues/issue-3680.stderr
+++ b/src/test/ui/issues/issue-3680.stderr
@@ -2,7 +2,7 @@ error[E0308]: mismatched types
   --> $DIR/issue-3680.rs:3:9
    |
 LL |     match None {
-   |           ---- this match expression has type `std::option::Option<_>`
+   |           ---- this expression has type `std::option::Option<_>`
 LL |         Err(_) => ()
    |         ^^^^^^ expected enum `std::option::Option`, found enum `std::result::Result`
    |
diff --git a/src/test/ui/issues/issue-37026.stderr b/src/test/ui/issues/issue-37026.stderr
index 00952356b18..361369e68bc 100644
--- a/src/test/ui/issues/issue-37026.stderr
+++ b/src/test/ui/issues/issue-37026.stderr
@@ -8,7 +8,9 @@ error[E0308]: mismatched types
   --> $DIR/issue-37026.rs:7:9
    |
 LL |     let empty_struct::XEmpty6(..) = ();
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found struct `empty_struct::XEmpty6`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^   -- this expression has type `()`
+   |         |
+   |         expected `()`, found struct `empty_struct::XEmpty6`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/issues/issue-5100.stderr b/src/test/ui/issues/issue-5100.stderr
index 9e1011496c4..c47e8689436 100644
--- a/src/test/ui/issues/issue-5100.stderr
+++ b/src/test/ui/issues/issue-5100.stderr
@@ -29,7 +29,7 @@ error[E0308]: mismatched types
   --> $DIR/issue-5100.rs:33:9
    |
 LL |     match (true, false) {
-   |           ------------- this match expression has type `(bool, bool)`
+   |           ------------- this expression has type `(bool, bool)`
 LL |         box (true, false) => ()
    |         ^^^^^^^^^^^^^^^^^ expected tuple, found struct `std::boxed::Box`
    |
diff --git a/src/test/ui/issues/issue-5358-1.stderr b/src/test/ui/issues/issue-5358-1.stderr
index ec79d874d03..314d1fddbd7 100644
--- a/src/test/ui/issues/issue-5358-1.stderr
+++ b/src/test/ui/issues/issue-5358-1.stderr
@@ -2,7 +2,7 @@ error[E0308]: mismatched types
   --> $DIR/issue-5358-1.rs:6:9
    |
 LL |     match S(Either::Left(5)) {
-   |           ------------------ this match expression has type `S`
+   |           ------------------ this expression has type `S`
 LL |         Either::Right(_) => {}
    |         ^^^^^^^^^^^^^^^^ expected struct `S`, found enum `Either`
    |
diff --git a/src/test/ui/issues/issue-57741-1.stderr b/src/test/ui/issues/issue-57741-1.stderr
index db6fa9db8ff..a4f1ac94825 100644
--- a/src/test/ui/issues/issue-57741-1.stderr
+++ b/src/test/ui/issues/issue-57741-1.stderr
@@ -2,7 +2,7 @@ error[E0308]: mismatched types
   --> $DIR/issue-57741-1.rs:14:9
    |
 LL |     let y = match x {
-   |                   - this match expression has type `std::boxed::Box<u32>`
+   |                   - this expression has type `std::boxed::Box<u32>`
 LL |         S::A { a } | S::B { b: a } => a,
    |         ^^^^^^^^^^ expected struct `std::boxed::Box`, found enum `S`
    |
@@ -13,7 +13,7 @@ error[E0308]: mismatched types
   --> $DIR/issue-57741-1.rs:14:22
    |
 LL |     let y = match x {
-   |                   - this match expression has type `std::boxed::Box<u32>`
+   |                   - this expression has type `std::boxed::Box<u32>`
 LL |         S::A { a } | S::B { b: a } => a,
    |                      ^^^^^^^^^^^^^ expected struct `std::boxed::Box`, found enum `S`
    |
diff --git a/src/test/ui/issues/issue-57741.stderr b/src/test/ui/issues/issue-57741.stderr
index c36dea7bf55..6f9e5b08a83 100644
--- a/src/test/ui/issues/issue-57741.stderr
+++ b/src/test/ui/issues/issue-57741.stderr
@@ -4,7 +4,7 @@ error[E0308]: mismatched types
 LL |     let y = match x {
    |                   -
    |                   |
-   |                   this match expression has type `std::boxed::Box<T>`
+   |                   this expression has type `std::boxed::Box<T>`
    |                   help: consider dereferencing the boxed value: `*x`
 LL |         T::A(a) | T::B(a) => a,
    |         ^^^^^^^ expected struct `std::boxed::Box`, found enum `T`
@@ -18,7 +18,7 @@ error[E0308]: mismatched types
 LL |     let y = match x {
    |                   -
    |                   |
-   |                   this match expression has type `std::boxed::Box<T>`
+   |                   this expression has type `std::boxed::Box<T>`
    |                   help: consider dereferencing the boxed value: `*x`
 LL |         T::A(a) | T::B(a) => a,
    |                   ^^^^^^^ expected struct `std::boxed::Box`, found enum `T`
@@ -32,7 +32,7 @@ error[E0308]: mismatched types
 LL |     let y = match x {
    |                   -
    |                   |
-   |                   this match expression has type `std::boxed::Box<S>`
+   |                   this expression has type `std::boxed::Box<S>`
    |                   help: consider dereferencing the boxed value: `*x`
 LL |         S::A { a } | S::B { b: a } => a,
    |         ^^^^^^^^^^ expected struct `std::boxed::Box`, found enum `S`
@@ -46,7 +46,7 @@ error[E0308]: mismatched types
 LL |     let y = match x {
    |                   -
    |                   |
-   |                   this match expression has type `std::boxed::Box<S>`
+   |                   this expression has type `std::boxed::Box<S>`
    |                   help: consider dereferencing the boxed value: `*x`
 LL |         S::A { a } | S::B { b: a } => a,
    |                      ^^^^^^^^^^^^^ expected struct `std::boxed::Box`, found enum `S`
diff --git a/src/test/ui/issues/issue-67037-pat-tup-scrut-ty-diff-less-fields.stderr b/src/test/ui/issues/issue-67037-pat-tup-scrut-ty-diff-less-fields.stderr
index 521dd0256f7..6e8ea6bf618 100644
--- a/src/test/ui/issues/issue-67037-pat-tup-scrut-ty-diff-less-fields.stderr
+++ b/src/test/ui/issues/issue-67037-pat-tup-scrut-ty-diff-less-fields.stderr
@@ -2,7 +2,9 @@ error[E0308]: mismatched types
   --> $DIR/issue-67037-pat-tup-scrut-ty-diff-less-fields.rs:19:9
    |
 LL |     let P() = U {};
-   |         ^^^ expected struct `U`, found struct `P`
+   |         ^^^   ---- this expression has type `U`
+   |         |
+   |         expected struct `U`, found struct `P`
    |
    = note: expected struct `U`
               found struct `P<_>`
diff --git a/src/test/ui/issues/issue-7092.stderr b/src/test/ui/issues/issue-7092.stderr
index 05c00da16b1..590dd40c653 100644
--- a/src/test/ui/issues/issue-7092.stderr
+++ b/src/test/ui/issues/issue-7092.stderr
@@ -2,7 +2,7 @@ error[E0308]: mismatched types
   --> $DIR/issue-7092.rs:6:9
    |
 LL |     match x {
-   |           - this match expression has type `Whatever`
+   |           - this expression has type `Whatever`
 LL |         Some(field) =>
    |         ^^^^^^^^^^^ expected enum `Whatever`, found enum `std::option::Option`
    |
diff --git a/src/test/ui/match/match-struct.stderr b/src/test/ui/match/match-struct.stderr
index c23451d51ec..a475bd5e581 100644
--- a/src/test/ui/match/match-struct.stderr
+++ b/src/test/ui/match/match-struct.stderr
@@ -2,7 +2,7 @@ error[E0308]: mismatched types
   --> $DIR/match-struct.rs:6:9
    |
 LL |     match (S { a: 1 }) {
-   |           ------------ this match expression has type `S`
+   |           ------------ this expression has type `S`
 LL |         E::C(_) => (),
    |         ^^^^^^^ expected struct `S`, found enum `E`
 
diff --git a/src/test/ui/match/match-tag-unary.stderr b/src/test/ui/match/match-tag-unary.stderr
index db5dcd2be3b..31f77bdff8b 100644
--- a/src/test/ui/match/match-tag-unary.stderr
+++ b/src/test/ui/match/match-tag-unary.stderr
@@ -4,7 +4,7 @@ error[E0308]: mismatched types
 LL | fn main() { let x: A = A::A(0); match x { B::B(y) => { } } }
    |                                       -   ^^^^^^^ expected enum `A`, found enum `B`
    |                                       |
-   |                                       this match expression has type `A`
+   |                                       this expression has type `A`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/mismatched_types/E0409.stderr b/src/test/ui/mismatched_types/E0409.stderr
index 098fd74a39d..2306fb35273 100644
--- a/src/test/ui/mismatched_types/E0409.stderr
+++ b/src/test/ui/mismatched_types/E0409.stderr
@@ -9,6 +9,8 @@ LL |         (0, ref y) | (y, 0) => {}
 error[E0308]: mismatched types
   --> $DIR/E0409.rs:5:23
    |
+LL |     match x {
+   |           - this expression has type `({integer}, {integer})`
 LL |         (0, ref y) | (y, 0) => {}
    |                       ^ expected `&{integer}`, found integer
 
diff --git a/src/test/ui/or-patterns/already-bound-name.stderr b/src/test/ui/or-patterns/already-bound-name.stderr
index 360699a8739..948c91370d0 100644
--- a/src/test/ui/or-patterns/already-bound-name.stderr
+++ b/src/test/ui/or-patterns/already-bound-name.stderr
@@ -94,7 +94,9 @@ error[E0308]: mismatched types
   --> $DIR/already-bound-name.rs:33:31
    |
 LL |     let B(A(a, _) | B(a)) | A(a, A(a, _) | B(a)) = B(B(1));
-   |                               ^ expected integer, found enum `E`
+   |                               ^                    ------- this expression has type `E<E<{integer}>>`
+   |                               |
+   |                               expected integer, found enum `E`
    |
    = note: expected type `{integer}`
               found type `E<{integer}>`
diff --git a/src/test/ui/or-patterns/inconsistent-modes.stderr b/src/test/ui/or-patterns/inconsistent-modes.stderr
index 0a36ed5548e..7c1638ff94d 100644
--- a/src/test/ui/or-patterns/inconsistent-modes.stderr
+++ b/src/test/ui/or-patterns/inconsistent-modes.stderr
@@ -60,7 +60,9 @@ error[E0308]: mismatched types
   --> $DIR/inconsistent-modes.rs:13:25
    |
 LL |     let Ok(ref a) | Err(ref mut a): Result<&u8, &mut u8> = Ok(&0);
-   |                         ^^^^^^^^^ types differ in mutability
+   |                         ^^^^^^^^^   -------------------- expected due to this
+   |                         |
+   |                         types differ in mutability
    |
    = note: expected type `&&u8`
               found type `&mut &mut u8`
@@ -69,7 +71,9 @@ error[E0308]: mismatched types
   --> $DIR/inconsistent-modes.rs:16:31
    |
 LL |     let Ok((ref a, b)) | Err((ref mut a, ref b)) = Ok((0, &0));
-   |                               ^^^^^^^^^ types differ in mutability
+   |                               ^^^^^^^^^            ----------- this expression has type `std::result::Result<({integer}, &{integer}), (_, _)>`
+   |                               |
+   |                               types differ in mutability
    |
    = note: expected type `&{integer}`
               found type `&mut _`
diff --git a/src/test/ui/or-patterns/or-pattern-mismatch.stderr b/src/test/ui/or-patterns/or-pattern-mismatch.stderr
index 253f3ef7725..bc288e06250 100644
--- a/src/test/ui/or-patterns/or-pattern-mismatch.stderr
+++ b/src/test/ui/or-patterns/or-pattern-mismatch.stderr
@@ -2,7 +2,7 @@ error[E0308]: mismatched types
   --> $DIR/or-pattern-mismatch.rs:3:68
    |
 LL | fn main() { match Blah::A(1, 1, 2) { Blah::A(_, x, y) | Blah::B(x, y) => { } } }
-   |                                                                    ^ expected `usize`, found `isize`
+   |                   ---------------- this expression has type `Blah` ^ expected `usize`, found `isize`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/pat-tuple-5.stderr b/src/test/ui/parser/pat-tuple-5.stderr
index 5b0253cd273..8ff4f948a05 100644
--- a/src/test/ui/parser/pat-tuple-5.stderr
+++ b/src/test/ui/parser/pat-tuple-5.stderr
@@ -17,7 +17,7 @@ error[E0308]: mismatched types
   --> $DIR/pat-tuple-5.rs:5:10
    |
 LL |     match (0, 1) {
-   |           ------ this match expression has type `({integer}, {integer})`
+   |           ------ this expression has type `({integer}, {integer})`
 LL |         (PAT ..) => {}
    |          ^^^ expected tuple, found `u8`
    |
diff --git a/src/test/ui/parser/recover-range-pats.stderr b/src/test/ui/parser/recover-range-pats.stderr
index 50a44192707..3fed64c191a 100644
--- a/src/test/ui/parser/recover-range-pats.stderr
+++ b/src/test/ui/parser/recover-range-pats.stderr
@@ -425,8 +425,9 @@ error[E0308]: mismatched types
   --> $DIR/recover-range-pats.rs:23:16
    |
 LL |     if let X.. .0 = 0 {}
-   |            -   ^^ expected integer, found floating-point number
-   |            |
+   |            -   ^^   - this expression has type `u8`
+   |            |   |
+   |            |   expected integer, found floating-point number
    |            this is of type `u8`
 
 error[E0029]: only char and numeric types are allowed in range patterns
@@ -457,8 +458,9 @@ error[E0308]: mismatched types
   --> $DIR/recover-range-pats.rs:36:16
    |
 LL |     if let X..=.0 = 0 {}
-   |            -   ^^ expected integer, found floating-point number
-   |            |
+   |            -   ^^   - this expression has type `u8`
+   |            |   |
+   |            |   expected integer, found floating-point number
    |            this is of type `u8`
 
 error[E0029]: only char and numeric types are allowed in range patterns
@@ -489,8 +491,9 @@ error[E0308]: mismatched types
   --> $DIR/recover-range-pats.rs:52:17
    |
 LL |     if let X... .0 = 0 {}
-   |            -    ^^ expected integer, found floating-point number
-   |            |
+   |            -    ^^   - this expression has type `u8`
+   |            |    |
+   |            |    expected integer, found floating-point number
    |            this is of type `u8`
 
 error[E0029]: only char and numeric types are allowed in range patterns
diff --git a/src/test/ui/pattern/pat-struct-field-expr-has-type.rs b/src/test/ui/pattern/pat-struct-field-expr-has-type.rs
new file mode 100644
index 00000000000..1d18214de7f
--- /dev/null
+++ b/src/test/ui/pattern/pat-struct-field-expr-has-type.rs
@@ -0,0 +1,9 @@
+struct S {
+    f: u8,
+}
+
+fn main() {
+    match (S { f: 42 }) {
+        S { f: Ok(_) } => {} //~ ERROR mismatched types
+    }
+}
diff --git a/src/test/ui/pattern/pat-struct-field-expr-has-type.stderr b/src/test/ui/pattern/pat-struct-field-expr-has-type.stderr
new file mode 100644
index 00000000000..d57a8a0dbc1
--- /dev/null
+++ b/src/test/ui/pattern/pat-struct-field-expr-has-type.stderr
@@ -0,0 +1,14 @@
+error[E0308]: mismatched types
+  --> $DIR/pat-struct-field-expr-has-type.rs:7:16
+   |
+LL |     match (S { f: 42 }) {
+   |           ------------- this expression has type `S`
+LL |         S { f: Ok(_) } => {}
+   |                ^^^^^ expected `u8`, found enum `std::result::Result`
+   |
+   = note: expected type `u8`
+              found enum `std::result::Result<_, _>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/pattern/pat-type-err-formal-param.rs b/src/test/ui/pattern/pat-type-err-formal-param.rs
new file mode 100644
index 00000000000..54336b34923
--- /dev/null
+++ b/src/test/ui/pattern/pat-type-err-formal-param.rs
@@ -0,0 +1,8 @@
+// Test the `.span_label(..)` to the type when there's a
+// type error in a pattern due to a the formal parameter.
+
+fn main() {}
+
+struct Tuple(u8);
+
+fn foo(Tuple(_): String) {} //~ ERROR mismatched types
diff --git a/src/test/ui/pattern/pat-type-err-formal-param.stderr b/src/test/ui/pattern/pat-type-err-formal-param.stderr
new file mode 100644
index 00000000000..2d7eb62faef
--- /dev/null
+++ b/src/test/ui/pattern/pat-type-err-formal-param.stderr
@@ -0,0 +1,11 @@
+error[E0308]: mismatched types
+  --> $DIR/pat-type-err-formal-param.rs:8:8
+   |
+LL | fn foo(Tuple(_): String) {}
+   |        ^^^^^^^^  ------ expected due to this
+   |        |
+   |        expected struct `std::string::String`, found struct `Tuple`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/pattern/pat-type-err-let-stmt.rs b/src/test/ui/pattern/pat-type-err-let-stmt.rs
new file mode 100644
index 00000000000..6e9850b655c
--- /dev/null
+++ b/src/test/ui/pattern/pat-type-err-let-stmt.rs
@@ -0,0 +1,16 @@
+// Test the `.span_label` to the type / scrutinee
+// when there's a type error in checking a pattern.
+
+fn main() {
+    // We want to point at the `Option<u8>`.
+    let Ok(0): Option<u8> = 42u8;
+    //~^ ERROR mismatched types
+    //~| ERROR mismatched types
+
+    // We want to point at the `Option<u8>`.
+    let Ok(0): Option<u8>;
+    //~^ ERROR mismatched types
+
+    // We want to point at the scrutinee.
+    let Ok(0) = 42u8; //~ ERROR mismatched types
+}
diff --git a/src/test/ui/pattern/pat-type-err-let-stmt.stderr b/src/test/ui/pattern/pat-type-err-let-stmt.stderr
new file mode 100644
index 00000000000..d75fa3f247c
--- /dev/null
+++ b/src/test/ui/pattern/pat-type-err-let-stmt.stderr
@@ -0,0 +1,49 @@
+error[E0308]: mismatched types
+  --> $DIR/pat-type-err-let-stmt.rs:6:29
+   |
+LL |     let Ok(0): Option<u8> = 42u8;
+   |                ----------   ^^^^
+   |                |            |
+   |                |            expected enum `std::option::Option`, found `u8`
+   |                |            help: try using a variant of the expected enum: `Some(42u8)`
+   |                expected due to this
+   |
+   = note: expected enum `std::option::Option<u8>`
+              found type `u8`
+
+error[E0308]: mismatched types
+  --> $DIR/pat-type-err-let-stmt.rs:6:9
+   |
+LL |     let Ok(0): Option<u8> = 42u8;
+   |         ^^^^^  ---------- expected due to this
+   |         |
+   |         expected enum `std::option::Option`, found enum `std::result::Result`
+   |
+   = note: expected enum `std::option::Option<u8>`
+              found enum `std::result::Result<_, _>`
+
+error[E0308]: mismatched types
+  --> $DIR/pat-type-err-let-stmt.rs:11:9
+   |
+LL |     let Ok(0): Option<u8>;
+   |         ^^^^^  ---------- expected due to this
+   |         |
+   |         expected enum `std::option::Option`, found enum `std::result::Result`
+   |
+   = note: expected enum `std::option::Option<u8>`
+              found enum `std::result::Result<_, _>`
+
+error[E0308]: mismatched types
+  --> $DIR/pat-type-err-let-stmt.rs:15:9
+   |
+LL |     let Ok(0) = 42u8;
+   |         ^^^^^   ---- this expression has type `u8`
+   |         |
+   |         expected `u8`, found enum `std::result::Result`
+   |
+   = note: expected type `u8`
+              found enum `std::result::Result<_, _>`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/pattern/pattern-error-continue.stderr b/src/test/ui/pattern/pattern-error-continue.stderr
index 2f9fe1981bc..60f76796c03 100644
--- a/src/test/ui/pattern/pattern-error-continue.stderr
+++ b/src/test/ui/pattern/pattern-error-continue.stderr
@@ -28,7 +28,7 @@ error[E0308]: mismatched types
   --> $DIR/pattern-error-continue.rs:22:9
    |
 LL |     match 'c' {
-   |           --- this match expression has type `char`
+   |           --- this expression has type `char`
 LL |         S { .. } => (),
    |         ^^^^^^^^ expected `char`, found struct `S`
 
diff --git a/src/test/ui/pattern/pattern-tyvar.stderr b/src/test/ui/pattern/pattern-tyvar.stderr
index b2afeacdf68..15425da69bc 100644
--- a/src/test/ui/pattern/pattern-tyvar.stderr
+++ b/src/test/ui/pattern/pattern-tyvar.stderr
@@ -2,7 +2,7 @@ error[E0308]: mismatched types
   --> $DIR/pattern-tyvar.rs:5:18
    |
 LL |     match t {
-   |           - this match expression has type `std::option::Option<std::vec::Vec<isize>>`
+   |           - this expression has type `Bar`
 LL |       Bar::T1(_, Some::<isize>(x)) => {
    |                  ^^^^^^^^^^^^^^^^ expected struct `std::vec::Vec`, found `isize`
    |
diff --git a/src/test/ui/resolve/resolve-inconsistent-binding-mode.stderr b/src/test/ui/resolve/resolve-inconsistent-binding-mode.stderr
index 58455024d38..61d1001ce91 100644
--- a/src/test/ui/resolve/resolve-inconsistent-binding-mode.stderr
+++ b/src/test/ui/resolve/resolve-inconsistent-binding-mode.stderr
@@ -23,18 +23,24 @@ LL |       Opts::A(ref mut i) | Opts::B(ref i) => {}
 error[E0308]: mismatched types
   --> $DIR/resolve-inconsistent-binding-mode.rs:7:32
    |
+LL |     match x {
+   |           - this expression has type `Opts`
 LL |       Opts::A(ref i) | Opts::B(i) => {}
    |                                ^ expected `&isize`, found `isize`
 
 error[E0308]: mismatched types
   --> $DIR/resolve-inconsistent-binding-mode.rs:16:32
    |
+LL |     match x {
+   |           - this expression has type `Opts`
 LL |       Opts::A(ref i) | Opts::B(i) => {}
    |                                ^ expected `&isize`, found `isize`
 
 error[E0308]: mismatched types
   --> $DIR/resolve-inconsistent-binding-mode.rs:25:36
    |
+LL |     match x {
+   |           - this expression has type `Opts`
 LL |       Opts::A(ref mut i) | Opts::B(ref i) => {}
    |                                    ^^^^^ types differ in mutability
    |
diff --git a/src/test/ui/resolve/resolve-inconsistent-names.stderr b/src/test/ui/resolve/resolve-inconsistent-names.stderr
index 271495012da..5c87f7c684f 100644
--- a/src/test/ui/resolve/resolve-inconsistent-names.stderr
+++ b/src/test/ui/resolve/resolve-inconsistent-names.stderr
@@ -86,6 +86,8 @@ LL |         (CONST1, _) | (_, Const2) => ()
 error[E0308]: mismatched types
   --> $DIR/resolve-inconsistent-names.rs:19:19
    |
+LL |     match x {
+   |           - this expression has type `(E, E)`
 LL |         (A, B) | (ref B, c) | (c, A) => ()
    |                   ^^^^^ expected enum `E`, found `&E`
 
diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr
index 1143bddfe45..7170adca60d 100644
--- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr
+++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr
@@ -630,7 +630,7 @@ error[E0308]: mismatched types
   --> $DIR/disallowed-positions.rs:67:12
    |
 LL |     if let Range { start: _, end: _ } = true..true && false {}
-   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this match expression has type `bool`
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this expression has type `bool`
    |            |
    |            expected `bool`, found struct `std::ops::Range`
    |
@@ -650,7 +650,7 @@ error[E0308]: mismatched types
   --> $DIR/disallowed-positions.rs:71:12
    |
 LL |     if let Range { start: _, end: _ } = true..true || false {}
-   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this match expression has type `bool`
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this expression has type `bool`
    |            |
    |            expected `bool`, found struct `std::ops::Range`
    |
@@ -697,7 +697,7 @@ error[E0308]: mismatched types
   --> $DIR/disallowed-positions.rs:86:12
    |
 LL |     if let Range { start: true, end } = t..&&false {}
-   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^   - this match expression has type `bool`
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^   - this expression has type `&&bool`
    |            |
    |            expected `bool`, found struct `std::ops::Range`
    |
@@ -818,7 +818,7 @@ error[E0308]: mismatched types
   --> $DIR/disallowed-positions.rs:131:15
    |
 LL |     while let Range { start: _, end: _ } = true..true && false {}
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this match expression has type `bool`
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this expression has type `bool`
    |               |
    |               expected `bool`, found struct `std::ops::Range`
    |
@@ -838,7 +838,7 @@ error[E0308]: mismatched types
   --> $DIR/disallowed-positions.rs:135:15
    |
 LL |     while let Range { start: _, end: _ } = true..true || false {}
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this match expression has type `bool`
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this expression has type `bool`
    |               |
    |               expected `bool`, found struct `std::ops::Range`
    |
@@ -885,7 +885,7 @@ error[E0308]: mismatched types
   --> $DIR/disallowed-positions.rs:150:15
    |
 LL |     while let Range { start: true, end } = t..&&false {}
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^   - this match expression has type `bool`
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^   - this expression has type `&&bool`
    |               |
    |               expected `bool`, found struct `std::ops::Range`
    |
@@ -961,7 +961,7 @@ error[E0308]: mismatched types
   --> $DIR/disallowed-positions.rs:198:10
    |
 LL |     (let Range { start: _, end: _ } = true..true || false);
-   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this match expression has type `bool`
+   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this expression has type `bool`
    |          |
    |          expected `bool`, found struct `std::ops::Range`
    |
diff --git a/src/test/ui/structs/structure-constructor-type-mismatch.stderr b/src/test/ui/structs/structure-constructor-type-mismatch.stderr
index 4bd3acac532..8cfa118a2da 100644
--- a/src/test/ui/structs/structure-constructor-type-mismatch.stderr
+++ b/src/test/ui/structs/structure-constructor-type-mismatch.stderr
@@ -86,7 +86,7 @@ error[E0308]: mismatched types
   --> $DIR/structure-constructor-type-mismatch.rs:54:9
    |
 LL |     match (Point { x: 1, y: 2 }) {
-   |           ---------------------- this match expression has type `Point<{integer}>`
+   |           ---------------------- this expression has type `Point<{integer}>`
 LL |         PointF::<u32> { .. } => {}
    |         ^^^^^^^^^^^^^^^^^^^^ expected integer, found `f32`
    |
@@ -97,7 +97,7 @@ error[E0308]: mismatched types
   --> $DIR/structure-constructor-type-mismatch.rs:59:9
    |
 LL |     match (Point { x: 1, y: 2 }) {
-   |           ---------------------- this match expression has type `Point<{integer}>`
+   |           ---------------------- this expression has type `Point<{integer}>`
 LL |         PointF { .. } => {}
    |         ^^^^^^^^^^^^^ expected integer, found `f32`
    |
@@ -108,7 +108,7 @@ error[E0308]: mismatched types
   --> $DIR/structure-constructor-type-mismatch.rs:67:9
    |
 LL |     match (Pair { x: 1, y: 2 }) {
-   |           --------------------- this match expression has type `Pair<{integer}, {integer}>`
+   |           --------------------- this expression has type `Pair<{integer}, {integer}>`
 LL |         PairF::<u32> { .. } => {}
    |         ^^^^^^^^^^^^^^^^^^^ expected integer, found `f32`
    |