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);
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 */ }
107 let arms = arms.iter().filter_map(unguarded_pat).collect::<~[~[@Pat]]>().concat_vec();
109 cx.tcx.sess.span_err(ex.span, "non-exhaustive patterns");
111 check_exhaustive(cx, ex.span, arms);
118 // Check for unreachable patterns
119 fn check_arms(cx: &MatchCheckCtxt, arms: &[Arm]) {
121 for arm in arms.iter() {
122 for pat in arm.pats.iter() {
124 // Check that we do not match against a static NaN (#6804)
125 let pat_matches_nan: |&Pat| -> bool = |p| {
127 let def_map = cx.tcx.def_map.borrow();
128 def_map.get().find_copy(&p.id)
131 Some(DefStatic(did, false)) => {
132 let const_expr = lookup_const_by_id(cx.tcx, did).unwrap();
133 match eval_const_expr(cx.tcx, const_expr) {
134 const_float(f) if f.is_nan() => true,
143 if pat_matches_nan(p) {
144 cx.tcx.sess.span_warn(p.span, "unmatchable NaN in pattern, \
145 use the is_nan method in a guard instead");
151 match is_useful(cx, &seen, v) {
153 cx.tcx.sess.span_err(pat.span, "unreachable pattern");
157 if arm.guard.is_none() { seen.push(v); }
162 fn raw_pat(p: @Pat) -> @Pat {
164 PatIdent(_, _, Some(s)) => { raw_pat(s) }
169 fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, pats: ~[@Pat]) {
170 assert!((!pats.is_empty()));
171 let ext = match is_useful(cx, &pats.map(|p| ~[*p]), [wild()]) {
173 // This is good, wildcard pattern isn't reachable
177 useful(ty, ref ctor) => {
178 match ty::get(ty).sty {
181 val(const_bool(true)) => Some(~"true"),
182 val(const_bool(false)) => Some(~"false"),
186 ty::ty_enum(id, _) => {
187 let vid = match *ctor {
189 _ => fail!("check_exhaustive: non-variant ctor"),
191 let variants = ty::enum_variants(cx.tcx, id);
193 match variants.iter().find(|v| v.id == vid) {
194 Some(v) => Some(token::get_ident(v.name).get().to_str()),
196 fail!("check_exhaustive: bad variant in ctor")
200 ty::ty_unboxed_vec(..) | ty::ty_vec(..) => {
202 vec(n) => Some(format!("vectors of length {}", n)),
210 let msg = ~"non-exhaustive patterns" + match ext {
211 Some(ref s) => format!(": {} not covered", *s),
214 cx.tcx.sess.span_err(sp, msg);
217 type matrix = ~[~[@Pat]];
226 #[deriving(Clone, Eq)]
231 range(const_val, const_val),
235 // Algorithm from http://moscova.inria.fr/~maranget/papers/warn/index.html
237 // Whether a vector `v` of patterns is 'useful' in relation to a set of such
238 // vectors `m` is defined as there being a set of inputs that will match `v`
239 // but not any of the sets in `m`.
241 // This is used both for reachability checking (if a pattern isn't useful in
242 // relation to preceding patterns, it is not reachable) and exhaustiveness
243 // checking (if a wildcard pattern is useful in relation to a matrix, the
244 // matrix isn't exhaustive).
246 // Note: is_useful doesn't work on empty types, as the paper notes.
247 // So it assumes that v is non-empty.
248 fn is_useful(cx: &MatchCheckCtxt, m: &matrix, v: &[@Pat]) -> useful {
249 if m.len() == 0u { return useful_; }
250 if m[0].len() == 0u { return not_useful; }
251 let real_pat = match m.iter().find(|r| r[0].id != 0) {
252 Some(r) => r[0], None => v[0]
254 let left_ty = if real_pat.id == 0 { ty::mk_nil() }
255 else { ty::node_id_to_type(cx.tcx, real_pat.id) };
257 match pat_ctor_id(cx, v[0]) {
259 match missing_ctor(cx, m, left_ty) {
261 match ty::get(left_ty).sty {
263 match is_useful_specialized(cx, m, v,
264 val(const_bool(true)),
267 is_useful_specialized(cx, m, v,
268 val(const_bool(false)),
271 ref u => (*u).clone(),
274 ty::ty_enum(eid, _) => {
275 for va in (*ty::enum_variants(cx.tcx, eid)).iter() {
276 match is_useful_specialized(cx, m, v, variant(va.id),
277 va.args.len(), left_ty) {
279 ref u => return (*u).clone(),
284 ty::ty_vec(_, ty::vstore_fixed(n)) => {
285 is_useful_specialized(cx, m, v, vec(n), n, left_ty)
287 ty::ty_unboxed_vec(..) | ty::ty_vec(..) => {
288 let max_len = m.rev_iter().fold(0, |max_len, r| {
290 PatVec(ref before, _, ref after) => {
291 cmp::max(before.len() + after.len(), max_len)
296 for n in iter::range(0u, max_len + 1) {
297 match is_useful_specialized(cx, m, v, vec(n), n, left_ty) {
299 ref u => return (*u).clone(),
305 let arity = ctor_arity(cx, &single, left_ty);
306 is_useful_specialized(cx, m, v, single, arity, left_ty)
312 &m.iter().filter_map(|r| default(cx, *r)).collect::<matrix>(),
314 useful_ => useful(left_ty, (*ctor).clone()),
315 ref u => (*u).clone(),
320 Some(ref v0_ctor) => {
321 let arity = ctor_arity(cx, v0_ctor, left_ty);
322 is_useful_specialized(cx, m, v, (*v0_ctor).clone(), arity, left_ty)
327 fn is_useful_specialized(cx: &MatchCheckCtxt,
334 let ms = m.iter().filter_map(|r| specialize(cx, *r, &ctor, arity, lty)).collect::<matrix>();
335 let could_be_useful = is_useful(
336 cx, &ms, specialize(cx, v, &ctor, arity, lty).unwrap());
337 match could_be_useful {
338 useful_ => useful(lty, ctor),
339 ref u => (*u).clone(),
343 fn pat_ctor_id(cx: &MatchCheckCtxt, p: @Pat) -> Option<ctor> {
344 let pat = raw_pat(p);
346 PatWild | PatWildMulti => { None }
347 PatIdent(_, _, _) | PatEnum(_, _) => {
349 let def_map = cx.tcx.def_map.borrow();
350 def_map.get().find_copy(&pat.id)
353 Some(DefVariant(_, id, _)) => Some(variant(id)),
354 Some(DefStatic(did, false)) => {
355 let const_expr = lookup_const_by_id(cx.tcx, did).unwrap();
356 Some(val(eval_const_expr(cx.tcx, const_expr)))
361 PatLit(expr) => { Some(val(eval_const_expr(cx.tcx, expr))) }
362 PatRange(lo, hi) => {
363 Some(range(eval_const_expr(cx.tcx, lo), eval_const_expr(cx.tcx, hi)))
366 let def_map = cx.tcx.def_map.borrow();
367 match def_map.get().find(&pat.id) {
368 Some(&DefVariant(_, id, _)) => Some(variant(id)),
372 PatUniq(_) | PatTup(_) | PatRegion(..) => {
375 PatVec(ref before, slice, ref after) => {
378 None => Some(vec(before.len() + after.len()))
384 fn is_wild(cx: &MatchCheckCtxt, p: @Pat) -> bool {
385 let pat = raw_pat(p);
387 PatWild | PatWildMulti => { true }
388 PatIdent(_, _, _) => {
389 let def_map = cx.tcx.def_map.borrow();
390 match def_map.get().find(&pat.id) {
391 Some(&DefVariant(_, _, _)) | Some(&DefStatic(..)) => { false }
399 fn missing_ctor(cx: &MatchCheckCtxt,
403 match ty::get(left_ty).sty {
404 ty::ty_box(_) | ty::ty_uniq(_) | ty::ty_rptr(..) | ty::ty_tup(_) |
405 ty::ty_struct(..) => {
407 if !is_wild(cx, r[0]) { return None; }
411 ty::ty_enum(eid, _) => {
414 let r = pat_ctor_id(cx, r[0]);
416 if !found.contains(id) {
417 found.push((*id).clone());
421 let variants = ty::enum_variants(cx.tcx, eid);
422 if found.len() != (*variants).len() {
423 for v in (*variants).iter() {
424 if !found.iter().any(|x| x == &(variant(v.id))) {
425 return Some(variant(v.id));
433 let mut true_found = false;
434 let mut false_found = false;
436 match pat_ctor_id(cx, r[0]) {
438 Some(val(const_bool(true))) => true_found = true,
439 Some(val(const_bool(false))) => false_found = true,
440 _ => fail!("impossible case")
443 if true_found && false_found { None }
444 else if true_found { Some(val(const_bool(false))) }
445 else { Some(val(const_bool(true))) }
447 ty::ty_vec(_, ty::vstore_fixed(n)) => {
448 let mut missing = true;
449 let mut wrong = false;
452 PatVec(ref before, ref slice, ref after) => {
453 let count = before.len() + after.len();
454 if (count < n && slice.is_none()) || count > n {
457 if count == n || (count < n && slice.is_some()) {
464 match (wrong, missing) {
465 (true, _) => Some(vec(n)), // should be compile-time error
466 (_, true) => Some(vec(n)),
470 ty::ty_unboxed_vec(..) | ty::ty_vec(..) => {
472 // Find the lengths and slices of all vector patterns.
473 let mut vec_pat_lens = m.iter().filter_map(|r| {
475 PatVec(ref before, ref slice, ref after) => {
476 Some((before.len() + after.len(), slice.is_some()))
480 }).collect::<~[(uint, bool)]>();
482 // Sort them by length such that for patterns of the same length,
483 // those with a destructured slice come first.
484 vec_pat_lens.sort_by(|&(len1, slice1), &(len2, slice2)| {
491 vec_pat_lens.dedup();
493 let mut found_slice = false;
495 let mut missing = None;
496 for &(length, slice) in vec_pat_lens.iter() {
498 missing = Some(next);
508 // We found patterns of all lengths within <0, next), yet there was no
509 // pattern with a slice - therefore, we report vec(next) as missing.
511 missing = Some(next);
514 Some(k) => Some(vec(k)),
522 fn ctor_arity(cx: &MatchCheckCtxt, ctor: &ctor, ty: ty::t) -> uint {
523 match ty::get(ty).sty {
524 ty::ty_tup(ref fs) => fs.len(),
525 ty::ty_box(_) | ty::ty_uniq(_) | ty::ty_rptr(..) => 1u,
526 ty::ty_enum(eid, _) => {
527 let id = match *ctor { variant(id) => id,
528 _ => fail!("impossible case") };
529 match ty::enum_variants(cx.tcx, eid).iter().find(|v| v.id == id ) {
530 Some(v) => v.args.len(),
531 None => fail!("impossible case")
534 ty::ty_struct(cid, _) => ty::lookup_struct_fields(cx.tcx, cid).len(),
535 ty::ty_unboxed_vec(..) | ty::ty_vec(..) => {
546 @Pat {id: 0, node: PatWild, span: DUMMY_SP}
549 fn wild_multi() -> @Pat {
550 @Pat {id: 0, node: PatWildMulti, span: DUMMY_SP}
553 fn specialize(cx: &MatchCheckCtxt,
559 // Sad, but I can't get rid of this easily
560 let r0 = (*raw_pat(r[0])).clone();
562 Pat{id: pat_id, node: n, span: pat_span} =>
565 Some(vec::append(vec::from_elem(arity, wild()), r.tail()))
568 Some(vec::append(vec::from_elem(arity, wild_multi()), r.tail()))
570 PatIdent(_, _, _) => {
572 let def_map = cx.tcx.def_map.borrow();
573 def_map.get().find_copy(&pat_id)
576 Some(DefVariant(_, id, _)) => {
577 if variant(id) == *ctor_id {
578 Some(r.tail().to_owned())
583 Some(DefStatic(did, _)) => {
585 lookup_const_by_id(cx.tcx, did).unwrap();
586 let e_v = eval_const_expr(cx.tcx, const_expr);
587 let match_ = match *ctor_id {
589 match compare_const_vals(&e_v, v) {
590 Some(val1) => (val1 == 0),
592 cx.tcx.sess.span_err(pat_span,
593 "mismatched types between arms");
598 range(ref c_lo, ref c_hi) => {
599 let m1 = compare_const_vals(c_lo, &e_v);
600 let m2 = compare_const_vals(c_hi, &e_v);
602 (Some(val1), Some(val2)) => {
603 (val1 >= 0 && val2 <= 0)
606 cx.tcx.sess.span_err(pat_span,
607 "mismatched types between ranges");
613 _ => fail!("type error")
616 Some(r.tail().to_owned())
624 vec::from_elem(arity, wild()),
631 PatEnum(_, args) => {
633 let def_map = cx.tcx.def_map.borrow();
634 def_map.get().get_copy(&pat_id)
637 DefStatic(did, _) => {
639 lookup_const_by_id(cx.tcx, did).unwrap();
640 let e_v = eval_const_expr(cx.tcx, const_expr);
641 let match_ = match *ctor_id {
643 match compare_const_vals(&e_v, v) {
644 Some(val1) => (val1 == 0),
646 cx.tcx.sess.span_err(pat_span,
647 "mismatched types between arms");
651 range(ref c_lo, ref c_hi) => {
652 let m1 = compare_const_vals(c_lo, &e_v);
653 let m2 = compare_const_vals(c_hi, &e_v);
655 (Some(val1), Some(val2)) => (val1 >= 0 && val2 <= 0),
657 cx.tcx.sess.span_err(pat_span,
658 "mismatched types between ranges");
664 _ => fail!("type error")
667 Some(r.tail().to_owned())
672 DefVariant(_, id, _) if variant(id) == *ctor_id => {
673 let args = match args {
675 None => vec::from_elem(arity, wild())
677 Some(vec::append(args, r.tail()))
679 DefVariant(_, _, _) => None,
685 Some(args) => new_args = args,
686 None => new_args = vec::from_elem(arity, wild())
688 Some(vec::append(new_args, r.tail()))
693 PatStruct(_, ref pattern_fields, _) => {
694 // Is this a struct or an enum variant?
696 let def_map = cx.tcx.def_map.borrow();
697 def_map.get().get_copy(&pat_id)
700 DefVariant(_, variant_id, _) => {
701 if variant(variant_id) == *ctor_id {
702 let struct_fields = ty::lookup_struct_fields(cx.tcx, variant_id);
703 let args = struct_fields.map(|sf| {
704 match pattern_fields.iter().find(|f| f.ident.name == sf.name) {
709 Some(vec::append(args, r.tail()))
715 // Grab the class data that we care about.
718 match ty::get(left_ty).sty {
719 ty::ty_struct(cid, _) => {
722 ty::lookup_struct_fields(cx.tcx,
726 cx.tcx.sess.span_bug(
728 format!("struct pattern resolved to {}, \
730 ty_to_str(cx.tcx, left_ty)));
733 let args = class_fields.iter().map(|class_field| {
734 match pattern_fields.iter().find(|f|
735 f.ident.name == class_field.name) {
740 Some(vec::append(args, r.tail()))
744 PatTup(args) => Some(vec::append(args, r.tail())),
745 PatUniq(a) | PatRegion(a) => {
746 Some(vec::append(~[a], r.tail()))
749 let e_v = eval_const_expr(cx.tcx, expr);
750 let match_ = match *ctor_id {
752 match compare_const_vals(&e_v, v) {
753 Some(val1) => val1 == 0,
755 cx.tcx.sess.span_err(pat_span,
756 "mismatched types between arms");
761 range(ref c_lo, ref c_hi) => {
762 let m1 = compare_const_vals(c_lo, &e_v);
763 let m2 = compare_const_vals(c_hi, &e_v);
765 (Some(val1), Some(val2)) => (val1 >= 0 && val2 <= 0),
767 cx.tcx.sess.span_err(pat_span,
768 "mismatched types between ranges");
774 _ => fail!("type error")
776 if match_ { Some(r.tail().to_owned()) } else { None }
778 PatRange(lo, hi) => {
779 let (c_lo, c_hi) = match *ctor_id {
780 val(ref v) => ((*v).clone(), (*v).clone()),
781 range(ref lo, ref hi) => ((*lo).clone(), (*hi).clone()),
782 single => return Some(r.tail().to_owned()),
783 _ => fail!("type error")
785 let v_lo = eval_const_expr(cx.tcx, lo);
786 let v_hi = eval_const_expr(cx.tcx, hi);
788 let m1 = compare_const_vals(&c_lo, &v_lo);
789 let m2 = compare_const_vals(&c_hi, &v_hi);
791 (Some(val1), Some(val2)) if val1 >= 0 && val2 <= 0 => {
792 Some(r.tail().to_owned())
794 (Some(_), Some(_)) => None,
796 cx.tcx.sess.span_err(pat_span,
797 "mismatched types between ranges");
802 PatVec(before, slice, after) => {
805 let num_elements = before.len() + after.len();
806 if num_elements < arity && slice.is_some() {
811 arity - num_elements, wild()),
816 } else if num_elements == arity {
818 vec::append(before, after),
832 fn default(cx: &MatchCheckCtxt, r: &[@Pat]) -> Option<~[@Pat]> {
833 if is_wild(cx, r[0]) { Some(r.tail().to_owned()) }
837 fn check_local(v: &mut CheckMatchVisitor,
841 visit::walk_local(v, loc, s);
842 if is_refutable(cx, loc.pat) {
843 cx.tcx.sess.span_err(loc.pat.span,
844 "refutable pattern in local binding");
847 // Check legality of move bindings.
848 check_legality_of_move_bindings(cx, false, [ loc.pat ]);
851 fn check_fn(v: &mut CheckMatchVisitor,
859 visit::walk_fn(v, kind, decl, body, sp, id, s);
860 for input in decl.inputs.iter() {
861 if is_refutable(cx, input.pat) {
862 cx.tcx.sess.span_err(input.pat.span,
863 "refutable pattern in function argument");
868 fn is_refutable(cx: &MatchCheckCtxt, pat: &Pat) -> bool {
870 let def_map = cx.tcx.def_map.borrow();
871 def_map.get().find_copy(&pat.id)
874 Some(DefVariant(enum_id, _, _)) => {
875 if ty::enum_variants(cx.tcx, enum_id).len() != 1u {
879 Some(DefStatic(..)) => return true,
884 PatUniq(sub) | PatRegion(sub) | PatIdent(_, _, Some(sub)) => {
885 is_refutable(cx, sub)
887 PatWild | PatWildMulti | PatIdent(_, _, None) => { false }
892 LitNil => false, // `()`
899 PatRange(_, _) => { true }
900 PatStruct(_, ref fields, _) => {
901 fields.iter().any(|f| is_refutable(cx, f.pat))
903 PatTup(ref elts) => {
904 elts.iter().any(|elt| is_refutable(cx, *elt))
906 PatEnum(_, Some(ref args)) => {
907 args.iter().any(|a| is_refutable(cx, *a))
909 PatEnum(_,_) => { false }
910 PatVec(..) => { true }
914 // Legality of move bindings checking
916 fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
920 let def_map = tcx.def_map;
921 let mut by_ref_span = None;
922 let mut any_by_move = false;
923 for pat in pats.iter() {
924 pat_bindings(def_map, *pat, |bm, id, span, _path| {
927 by_ref_span = Some(span);
930 let moves_map = cx.moves_map.borrow();
931 if moves_map.get().contains(&id) {
939 let check_move: |&Pat, Option<@Pat>| = |p, sub| {
940 // check legality of moving out of the enum
942 // x @ Foo(..) is legal, but x @ Foo(y) isn't.
943 if sub.map_or(false, |p| pat_contains_bindings(def_map, p)) {
946 "cannot bind by-move with sub-bindings");
947 } else if has_guard {
950 "cannot bind by-move into a pattern guard");
951 } else if by_ref_span.is_some() {
954 "cannot bind by-move and by-ref \
955 in the same pattern");
957 by_ref_span.unwrap(),
958 "by-ref binding occurs here");
962 if !any_by_move { return; } // pointless micro-optimization
963 for pat in pats.iter() {
965 if pat_is_binding(def_map, p) {
967 PatIdent(_, _, sub) => {
968 let moves_map = cx.moves_map.borrow();
969 if moves_map.get().contains(&p.id) {
974 cx.tcx.sess.span_bug(
976 format!("binding pattern {} is \
977 not an identifier: {:?}",