]> git.lizzy.rs Git - rust.git/commitdiff
typeck: move `check_pat_walk` and children to `pat.rs`.
authorMazdak Farrokhzad <twingoow@gmail.com>
Fri, 23 Aug 2019 13:03:21 +0000 (15:03 +0200)
committerMazdak Farrokhzad <twingoow@gmail.com>
Sat, 24 Aug 2019 17:13:06 +0000 (19:13 +0200)
src/librustc_typeck/check/_match.rs
src/librustc_typeck/check/mod.rs
src/librustc_typeck/check/pat.rs [new file with mode: 0644]

index fc25eb44cbd888888c1b01b47622e16472754230..8cb365d91fa7941cdc321d787637e35ce7b40e85 100644 (file)
 use crate::check::{FnCtxt, Expectation, Diverges, Needs};
 use crate::check::coercion::CoerceMany;
-use crate::util::nodemap::FxHashMap;
-use errors::{Applicability, DiagnosticBuilder};
-use rustc::hir::{self, PatKind, Pat, ExprKind};
-use rustc::hir::def::{Res, DefKind, CtorKind};
-use rustc::hir::pat_util::EnumerateAndAdjustIterator;
-use rustc::hir::ptr::P;
-use rustc::infer;
+use rustc::hir::{self, ExprKind};
 use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc::traits::{ObligationCause, ObligationCauseCode};
-use rustc::ty::{self, Ty, TypeFoldable};
-use rustc::ty::subst::Kind;
-use syntax::ast;
-use syntax::util::lev_distance::find_best_match_for_name;
+use rustc::ty::{self, Ty};
 use syntax_pos::Span;
