diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index fc25eb44cbd88..efc37cc04b212 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -1,631 +1,12 @@ 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::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, - ) { - 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", 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, @@ -678,8 +59,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"); let mut all_pats_diverge = Diverges::WarnedAlways; for p in &arm.pats { self.diverges.set(Diverges::Maybe); - let binding_mode = ty::BindingMode::BindByValue(hir::Mutability::MutImmutable); - self.check_pat_walk(&p, discrim_ty, binding_mode, Some(discrim.span)); + self.check_pat_top(&p, discrim_ty, Some(discrim.span)); all_pats_diverge &= self.diverges.get(); } @@ -1038,346 +418,4 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"); 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, - ) -> 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>, &'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], - ddpos: Option, - expected: Ty<'tcx>, - def_bm: ty::BindingMode, - match_arm_pat_span: Option, - ) -> 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::>(); - - // 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::>(); - 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::>() - .join(", ")), "these", "s") - }; - let spans = inexistent_fields.iter().map(|ident| ident.span).collect::>(); - 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::>() - .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 - } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 9c7ac83e82e97..ee505b2487532 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -70,6 +70,7 @@ type parameter). mod autoderef; pub mod dropck; pub mod _match; +mod pat; pub mod writeback; mod regionck; pub mod coercion; @@ -1103,8 +1104,7 @@ fn check_fn<'a, 'tcx>( // Add formal parameters. for (arg_ty, arg) in fn_sig.inputs().iter().zip(&body.arguments) { // Check the pattern. - let binding_mode = ty::BindingMode::BindByValue(hir::Mutability::MutImmutable); - fcx.check_pat_walk(&arg.pat, arg_ty, binding_mode, None); + fcx.check_pat_top(&arg.pat, arg_ty, None); // Check that argument is Sized. // The check for a non-trivial pattern is a hack to avoid duplicate warnings @@ -3630,12 +3630,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - self.check_pat_walk( - &local.pat, - t, - ty::BindingMode::BindByValue(hir::Mutability::MutImmutable), - None, - ); + self.check_pat_top(&local.pat, t, None); let pat_ty = self.node_ty(local.pat.hir_id); if pat_ty.references_error() { self.write_ty(local.hir_id, pat_ty); diff --git a/src/librustc_typeck/check/pat.rs b/src/librustc_typeck/check/pat.rs new file mode 100644 index 0000000000000..3f6fc95360a5e --- /dev/null +++ b/src/librustc_typeck/check/pat.rs @@ -0,0 +1,1123 @@ +use crate::check::FnCtxt; +use crate::util::nodemap::FxHashMap; +use errors::{Applicability, DiagnosticBuilder}; +use rustc::hir::{self, PatKind, Pat, HirId}; +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, BindingMode, 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; + +const CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ: &str = "\ +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"; + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + pub fn check_pat_top(&self, pat: &'tcx Pat, expected: Ty<'tcx>, discrim_span: Option) { + let def_bm = BindingMode::BindByValue(hir::Mutability::MutImmutable); + 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: + /// + /// ```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<_, _>` + /// ``` + fn check_pat( + &self, + pat: &'tcx Pat, + expected: Ty<'tcx>, + def_bm: BindingMode, + discrim_span: Option, + ) { + debug!("check_pat(pat={:?},expected={:?},def_bm={:?})", pat, expected, def_bm); + + let path_resolution = match &pat.node { + PatKind::Path(qpath) => Some(self.resolve_ty_and_res_ufcs(qpath, pat.hir_id, pat.span)), + _ => None, + }; + 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.node { + PatKind::Wild => expected, + PatKind::Lit(lt) => self.check_pat_lit(pat.span, lt, expected, discrim_span), + PatKind::Range(begin, end, _) => { + match self.check_pat_range(pat.span, begin, end, expected, discrim_span) { + 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) + } + PatKind::TupleStruct(qpath, subpats, ddpos) => { + self.check_pat_tuple_struct( + pat, + qpath, + subpats, + *ddpos, + expected, + def_bm, + discrim_span, + ) + } + PatKind::Path(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::Or(pats) => { + let expected_ty = self.structurally_resolved_type(pat.span, expected); + for pat in pats { + self.check_pat(pat, expected, def_bm, discrim_span); + } + expected_ty + } + 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) + } + PatKind::Ref(inner, mutbl) => { + self.check_pat_ref(pat, inner, *mutbl, expected, def_bm, discrim_span) + } + 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.write_ty(pat.hir_id, ty); + + // (note_1): In most of the cases where (note_1) is referenced + // (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. + } + + /// Compute the new expected type and default binding mode from the old ones + /// as well as the pattern form we are currently checking. + fn calc_default_binding_mode( + &self, + pat: &'tcx Pat, + expected: Ty<'tcx>, + def_bm: BindingMode, + is_non_ref_pat: bool, + ) -> (Ty<'tcx>, BindingMode) { + if is_non_ref_pat { + debug!("pattern is non reference pattern"); + self.peel_off_references(pat, expected, def_bm) + } else { + // 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. + let def_bm = match pat.node { + PatKind::Ref(..) => ty::BindByValue(hir::MutImmutable), + _ => def_bm, + }; + (expected, def_bm) + } + } + + /// Is the pattern a "non reference pattern"? + /// When the pattern is a path pattern, `opt_path_res` must be `Some(res)`. + fn is_non_ref_pat(&self, pat: &'tcx Pat, opt_path_res: Option) -> bool { + 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(_) => { + match opt_path_res.unwrap() { + Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => false, + _ => true, + } + } + PatKind::Wild | + PatKind::Binding(..) | + PatKind::Ref(..) => false, + } + } + + /// Peel off as many immediately nested `& mut?` from the expected type as possible + /// and return the new expected type and binding default binding mode. + /// The adjustments vector, if non-empty is stored in a table. + fn peel_off_references( + &self, + pat: &'tcx Pat, + expected: Ty<'tcx>, + mut def_bm: BindingMode, + ) -> (Ty<'tcx>, BindingMode) { + let mut expected = self.resolve_type_vars_with_obligations(&expected); + + // Peel off as many `&` or `&mut` from the scrutinee type 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) = expected.sty { + debug!("inspecting {:?}", expected); + + debug!("current discriminant is Ref, inserting implicit deref"); + // Preserve the reference type. We'll need it later during HAIR lowering. + pat_adjustments.push(expected); + + expected = inner_ty; + def_bm = ty::BindByReference(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(_) | + // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`). + ty::BindByReference(hir::Mutability::MutMutable) => inner_mutability, + // Once a `ref`, always a `ref`. + // This is because a `& &mut` cannot mutate the underlying value. + ty::BindByReference(m @ hir::Mutability::MutImmutable) => m, + }); + } + + 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); + } + + (expected, def_bm) + } + + fn check_pat_lit( + &self, + span: Span, + lt: &hir::Expr, + expected: Ty<'tcx>, + discrim_span: Option, + ) -> Ty<'tcx> { + // 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(span, expected); + if let ty::Ref(_, r_ty, _) = expected_ty.sty { + if let ty::Slice(_) = r_ty.sty { + let tcx = self.tcx; + 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(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 + } + + fn check_pat_range( + &self, + span: Span, + begin: &'tcx hir::Expr, + end: &'tcx hir::Expr, + expected: Ty<'tcx>, + discrim_span: Option, + ) -> Option> { + 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 { + span + } else if !lhs_compat { + begin.span + } else { + end.span + }; + + let mut err = struct_span_err!( + self.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 self.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 None; + } + + // 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(span, expected, lhs_ty, discrim_span); + self.demand_eqtype_pat(span, expected, rhs_ty, discrim_span); + Some(common_type) + } + + fn check_pat_ident( + &self, + pat: &Pat, + ba: hir::BindingAnnotation, + var_id: HirId, + sub: Option<&'tcx Pat>, + expected: Ty<'tcx>, + def_bm: BindingMode, + discrim_span: Option, + ) -> Ty<'tcx> { + // Determine the binding mode... + let bm = match ba { + hir::BindingAnnotation::Unannotated => def_bm, + _ => BindingMode::convert(ba), + }; + // ...and store it in a side table: + self.inh + .tables + .borrow_mut() + .pat_binding_modes_mut() + .insert(pat.hir_id, bm); + + debug!("check_pat_ident: pat.hir_id={:?} bm={:?}", pat.hir_id, bm); + + let local_ty = self.local_ty(pat.span, pat.hir_id).decl_ty; + let eq_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_ty = self.new_ref_ty(pat.span, mutbl, expected); + + // `x` is assigned a value of type `&M T`, hence `&M T <: typeof(x)` + // is required. However, we use equality, which is stronger. + // See (note_1) for an explanation. + region_ty + } + // Otherwise, the type of x is the expected type `T`. + ty::BindByValue(_) => { + // As above, `T <: typeof(x)` is required, but we use equality, see (note_1). + expected + } + }; + self.demand_eqtype_pat(pat.span, eq_ty, 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(p) = sub { + self.check_pat(&p, expected, def_bm, discrim_span); + } + + local_ty + } + + 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: &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", 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(CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ); + } + err.emit(); + return false + } + } + } + true + } + + fn check_pat_struct( + &self, + pat: &'tcx Pat, + qpath: &hir::QPath, + fields: &'tcx [hir::FieldPat], + etc: bool, + expected: Ty<'tcx>, + def_bm: BindingMode, + discrim_span: Option, + ) -> 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(&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: &Pat, + path_resolution: (Res, Option>, &'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: &Pat, + qpath: &hir::QPath, + subpats: &'tcx [P], + ddpos: Option, + expected: Ty<'tcx>, + def_bm: BindingMode, + match_arm_pat_span: Option, + ) -> 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); + } + }; + 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 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(&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_pat_tuple( + &self, + span: Span, + elements: &'tcx [P], + ddpos: Option, + expected: Ty<'tcx>, + def_bm: BindingMode, + discrim_span: Option, + ) -> Ty<'tcx> { + let tcx = self.tcx; + 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(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, + }, + )) + }); + 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(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(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(elem, &element_tys[i].expect_ty(), def_bm, discrim_span); + } + pat_ty + } + } + + fn check_struct_pat_fields( + &self, + adt_ty: Ty<'tcx>, + pat_id: HirId, + span: Span, + variant: &'tcx ty::VariantDef, + fields: &'tcx [hir::FieldPat], + etc: bool, + def_bm: 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::>(); + + // 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) => { + self.error_field_already_bound(span, field.ident, *occupied.get()); + 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(&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::>(); + + if inexistent_fields.len() > 0 && !variant.recovered { + self.error_inexistent_fields( + kind_name, + &inexistent_fields, + &mut unmentioned_fields, + variant + ); + } + + // 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 && unmentioned_fields.len() > 0 { + self.error_unmentioned_fields(span, &unmentioned_fields, variant); + } + no_field_errors + } + + fn error_field_already_bound(&self, span: Span, ident: ast::Ident, other_field: Span) { + struct_span_err!( + self.tcx.sess, span, E0025, + "field `{}` bound multiple times in the pattern", + ident + ) + .span_label(span, format!("multiple uses of `{}` in pattern", ident)) + .span_label(other_field, format!("first use of `{}`", ident)) + .emit(); + } + + fn error_inexistent_fields( + &self, + kind_name: &str, + inexistent_fields: &[ast::Ident], + unmentioned_fields: &mut Vec, + variant: &ty::VariantDef, + ) { + let tcx = self.tcx; + 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::>() + .join(", ")), "these", "s") + }; + let spans = inexistent_fields.iter().map(|ident| ident.span).collect::>(); + 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(); + } + + fn error_unmentioned_fields( + &self, + span: Span, + unmentioned_fields: &[ast::Ident], + variant: &ty::VariantDef, + ) { + let field_names = if unmentioned_fields.len() == 1 { + format!("field `{}`", unmentioned_fields[0]) + } else { + let fields = unmentioned_fields.iter() + .map(|name| format!("`{}`", name)) + .collect::>() + .join(", "); + format!("fields {}", fields) + }; + let mut diag = struct_span_err!( + self.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 self.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(); + } + + fn check_pat_box( + &self, + span: Span, + inner: &'tcx Pat, + expected: Ty<'tcx>, + def_bm: BindingMode, + discrim_span: Option, + ) -> Ty<'tcx> { + let tcx = self.tcx; + let (box_ty, inner_ty) = if self.check_dereferencable(span, expected, &inner) { + // Here, `demand::subtype` is good enough, but I don't + // think any errors can be introduced by using `demand::eqtype`. + let inner_ty = self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span: inner.span, + }); + let box_ty = tcx.mk_box(inner_ty); + self.demand_eqtype_pat(span, expected, box_ty, discrim_span); + (box_ty, inner_ty) + } else { + (tcx.types.err, tcx.types.err) + }; + self.check_pat(&inner, inner_ty, def_bm, discrim_span); + box_ty + } + + fn check_pat_ref( + &self, + pat: &Pat, + inner: &'tcx Pat, + mutbl: hir::Mutability, + expected: Ty<'tcx>, + def_bm: BindingMode, + discrim_span: Option, + ) -> Ty<'tcx> { + let tcx = self.tcx; + let expected = self.shallow_resolve(expected); + let (rptr_ty, inner_ty) = if self.check_dereferencable(pat.span, expected, &inner) { + // `demand::subtype` would be good enough, but using `eqtype` turns + // out to be equally general. See (note_1) 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 (note_1). + debug!("check_pat_ref: expected={:?}", expected); + 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 rptr_ty = self.new_ref_ty(pat.span, mutbl, inner_ty); + debug!("check_pat_ref: 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) + } + } + } else { + (tcx.types.err, tcx.types.err) + }; + self.check_pat(&inner, inner_ty, def_bm, discrim_span); + rptr_ty + } + + /// Create a reference type with a fresh region variable. + fn new_ref_ty(&self, span: Span, mutbl: hir::Mutability, ty: Ty<'tcx>) -> Ty<'tcx> { + let region = self.next_region_var(infer::PatternRegion(span)); + let mt = ty::TypeAndMut { ty, mutbl }; + self.tcx.mk_ref(region, mt) + } + + fn check_pat_slice( + &self, + span: Span, + before: &'tcx [P], + slice: Option<&'tcx Pat>, + after: &'tcx [P], + expected: Ty<'tcx>, + def_bm: BindingMode, + discrim_span: Option, + ) -> Ty<'tcx> { + let tcx = self.tcx; + let expected_ty = self.structurally_resolved_type(span, expected); + let (inner_ty, slice_ty) = match expected_ty.sty { + ty::Array(inner_ty, size) => { + let slice_ty = 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 { + self.error_scrutinee_inconsistent_length(span, min_len, size) + } + tcx.types.err + } else if let Some(rest) = size.checked_sub(min_len) { + tcx.mk_array(inner_ty, rest) + } else { + self.error_scrutinee_with_rest_inconsistent_length(span, min_len, size); + tcx.types.err + } + } else { + self.error_scrutinee_unfixed_length(span); + tcx.types.err + }; + (inner_ty, slice_ty) + } + ty::Slice(inner_ty) => (inner_ty, expected_ty), + _ => { + if !expected_ty.references_error() { + self.error_expected_array_or_slice(span, expected_ty); + } + (tcx.types.err, tcx.types.err) + } + }; + + for elt in before { + self.check_pat(&elt, inner_ty, def_bm, discrim_span); + } + if let Some(slice) = slice { + self.check_pat(&slice, slice_ty, def_bm, discrim_span); + } + for elt in after { + self.check_pat(&elt, inner_ty, def_bm, discrim_span); + } + expected_ty + } + + fn error_scrutinee_inconsistent_length(&self, span: Span, min_len: u64, size: u64) { + struct_span_err!( + self.tcx.sess, span, E0527, + "pattern requires {} elements but array has {}", + min_len, size + ) + .span_label(span, format!("expected {} elements", size)) + .emit(); + } + + fn error_scrutinee_with_rest_inconsistent_length(&self, span: Span, min_len: u64, size: u64) { + struct_span_err!( + self.tcx.sess, span, E0528, + "pattern requires at least {} elements but array has {}", + min_len, size + ) + .span_label(span, format!("pattern cannot match array of {} elements", size)) + .emit(); + } + + fn error_scrutinee_unfixed_length(&self, span: Span) { + struct_span_err!( + self.tcx.sess, span, E0730, + "cannot pattern-match on an array without a fixed length", + ) + .emit(); + } + + fn error_expected_array_or_slice(&self, span: Span, expected_ty: Ty<'tcx>) { + let mut err = struct_span_err!( + self.tcx.sess, span, E0529, + "expected an array or slice, found `{}`", + expected_ty + ); + if let ty::Ref(_, ty, _) = expected_ty.sty { + if let ty::Array(..) | ty::Slice(..) = ty.sty { + err.help("the semantics of slice patterns changed recently; see issue #62254"); + } + } + err.span_label(span, format!("pattern cannot match with input type `{}`", expected_ty)); + err.emit(); + } +}