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 util::ppaux::ty_to_str;
23 use syntax::ast_util::{is_unguarded, walk_pat};
24 use syntax::codemap::{DUMMY_SP, Span};
25 use syntax::parse::token;
27 use syntax::visit::{Visitor, FnKind};
29 struct MatchCheckCtxt<'a> {
33 impl<'a> Visitor<()> for MatchCheckCtxt<'a> {
34 fn visit_expr(&mut self, ex: &Expr, _: ()) {
37 fn visit_local(&mut self, l: &Local, _: ()) {
40 fn visit_fn(&mut self, fk: &FnKind, fd: &FnDecl, b: &Block, s: Span, _: NodeId, _: ()) {
41 check_fn(self, fk, fd, b, s);
45 pub fn check_crate(tcx: &ty::ctxt,
47 let mut cx = MatchCheckCtxt {
51 visit::walk_crate(&mut cx, krate, ());
53 tcx.sess.abort_if_errors();
56 fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) {
57 visit::walk_expr(cx, ex, ());
59 ExprMatch(scrut, ref arms) => {
60 // First, check legality of move bindings.
61 for arm in arms.iter() {
62 check_legality_of_move_bindings(cx,
67 check_arms(cx, arms.as_slice());
68 /* Check for exhaustiveness */
69 // Check for empty enum, because is_useful only works on inhabited
71 let pat_ty = node_id_to_type(cx.tcx, scrut.id);
72 if (*arms).is_empty() {
73 if !type_is_empty(cx.tcx, pat_ty) {
74 // We know the type is inhabited, so this must be wrong
75 cx.tcx.sess.span_err(ex.span, format!("non-exhaustive patterns: \
76 type {} is non-empty",
77 ty_to_str(cx.tcx, pat_ty)).as_slice());
79 // If the type *is* empty, it's vacuously exhaustive
84 .filter(|&arm| is_unguarded(arm))
85 .flat_map(|arm| arm.pats.iter())
86 .map(|pat| vec!(pat.clone()))
88 check_exhaustive(cx, ex.span, &m);
94 // Check for unreachable patterns
95 fn check_arms(cx: &MatchCheckCtxt, arms: &[Arm]) {
96 let mut seen = Vec::new();
97 for arm in arms.iter() {
98 for pat in arm.pats.iter() {
100 // Check that we do not match against a static NaN (#6804)
101 let pat_matches_nan: |&Pat| -> bool = |p| {
102 let opt_def = cx.tcx.def_map.borrow().find_copy(&p.id);
104 Some(DefStatic(did, false)) => {
105 let const_expr = lookup_const_by_id(cx.tcx, did).unwrap();
106 match eval_const_expr(cx.tcx, const_expr) {
107 const_float(f) if f.is_nan() => true,
116 if pat_matches_nan(p) {
117 cx.tcx.sess.span_warn(p.span, "unmatchable NaN in pattern, \
118 use the is_nan method in a guard instead");
124 match is_useful(cx, &seen, v.as_slice()) {
126 cx.tcx.sess.span_err(pat.span, "unreachable pattern");
130 if arm.guard.is_none() { seen.push(v); }
135 fn raw_pat(p: @Pat) -> @Pat {
137 PatIdent(_, _, Some(s)) => { raw_pat(s) }
142 fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, m: &matrix) {
143 let ext = match is_useful(cx, m, [wild()]) {
145 // This is good, wildcard pattern isn't reachable
149 useful(ty, ref ctor) => {
150 match ty::get(ty).sty {
153 val(const_bool(true)) => Some("true".to_string()),
154 val(const_bool(false)) => Some("false".to_string()),
158 ty::ty_enum(id, _) => {
159 let vid = match *ctor {
161 _ => fail!("check_exhaustive: non-variant ctor"),
163 let variants = ty::enum_variants(cx.tcx, id);
165 match variants.iter().find(|v| v.id == vid) {
167 Some(token::get_ident(v.name).get()
172 fail!("check_exhaustive: bad variant in ctor")
176 ty::ty_vec(..) | ty::ty_rptr(..) => {
179 Some(format!("vectors of length {}", n))
188 let msg = format!("non-exhaustive patterns{}", match ext {
189 Some(ref s) => format!(": {} not covered", *s),
190 None => "".to_string()
192 cx.tcx.sess.span_err(sp, msg.as_slice());
195 type matrix = Vec<Vec<@Pat> > ;
204 #[deriving(Clone, PartialEq)]
209 range(const_val, const_val),
213 // Algorithm from http://moscova.inria.fr/~maranget/papers/warn/index.html
215 // Whether a vector `v` of patterns is 'useful' in relation to a set of such
216 // vectors `m` is defined as there being a set of inputs that will match `v`
217 // but not any of the sets in `m`.
219 // This is used both for reachability checking (if a pattern isn't useful in
220 // relation to preceding patterns, it is not reachable) and exhaustiveness
221 // checking (if a wildcard pattern is useful in relation to a matrix, the
222 // matrix isn't exhaustive).
224 // Note: is_useful doesn't work on empty types, as the paper notes.
225 // So it assumes that v is non-empty.
226 fn is_useful(cx: &MatchCheckCtxt, m: &matrix, v: &[@Pat]) -> useful {
230 if m.get(0).len() == 0u {
233 let real_pat = match m.iter().find(|r| r.get(0).id != 0) {
235 match r.get(0).node {
236 // An arm of the form `ref x @ sub_pat` has type
237 // `sub_pat`, not `&sub_pat` as `x` itself does.
238 PatIdent(BindByRef(_), _, Some(sub)) => sub,
242 None if v.len() == 0 => return not_useful,
245 let left_ty = if real_pat.id == 0 { ty::mk_nil() }
246 else { ty::node_id_to_type(cx.tcx, real_pat.id) };
248 match pat_ctor_id(cx, v[0]) {
250 match missing_ctor(cx, m, left_ty) {
252 match ty::get(left_ty).sty {
254 match is_useful_specialized(cx, m, v,
255 val(const_bool(true)),
258 is_useful_specialized(cx, m, v,
259 val(const_bool(false)),
265 ty::ty_enum(eid, _) => {
266 for va in (*ty::enum_variants(cx.tcx, eid)).iter() {
267 match is_useful_specialized(cx, m, v, variant(va.id),
268 va.args.len(), left_ty) {
275 ty::ty_vec(_, Some(n)) => {
276 is_useful_specialized(cx, m, v, vec(n), n, left_ty)
278 ty::ty_vec(..) => fail!("impossible case"),
279 ty::ty_rptr(_, ty::mt{ty: ty, ..}) | ty::ty_uniq(ty) => match ty::get(ty).sty {
280 ty::ty_vec(_, None) => {
281 let max_len = m.iter().rev().fold(0, |max_len, r| {
282 match r.get(0).node {
283 PatVec(ref before, _, ref after) => {
284 cmp::max(before.len() + after.len(), max_len)
289 for n in iter::range(0u, max_len + 1) {
290 match is_useful_specialized(cx, m, v, vec(n), n, left_ty) {
298 let arity = ctor_arity(cx, &single, left_ty);
299 is_useful_specialized(cx, m, v, single, arity, left_ty)
303 let arity = ctor_arity(cx, &single, left_ty);
304 is_useful_specialized(cx, m, v, single, arity, left_ty)
310 &m.iter().filter_map(|r| {
311 default(cx, r.as_slice())
312 }).collect::<matrix>(),
314 useful_ => useful(left_ty, ctor),
321 let arity = ctor_arity(cx, &v0_ctor, left_ty);
322 is_useful_specialized(cx, m, v, v0_ctor, arity, left_ty)
327 fn is_useful_specialized(cx: &MatchCheckCtxt,
334 let ms = m.iter().filter_map(|r| {
335 specialize(cx, r.as_slice(), &ctor, arity, lty)
336 }).collect::<matrix>();
337 let could_be_useful = match specialize(cx, v, &ctor, arity, lty) {
338 Some(v) => is_useful(cx, &ms, v.as_slice()),
339 None => return not_useful,
341 match could_be_useful {
342 useful_ => useful(lty, ctor),
347 fn pat_ctor_id(cx: &MatchCheckCtxt, p: @Pat) -> Option<ctor> {
348 let pat = raw_pat(p);
350 PatWild | PatWildMulti => { None }
351 PatIdent(_, _, _) | PatEnum(_, _) => {
352 let opt_def = cx.tcx.def_map.borrow().find_copy(&pat.id);
354 Some(DefVariant(_, id, _)) => Some(variant(id)),
355 Some(DefStatic(did, false)) => {
356 let const_expr = lookup_const_by_id(cx.tcx, did).unwrap();
357 Some(val(eval_const_expr(cx.tcx, const_expr)))
362 PatLit(expr) => { Some(val(eval_const_expr(cx.tcx, expr))) }
363 PatRange(lo, hi) => {
364 Some(range(eval_const_expr(cx.tcx, lo), eval_const_expr(cx.tcx, hi)))
367 match cx.tcx.def_map.borrow().find(&pat.id) {
368 Some(&DefVariant(_, id, _)) => Some(variant(id)),
372 PatBox(_) | PatTup(_) | PatRegion(..) => {
375 PatVec(ref before, slice, ref after) => {
378 None => Some(vec(before.len() + after.len()))
381 PatMac(_) => cx.tcx.sess.bug("unexpanded macro"),
385 fn is_wild(cx: &MatchCheckCtxt, p: @Pat) -> bool {
386 let pat = raw_pat(p);
388 PatWild | PatWildMulti => { true }
389 PatIdent(_, _, _) => {
390 match cx.tcx.def_map.borrow().find(&pat.id) {
391 Some(&DefVariant(_, _, _)) | Some(&DefStatic(..)) => { false }
399 fn missing_ctor(cx: &MatchCheckCtxt,
403 return match ty::get(left_ty).sty {
404 ty::ty_box(_) | ty::ty_tup(_) |
405 ty::ty_struct(..) => check_matrix_for_wild(cx, m),
406 ty::ty_uniq(ty) | ty::ty_rptr(_, ty::mt{ty: ty, ..}) => match ty::get(ty).sty {
407 ty::ty_vec(_, None) => ctor_for_slice(m),
408 ty::ty_str => Some(single),
409 _ => check_matrix_for_wild(cx, m),
411 ty::ty_enum(eid, _) => {
412 let mut found = Vec::new();
414 let r = pat_ctor_id(cx, *r.get(0));
415 for id in r.move_iter() {
416 if !found.contains(&id) {
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.get(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(_, Some(n)) => {
448 let mut missing = true;
449 let mut wrong = false;
451 match r.get(0).node {
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_vec(..) => fail!("impossible case"),
474 fn check_matrix_for_wild(cx: &MatchCheckCtxt, m: &matrix) -> Option<ctor> {
476 if !is_wild(cx, *r.get(0)) { return None; }
481 // For slice and ~[T].
482 fn ctor_for_slice(m: &matrix) -> Option<ctor> {
483 // Find the lengths and slices of all vector patterns.
484 let mut vec_pat_lens = m.iter().filter_map(|r| {
485 match r.get(0).node {
486 PatVec(ref before, ref slice, ref after) => {
487 Some((before.len() + after.len(), slice.is_some()))
491 }).collect::<Vec<(uint, bool)> >();
493 // Sort them by length such that for patterns of the same length,
494 // those with a destructured slice come first.
495 vec_pat_lens.sort_by(|&(len1, slice1), &(len2, slice2)| {
502 vec_pat_lens.dedup();
504 let mut found_slice = false;
506 let mut missing = None;
507 for &(length, slice) in vec_pat_lens.iter() {
509 missing = Some(next);
519 // We found patterns of all lengths within <0, next), yet there was no
520 // pattern with a slice - therefore, we report vec(next) as missing.
522 missing = Some(next);
525 Some(k) => Some(vec(k)),
531 fn ctor_arity(cx: &MatchCheckCtxt, ctor: &ctor, ty: ty::t) -> uint {
532 fn vec_ctor_arity(ctor: &ctor) -> uint {
539 match ty::get(ty).sty {
540 ty::ty_tup(ref fs) => fs.len(),
542 ty::ty_uniq(ty) | ty::ty_rptr(_, ty::mt{ty: ty, ..}) => match ty::get(ty).sty {
543 ty::ty_vec(_, None) => vec_ctor_arity(ctor),
546 ty::ty_enum(eid, _) => {
547 let id = match *ctor {
549 _ => fail!("impossible case")
551 match ty::enum_variants(cx.tcx, eid).iter().find(|v| v.id == id ) {
552 Some(v) => v.args.len(),
553 None => fail!("impossible case")
556 ty::ty_struct(cid, _) => ty::lookup_struct_fields(cx.tcx, cid).len(),
557 ty::ty_vec(_, Some(_)) => vec_ctor_arity(ctor),
563 @Pat {id: 0, node: PatWild, span: DUMMY_SP}
566 fn wild_multi() -> @Pat {
567 @Pat {id: 0, node: PatWildMulti, span: DUMMY_SP}
570 fn specialize(cx: &MatchCheckCtxt,
575 -> Option<Vec<@Pat> > {
576 let &Pat{id: ref pat_id, node: ref n, span: ref pat_span} = &(*raw_pat(r[0]));
577 let head: Option<Vec<@Pat>> = match n {
579 Some(Vec::from_elem(arity, wild()))
582 Some(Vec::from_elem(arity, wild_multi()))
584 &PatIdent(_, _, _) => {
585 let opt_def = cx.tcx.def_map.borrow().find_copy(pat_id);
587 Some(DefVariant(_, id, _)) => {
588 if variant(id) == *ctor_id {
594 Some(DefStatic(did, _)) => {
596 lookup_const_by_id(cx.tcx, did).unwrap();
597 let e_v = eval_const_expr(cx.tcx, const_expr);
598 let match_ = match *ctor_id {
600 match compare_const_vals(&e_v, v) {
601 Some(val1) => (val1 == 0),
603 cx.tcx.sess.span_err(*pat_span,
604 "mismatched types between arms");
609 range(ref c_lo, ref c_hi) => {
610 let m1 = compare_const_vals(c_lo, &e_v);
611 let m2 = compare_const_vals(c_hi, &e_v);
613 (Some(val1), Some(val2)) => {
614 (val1 >= 0 && val2 <= 0)
617 cx.tcx.sess.span_err(*pat_span,
618 "mismatched types between ranges");
624 _ => fail!("type error")
633 Some(Vec::from_elem(arity, wild()))
637 &PatEnum(_, ref args) => {
638 let def = cx.tcx.def_map.borrow().get_copy(pat_id);
640 DefStatic(did, _) => {
642 lookup_const_by_id(cx.tcx, did).unwrap();
643 let e_v = eval_const_expr(cx.tcx, const_expr);
644 let match_ = match *ctor_id {
646 match compare_const_vals(&e_v, v) {
647 Some(val1) => (val1 == 0),
649 cx.tcx.sess.span_err(*pat_span,
650 "mismatched types between arms");
654 range(ref c_lo, ref c_hi) => {
655 let m1 = compare_const_vals(c_lo, &e_v);
656 let m2 = compare_const_vals(c_hi, &e_v);
658 (Some(val1), Some(val2)) => (val1 >= 0 && val2 <= 0),
660 cx.tcx.sess.span_err(*pat_span,
661 "mismatched types between ranges");
667 _ => fail!("type error")
675 DefVariant(_, id, _) if variant(id) != *ctor_id => None,
676 DefVariant(..) | DefFn(..) | DefStruct(..) => {
678 &Some(ref args) => args.clone(),
679 &None => Vec::from_elem(arity, wild())
685 &PatStruct(_, ref pattern_fields, _) => {
686 // Is this a struct or an enum variant?
687 let def = cx.tcx.def_map.borrow().get_copy(pat_id);
688 let class_id = match def {
689 DefVariant(_, variant_id, _) => {
690 if variant(variant_id) == *ctor_id {
697 match ty::get(left_ty).sty {
698 ty::ty_struct(cid, _) => Some(cid),
700 cx.tcx.sess.span_bug(
702 format!("struct pattern resolved to {}, \
705 left_ty)).as_slice());
710 class_id.map(|variant_id| {
711 let struct_fields = ty::lookup_struct_fields(cx.tcx, variant_id);
712 let args = struct_fields.iter().map(|sf| {
713 match pattern_fields.iter().find(|f| f.ident.name == sf.name) {
722 &PatTup(ref args) => {
723 Some(args.iter().map(|x| *x).collect())
725 &PatBox(ref inner) | &PatRegion(ref inner) => {
726 Some(vec!(inner.clone()))
728 &PatLit(ref expr) => {
729 let e_v = eval_const_expr(cx.tcx, *expr);
730 let match_ = match *ctor_id {
732 match compare_const_vals(&e_v, v) {
733 Some(val1) => val1 == 0,
735 cx.tcx.sess.span_err(*pat_span,
736 "mismatched types between arms");
741 range(ref c_lo, ref c_hi) => {
742 let m1 = compare_const_vals(c_lo, &e_v);
743 let m2 = compare_const_vals(c_hi, &e_v);
745 (Some(val1), Some(val2)) => (val1 >= 0 && val2 <= 0),
747 cx.tcx.sess.span_err(*pat_span,
748 "mismatched types between ranges");
754 _ => fail!("type error")
762 &PatRange(lo, hi) => {
763 let (c_lo, c_hi) = match *ctor_id {
764 val(ref v) => (v, v),
765 range(ref lo, ref hi) => (lo, hi),
766 single => return Some(vec!()),
767 _ => fail!("type error")
769 let v_lo = eval_const_expr(cx.tcx, lo);
770 let v_hi = eval_const_expr(cx.tcx, hi);
772 let m1 = compare_const_vals(c_lo, &v_lo);
773 let m2 = compare_const_vals(c_hi, &v_hi);
775 (Some(val1), Some(val2)) if val1 >= 0 && val2 <= 0 => {
778 (Some(_), Some(_)) => None,
780 cx.tcx.sess.span_err(*pat_span,
781 "mismatched types between ranges");
786 &PatVec(ref before, ref slice, ref after) => {
789 let num_elements = before.len() + after.len();
790 if num_elements < arity && slice.is_some() {
791 let mut result = Vec::new();
792 result.push_all(before.as_slice());
793 result.grow_fn(arity - num_elements, |_| wild());
794 result.push_all(after.as_slice());
796 } else if num_elements == arity {
797 let mut result = Vec::new();
798 result.push_all(before.as_slice());
799 result.push_all(after.as_slice());
809 cx.tcx.sess.span_err(*pat_span, "unexpanded macro");
813 head.map(|head| head.append(r.tail()))
816 fn default(cx: &MatchCheckCtxt, r: &[@Pat]) -> Option<Vec<@Pat> > {
817 if is_wild(cx, r[0]) {
818 Some(Vec::from_slice(r.tail()))
824 fn check_local(cx: &mut MatchCheckCtxt, loc: &Local) {
825 visit::walk_local(cx, loc, ());
827 let name = match loc.source {
829 LocalFor => "`for` loop"
832 let mut spans = vec![];
833 find_refutable(cx, loc.pat, &mut spans);
835 for span in spans.iter() {
836 cx.tcx.sess.span_err(*span,
837 format!("refutable pattern in {} binding", name).as_slice());
840 // Check legality of move bindings.
841 check_legality_of_move_bindings(cx, false, [ loc.pat ]);
844 fn check_fn(cx: &mut MatchCheckCtxt,
849 visit::walk_fn(cx, kind, decl, body, sp, ());
850 for input in decl.inputs.iter() {
851 let mut spans = vec![];
852 find_refutable(cx, input.pat, &mut spans);
854 for span in spans.iter() {
855 cx.tcx.sess.span_err(*span,
856 "refutable pattern in function argument");
861 fn find_refutable(cx: &MatchCheckCtxt, pat: &Pat, spans: &mut Vec<Span>) {
862 macro_rules! this_pattern {
865 spans.push(pat.span);
870 let opt_def = cx.tcx.def_map.borrow().find_copy(&pat.id);
872 Some(DefVariant(enum_id, _, _)) => {
873 if ty::enum_variants(cx.tcx, enum_id).len() != 1u {
877 Some(DefStatic(..)) => this_pattern!(),
882 PatBox(sub) | PatRegion(sub) | PatIdent(_, _, Some(sub)) => {
883 find_refutable(cx, sub, spans)
885 PatWild | PatWildMulti | PatIdent(_, _, None) => {}
891 _ => this_pattern!(),
894 _ => this_pattern!(),
897 PatRange(_, _) => { this_pattern!() }
898 PatStruct(_, ref fields, _) => {
899 for f in fields.iter() {
900 find_refutable(cx, f.pat, spans);
903 PatTup(ref elts) | PatEnum(_, Some(ref elts))=> {
904 for elt in elts.iter() {
905 find_refutable(cx, *elt, spans)
909 PatVec(..) => { this_pattern!() }
910 PatMac(_) => cx.tcx.sess.bug("unexpanded macro"),
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 for pat in pats.iter() {
923 pat_bindings(def_map, *pat, |bm, _, span, _path| {
926 by_ref_span = Some(span);
934 let check_move: |&Pat, Option<@Pat>| = |p, sub| {
935 // check legality of moving out of the enum
937 // x @ Foo(..) is legal, but x @ Foo(y) isn't.
938 if sub.map_or(false, |p| pat_contains_bindings(def_map, p)) {
941 "cannot bind by-move with sub-bindings");
942 } else if has_guard {
945 "cannot bind by-move into a pattern guard");
946 } else if by_ref_span.is_some() {
949 "cannot bind by-move and by-ref \
950 in the same pattern");
952 by_ref_span.unwrap(),
953 "by-ref binding occurs here");
957 for pat in pats.iter() {
959 if pat_is_binding(def_map, p) {
961 PatIdent(BindByValue(_), _, sub) => {
962 let pat_ty = ty::node_id_to_type(tcx, p.id);
963 if ty::type_moves_by_default(tcx, pat_ty) {
967 PatIdent(BindByRef(_), _, _) => {
970 cx.tcx.sess.span_bug(
972 format!("binding pattern {} is not an \