-use syntax_pos::hygiene::DesugaringKind;
-
-use std::collections::hash_map::Entry::{Occupied, Vacant};
-use std::cmp;
-
-use super::report_unexpected_variant_res;
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
-    /// `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:
-    ///
-    /// ```text
-    /// error[E0308]: mismatched types
-    ///  --> src/main.rs:5:9
-    ///   |
-    /// 4 |    let temp: usize = match a + b {
-    ///   |                            ----- this expression has type `usize`
-    /// 5 |         Ok(num) => num,
-    ///   |         ^^^^^^^ expected usize, found enum `std::result::Result`
-    ///   |
-    ///   = note: expected type `usize`
-    ///              found type `std::result::Result<_, _>`
-    /// ```
-    pub fn check_pat_walk(
-        &self,
-        pat: &'tcx hir::Pat,
-        mut expected: Ty<'tcx>,
-        mut def_bm: ty::BindingMode,
-        discrim_span: Option<Span>,
-    ) {
-        let tcx = self.tcx;
-
-        debug!("check_pat_walk(pat={:?},expected={:?},def_bm={:?})", pat, expected, def_bm);
-
-        let mut path_resolution = None;
-        let is_non_ref_pat = match pat.node {
-            PatKind::Struct(..) |
-            PatKind::TupleStruct(..) |
-            PatKind::Or(_) |
-            PatKind::Tuple(..) |
-            PatKind::Box(_) |
-            PatKind::Range(..) |
-            PatKind::Slice(..) => true,
-            PatKind::Lit(ref lt) => {
-                let ty = self.check_expr(lt);
-                match ty.sty {
-                    ty::Ref(..) => false,
-                    _ => true,
-                }
-            }
-            PatKind::Path(ref qpath) => {
-                let resolution = self.resolve_ty_and_res_ufcs(qpath, pat.hir_id, pat.span);
-                path_resolution = Some(resolution);
-                match resolution.0 {
-                    Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => false,
-                    _ => true,
-                }
-            }
-            PatKind::Wild |
-            PatKind::Binding(..) |
-            PatKind::Ref(..) => false,
-        };
-        if is_non_ref_pat {
-            debug!("pattern is non reference pattern");
-            let mut exp_ty = self.resolve_type_vars_with_obligations(&expected);
-
-            // Peel off as many `&` or `&mut` from the discriminant as possible. For example,
-            // for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches
-            // the `Some(5)` which is not of type Ref.
-            //
-            // For each ampersand peeled off, update the binding mode and push the original
-            // type into the adjustments vector.
-            //
-            // See the examples in `ui/match-defbm*.rs`.
-            let mut pat_adjustments = vec![];
-            while let ty::Ref(_, inner_ty, inner_mutability) = exp_ty.sty {
-                debug!("inspecting {:?}", exp_ty);
-
-                debug!("current discriminant is Ref, inserting implicit deref");
-                // Preserve the reference type. We'll need it later during HAIR lowering.
-                pat_adjustments.push(exp_ty);
-
-                exp_ty = inner_ty;
-                def_bm = match def_bm {
-                    // If default binding mode is by value, make it `ref` or `ref mut`
-                    // (depending on whether we observe `&` or `&mut`).
-                    ty::BindByValue(_) =>
-                        ty::BindByReference(inner_mutability),
-
-                    // Once a `ref`, always a `ref`. This is because a `& &mut` can't mutate
-                    // the underlying value.
-                    ty::BindByReference(hir::Mutability::MutImmutable) =>
-                        ty::BindByReference(hir::Mutability::MutImmutable),
-
-                    // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref`
-                    // (on `&`).
-                    ty::BindByReference(hir::Mutability::MutMutable) =>
-                        ty::BindByReference(inner_mutability),
-                };
-            }
-            expected = exp_ty;
-
-            if pat_adjustments.len() > 0 {
-                debug!("default binding mode is now {:?}", def_bm);
-                self.inh.tables.borrow_mut()
-                    .pat_adjustments_mut()
-                    .insert(pat.hir_id, pat_adjustments);
-            }
-        } else if let PatKind::Ref(..) = pat.node {
-            // When you encounter a `&pat` pattern, reset to "by
-            // value". This is so that `x` and `y` here are by value,
-            // as they appear to be:
-            //
-            // ```
-            // match &(&22, &44) {
-            //   (&x, &y) => ...
-            // }
-            // ```
-            //
-            // See issue #46688.
-            def_bm = ty::BindByValue(hir::MutImmutable);
-        }
-
-        // Lose mutability now that we know binding mode and discriminant type.
-        let def_bm = def_bm;
-        let expected = expected;
-
-        let ty = match pat.node {
-            PatKind::Wild => {
-                expected
-            }
-            PatKind::Lit(ref lt) => {
-                // We've already computed the type above (when checking for a non-ref pat), so
-                // avoid computing it again.
-                let ty = self.node_ty(lt.hir_id);
-
-                // Byte string patterns behave the same way as array patterns
-                // They can denote both statically and dynamically-sized byte arrays.
-                let mut pat_ty = ty;
-                if let hir::ExprKind::Lit(ref lt) = lt.node {
-                    if let ast::LitKind::ByteStr(_) = lt.node {
-                        let expected_ty = self.structurally_resolved_type(pat.span, expected);
-                        if let ty::Ref(_, r_ty, _) = expected_ty.sty {
-                            if let ty::Slice(_) = r_ty.sty {
-                                pat_ty = tcx.mk_imm_ref(tcx.lifetimes.re_static,
-                                                        tcx.mk_slice(tcx.types.u8))
-                            }
-                        }
-                    }
-                }
-
-                // Somewhat surprising: in this case, the subtyping
-                // relation goes the opposite way as the other
-                // cases. Actually what we really want is not a subtyping
-                // relation at all but rather that there exists a LUB (so
-                // that they can be compared). However, in practice,
-                // constants are always scalars or strings.  For scalars
-                // subtyping is irrelevant, and for strings `ty` is
-                // type is `&'static str`, so if we say that
-                //
-                //     &'static str <: expected
-                //
-                // then that's equivalent to there existing a LUB.
-                if let Some(mut err) = self.demand_suptype_diag(pat.span, expected, pat_ty) {
-                    err.emit_unless(discrim_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`,
-                            // so an error here would be a duplicate and from the wrong POV.
-                            s.is_desugaring(DesugaringKind::CondTemporary)
-                        })
-                        .is_some());
-                }
-
-                pat_ty
-            }
-            PatKind::Range(ref begin, ref end, _) => {
-                let lhs_ty = self.check_expr(begin);
-                let rhs_ty = self.check_expr(end);
-
-                // Check that both end-points are of numeric or char type.
-                let numeric_or_char = |ty: Ty<'_>| {
-                    ty.is_numeric()
-                    || ty.is_char()
-                    || ty.references_error()
-                };
-                let lhs_compat = numeric_or_char(lhs_ty);
-                let rhs_compat = numeric_or_char(rhs_ty);
-
-                if !lhs_compat || !rhs_compat {
-                    let span = if !lhs_compat && !rhs_compat {
-                        pat.span
-                    } else if !lhs_compat {
-                        begin.span
-                    } else {
-                        end.span
-                    };
-
-                    let mut err = struct_span_err!(
-                        tcx.sess,
-                        span,
-                        E0029,
-                        "only char and numeric types are allowed in range patterns"
-                    );
-                    err.span_label(span, "ranges require char or numeric types");
-                    err.note(&format!("start type: {}", self.ty_to_string(lhs_ty)));
-                    err.note(&format!("end type: {}", self.ty_to_string(rhs_ty)));
-                    if tcx.sess.teach(&err.get_code().unwrap()) {
-                        err.note(
-                            "In a match expression, only numbers and characters can be matched \
-                             against a range. This is because the compiler checks that the range \
-                             is non-empty at compile-time, and is unable to evaluate arbitrary \
-                             comparison functions. If you want to capture values of an orderable \
-                             type between two end-points, you can use a guard."
-                         );
-                    }
-                    err.emit();
-                    return;
-                }
-
-                // Now that we know the types can be unified we find the unified type and use
-                // it to type the entire expression.
-                let common_type = self.resolve_vars_if_possible(&lhs_ty);
-
-                // Subtyping doesn't matter here, as the value is some kind of scalar.
-                self.demand_eqtype_pat(pat.span, expected, lhs_ty, discrim_span);
-                self.demand_eqtype_pat(pat.span, expected, rhs_ty, discrim_span);
-                common_type
-            }
-            PatKind::Binding(ba, var_id, _, ref sub) => {
-                let bm = if ba == hir::BindingAnnotation::Unannotated {
-                    def_bm
-                } else {
-                    ty::BindingMode::convert(ba)
-                };
-                self.inh
-                    .tables
-                    .borrow_mut()
-                    .pat_binding_modes_mut()
-                    .insert(pat.hir_id, bm);
-                debug!("check_pat_walk: pat.hir_id={:?} bm={:?}", pat.hir_id, bm);
-                let local_ty = self.local_ty(pat.span, pat.hir_id).decl_ty;
-                match bm {
-                    ty::BindByReference(mutbl) => {
-                        // If the binding is like
-                        //     ref x | ref const x | ref mut x
-                        // then `x` is assigned a value of type `&M T` where M is the mutability
-                        // and T is the expected type.
-                        let region_var = self.next_region_var(infer::PatternRegion(pat.span));
-                        let mt = ty::TypeAndMut { ty: expected, mutbl: mutbl };
-                        let region_ty = tcx.mk_ref(region_var, mt);
-
-                        // `x` is assigned a value of type `&M T`, hence `&M T <: typeof(x)` is
-                        // required. However, we use equality, which is stronger. See (*) for
-                        // an explanation.
-                        self.demand_eqtype_pat(pat.span, region_ty, local_ty, discrim_span);
-                    }
-                    // Otherwise, the type of x is the expected type `T`.
-                    ty::BindByValue(_) => {
-                        // As above, `T <: typeof(x)` is required, but we
-                        // use equality, see (*) below.
-                        self.demand_eqtype_pat(pat.span, expected, local_ty, discrim_span);
-                    }
-                }
-
-                // 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);
-                }
-
-                if let Some(ref p) = *sub {
-                    self.check_pat_walk(&p, expected, def_bm, discrim_span);
-                }
-
-                local_ty
-            }
-            PatKind::TupleStruct(ref qpath, ref subpats, ddpos) => {
-                self.check_pat_tuple_struct(
-                    pat,
-                    qpath,
-                    &subpats,
-                    ddpos,
-                    expected,
-                    def_bm,
-                    discrim_span,
-                )
-            }
-            PatKind::Path(ref qpath) => {
-                self.check_pat_path(pat, path_resolution.unwrap(), qpath, expected)
-            }
-            PatKind::Struct(ref qpath, ref fields, etc) => {
-                self.check_pat_struct(pat, qpath, fields, etc, expected, def_bm, discrim_span)
-            }
-            PatKind::Or(ref pats) => {
-                let expected_ty = self.structurally_resolved_type(pat.span, expected);
-                for pat in pats {
-                    self.check_pat_walk(pat, expected, def_bm, discrim_span);
-                }
-                expected_ty
-            }
-            PatKind::Tuple(ref elements, ddpos) => {
-                let mut expected_len = elements.len();
-                if ddpos.is_some() {
-                    // Require known type only when `..` is present.
-                    if let ty::Tuple(ref tys) =
-                            self.structurally_resolved_type(pat.span, expected).sty {
-                        expected_len = tys.len();
-                    }
-                }
-                let max_len = cmp::max(expected_len, elements.len());
-
-                let element_tys_iter = (0..max_len).map(|_| {
-                    Kind::from(self.next_ty_var(
-                        // FIXME: `MiscVariable` for now -- obtaining the span and name information
-                        // from all tuple elements isn't trivial.
-                        TypeVariableOrigin {
-                            kind: TypeVariableOriginKind::TypeInference,
-                            span: pat.span,
-                        },
-                    ))
-                });
-                let element_tys = tcx.mk_substs(element_tys_iter);
-                let pat_ty = tcx.mk_ty(ty::Tuple(element_tys));
-                if let Some(mut err) = self.demand_eqtype_diag(pat.span, expected, pat_ty) {
-                    err.emit();
-                    // Walk subpatterns with an expected type of `err` in this case to silence
-                    // 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_walk(elem, &tcx.types.err, def_bm, discrim_span);
-                    }
-                    tcx.mk_tup(element_tys_iter)
-                } else {
-                    for (i, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) {
-                        self.check_pat_walk(
-                            elem,
-                            &element_tys[i].expect_ty(),
-                            def_bm,
-                            discrim_span,
-                        );
-                    }
-                    pat_ty
-                }
-            }
-            PatKind::Box(ref inner) => {
-                let inner_ty = self.next_ty_var(TypeVariableOrigin {
-                    kind: TypeVariableOriginKind::TypeInference,
-                    span: inner.span,
-                });
-                let uniq_ty = tcx.mk_box(inner_ty);
-
-                if self.check_dereferencable(pat.span, expected, &inner) {
-                    // Here, `demand::subtype` is good enough, but I don't
-                    // think any errors can be introduced by using
-                    // `demand::eqtype`.
-                    self.demand_eqtype_pat(pat.span, expected, uniq_ty, discrim_span);
-                    self.check_pat_walk(&inner, inner_ty, def_bm, discrim_span);
-                    uniq_ty
-                } else {
-                    self.check_pat_walk(&inner, tcx.types.err, def_bm, discrim_span);
-                    tcx.types.err
-                }
-            }
-            PatKind::Ref(ref inner, mutbl) => {
-                let expected = self.shallow_resolve(expected);
-                if self.check_dereferencable(pat.span, expected, &inner) {
-                    // `demand::subtype` would be good enough, but using
-                    // `eqtype` turns out to be equally general. See (*)
-                    // below for details.
-
-                    // Take region, inner-type from expected type if we
-                    // can, to avoid creating needless variables.  This
-                    // also helps with the bad interactions of the given
-                    // hack detailed in (*) below.
-                    debug!("check_pat_walk: expected={:?}", expected);
-                    let (rptr_ty, inner_ty) = match expected.sty {
-                        ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => {
-                            (expected, r_ty)
-                        }
-                        _ => {
-                            let inner_ty = self.next_ty_var(
-                                TypeVariableOrigin {
-                                    kind: TypeVariableOriginKind::TypeInference,
-                                    span: inner.span,
-                                }
-                            );
-                            let mt = ty::TypeAndMut { ty: inner_ty, mutbl: mutbl };
-                            let region = self.next_region_var(infer::PatternRegion(pat.span));
-                            let rptr_ty = tcx.mk_ref(region, mt);
-                            debug!("check_pat_walk: demanding {:?} = {:?}", expected, rptr_ty);
-                            let err = self.demand_eqtype_diag(pat.span, expected, rptr_ty);
-
-                            // Look for a case like `fn foo(&foo: u32)` and suggest
-                            // `fn foo(foo: &u32)`
-                            if let Some(mut err) = err {
-                                self.borrow_pat_suggestion(&mut err, &pat, &inner, &expected);
-                                err.emit();
-                            }
-                            (rptr_ty, inner_ty)
-                        }
-                    };
-
-                    self.check_pat_walk(&inner, inner_ty, def_bm, discrim_span);
-                    rptr_ty
-                } else {
-                    self.check_pat_walk(&inner, tcx.types.err, def_bm, discrim_span);
-                    tcx.types.err
-                }
-            }
-            PatKind::Slice(ref before, ref slice, ref after) => {
-                let expected_ty = self.structurally_resolved_type(pat.span, expected);
-                let (inner_ty, slice_ty) = match expected_ty.sty {
-                    ty::Array(inner_ty, size) => {
-                        if let Some(size) = size.try_eval_usize(tcx, self.param_env) {
-                            let min_len = before.len() as u64 + after.len() as u64;
-                            if slice.is_none() {
-                                if min_len != size {
-                                    struct_span_err!(
-                                        tcx.sess, pat.span, E0527,
-                                        "pattern requires {} elements but array has {}",
-                                        min_len, size)
-                                        .span_label(pat.span, format!("expected {} elements", size))
-                                        .emit();
-                                }
-                                (inner_ty, tcx.types.err)
-                            } else if let Some(rest) = size.checked_sub(min_len) {
-                                (inner_ty, tcx.mk_array(inner_ty, rest))
-                            } else {
-                                struct_span_err!(tcx.sess, pat.span, E0528,
-                                        "pattern requires at least {} elements but array has {}",
-                                        min_len, size)
-                                    .span_label(pat.span,
-                                        format!("pattern cannot match array of {} elements", size))
-                                    .emit();
-                                (inner_ty, tcx.types.err)
-                            }
-                        } else {
-                            struct_span_err!(
-                                tcx.sess,
-                                pat.span,
-                                E0730,
-                                "cannot pattern-match on an array without a fixed length",
-                            ).emit();
-                            (inner_ty, tcx.types.err)
-                        }
-                    }
-                    ty::Slice(inner_ty) => (inner_ty, expected_ty),
-                    _ => {
-                        if !expected_ty.references_error() {
-                            let mut err = struct_span_err!(
-                                tcx.sess, pat.span, E0529,
-                                "expected an array or slice, found `{}`",
-                                expected_ty);
-                            if let ty::Ref(_, ty, _) = expected_ty.sty {
-                                match ty.sty {
-                                    ty::Array(..) | ty::Slice(..) => {
-                                        err.help("the semantics of slice patterns changed \
-                                                  recently; see issue #62254");
-                                    }
-                                    _ => {}
-                                }
-                            }
-
-                            err.span_label( pat.span,
-                                format!("pattern cannot match with input type `{}`", expected_ty)
-                            ).emit();
-                        }
-                        (tcx.types.err, tcx.types.err)
-                    }
-                };
-
-                for elt in before {
-                    self.check_pat_walk(&elt, inner_ty, def_bm, discrim_span);
-                }
-                if let Some(ref slice) = *slice {
-                    self.check_pat_walk(&slice, slice_ty, def_bm, discrim_span);
-                }
-                for elt in after {
-                    self.check_pat_walk(&elt, inner_ty, def_bm, discrim_span);
-                }
-                expected_ty
-            }
-        };
-
-        self.write_ty(pat.hir_id, ty);
-
-        // (*) In most of the cases above (literals and constants being
-        // the exception), we relate types using strict equality, even
-        // though subtyping would be sufficient. There are a few reasons
-        // for this, some of which are fairly subtle and which cost me
-        // (nmatsakis) an hour or two debugging to remember, so I thought
-        // I'd write them down this time.
-        //
-        // 1. There is no loss of expressiveness here, though it does
-        // cause some inconvenience. What we are saying is that the type
-        // of `x` becomes *exactly* what is expected. This can cause unnecessary
-        // errors in some cases, such as this one:
-        //
-        // ```
-        // fn foo<'x>(x: &'x int) {
-        //    let a = 1;
-        //    let mut z = x;
-        //    z = &a;
-        // }
-        // ```
-        //
-        // The reason we might get an error is that `z` might be
-        // assigned a type like `&'x int`, and then we would have
-        // a problem when we try to assign `&a` to `z`, because
-        // the lifetime of `&a` (i.e., the enclosing block) is
-        // shorter than `'x`.
-        //
-        // HOWEVER, this code works fine. The reason is that the
-        // expected type here is whatever type the user wrote, not
-        // the initializer's type. In this case the user wrote
-        // nothing, so we are going to create a type variable `Z`.
-        // Then we will assign the type of the initializer (`&'x
-        // int`) as a subtype of `Z`: `&'x int <: Z`. And hence we
-        // will instantiate `Z` as a type `&'0 int` where `'0` is
-        // a fresh region variable, with the constraint that `'x :
-        // '0`.  So basically we're all set.
-        //
-        // Note that there are two tests to check that this remains true
-        // (`regions-reassign-{match,let}-bound-pointer.rs`).
-        //
-        // 2. Things go horribly wrong if we use subtype. The reason for
-        // THIS is a fairly subtle case involving bound regions. See the
-        // `givens` field in `region_constraints`, as well as the test
-        // `regions-relate-bound-regions-on-closures-to-inference-variables.rs`,
-        // for details. Short version is that we must sometimes detect
-        // relationships between specific region variables and regions
-        // bound in a closure signature, and that detection gets thrown
-        // off when we substitute fresh region variables here to enable
-        // subtyping.
-    }
-
-    fn borrow_pat_suggestion(
-        &self,
-        err: &mut DiagnosticBuilder<'_>,
-        pat: &Pat,
-        inner: &Pat,
-        expected: Ty<'tcx>,
-    ) {
-        let tcx = self.tcx;
-        if let PatKind::Binding(..) = inner.node {
-            let binding_parent_id = tcx.hir().get_parent_node(pat.hir_id);
-            let binding_parent = tcx.hir().get(binding_parent_id);
-            debug!("inner {:?} pat {:?} parent {:?}", inner, pat, binding_parent);
-            match binding_parent {
-                hir::Node::Arg(hir::Arg { span, .. }) => {
-                    if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(inner.span) {
-                        err.span_suggestion(
-                            *span,
-                            &format!("did you mean `{}`", snippet),
-                            format!(" &{}", expected),
-                            Applicability::MachineApplicable,
-                        );
-                    }
-                }
-                hir::Node::Arm(_) |
-                hir::Node::Pat(_) => {
-                    // rely on match ergonomics or it might be nested `&&pat`
-                    if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(inner.span) {
-                        err.span_suggestion(
-                            pat.span,
-                            "you can probably remove the explicit borrow",
-                            snippet,
-                            Applicability::MaybeIncorrect,
-                        );
-                    }
-                }
-                _ => {} // don't provide suggestions in other cases #55175
-            }
-        }
-    }
-
-    pub fn check_dereferencable(&self, span: Span, expected: Ty<'tcx>, inner: &hir::Pat) -> bool {
-        if let PatKind::Binding(..) = inner.node {
-            if let Some(mt) = self.shallow_resolve(expected).builtin_deref(true) {
-                if let ty::Dynamic(..) = mt.ty.sty {
-                    // This is "x = SomeTrait" being reduced from
-                    // "let &x = &SomeTrait" or "let box x = Box<SomeTrait>", an error.
-                    let type_str = self.ty_to_string(expected);
-                    let mut err = struct_span_err!(
-                        self.tcx.sess,
-                        span,
-                        E0033,
-                        "type `{}` cannot be dereferenced",
-                        type_str
-                    );
-                    err.span_label(span, format!("type `{}` cannot be dereferenced", type_str));
-                    if self.tcx.sess.teach(&err.get_code().unwrap()) {
-                        err.note("\
-This error indicates that a pointer to a trait type cannot be implicitly dereferenced by a \
-pattern. Every trait defines a type, but because the size of trait implementors isn't fixed, \
-this type has no compile-time size. Therefore, all accesses to trait types must be through \
-pointers. If you encounter this error you should try to avoid dereferencing the pointer.
-
-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");
-                    }
-                    err.emit();
-                    return false
-                }
-            }
-        }
-        true
-    }
-
     pub fn check_match(
         &self,
         expr: &'tcx hir::Expr,
@@ -1038,346 +419,4 @@ fn demand_discriminant_type(
             discrim_ty
         }
     }
-
-    fn check_pat_struct(
-        &self,
-        pat: &'tcx hir::Pat,
-        qpath: &hir::QPath,
-        fields: &'tcx [hir::FieldPat],
-        etc: bool,
-        expected: Ty<'tcx>,
-        def_bm: ty::BindingMode,
-        discrim_span: Option<Span>,
-    ) -> 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)
-        {
-            variant_ty
-        } else {
-            for field in fields {
-                self.check_pat_walk(&field.pat, self.tcx.types.err, def_bm, discrim_span);
-            }
-            return self.tcx.types.err;
-        };
-
-        // Type-check the path.
-        self.demand_eqtype_pat(pat.span, expected, pat_ty, discrim_span);
-
-        // Type-check subpatterns.
-        if self.check_struct_pat_fields(pat_ty, pat.hir_id, pat.span, variant, fields, etc, def_bm)
-        {
-            pat_ty
-        } else {
-            self.tcx.types.err
-        }
-    }
-
-    fn check_pat_path(
-        &self,
-        pat: &hir::Pat,
-        path_resolution: (Res, Option<Ty<'tcx>>, &'b [hir::PathSegment]),
-        qpath: &hir::QPath,
-        expected: Ty<'tcx>,
-    ) -> Ty<'tcx> {
-        let tcx = self.tcx;
-
-        // We have already resolved the path.
-        let (res, opt_ty, segments) = path_resolution;
-        match res {
-            Res::Err => {
-                self.set_tainted_by_errors();
-                return tcx.types.err;
-            }
-            Res::Def(DefKind::Method, _) |
-            Res::Def(DefKind::Ctor(_, CtorKind::Fictive), _) |
-            Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) => {
-                report_unexpected_variant_res(tcx, res, pat.span, qpath);
-                return tcx.types.err;
-            }
-            Res::Def(DefKind::Ctor(_, CtorKind::Const), _) | Res::SelfCtor(..) |
-            Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => {} // OK
-            _ => bug!("unexpected pattern resolution: {:?}", res)
-        }
-
-        // Type-check the path.
-        let pat_ty = self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.hir_id).0;
-        self.demand_suptype(pat.span, expected, pat_ty);
-        pat_ty
-    }
-
-    fn check_pat_tuple_struct(
-        &self,
-        pat: &hir::Pat,
-        qpath: &hir::QPath,
-        subpats: &'tcx [P<hir::Pat>],
-        ddpos: Option<usize>,
-        expected: Ty<'tcx>,
-        def_bm: ty::BindingMode,
-        match_arm_pat_span: Option<Span>,
-    ) -> Ty<'tcx> {
-        let tcx = self.tcx;
-        let on_error = || {
-            for pat in subpats {
-                self.check_pat_walk(&pat, tcx.types.err, def_bm, match_arm_pat_span);
-            }
-        };
-        let report_unexpected_res = |res: Res| {
-            let msg = format!("expected tuple struct/variant, found {} `{}`",
-                              res.descr(),
-                              hir::print::to_string(tcx.hir(), |s| s.print_qpath(qpath, false)));
-            let mut err = struct_span_err!(tcx.sess, pat.span, E0164, "{}", msg);
-            match (res, &pat.node) {
-                (Res::Def(DefKind::Fn, _), _) | (Res::Def(DefKind::Method, _), _) => {
-                    err.span_label(pat.span, "`fn` calls are not allowed in patterns");
-                    err.help("for more information, visit \
-                              https://doc.rust-lang.org/book/ch18-00-patterns.html");
-                }
-                _ => {
-                    err.span_label(pat.span, "not a tuple variant or struct");
-                }
-            }
-            err.emit();
-            on_error();
-        };
-
-        // Resolve the path and check the definition for errors.
-        let (res, opt_ty, segments) = self.resolve_ty_and_res_ufcs(qpath, pat.hir_id, pat.span);
-        if res == Res::Err {
-            self.set_tainted_by_errors();
-            on_error();
-            return self.tcx.types.err;
-        }
-
-        // Type-check the path.
-        let (pat_ty, res) = self.instantiate_value_path(segments, opt_ty, res, pat.span,
-            pat.hir_id);
-        if !pat_ty.is_fn() {
-            report_unexpected_res(res);
-            return self.tcx.types.err;
-        }
-
-        let variant = match res {
-            Res::Err => {
-                self.set_tainted_by_errors();
-                on_error();
-                return tcx.types.err;
-            }
-            Res::Def(DefKind::AssocConst, _) | Res::Def(DefKind::Method, _) => {
-                report_unexpected_res(res);
-                return tcx.types.err;
-            }
-            Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) => {
-                tcx.expect_variant_res(res)
-            }
-            _ => bug!("unexpected pattern resolution: {:?}", res)
-        };
-
-        // Replace constructor type with constructed type for tuple struct patterns.
-        let pat_ty = pat_ty.fn_sig(tcx).output();
-        let pat_ty = pat_ty.no_bound_vars().expect("expected fn type");
-
-        self.demand_eqtype_pat(pat.span, expected, pat_ty, match_arm_pat_span);
-
-        // Type-check subpatterns.
-        if subpats.len() == variant.fields.len() ||
-                subpats.len() < variant.fields.len() && ddpos.is_some() {
-            let substs = match pat_ty.sty {
-                ty::Adt(_, substs) => substs,
-                _ => bug!("unexpected pattern type {:?}", pat_ty),
-            };
-            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_walk(&subpat, field_ty, def_bm, match_arm_pat_span);
-
-                self.tcx.check_stability(variant.fields[i].did, Some(pat.hir_id), subpat.span);
-            }
-        } else {
-            let subpats_ending = if subpats.len() == 1 { "" } else { "s" };
-            let fields_ending = if variant.fields.len() == 1 { "" } else { "s" };
-            struct_span_err!(tcx.sess, pat.span, E0023,
-                             "this pattern has {} field{}, but the corresponding {} has {} field{}",
-                             subpats.len(), subpats_ending, res.descr(),
-                             variant.fields.len(),  fields_ending)
-                .span_label(pat.span, format!("expected {} field{}, found {}",
-                                              variant.fields.len(), fields_ending, subpats.len()))
-                .emit();
-            on_error();
-            return tcx.types.err;
-        }
-        pat_ty
-    }
-
-    fn check_struct_pat_fields(
-        &self,
-        adt_ty: Ty<'tcx>,
-        pat_id: hir::HirId,
-        span: Span,
-        variant: &'tcx ty::VariantDef,
-        fields: &'tcx [hir::FieldPat],
-        etc: bool,
-        def_bm: ty::BindingMode,
-    ) -> bool {
-        let tcx = self.tcx;
-
-        let (substs, adt) = match adt_ty.sty {
-            ty::Adt(adt, substs) => (substs, adt),
-            _ => span_bug!(span, "struct pattern is not an ADT")
-        };
-        let kind_name = adt.variant_descr();
-
-        // Index the struct fields' types.
-        let field_map = variant.fields
-            .iter()
-            .enumerate()
-            .map(|(i, field)| (field.ident.modern(), (i, field)))
-            .collect::<FxHashMap<_, _>>();
-
-        // Keep track of which fields have already appeared in the pattern.
-        let mut used_fields = FxHashMap::default();
-        let mut no_field_errors = true;
-
-        let mut inexistent_fields = vec![];
-        // Typecheck each field.
-        for field in fields {
-            let span = field.span;
-            let ident = tcx.adjust_ident(field.ident, variant.def_id);
-            let field_ty = match used_fields.entry(ident) {
-                Occupied(occupied) => {
-                    struct_span_err!(tcx.sess, span, E0025,
-                                     "field `{}` bound multiple times \
-                                      in the pattern",
-                                     field.ident)
-                        .span_label(span,
-                                    format!("multiple uses of `{}` in pattern", field.ident))
-                        .span_label(*occupied.get(), format!("first use of `{}`", field.ident))
-                        .emit();
-                    no_field_errors = false;
-                    tcx.types.err
-                }
-                Vacant(vacant) => {
-                    vacant.insert(span);
-                    field_map.get(&ident)
-                        .map(|(i, f)| {
-                            self.write_field_index(field.hir_id, *i);
-                            self.tcx.check_stability(f.did, Some(pat_id), span);
-                            self.field_ty(span, f, substs)
-                        })
-                        .unwrap_or_else(|| {
-                            inexistent_fields.push(field.ident);
-                            no_field_errors = false;
-                            tcx.types.err
-                        })
-                }
-            };
-
-            self.check_pat_walk(&field.pat, field_ty, def_bm, None);
-        }
-        let mut unmentioned_fields = variant.fields
-                .iter()
-                .map(|field| field.ident.modern())
-                .filter(|ident| !used_fields.contains_key(&ident))
-                .collect::<Vec<_>>();
-        if inexistent_fields.len() > 0 && !variant.recovered {
-            let (field_names, t, plural) = if inexistent_fields.len() == 1 {
-                (format!("a field named `{}`", inexistent_fields[0]), "this", "")
-            } else {
-                (format!("fields named {}",
-                         inexistent_fields.iter()
-                            .map(|ident| format!("`{}`", ident))
-                            .collect::<Vec<String>>()
-                            .join(", ")), "these", "s")
-            };
-            let spans = inexistent_fields.iter().map(|ident| ident.span).collect::<Vec<_>>();
-            let mut err = struct_span_err!(tcx.sess,
-                                           spans,
-                                           E0026,
-                                           "{} `{}` does not have {}",
-                                           kind_name,
-                                           tcx.def_path_str(variant.def_id),
-                                           field_names);
-            if let Some(ident) = inexistent_fields.last() {
-                err.span_label(ident.span,
-                               format!("{} `{}` does not have {} field{}",
-                                       kind_name,
-                                       tcx.def_path_str(variant.def_id),
-                                       t,
-                                       plural));
-                if plural == "" {
-                    let input = unmentioned_fields.iter().map(|field| &field.name);
-                    let suggested_name =
-                        find_best_match_for_name(input, &ident.as_str(), None);
-                    if let Some(suggested_name) = suggested_name {
-                        err.span_suggestion(
-                            ident.span,
-                            "a field with a similar name exists",
-                            suggested_name.to_string(),
-                            Applicability::MaybeIncorrect,
-                        );
-
-                        // we don't want to throw `E0027` in case we have thrown `E0026` for them
-                        unmentioned_fields.retain(|&x| x.as_str() != suggested_name.as_str());
-                    }
-                }
-            }
-            if tcx.sess.teach(&err.get_code().unwrap()) {
-                err.note(
-                    "This error indicates that a struct pattern attempted to \
-                     extract a non-existent field from a struct. Struct fields \
-                     are identified by the name used before the colon : so struct \
-                     patterns should resemble the declaration of the struct type \
-                     being matched.\n\n\
-                     If you are using shorthand field patterns but want to refer \
-                     to the struct field by a different name, you should rename \
-                     it explicitly."
-                );
-            }
-            err.emit();
-        }
-
-        // Require `..` if struct has non_exhaustive attribute.
-        if variant.is_field_list_non_exhaustive() && !adt.did.is_local() && !etc {
-            span_err!(tcx.sess, span, E0638,
-                      "`..` required with {} marked as non-exhaustive",
-                      kind_name);
-        }
-
-        // Report an error if incorrect number of the fields were specified.
-        if kind_name == "union" {
-            if fields.len() != 1 {
-                tcx.sess.span_err(span, "union patterns should have exactly one field");
-            }
-            if etc {
-                tcx.sess.span_err(span, "`..` cannot be used in union patterns");
-            }
-        } else if !etc {
-            if unmentioned_fields.len() > 0 {
-                let field_names = if unmentioned_fields.len() == 1 {
-                    format!("field `{}`", unmentioned_fields[0])
-                } else {
-                    format!("fields {}",
-                            unmentioned_fields.iter()
-                                .map(|name| format!("`{}`", name))
-                                .collect::<Vec<String>>()
-                                .join(", "))
-                };
-                let mut diag = struct_span_err!(tcx.sess, span, E0027,
-                                                "pattern does not mention {}",
-                                                field_names);
-                diag.span_label(span, format!("missing {}", field_names));
-                if variant.ctor_kind == CtorKind::Fn {
-                    diag.note("trying to match a tuple variant with a struct variant pattern");
-                }
-                if tcx.sess.teach(&diag.get_code().unwrap()) {
-                    diag.note(
-                        "This error indicates that a pattern for a struct fails to specify a \
-                         sub-pattern for every one of the struct's fields. Ensure that each field \
-                         from the struct's definition is mentioned in the pattern, or use `..` to \
-                         ignore unwanted fields."
-                    );
-                }
-                diag.emit();
-            }
-        }
-        no_field_errors
-    }
 }
