1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 #[allow(non_camel_case_types)];
13 use middle::const_eval::{compare_const_vals, lookup_const_by_id};
14 use middle::const_eval::{eval_const_expr, const_val, const_bool, const_float};
15 use middle::pat_util::*;
18 use middle::typeck::MethodMap;
20 use util::ppaux::ty_to_str;
26 use syntax::ast_util::{unguarded_pat, walk_pat};
27 use syntax::codemap::{DUMMY_SP, Span};
28 use syntax::parse::token;
30 use syntax::visit::{Visitor, FnKind};
32 struct MatchCheckCtxt {
34 method_map: MethodMap,
35 moves_map: moves::MovesMap
38 struct CheckMatchVisitor {
42 impl Visitor<()> for CheckMatchVisitor {
43 fn visit_expr(&mut self, ex: &Expr, _: ()) {
44 check_expr(self, self.cx, ex, ());
46 fn visit_local(&mut self, l: &Local, _: ()) {
47 check_local(self, self.cx, l, ());
49 fn visit_fn(&mut self, fk: &FnKind, fd: &FnDecl, b: &Block, s: Span, n: NodeId, _: ()) {
50 check_fn(self, self.cx, fk, fd, b, s, n, ());
54 pub fn check_crate(tcx: ty::ctxt,
55 method_map: MethodMap,
56 moves_map: moves::MovesMap,
58 let cx = @MatchCheckCtxt {tcx: tcx,
59 method_map: method_map,
60 moves_map: moves_map};
61 let mut v = CheckMatchVisitor { cx: cx };
63 visit::walk_crate(&mut v, krate, ());
65 tcx.sess.abort_if_errors();
68 fn check_expr(v: &mut CheckMatchVisitor,
72 visit::walk_expr(v, ex, s);
74 ExprMatch(scrut, ref arms) => {
75 // First, check legality of move bindings.
76 for arm in arms.iter() {
77 check_legality_of_move_bindings(cx,
82 check_arms(cx, arms.as_slice());
83 /* Check for exhaustiveness */
84 // Check for empty enum, because is_useful only works on inhabited
86 let pat_ty = node_id_to_type(cx.tcx, scrut.id);
87 if (*arms).is_empty() {
88 if !type_is_empty(cx.tcx, pat_ty) {
89 // We know the type is inhabited, so this must be wrong
90 cx.tcx.sess.span_err(ex.span, format!("non-exhaustive patterns: \
91 type {} is non-empty",
92 ty_to_str(cx.tcx, pat_ty)));
94 // If the type *is* empty, it's vacuously exhaustive
97 match ty::get(pat_ty).sty {
99 if (*enum_variants(cx.tcx, did)).is_empty() &&
105 _ => { /* We assume only enum types can be uninhabited */ }
108 let pats: ~[@Pat] = arms.iter()
109 .filter_map(unguarded_pat)
110 .flat_map(|pats| pats.move_iter())
113 cx.tcx.sess.span_err(ex.span, "non-exhaustive patterns");
115 check_exhaustive(cx, ex.span, pats);
122 // Check for unreachable patterns
123 fn check_arms(cx: &MatchCheckCtxt, arms: &[Arm]) {
125 for arm in arms.iter() {
126 for pat in arm.pats.iter() {
128 // Check that we do not match against a static NaN (#6804)
129 let pat_matches_nan: |&Pat| -> bool = |p| {
131 let def_map = cx.tcx.def_map.borrow();
132 def_map.get().find_copy(&p.id)
135 Some(DefStatic(did, false)) => {
136 let const_expr = lookup_const_by_id(cx.tcx, did).unwrap();
137 match eval_const_expr(cx.tcx, const_expr) {
138 const_float(f) if f.is_nan() => true,
147 if pat_matches_nan(p) {
148 cx.tcx.sess.span_warn(p.span, "unmatchable NaN in pattern, \
149 use the is_nan method in a guard instead");
155 match is_useful(cx, &seen, v) {
157 cx.tcx.sess.span_err(pat.span, "unreachable pattern");
161 if arm.guard.is_none() { seen.push(v); }
166 fn raw_pat(p: @Pat) -> @Pat {
168 PatIdent(_, _, Some(s)) => { raw_pat(s) }
173 fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, pats: ~[@Pat]) {
174 assert!((!pats.is_empty()));
175 let ext = match is_useful(cx, &pats.map(|p| ~[*p]), [wild()]) {
177 // This is good, wildcard pattern isn't reachable
181 useful(ty, ref ctor) => {
182 match ty::get(ty).sty {
185 val(const_bool(true)) => Some(~"true"),
186 val(const_bool(false)) => Some(~"false"),
190 ty::ty_enum(id, _) => {
191 let vid = match *ctor {
193 _ => fail!("check_exhaustive: non-variant ctor"),
195 let variants = ty::enum_variants(cx.tcx, id);
197 match variants.iter().find(|v| v.id == vid) {
198 Some(v) => Some(token::get_ident(v.name).get().to_str()),
200 fail!("check_exhaustive: bad variant in ctor")
204 ty::ty_unboxed_vec(..) | ty::ty_vec(..) => {
206 vec(n) => Some(format!("vectors of length {}", n)),
214 let msg = ~"non-exhaustive patterns" + match ext {
215 Some(ref s) => format!(": {} not covered", *s),
218 cx.tcx.sess.span_err(sp, msg);
221 type matrix = ~[~[@Pat]];
230 #[deriving(Clone, Eq)]
235 range(const_val, const_val),
239 // Algorithm from http://moscova.inria.fr/~maranget/papers/warn/index.html
241 // Whether a vector `v` of patterns is 'useful' in relation to a set of such
242 // vectors `m` is defined as there being a set of inputs that will match `v`
243 // but not any of the sets in `m`.
245 // This is used both for reachability checking (if a pattern isn't useful in
246 // relation to preceding patterns, it is not reachable) and exhaustiveness
247 // checking (if a wildcard pattern is useful in relation to a matrix, the
248 // matrix isn't exhaustive).
250 // Note: is_useful doesn't work on empty types, as the paper notes.
251 // So it assumes that v is non-empty.
252 fn is_useful(cx: &MatchCheckCtxt, m: &matrix, v: &[@Pat]) -> useful {
253 if m.len() == 0u { return useful_; }
254 if m[0].len() == 0u { return not_useful; }
255 let real_pat = match m.iter().find(|r| r[0].id != 0) {
256 Some(r) => r[0], None => v[0]
258 let left_ty = if real_pat.id == 0 { ty::mk_nil() }
259 else { ty::node_id_to_type(cx.tcx, real_pat.id) };
261 match pat_ctor_id(cx, v[0]) {
263 match missing_ctor(cx, m, left_ty) {
265 match ty::get(left_ty).sty {
267 match is_useful_specialized(cx, m, v,
268 val(const_bool(true)),
271 is_useful_specialized(cx, m, v,
272 val(const_bool(false)),
275 ref u => (*u).clone(),
278 ty::ty_enum(eid, _) => {
279 for va in (*ty::enum_variants(cx.tcx, eid)).iter() {
280 match is_useful_specialized(cx, m, v, variant(va.id),
281 va.args.len(), left_ty) {
283 ref u => return (*u).clone(),
288 ty::ty_vec(_, ty::vstore_fixed(n)) => {
289 is_useful_specialized(cx, m, v, vec(n), n, left_ty)
291 ty::ty_unboxed_vec(..) | ty::ty_vec(..) => {
292 let max_len = m.rev_iter().fold(0, |max_len, r| {
294 PatVec(ref before, _, ref after) => {
295 cmp::max(before.len() + after.len(), max_len)
300 for n in iter::range(0u, max_len + 1) {
301 match is_useful_specialized(cx, m, v, vec(n), n, left_ty) {
303 ref u => return (*u).clone(),
309 let arity = ctor_arity(cx, &single, left_ty);
310 is_useful_specialized(cx, m, v, single, arity, left_ty)
316 &m.iter().filter_map(|r| default(cx, *r)).collect::<matrix>(),
318 useful_ => useful(left_ty, (*ctor).clone()),
319 ref u => (*u).clone(),
324 Some(ref v0_ctor) => {
325 let arity = ctor_arity(cx, v0_ctor, left_ty);
326 is_useful_specialized(cx, m, v, (*v0_ctor).clone(), arity, left_ty)
331 fn is_useful_specialized(cx: &MatchCheckCtxt,
338 let ms = m.iter().filter_map(|r| specialize(cx, *r, &ctor, arity, lty)).collect::<matrix>();
339 let could_be_useful = is_useful(
340 cx, &ms, specialize(cx, v, &ctor, arity, lty).unwrap());
341 match could_be_useful {
342 useful_ => useful(lty, ctor),
343 ref u => (*u).clone(),
347 fn pat_ctor_id(cx: &MatchCheckCtxt, p: @Pat) -> Option<ctor> {
348 let pat = raw_pat(p);
350 PatWild | PatWildMulti => { None }
351 PatIdent(_, _, _) | PatEnum(_, _) => {
353 let def_map = cx.tcx.def_map.borrow();
354 def_map.get().find_copy(&pat.id)
357 Some(DefVariant(_, id, _)) => Some(variant(id)),
358 Some(DefStatic(did, false)) => {
359 let const_expr = lookup_const_by_id(cx.tcx, did).unwrap();
360 Some(val(eval_const_expr(cx.tcx, const_expr)))
365 PatLit(expr) => { Some(val(eval_const_expr(cx.tcx, expr))) }
366 PatRange(lo, hi) => {
367 Some(range(eval_const_expr(cx.tcx, lo), eval_const_expr(cx.tcx, hi)))
370 let def_map = cx.tcx.def_map.borrow();
371 match def_map.get().find(&pat.id) {
372 Some(&DefVariant(_, id, _)) => Some(variant(id)),
376 PatUniq(_) | PatTup(_) | PatRegion(..) => {
379 PatVec(ref before, slice, ref after) => {
382 None => Some(vec(before.len() + after.len()))
388 fn is_wild(cx: &MatchCheckCtxt, p: @Pat) -> bool {
389 let pat = raw_pat(p);
391 PatWild | PatWildMulti => { true }
392 PatIdent(_, _, _) => {
393 let def_map = cx.tcx.def_map.borrow();
394 match def_map.get().find(&pat.id) {
395 Some(&DefVariant(_, _, _)) | Some(&DefStatic(..)) => { false }
403 fn missing_ctor(cx: &MatchCheckCtxt,
407 match ty::get(left_ty).sty {
408 ty::ty_box(_) | ty::ty_uniq(_) | ty::ty_rptr(..) | ty::ty_tup(_) |
409 ty::ty_struct(..) => {
411 if !is_wild(cx, r[0]) { return None; }
415 ty::ty_enum(eid, _) => {
418 let r = pat_ctor_id(cx, r[0]);
420 if !found.contains(id) {
421 found.push((*id).clone());
425 let variants = ty::enum_variants(cx.tcx, eid);
426 if found.len() != (*variants).len() {
427 for v in (*variants).iter() {
428 if !found.iter().any(|x| x == &(variant(v.id))) {
429 return Some(variant(v.id));
437 let mut true_found = false;
438 let mut false_found = false;
440 match pat_ctor_id(cx, r[0]) {
442 Some(val(const_bool(true))) => true_found = true,
443 Some(val(const_bool(false))) => false_found = true,
444 _ => fail!("impossible case")
447 if true_found && false_found { None }
448 else if true_found { Some(val(const_bool(false))) }
449 else { Some(val(const_bool(true))) }
451 ty::ty_vec(_, ty::vstore_fixed(n)) => {
452 let mut missing = true;
453 let mut wrong = false;
456 PatVec(ref before, ref slice, ref after) => {
457 let count = before.len() + after.len();
458 if (count < n && slice.is_none()) || count > n {
461 if count == n || (count < n && slice.is_some()) {
468 match (wrong, missing) {
469 (true, _) => Some(vec(n)), // should be compile-time error
470 (_, true) => Some(vec(n)),
474 ty::ty_unboxed_vec(..) | ty::ty_vec(..) => {
476 // Find the lengths and slices of all vector patterns.
477 let mut vec_pat_lens = m.iter().filter_map(|r| {
479 PatVec(ref before, ref slice, ref after) => {
480 Some((before.len() + after.len(), slice.is_some()))
484 }).collect::<~[(uint, bool)]>();
486 // Sort them by length such that for patterns of the same length,
487 // those with a destructured slice come first.
488 vec_pat_lens.sort_by(|&(len1, slice1), &(len2, slice2)| {
495 vec_pat_lens.dedup();
497 let mut found_slice = false;
499 let mut missing = None;
500 for &(length, slice) in vec_pat_lens.iter() {
502 missing = Some(next);
512 // We found patterns of all lengths within <0, next), yet there was no
513 // pattern with a slice - therefore, we report vec(next) as missing.
515 missing = Some(next);
518 Some(k) => Some(vec(k)),
526 fn ctor_arity(cx: &MatchCheckCtxt, ctor: &ctor, ty: ty::t) -> uint {
527 match ty::get(ty).sty {
528 ty::ty_tup(ref fs) => fs.len(),
529 ty::ty_box(_) | ty::ty_uniq(_) | ty::ty_rptr(..) => 1u,
530 ty::ty_enum(eid, _) => {
531 let id = match *ctor { variant(id) => id,
532 _ => fail!("impossible case") };
533 match ty::enum_variants(cx.tcx, eid).iter().find(|v| v.id == id ) {
534 Some(v) => v.args.len(),
535 None => fail!("impossible case")
538 ty::ty_struct(cid, _) => ty::lookup_struct_fields(cx.tcx, cid).len(),
539 ty::ty_unboxed_vec(..) | ty::ty_vec(..) => {
550 @Pat {id: 0, node: PatWild, span: DUMMY_SP}
553 fn wild_multi() -> @Pat {
554 @Pat {id: 0, node: PatWildMulti, span: DUMMY_SP}
557 fn specialize(cx: &MatchCheckCtxt,
563 // Sad, but I can't get rid of this easily
564 let r0 = (*raw_pat(r[0])).clone();
566 Pat{id: pat_id, node: n, span: pat_span} =>
569 Some(vec::append(vec::from_elem(arity, wild()), r.tail()))
572 Some(vec::append(vec::from_elem(arity, wild_multi()), r.tail()))
574 PatIdent(_, _, _) => {
576 let def_map = cx.tcx.def_map.borrow();
577 def_map.get().find_copy(&pat_id)
580 Some(DefVariant(_, id, _)) => {
581 if variant(id) == *ctor_id {
582 Some(r.tail().to_owned())
587 Some(DefStatic(did, _)) => {
589 lookup_const_by_id(cx.tcx, did).unwrap();
590 let e_v = eval_const_expr(cx.tcx, const_expr);
591 let match_ = match *ctor_id {
593 match compare_const_vals(&e_v, v) {
594 Some(val1) => (val1 == 0),
596 cx.tcx.sess.span_err(pat_span,
597 "mismatched types between arms");
602 range(ref c_lo, ref c_hi) => {
603 let m1 = compare_const_vals(c_lo, &e_v);
604 let m2 = compare_const_vals(c_hi, &e_v);
606 (Some(val1), Some(val2)) => {
607 (val1 >= 0 && val2 <= 0)
610 cx.tcx.sess.span_err(pat_span,
611 "mismatched types between ranges");
617 _ => fail!("type error")
620 Some(r.tail().to_owned())
628 vec::from_elem(arity, wild()),
635 PatEnum(_, args) => {
637 let def_map = cx.tcx.def_map.borrow();
638 def_map.get().get_copy(&pat_id)
641 DefStatic(did, _) => {
643 lookup_const_by_id(cx.tcx, did).unwrap();
644 let e_v = eval_const_expr(cx.tcx, const_expr);
645 let match_ = match *ctor_id {
647 match compare_const_vals(&e_v, v) {
648 Some(val1) => (val1 == 0),
650 cx.tcx.sess.span_err(pat_span,
651 "mismatched types between arms");
655 range(ref c_lo, ref c_hi) => {
656 let m1 = compare_const_vals(c_lo, &e_v);
657 let m2 = compare_const_vals(c_hi, &e_v);
659 (Some(val1), Some(val2)) => (val1 >= 0 && val2 <= 0),
661 cx.tcx.sess.span_err(pat_span,
662 "mismatched types between ranges");
668 _ => fail!("type error")
671 Some(r.tail().to_owned())
676 DefVariant(_, id, _) if variant(id) == *ctor_id => {
677 let args = match args {
678 Some(args) => args.iter().map(|x| *x).collect(),
679 None => vec::from_elem(arity, wild())
681 Some(vec::append(args, r.tail()))
683 DefVariant(_, _, _) => None,
690 new_args = args.iter().map(|x| *x).collect()
692 None => new_args = vec::from_elem(arity, wild())
694 Some(vec::append(new_args, r.tail()))
699 PatStruct(_, ref pattern_fields, _) => {
700 // Is this a struct or an enum variant?
702 let def_map = cx.tcx.def_map.borrow();
703 def_map.get().get_copy(&pat_id)
706 DefVariant(_, variant_id, _) => {
707 if variant(variant_id) == *ctor_id {
708 let struct_fields = ty::lookup_struct_fields(cx.tcx, variant_id);
709 let args = struct_fields.map(|sf| {
710 match pattern_fields.iter().find(|f| f.ident.name == sf.name) {
715 Some(vec::append(args, r.tail()))
721 // Grab the class data that we care about.
724 match ty::get(left_ty).sty {
725 ty::ty_struct(cid, _) => {
728 ty::lookup_struct_fields(cx.tcx,
732 cx.tcx.sess.span_bug(
734 format!("struct pattern resolved to {}, \
736 ty_to_str(cx.tcx, left_ty)));
739 let args = class_fields.iter().map(|class_field| {
740 match pattern_fields.iter().find(|f|
741 f.ident.name == class_field.name) {
746 Some(vec::append(args, r.tail()))
751 Some(vec::append(args.iter().map(|x| *x).collect(), r.tail()))
753 PatUniq(a) | PatRegion(a) => {
754 Some(vec::append(~[a], r.tail()))
757 let e_v = eval_const_expr(cx.tcx, expr);
758 let match_ = match *ctor_id {
760 match compare_const_vals(&e_v, v) {
761 Some(val1) => val1 == 0,
763 cx.tcx.sess.span_err(pat_span,
764 "mismatched types between arms");
769 range(ref c_lo, ref c_hi) => {
770 let m1 = compare_const_vals(c_lo, &e_v);
771 let m2 = compare_const_vals(c_hi, &e_v);
773 (Some(val1), Some(val2)) => (val1 >= 0 && val2 <= 0),
775 cx.tcx.sess.span_err(pat_span,
776 "mismatched types between ranges");
782 _ => fail!("type error")
784 if match_ { Some(r.tail().to_owned()) } else { None }
786 PatRange(lo, hi) => {
787 let (c_lo, c_hi) = match *ctor_id {
788 val(ref v) => ((*v).clone(), (*v).clone()),
789 range(ref lo, ref hi) => ((*lo).clone(), (*hi).clone()),
790 single => return Some(r.tail().to_owned()),
791 _ => fail!("type error")
793 let v_lo = eval_const_expr(cx.tcx, lo);
794 let v_hi = eval_const_expr(cx.tcx, hi);
796 let m1 = compare_const_vals(&c_lo, &v_lo);
797 let m2 = compare_const_vals(&c_hi, &v_hi);
799 (Some(val1), Some(val2)) if val1 >= 0 && val2 <= 0 => {
800 Some(r.tail().to_owned())
802 (Some(_), Some(_)) => None,
804 cx.tcx.sess.span_err(pat_span,
805 "mismatched types between ranges");
810 PatVec(before, slice, after) => {
813 let num_elements = before.len() + after.len();
814 if num_elements < arity && slice.is_some() {
815 let mut result = ~[];
816 for pat in before.iter() {
817 result.push((*pat).clone());
819 for _ in iter::range(0, arity - num_elements) {
822 for pat in after.iter() {
823 result.push((*pat).clone());
825 for pat in r.tail().iter() {
826 result.push((*pat).clone());
829 } else if num_elements == arity {
830 let mut result = ~[];
831 for pat in before.iter() {
832 result.push((*pat).clone());
834 for pat in after.iter() {
835 result.push((*pat).clone());
837 for pat in r.tail().iter() {
838 result.push((*pat).clone());
852 fn default(cx: &MatchCheckCtxt, r: &[@Pat]) -> Option<~[@Pat]> {
853 if is_wild(cx, r[0]) { Some(r.tail().to_owned()) }
857 fn check_local(v: &mut CheckMatchVisitor,
861 visit::walk_local(v, loc, s);
862 if is_refutable(cx, loc.pat) {
863 cx.tcx.sess.span_err(loc.pat.span,
864 "refutable pattern in local binding");
867 // Check legality of move bindings.
868 check_legality_of_move_bindings(cx, false, [ loc.pat ]);
871 fn check_fn(v: &mut CheckMatchVisitor,
879 visit::walk_fn(v, kind, decl, body, sp, id, s);
880 for input in decl.inputs.iter() {
881 if is_refutable(cx, input.pat) {
882 cx.tcx.sess.span_err(input.pat.span,
883 "refutable pattern in function argument");
888 fn is_refutable(cx: &MatchCheckCtxt, pat: &Pat) -> bool {
890 let def_map = cx.tcx.def_map.borrow();
891 def_map.get().find_copy(&pat.id)
894 Some(DefVariant(enum_id, _, _)) => {
895 if ty::enum_variants(cx.tcx, enum_id).len() != 1u {
899 Some(DefStatic(..)) => return true,
904 PatUniq(sub) | PatRegion(sub) | PatIdent(_, _, Some(sub)) => {
905 is_refutable(cx, sub)
907 PatWild | PatWildMulti | PatIdent(_, _, None) => { false }
912 LitNil => false, // `()`
919 PatRange(_, _) => { true }
920 PatStruct(_, ref fields, _) => {
921 fields.iter().any(|f| is_refutable(cx, f.pat))
923 PatTup(ref elts) => {
924 elts.iter().any(|elt| is_refutable(cx, *elt))
926 PatEnum(_, Some(ref args)) => {
927 args.iter().any(|a| is_refutable(cx, *a))
929 PatEnum(_,_) => { false }
930 PatVec(..) => { true }
934 // Legality of move bindings checking
936 fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
940 let def_map = tcx.def_map;
941 let mut by_ref_span = None;
942 let mut any_by_move = false;
943 for pat in pats.iter() {
944 pat_bindings(def_map, *pat, |bm, id, span, _path| {
947 by_ref_span = Some(span);
950 let moves_map = cx.moves_map.borrow();
951 if moves_map.get().contains(&id) {
959 let check_move: |&Pat, Option<@Pat>| = |p, sub| {
960 // check legality of moving out of the enum
962 // x @ Foo(..) is legal, but x @ Foo(y) isn't.
963 if sub.map_or(false, |p| pat_contains_bindings(def_map, p)) {
966 "cannot bind by-move with sub-bindings");
967 } else if has_guard {
970 "cannot bind by-move into a pattern guard");
971 } else if by_ref_span.is_some() {
974 "cannot bind by-move and by-ref \
975 in the same pattern");
977 by_ref_span.unwrap(),
978 "by-ref binding occurs here");
982 if !any_by_move { return; } // pointless micro-optimization
983 for pat in pats.iter() {
985 if pat_is_binding(def_map, p) {
987 PatIdent(_, _, sub) => {
988 let moves_map = cx.moves_map.borrow();
989 if moves_map.get().contains(&p.id) {
994 cx.tcx.sess.span_bug(
996 format!("binding pattern {} is \
997 not an identifier: {:?}",