]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_typeck/check/_match.rs
Auto merge of #30145 - petrochenkov:hyg, r=nrc
[rust.git] / src / librustc_typeck / check / _match.rs
index 5ffe34d61bf7d72243f3d9a70997cd3e0d779fa4..38c714fa8f292ec0b755769be81e01fa0529b3a3 100644 (file)
 use check::{instantiate_path, resolve_ty_and_def_ufcs, structurally_resolved_type};
 use require_same_types;
 use util::nodemap::FnvHashMap;
+use session::Session;
 
 use std::cmp;
 use std::collections::hash_map::Entry::{Occupied, Vacant};
 use syntax::ast;
-use syntax::ext::mtwt;
 use syntax::codemap::{Span, Spanned};
 use syntax::ptr::P;
 
@@ -136,6 +136,12 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
         }
         hir::PatEnum(..) | hir::PatIdent(..)
                 if pat_is_resolved_const(&tcx.def_map.borrow(), pat) => {
+            if let hir::PatEnum(ref path, ref subpats) = pat.node {
+                if !(subpats.is_some() && subpats.as_ref().unwrap().is_empty()) {
+                    bad_struct_kind_err(tcx.sess, pat.span, path, false);
+                    return;
+                }
+            }
             let const_did = tcx.def_map.borrow().get(&pat.id).unwrap().def_id();
             let const_scheme = tcx.lookup_item_type(const_did);
             assert!(const_scheme.generics.is_empty());
@@ -180,7 +186,7 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
 
             // if there are multiple arms, make sure they all agree on
             // what the type of the binding `x` ought to be
-            let canon_id = *pcx.map.get(&mtwt::resolve(path.node)).unwrap();
+            let canon_id = *pcx.map.get(&path.node.name).unwrap();
             if canon_id != pat.id {
                 let ct = fcx.local_ty(pat.span, canon_id);
                 demand::eqtype(fcx, pat.span, ct, typ);
@@ -192,11 +198,12 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
         }
         hir::PatIdent(_, ref path, _) => {
             let path = hir_util::ident_to_path(path.span, path.node);
-            check_pat_enum(pcx, pat, &path, Some(&[]), expected);
+            check_pat_enum(pcx, pat, &path, Some(&[]), expected, false);
         }
         hir::PatEnum(ref path, ref subpats) => {
             let subpats = subpats.as_ref().map(|v| &v[..]);
-            check_pat_enum(pcx, pat, path, subpats, expected);
+            let is_tuple_struct_pat = !(subpats.is_some() && subpats.unwrap().is_empty());
+            check_pat_enum(pcx, pat, path, subpats, expected, is_tuple_struct_pat);
         }
         hir::PatQPath(ref qself, ref path) => {
             let self_ty = fcx.to_ty(&qself.ty);
@@ -443,7 +450,7 @@ pub fn check_match<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
     // supertype, as the "discriminant type" (issue #23116).
     let contains_ref_bindings = arms.iter()
                                     .filter_map(|a| tcx.arm_contains_ref_binding(a))
-                                    .max_by(|m| match *m {
+                                    .max_by_key(|m| match *m {
                                         hir::MutMutable => 1,
                                         hir::MutImmutable => 0,
                                     });
@@ -572,11 +579,19 @@ pub fn check_pat_struct<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &'tcx hir::Pat,
     fcx.write_substs(pat.id, ty::ItemSubsts { substs: item_substs.clone() });
 }
 
+// This function exists due to the warning "diagnostic code E0164 already used"
+fn bad_struct_kind_err(sess: &Session, span: Span, path: &hir::Path, is_warning: bool) {
+    let name = pprust::path_to_string(path);
+    span_err_or_warn!(is_warning, sess, span, E0164,
+        "`{}` does not name a tuple variant or a tuple struct", name);
+}
+
 pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
                                 pat: &hir::Pat,
                                 path: &hir::Path,
                                 subpats: Option<&'tcx [P<hir::Pat>]>,
-                                expected: Ty<'tcx>)
+                                expected: Ty<'tcx>,
+                                is_tuple_struct_pat: bool)
 {
     // Typecheck the path.
     let fcx = pcx.fcx;
@@ -618,25 +633,55 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
                      path_scheme, &ctor_predicates,
                      opt_ty, def, pat.span, pat.id);
 
+    let report_bad_struct_kind = |is_warning| {
+        bad_struct_kind_err(tcx.sess, pat.span, path, is_warning);
+        if is_warning {
+            return
+        }
+
+        fcx.write_error(pat.id);
+        if let Some(subpats) = subpats {
+            for pat in subpats {
+                check_pat(pcx, &**pat, tcx.types.err);
+            }
+        }
+    };
+
     // If we didn't have a fully resolved path to start with, we had an
     // associated const, and we should quit now, since the rest of this
     // function uses checks specific to structs and enums.
     if path_res.depth != 0 {
-        let pat_ty = fcx.node_ty(pat.id);
-        demand::suptype(fcx, pat.span, expected, pat_ty);
+        if is_tuple_struct_pat {
+            report_bad_struct_kind(false);
+        } else {
+            let pat_ty = fcx.node_ty(pat.id);
+            demand::suptype(fcx, pat.span, expected, pat_ty);
+        }
         return;
     }
 
     let pat_ty = fcx.node_ty(pat.id);
     demand::eqtype(fcx, pat.span, expected, pat_ty);
 
-
     let real_path_ty = fcx.node_ty(pat.id);
     let (arg_tys, kind_name): (Vec<_>, &'static str) = match real_path_ty.sty {
         ty::TyEnum(enum_def, expected_substs)
             if def == def::DefVariant(enum_def.did, def.def_id(), false) =>
         {
             let variant = enum_def.variant_of_def(def);
+            if is_tuple_struct_pat && variant.kind() != ty::VariantKind::Tuple {
+                // Matching unit variants with tuple variant patterns (`UnitVariant(..)`)
+                // is allowed for backward compatibility.
+                let is_special_case = variant.kind() == ty::VariantKind::Unit;
+                report_bad_struct_kind(is_special_case);
+                if !is_special_case {
+                    return
+                } else {
+                    span_note!(tcx.sess, pat.span,
+                        "this warning will become a HARD ERROR in a future release. \
+                        See RFC 218 for details.");
+                }
+            }
             (variant.fields
                     .iter()
                     .map(|f| fcx.instantiate_type_scheme(pat.span,
@@ -646,26 +691,21 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
              "variant")
         }
         ty::TyStruct(struct_def, expected_substs) => {
-            (struct_def.struct_variant()
-                       .fields
-                       .iter()
-                       .map(|f| fcx.instantiate_type_scheme(pat.span,
-                                                            expected_substs,
-                                                            &f.unsubst_ty()))
-                       .collect(),
+            let variant = struct_def.struct_variant();
+            if is_tuple_struct_pat && variant.kind() != ty::VariantKind::Tuple {
+                report_bad_struct_kind(false);
+                return;
+            }
+            (variant.fields
+                    .iter()
+                    .map(|f| fcx.instantiate_type_scheme(pat.span,
+                                                         expected_substs,
+                                                         &f.unsubst_ty()))
+                    .collect(),
              "struct")
         }
         _ => {
-            let name = pprust::path_to_string(path);
-            span_err!(tcx.sess, pat.span, E0164,
-                "`{}` does not name a non-struct variant or a tuple struct", name);
-            fcx.write_error(pat.id);
-
-            if let Some(subpats) = subpats {
-                for pat in subpats {
-                    check_pat(pcx, &**pat, tcx.types.err);
-                }
-            }
+            report_bad_struct_kind(false);
             return;
         }
     };