index 9c7ac83e82e9712d10de1bc31b0930a5788cebc7..a130d11a5e64518651f3dfc54eea943c2c5151a5 100644 (file)
@@ -70,6 +70,7 @@
 mod autoderef;
 pub mod dropck;
 pub mod _match;
+mod pat;
 pub mod writeback;
 mod regionck;
 pub mod coercion;
diff --git a/src/librustc_typeck/check/pat.rs b/src/librustc_typeck/check/pat.rs
new file mode 100644 (file)
index 0000000..e2d0f48
--- /dev/null
@@ -0,0 +1,968 @@
+use crate::check::FnCtxt;
+use crate::util::nodemap::FxHashMap;
+use errors::{Applicability, DiagnosticBuilder};
+use rustc::hir::{self, PatKind, Pat};
+use rustc::hir::def::{Res, DefKind, CtorKind};
+use rustc::hir::pat_util::EnumerateAndAdjustIterator;
+use rustc::hir::ptr::P;
+use rustc::infer;
+use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc::ty::{self, Ty, TypeFoldable};
+use rustc::ty::subst::Kind;
+use syntax::ast;
+use syntax::util::lev_distance::find_best_match_for_name;
+use syntax_pos::Span;
+use syntax_pos::hygiene::DesugaringKind;
+
+use std::collections::hash_map::Entry::{Occupied, Vacant};
+use std::cmp;
+
+use super::report_unexpected_variant_res;
+
+impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
+    /// `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:
+    ///
+    /// ```text
+    /// error[E0308]: mismatched types
+    ///  --> src/main.rs:5:9
+    ///   |
+    /// 4 |    let temp: usize = match a + b {
+    ///   |                            ----- this expression has type `usize`
+    /// 5 |         Ok(num) => num,
+    ///   |         ^^^^^^^ expected usize, found enum `std::result::Result`
+    ///   |
+    ///   = note: expected type `usize`
+    ///              found type `std::result::Result<_, _>`
+    /// ```
+    pub fn check_pat_walk(
+        &self,
+        pat: &'tcx hir::Pat,
+        mut expected: Ty<'tcx>,
+        mut def_bm: ty::BindingMode,
+        discrim_span: Option<Span>,
+    ) {
+        let tcx = self.tcx;
+
+        debug!("check_pat_walk(pat={:?},expected={:?},def_bm={:?})", pat, expected, def_bm);
+
+        let mut path_resolution = None;
+        let is_non_ref_pat = match pat.node {
+            PatKind::Struct(..) |
+            PatKind::TupleStruct(..) |
+            PatKind::Or(_) |
+            PatKind::Tuple(..) |
+            PatKind::Box(_) |
+            PatKind::Range(..) |
+            PatKind::Slice(..) => true,
+            PatKind::Lit(ref lt) => {
+                let ty = self.check_expr(lt);
+                match ty.sty {
+                    ty::Ref(..) => false,
+                    _ => true,
+                }
+            }
+            PatKind::Path(ref qpath) => {
+                let resolution = self.resolve_ty_and_res_ufcs(qpath, pat.hir_id, pat.span);
+                path_resolution = Some(resolution);
+                match resolution.0 {
+                    Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => false,
+                    _ => true,
+                }
+            }
+            PatKind::Wild |
+            PatKind::Binding(..) |
+            PatKind::Ref(..) => false,
+        };
+        if is_non_ref_pat {
+            debug!("pattern is non reference pattern");
+            let mut exp_ty = self.resolve_type_vars_with_obligations(&expected);
+
+            // Peel off as many `&` or `&mut` from the discriminant as possible. For example,
+            // for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches
+            // the `Some(5)` which is not of type Ref.
+            //
+            // For each ampersand peeled off, update the binding mode and push the original
+            // type into the adjustments vector.
+            //
+            // See the examples in `ui/match-defbm*.rs`.
+            let mut pat_adjustments = vec![];
+            while let ty::Ref(_, inner_ty, inner_mutability) = exp_ty.sty {
+                debug!("inspecting {:?}", exp_ty);
+
+                debug!("current discriminant is Ref, inserting implicit deref");
+                // Preserve the reference type. We'll need it later during HAIR lowering.
+                pat_adjustments.push(exp_ty);
+
+                exp_ty = inner_ty;
+                def_bm = match def_bm {
+                    // If default binding mode is by value, make it `ref` or `ref mut`
+                    // (depending on whether we observe `&` or `&mut`).
+                    ty::BindByValue(_) =>
+                        ty::BindByReference(inner_mutability),
+
+                    // Once a `ref`, always a `ref`. This is because a `& &mut` can't mutate
+                    // the underlying value.
+                    ty::BindByReference(hir::Mutability::MutImmutable) =>
+                        ty::BindByReference(hir::Mutability::MutImmutable),
+
+                    // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref`
+                    // (on `&`).
+                    ty::BindByReference(hir::Mutability::MutMutable) =>
+                        ty::BindByReference(inner_mutability),
+                };
+            }
+            expected = exp_ty;
+
+            if pat_adjustments.len() > 0 {
+                debug!("default binding mode is now {:?}", def_bm);
+                self.inh.tables.borrow_mut()
+                    .pat_adjustments_mut()
+                    .insert(pat.hir_id, pat_adjustments);
+            }
+        } else if let PatKind::Ref(..) = pat.node {
+            // When you encounter a `&pat` pattern, reset to "by
+            // value". This is so that `x` and `y` here are by value,
+            // as they appear to be:
+            //
+            // ```
+            // match &(&22, &44) {
+            //   (&x, &y) => ...
+            // }
+            // ```
+            //
+            // See issue #46688.
+            def_bm = ty::BindByValue(hir::MutImmutable);
+        }
+
+        // Lose mutability now that we know binding mode and discriminant type.
+        let def_bm = def_bm;
+        let expected = expected;
+
+        let ty = match pat.node {
+            PatKind::Wild => {
+                expected
+            }
+            PatKind::Lit(ref lt) => {
+                // We've already computed the type above (when checking for a non-ref pat), so
+                // avoid computing it again.
+                let ty = self.node_ty(lt.hir_id);
+
+                // Byte string patterns behave the same way as array patterns
+                // They can denote both statically and dynamically-sized byte arrays.
+                let mut pat_ty = ty;
+                if let hir::ExprKind::Lit(ref lt) = lt.node {
+                    if let ast::LitKind::ByteStr(_) = lt.node {
+                        let expected_ty = self.structurally_resolved_type(pat.span, expected);
+                        if let ty::Ref(_, r_ty, _) = expected_ty.sty {
+                            if let ty::Slice(_) = r_ty.sty {
+                                pat_ty = tcx.mk_imm_ref(tcx.lifetimes.re_static,
+                                                        tcx.mk_slice(tcx.types.u8))
+                            }
+                        }
+                    }
+                }
+
+                // Somewhat surprising: in this case, the subtyping
+                // relation goes the opposite way as the other
+                // cases. Actually what we really want is not a subtyping
+                // relation at all but rather that there exists a LUB (so
+                // that they can be compared). However, in practice,
+                // constants are always scalars or strings.  For scalars
+                // subtyping is irrelevant, and for strings `ty` is
+                // type is `&'static str`, so if we say that
+                //
+                //     &'static str <: expected
+                //
+                // then that's equivalent to there existing a LUB.
+                if let Some(mut err) = self.demand_suptype_diag(pat.span, expected, pat_ty) {
+                    err.emit_unless(discrim_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`,
+                            // so an error here would be a duplicate and from the wrong POV.
+                            s.is_desugaring(DesugaringKind::CondTemporary)
+                        })
+                        .is_some());
+                }
+
+                pat_ty
+            }
+            PatKind::Range(ref begin, ref end, _) => {
+                let lhs_ty = self.check_expr(begin);
+                let rhs_ty = self.check_expr(end);
+
+                // Check that both end-points are of numeric or char type.
+                let numeric_or_char = |ty: Ty<'_>| {
+                    ty.is_numeric()
+                    || ty.is_char()
+                    || ty.references_error()
+                };
+                let lhs_compat = numeric_or_char(lhs_ty);
+                let rhs_compat = numeric_or_char(rhs_ty);
+
+                if !lhs_compat || !rhs_compat {
+                    let span = if !lhs_compat && !rhs_compat {
+                        pat.span
+                    } else if !lhs_compat {
+                        begin.span
+                    } else {
+                        end.span
+                    };
+
+                    let mut err = struct_span_err!(
+                        tcx.sess,
+                        span,
+                        E0029,
+                        "only char and numeric types are allowed in range patterns"
+                    );
+                    err.span_label(span, "ranges require char or numeric types");
+                    err.note(&format!("start type: {}", self.ty_to_string(lhs_ty)));
+                    err.note(&format!("end type: {}", self.ty_to_string(rhs_ty)));
+                    if tcx.sess.teach(&err.get_code().unwrap()) {
+                        err.note(
+                            "In a match expression, only numbers and characters can be matched \
+                             against a range. This is because the compiler checks that the range \
+                             is non-empty at compile-time, and is unable to evaluate arbitrary \
+                             comparison functions. If you want to capture values of an orderable \
+                             type between two end-points, you can use a guard."
+                         );
+                    }
+                    err.emit();
+                    return;
+                }
+
+                // Now that we know the types can be unified we find the unified type and use
+                // it to type the entire expression.
+                let common_type = self.resolve_vars_if_possible(&lhs_ty);
+
+                // Subtyping doesn't matter here, as the value is some kind of scalar.
+                self.demand_eqtype_pat(pat.span, expected, lhs_ty, discrim_span);
+                self.demand_eqtype_pat(pat.span, expected, rhs_ty, discrim_span);
+                common_type
+            }
+            PatKind::Binding(ba, var_id, _, ref sub) => {
+                let bm = if ba == hir::BindingAnnotation::Unannotated {
+                    def_bm
+                } else {
+                    ty::BindingMode::convert(ba)
+                };
+                self.inh
+                    .tables
+                    .borrow_mut()
+                    .pat_binding_modes_mut()
+                    .insert(pat.hir_id, bm);
+                debug!("check_pat_walk: pat.hir_id={:?} bm={:?}", pat.hir_id, bm);
+                let local_ty = self.local_ty(pat.span, pat.hir_id).decl_ty;
+                match bm {
+                    ty::BindByReference(mutbl) => {
+                        // If the binding is like
+                        //     ref x | ref const x | ref mut x
+                        // then `x` is assigned a value of type `&M T` where M is the mutability
+                        // and T is the expected type.
+                        let region_var = self.next_region_var(infer::PatternRegion(pat.span));
+                        let mt = ty::TypeAndMut { ty: expected, mutbl: mutbl };
+                        let region_ty = tcx.mk_ref(region_var, mt);
+
+                        // `x` is assigned a value of type `&M T`, hence `&M T <: typeof(x)` is
+                        // required. However, we use equality, which is stronger. See (*) for
+                        // an explanation.
+                        self.demand_eqtype_pat(pat.span, region_ty, local_ty, discrim_span);
+                    }
+                    // Otherwise, the type of x is the expected type `T`.
+                    ty::BindByValue(_) => {
+                        // As above, `T <: typeof(x)` is required, but we
+                        // use equality, see (*) below.
+                        self.demand_eqtype_pat(pat.span, expected, local_ty, discrim_span);
+                    }
+                }
+
+                // 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);
+                }
+
+                if let Some(ref p) = *sub {
+                    self.check_pat_walk(&p, expected, def_bm, discrim_span);
+                }
+
+                local_ty
+            }
+            PatKind::TupleStruct(ref qpath, ref subpats, ddpos) => {
+                self.check_pat_tuple_struct(
+                    pat,
+                    qpath,
+                    &subpats,
+                    ddpos,
+                    expected,
+                    def_bm,
+                    discrim_span,
+                )
+            }
+            PatKind::Path(ref qpath) => {
+                self.check_pat_path(pat, path_resolution.unwrap(), qpath, expected)
+            }
+            PatKind::Struct(ref qpath, ref fields, etc) => {
+                self.check_pat_struct(pat, qpath, fields, etc, expected, def_bm, discrim_span)
+            }
+            PatKind::Or(ref pats) => {
+                let expected_ty = self.structurally_resolved_type(pat.span, expected);
+                for pat in pats {
+                    self.check_pat_walk(pat, expected, def_bm, discrim_span);
+                }
+                expected_ty
+            }
+            PatKind::Tuple(ref elements, ddpos) => {
+                let mut expected_len = elements.len();
+                if ddpos.is_some() {
+                    // Require known type only when `..` is present.
+                    if let ty::Tuple(ref tys) =
+                            self.structurally_resolved_type(pat.span, expected).sty {
+                        expected_len = tys.len();
+                    }
+                }
+                let max_len = cmp::max(expected_len, elements.len());
+
+                let element_tys_iter = (0..max_len).map(|_| {
+                    Kind::from(self.next_ty_var(
+                        // FIXME: `MiscVariable` for now -- obtaining the span and name information
+                        // from all tuple elements isn't trivial.
+                        TypeVariableOrigin {
+                            kind: TypeVariableOriginKind::TypeInference,
+                            span: pat.span,
+                        },
+                    ))
+                });
+                let element_tys = tcx.mk_substs(element_tys_iter);
+                let pat_ty = tcx.mk_ty(ty::Tuple(element_tys));
+                if let Some(mut err) = self.demand_eqtype_diag(pat.span, expected, pat_ty) {
+                    err.emit();
+                    // Walk subpatterns with an expected type of `err` in this case to silence
+                    // 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_walk(elem, &tcx.types.err, def_bm, discrim_span);
+                    }
+                    tcx.mk_tup(element_tys_iter)
+                } else {
+                    for (i, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) {
+                        self.check_pat_walk(
+                            elem,
+                            &element_tys[i].expect_ty(),
+                            def_bm,
+                            discrim_span,
+                        );
+                    }
+                    pat_ty
+                }
+            }
+            PatKind::Box(ref inner) => {
+                let inner_ty = self.next_ty_var(TypeVariableOrigin {
+                    kind: TypeVariableOriginKind::TypeInference,
+                    span: inner.span,
+                });
+                let uniq_ty = tcx.mk_box(inner_ty);
+
+                if self.check_dereferencable(pat.span, expected, &inner) {
+                    // Here, `demand::subtype` is good enough, but I don't
+                    // think any errors can be introduced by using
+                    // `demand::eqtype`.
+                    self.demand_eqtype_pat(pat.span, expected, uniq_ty, discrim_span);
+                    self.check_pat_walk(&inner, inner_ty, def_bm, discrim_span);
+                    uniq_ty
+                } else {
+                    self.check_pat_walk(&inner, tcx.types.err, def_bm, discrim_span);
+                    tcx.types.err
+                }
+            }
+            PatKind::Ref(ref inner, mutbl) => {
+                let expected = self.shallow_resolve(expected);
+                if self.check_dereferencable(pat.span, expected, &inner) {
+                    // `demand::subtype` would be good enough, but using
+                    // `eqtype` turns out to be equally general. See (*)
+                    // below for details.
+
+                    // Take region, inner-type from expected type if we
+                    // can, to avoid creating needless variables.  This
+                    // also helps with the bad interactions of the given
+                    // hack detailed in (*) below.
+                    debug!("check_pat_walk: expected={:?}", expected);
+                    let (rptr_ty, inner_ty) = match expected.sty {
+                        ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => {
+                            (expected, r_ty)
+                        }
+                        _ => {
+                            let inner_ty = self.next_ty_var(
+                                TypeVariableOrigin {
+                                    kind: TypeVariableOriginKind::TypeInference,
+                                    span: inner.span,
+                                }
+                            );
+                            let mt = ty::TypeAndMut { ty: inner_ty, mutbl: mutbl };
+                            let region = self.next_region_var(infer::PatternRegion(pat.span));
+                            let rptr_ty = tcx.mk_ref(region, mt);
+                            debug!("check_pat_walk: demanding {:?} = {:?}", expected, rptr_ty);
+                            let err = self.demand_eqtype_diag(pat.span, expected, rptr_ty);
+
+                            // Look for a case like `fn foo(&foo: u32)` and suggest
+                            // `fn foo(foo: &u32)`
+                            if let Some(mut err) = err {
+                                self.borrow_pat_suggestion(&mut err, &pat, &inner, &expected);
+                                err.emit();
+                            }
+                            (rptr_ty, inner_ty)
+                        }
+                    };
+
+                    self.check_pat_walk(&inner, inner_ty, def_bm, discrim_span);
+                    rptr_ty
+                } else {
+                    self.check_pat_walk(&inner, tcx.types.err, def_bm, discrim_span);
+                    tcx.types.err
+                }
+            }
+            PatKind::Slice(ref before, ref slice, ref after) => {
+                let expected_ty = self.structurally_resolved_type(pat.span, expected);
+                let (inner_ty, slice_ty) = match expected_ty.sty {
+                    ty::Array(inner_ty, size) => {
+                        if let Some(size) = size.try_eval_usize(tcx, self.param_env) {
+                            let min_len = before.len() as u64 + after.len() as u64;
+                            if slice.is_none() {
+                                if min_len != size {
+                                    struct_span_err!(
+                                        tcx.sess, pat.span, E0527,
+                                        "pattern requires {} elements but array has {}",
+                                        min_len, size)
+                                        .span_label(pat.span, format!("expected {} elements", size))
+                                        .emit();
+                                }
+                                (inner_ty, tcx.types.err)
+                            } else if let Some(rest) = size.checked_sub(min_len) {
+                                (inner_ty, tcx.mk_array(inner_ty, rest))
+                            } else {
+                                struct_span_err!(tcx.sess, pat.span, E0528,
+                                        "pattern requires at least {} elements but array has {}",
+                                        min_len, size)
+                                    .span_label(pat.span,
+                                        format!("pattern cannot match array of {} elements", size))
+                                    .emit();
+                                (inner_ty, tcx.types.err)
+                            }
+                        } else {
+                            struct_span_err!(
+                                tcx.sess,
+                                pat.span,
+                                E0730,
+                                "cannot pattern-match on an array without a fixed length",
+                            ).emit();
+                            (inner_ty, tcx.types.err)
+                        }
+                    }
+                    ty::Slice(inner_ty) => (inner_ty, expected_ty),
+                    _ => {
+                        if !expected_ty.references_error() {
+                            let mut err = struct_span_err!(
+                                tcx.sess, pat.span, E0529,
+                                "expected an array or slice, found `{}`",
+                                expected_ty);
+                            if let ty::Ref(_, ty, _) = expected_ty.sty {
+                                match ty.sty {
+                                    ty::Array(..) | ty::Slice(..) => {
+                                        err.help("the semantics of slice patterns changed \
+                                                  recently; see issue #62254");
+                                    }
+                                    _ => {}
+                                }
+                            }
+
+                            err.span_label( pat.span,
+                                format!("pattern cannot match with input type `{}`", expected_ty)
+                            ).emit();
+                        }
+                        (tcx.types.err, tcx.types.err)
+                    }
+                };
+
+                for elt in before {
+                    self.check_pat_walk(&elt, inner_ty, def_bm, discrim_span);
+                }
+                if let Some(ref slice) = *slice {
+                    self.check_pat_walk(&slice, slice_ty, def_bm, discrim_span);
+                }
+                for elt in after {
+                    self.check_pat_walk(&elt, inner_ty, def_bm, discrim_span);
+                }
+                expected_ty
+            }
+        };
+
+        self.write_ty(pat.hir_id, ty);
+
+        // (*) In most of the cases above (literals and constants being
+        // the exception), we relate types using strict equality, even
+        // though subtyping would be sufficient. There are a few reasons
+        // for this, some of which are fairly subtle and which cost me
+        // (nmatsakis) an hour or two debugging to remember, so I thought
+        // I'd write them down this time.
+        //
+        // 1. There is no loss of expressiveness here, though it does
+        // cause some inconvenience. What we are saying is that the type
+        // of `x` becomes *exactly* what is expected. This can cause unnecessary
+        // errors in some cases, such as this one:
+        //
+        // ```
+        // fn foo<'x>(x: &'x int) {
+        //    let a = 1;
+        //    let mut z = x;
+        //    z = &a;
+        // }
+        // ```
+        //
+        // The reason we might get an error is that `z` might be
+        // assigned a type like `&'x int`, and then we would have
+        // a problem when we try to assign `&a` to `z`, because
+        // the lifetime of `&a` (i.e., the enclosing block) is
+        // shorter than `'x`.
+        //
+        // HOWEVER, this code works fine. The reason is that the
+        // expected type here is whatever type the user wrote, not
+        // the initializer's type. In this case the user wrote
+        // nothing, so we are going to create a type variable `Z`.
+        // Then we will assign the type of the initializer (`&'x
+        // int`) as a subtype of `Z`: `&'x int <: Z`. And hence we
+        // will instantiate `Z` as a type `&'0 int` where `'0` is
+        // a fresh region variable, with the constraint that `'x :
+        // '0`.  So basically we're all set.
+        //
+        // Note that there are two tests to check that this remains true
+        // (`regions-reassign-{match,let}-bound-pointer.rs`).
+        //
+        // 2. Things go horribly wrong if we use subtype. The reason for
+        // THIS is a fairly subtle case involving bound regions. See the
+        // `givens` field in `region_constraints`, as well as the test
+        // `regions-relate-bound-regions-on-closures-to-inference-variables.rs`,
+        // for details. Short version is that we must sometimes detect
+        // relationships between specific region variables and regions
+        // bound in a closure signature, and that detection gets thrown
+        // off when we substitute fresh region variables here to enable
+        // subtyping.
+    }
+
+    fn borrow_pat_suggestion(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        pat: &Pat,
+        inner: &Pat,
+        expected: Ty<'tcx>,
+    ) {
+        let tcx = self.tcx;
+        if let PatKind::Binding(..) = inner.node {
+            let binding_parent_id = tcx.hir().get_parent_node(pat.hir_id);
+            let binding_parent = tcx.hir().get(binding_parent_id);
+            debug!("inner {:?} pat {:?} parent {:?}", inner, pat, binding_parent);
+            match binding_parent {
+                hir::Node::Arg(hir::Arg { span, .. }) => {
+                    if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(inner.span) {
+                        err.span_suggestion(
+                            *span,
+                            &format!("did you mean `{}`", snippet),
+                            format!(" &{}", expected),
+                            Applicability::MachineApplicable,
+                        );
+                    }
+                }
+                hir::Node::Arm(_) |
+                hir::Node::Pat(_) => {
+                    // rely on match ergonomics or it might be nested `&&pat`
+                    if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(inner.span) {
+                        err.span_suggestion(
+                            pat.span,
+                            "you can probably remove the explicit borrow",
+                            snippet,
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                }
+                _ => {} // don't provide suggestions in other cases #55175
+            }
+        }
+    }
+
+    pub fn check_dereferencable(&self, span: Span, expected: Ty<'tcx>, inner: &hir::Pat) -> bool {
+        if let PatKind::Binding(..) = inner.node {
+            if let Some(mt) = self.shallow_resolve(expected).builtin_deref(true) {
+                if let ty::Dynamic(..) = mt.ty.sty {
+                    // This is "x = SomeTrait" being reduced from
+                    // "let &x = &SomeTrait" or "let box x = Box<SomeTrait>", an error.
+                    let type_str = self.ty_to_string(expected);
+                    let mut err = struct_span_err!(
+                        self.tcx.sess,
+                        span,
+                        E0033,
+                        "type `{}` cannot be dereferenced",
+                        type_str
+                    );
+                    err.span_label(span, format!("type `{}` cannot be dereferenced", type_str));
+                    if self.tcx.sess.teach(&err.get_code().unwrap()) {
+                        err.note("\
+This error indicates that a pointer to a trait type cannot be implicitly dereferenced by a \
+pattern. Every trait defines a type, but because the size of trait implementors isn't fixed, \
+this type has no compile-time size. Therefore, all accesses to trait types must be through \
+pointers. If you encounter this error you should try to avoid dereferencing the pointer.
+
+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");
+                    }
+                    err.emit();
+                    return false
+                }
+            }
+        }
+        true
+    }
+
+    fn check_pat_struct(
+        &self,
+        pat: &'tcx hir::Pat,
+        qpath: &hir::QPath,
+        fields: &'tcx [hir::FieldPat],
+        etc: bool,
+        expected: Ty<'tcx>,
+        def_bm: ty::BindingMode,
+        discrim_span: Option<Span>,
+    ) -> 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)
+        {
+            variant_ty
+        } else {
+            for field in fields {
+                self.check_pat_walk(&field.pat, self.tcx.types.err, def_bm, discrim_span);
+            }
+            return self.tcx.types.err;
+        };
+
+        // Type-check the path.
+        self.demand_eqtype_pat(pat.span, expected, pat_ty, discrim_span);
+
+        // Type-check subpatterns.
+        if self.check_struct_pat_fields(pat_ty, pat.hir_id, pat.span, variant, fields, etc, def_bm)
+        {
+            pat_ty
+        } else {
+            self.tcx.types.err
+        }
+    }
+
+    fn check_pat_path(
+        &self,
+        pat: &hir::Pat,
+        path_resolution: (Res, Option<Ty<'tcx>>, &'b [hir::PathSegment]),
+        qpath: &hir::QPath,
+        expected: Ty<'tcx>,
+    ) -> Ty<'tcx> {
+        let tcx = self.tcx;
+
+        // We have already resolved the path.
+        let (res, opt_ty, segments) = path_resolution;
+        match res {
+            Res::Err => {
+                self.set_tainted_by_errors();
+                return tcx.types.err;
+            }
+            Res::Def(DefKind::Method, _) |
+            Res::Def(DefKind::Ctor(_, CtorKind::Fictive), _) |
+            Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) => {
+                report_unexpected_variant_res(tcx, res, pat.span, qpath);
+                return tcx.types.err;
+            }
+            Res::Def(DefKind::Ctor(_, CtorKind::Const), _) | Res::SelfCtor(..) |
+            Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => {} // OK
+            _ => bug!("unexpected pattern resolution: {:?}", res)
+        }
+
+        // Type-check the path.
+        let pat_ty = self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.hir_id).0;
+        self.demand_suptype(pat.span, expected, pat_ty);
+        pat_ty
+    }
+
+    fn check_pat_tuple_struct(
+        &self,
+        pat: &hir::Pat,
+        qpath: &hir::QPath,
+        subpats: &'tcx [P<hir::Pat>],
+        ddpos: Option<usize>,
+        expected: Ty<'tcx>,
+        def_bm: ty::BindingMode,
+        match_arm_pat_span: Option<Span>,
+    ) -> Ty<'tcx> {
+        let tcx = self.tcx;
+        let on_error = || {
+            for pat in subpats {
+                self.check_pat_walk(&pat, tcx.types.err, def_bm, match_arm_pat_span);
+            }
+        };
+        let report_unexpected_res = |res: Res| {
+            let msg = format!("expected tuple struct/variant, found {} `{}`",
+                              res.descr(),
+                              hir::print::to_string(tcx.hir(), |s| s.print_qpath(qpath, false)));
+            let mut err = struct_span_err!(tcx.sess, pat.span, E0164, "{}", msg);
+            match (res, &pat.node) {
+                (Res::Def(DefKind::Fn, _), _) | (Res::Def(DefKind::Method, _), _) => {
+                    err.span_label(pat.span, "`fn` calls are not allowed in patterns");
+                    err.help("for more information, visit \
+                              https://doc.rust-lang.org/book/ch18-00-patterns.html");
+                }
+                _ => {
+                    err.span_label(pat.span, "not a tuple variant or struct");
+                }
+            }
+            err.emit();
+            on_error();
+        };
+
+        // Resolve the path and check the definition for errors.
+        let (res, opt_ty, segments) = self.resolve_ty_and_res_ufcs(qpath, pat.hir_id, pat.span);
+        if res == Res::Err {
+            self.set_tainted_by_errors();
+            on_error();
+            return self.tcx.types.err;
+        }
+
+        // Type-check the path.
+        let (pat_ty, res) = self.instantiate_value_path(segments, opt_ty, res, pat.span,
+            pat.hir_id);
+        if !pat_ty.is_fn() {
+            report_unexpected_res(res);
+            return self.tcx.types.err;
+        }
+
+        let variant = match res {
+            Res::Err => {
+                self.set_tainted_by_errors();
+                on_error();
+                return tcx.types.err;
+            }
+            Res::Def(DefKind::AssocConst, _) | Res::Def(DefKind::Method, _) => {
+                report_unexpected_res(res);
+                return tcx.types.err;
+            }
+            Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) => {
+                tcx.expect_variant_res(res)
+            }
+            _ => bug!("unexpected pattern resolution: {:?}", res)
+        };
+
+        // Replace constructor type with constructed type for tuple struct patterns.
+        let pat_ty = pat_ty.fn_sig(tcx).output();
+        let pat_ty = pat_ty.no_bound_vars().expect("expected fn type");
+
+        self.demand_eqtype_pat(pat.span, expected, pat_ty, match_arm_pat_span);
+
+        // Type-check subpatterns.
+        if subpats.len() == variant.fields.len() ||
+                subpats.len() < variant.fields.len() && ddpos.is_some() {
+            let substs = match pat_ty.sty {
+                ty::Adt(_, substs) => substs,
+                _ => bug!("unexpected pattern type {:?}", pat_ty),
+            };
+            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_walk(&subpat, field_ty, def_bm, match_arm_pat_span);
+
+                self.tcx.check_stability(variant.fields[i].did, Some(pat.hir_id), subpat.span);
+            }
+        } else {
+            let subpats_ending = if subpats.len() == 1 { "" } else { "s" };
+            let fields_ending = if variant.fields.len() == 1 { "" } else { "s" };
+            struct_span_err!(tcx.sess, pat.span, E0023,
+                             "this pattern has {} field{}, but the corresponding {} has {} field{}",
+                             subpats.len(), subpats_ending, res.descr(),
+                             variant.fields.len(),  fields_ending)
+                .span_label(pat.span, format!("expected {} field{}, found {}",
+                                              variant.fields.len(), fields_ending, subpats.len()))
+                .emit();
+            on_error();
+            return tcx.types.err;
+        }
+        pat_ty
+    }
+
+    fn check_struct_pat_fields(
+        &self,
+        adt_ty: Ty<'tcx>,
+        pat_id: hir::HirId,
+        span: Span,
+        variant: &'tcx ty::VariantDef,
+        fields: &'tcx [hir::FieldPat],
+        etc: bool,
+        def_bm: ty::BindingMode,
+    ) -> bool {
+        let tcx = self.tcx;
+
+        let (substs, adt) = match adt_ty.sty {
+            ty::Adt(adt, substs) => (substs, adt),
+            _ => span_bug!(span, "struct pattern is not an ADT")
+        };
+        let kind_name = adt.variant_descr();
+
+        // Index the struct fields' types.
+        let field_map = variant.fields
+            .iter()
+            .enumerate()
+            .map(|(i, field)| (field.ident.modern(), (i, field)))
+            .collect::<FxHashMap<_, _>>();
+
+        // Keep track of which fields have already appeared in the pattern.
+        let mut used_fields = FxHashMap::default();
+        let mut no_field_errors = true;
+
+        let mut inexistent_fields = vec![];
+        // Typecheck each field.
+        for field in fields {
+            let span = field.span;
+            let ident = tcx.adjust_ident(field.ident, variant.def_id);
+            let field_ty = match used_fields.entry(ident) {
+                Occupied(occupied) => {
+                    struct_span_err!(tcx.sess, span, E0025,
+                                     "field `{}` bound multiple times \
+                                      in the pattern",
+                                     field.ident)
+                        .span_label(span,
+                                    format!("multiple uses of `{}` in pattern", field.ident))
+                        .span_label(*occupied.get(), format!("first use of `{}`", field.ident))
+                        .emit();
+                    no_field_errors = false;
+                    tcx.types.err
+                }
+                Vacant(vacant) => {
+                    vacant.insert(span);
+                    field_map.get(&ident)
+                        .map(|(i, f)| {
+                            self.write_field_index(field.hir_id, *i);
+                            self.tcx.check_stability(f.did, Some(pat_id), span);
+                            self.field_ty(span, f, substs)
+                        })
+                        .unwrap_or_else(|| {
+                            inexistent_fields.push(field.ident);
+                            no_field_errors = false;
+                            tcx.types.err
+                        })
+                }
+            };
+
+            self.check_pat_walk(&field.pat, field_ty, def_bm, None);
+        }
+        let mut unmentioned_fields = variant.fields
+                .iter()
+                .map(|field| field.ident.modern())
+                .filter(|ident| !used_fields.contains_key(&ident))
+                .collect::<Vec<_>>();
+        if inexistent_fields.len() > 0 && !variant.recovered {
+            let (field_names, t, plural) = if inexistent_fields.len() == 1 {
+                (format!("a field named `{}`", inexistent_fields[0]), "this", "")
+            } else {
+                (format!("fields named {}",
+                         inexistent_fields.iter()
+                            .map(|ident| format!("`{}`", ident))
+                            .collect::<Vec<String>>()
+                            .join(", ")), "these", "s")
+            };
+            let spans = inexistent_fields.iter().map(|ident| ident.span).collect::<Vec<_>>();
+            let mut err = struct_span_err!(tcx.sess,
+                                           spans,
+                                           E0026,
+                                           "{} `{}` does not have {}",
+                                           kind_name,
+                                           tcx.def_path_str(variant.def_id),
+                                           field_names);
+            if let Some(ident) = inexistent_fields.last() {
+                err.span_label(ident.span,
+                               format!("{} `{}` does not have {} field{}",
+                                       kind_name,
+                                       tcx.def_path_str(variant.def_id),
+                                       t,
+                                       plural));
+                if plural == "" {
+                    let input = unmentioned_fields.iter().map(|field| &field.name);
+                    let suggested_name =
+                        find_best_match_for_name(input, &ident.as_str(), None);
+                    if let Some(suggested_name) = suggested_name {
+                        err.span_suggestion(
+                            ident.span,
+                            "a field with a similar name exists",
+                            suggested_name.to_string(),
+                            Applicability::MaybeIncorrect,
+                        );
+
+                        // we don't want to throw `E0027` in case we have thrown `E0026` for them
+                        unmentioned_fields.retain(|&x| x.as_str() != suggested_name.as_str());
+                    }
+                }
+            }
+            if tcx.sess.teach(&err.get_code().unwrap()) {
+                err.note(
+                    "This error indicates that a struct pattern attempted to \
+                     extract a non-existent field from a struct. Struct fields \
+                     are identified by the name used before the colon : so struct \
+                     patterns should resemble the declaration of the struct type \
+                     being matched.\n\n\
+                     If you are using shorthand field patterns but want to refer \
+                     to the struct field by a different name, you should rename \
+                     it explicitly."
+                );
+            }
+            err.emit();
+        }
+
+        // Require `..` if struct has non_exhaustive attribute.
+        if variant.is_field_list_non_exhaustive() && !adt.did.is_local() && !etc {
+            span_err!(tcx.sess, span, E0638,
+                      "`..` required with {} marked as non-exhaustive",
+                      kind_name);
+        }
+
+        // Report an error if incorrect number of the fields were specified.
+        if kind_name == "union" {
+            if fields.len() != 1 {
+                tcx.sess.span_err(span, "union patterns should have exactly one field");
+            }
+            if etc {
+                tcx.sess.span_err(span, "`..` cannot be used in union patterns");
+            }
+        } else if !etc {
+            if unmentioned_fields.len() > 0 {
+                let field_names = if unmentioned_fields.len() == 1 {
+                    format!("field `{}`", unmentioned_fields[0])
+                } else {
+                    format!("fields {}",
+                            unmentioned_fields.iter()
+                                .map(|name| format!("`{}`", name))
+                                .collect::<Vec<String>>()
+                                .join(", "))
+                };
+                let mut diag = struct_span_err!(tcx.sess, span, E0027,
+                                                "pattern does not mention {}",
+                                                field_names);
+                diag.span_label(span, format!("missing {}", field_names));
+                if variant.ctor_kind == CtorKind::Fn {
+                    diag.note("trying to match a tuple variant with a struct variant pattern");
+                }
+                if tcx.sess.teach(&diag.get_code().unwrap()) {
+                    diag.note(
+                        "This error indicates that a pattern for a struct fails to specify a \
+                         sub-pattern for every one of the struct's fields. Ensure that each field \
+                         from the struct's definition is mentioned in the pattern, or use `..` to \
+                         ignore unwanted fields."
+                    );
+                }
+                diag.emit();
+            }
+        }
+        no_field_errors
+    }
+}