diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index d8f7a97ce13ff..6d8b178ba0014 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -10,23 +10,57 @@ #![allow(non_camel_case_types)] -use middle::const_eval::{compare_const_vals, lookup_const_by_id}; -use middle::const_eval::{eval_const_expr, const_val, const_bool, const_float}; +use middle::const_eval::{compare_const_vals, const_bool, const_float, const_val}; +use middle::const_eval::{eval_const_expr, lookup_const_by_id}; use middle::def::*; use middle::pat_util::*; use middle::ty::*; use middle::ty; -use util::ppaux::ty_to_str; -use std::cmp; use std::gc::{Gc, GC}; use std::iter; use syntax::ast::*; use syntax::ast_util::{is_unguarded, walk_pat}; -use syntax::codemap::{DUMMY_SP, Span}; -use syntax::parse::token; +use syntax::codemap::{Span, Spanned, DUMMY_SP}; +use syntax::owned_slice::OwnedSlice; +use syntax::print::pprust::pat_to_str; use syntax::visit; use syntax::visit::{Visitor, FnKind}; +use util::ppaux::ty_to_str; + +type Matrix = Vec>>; + +#[deriving(Clone)] +enum Usefulness { + Useful(Vec>), + NotUseful +} + +enum WitnessPreference { + ConstructWitness, + LeaveOutWitness +} + +impl Usefulness { + fn useful(self) -> Option>> { + match self { + Useful(pats) => Some(pats), + _ => None + } + } +} + +fn def_to_path(tcx: &ty::ctxt, id: DefId) -> Path { + ty::with_path(tcx, id, |mut path| Path { + global: false, + segments: path.last().map(|elem| PathSegment { + identifier: Ident::new(elem.name()), + lifetimes: vec!(), + types: OwnedSlice::empty() + }).move_iter().collect(), + span: DUMMY_SP, + }) +} struct MatchCheckCtxt<'a> { tcx: &'a ty::ctxt, @@ -58,38 +92,39 @@ pub fn check_crate(tcx: &ty::ctxt, fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) { visit::walk_expr(cx, ex, ()); match ex.node { - ExprMatch(scrut, ref arms) => { - // First, check legality of move bindings. - for arm in arms.iter() { - check_legality_of_move_bindings(cx, - arm.guard.is_some(), - arm.pats.as_slice()); - } + ExprMatch(scrut, ref arms) => { + // First, check legality of move bindings. + for arm in arms.iter() { + check_legality_of_move_bindings(cx, + arm.guard.is_some(), + arm.pats.as_slice()); + } - check_arms(cx, arms.as_slice()); - /* Check for exhaustiveness */ - // Check for empty enum, because is_useful only works on inhabited - // types. - let pat_ty = node_id_to_type(cx.tcx, scrut.id); - if (*arms).is_empty() { - if !type_is_empty(cx.tcx, pat_ty) { - // We know the type is inhabited, so this must be wrong - cx.tcx.sess.span_err(ex.span, format!("non-exhaustive patterns: \ - type {} is non-empty", - ty_to_str(cx.tcx, pat_ty)).as_slice()); - } - // If the type *is* empty, it's vacuously exhaustive - return; - } - let m: matrix = arms - .iter() - .filter(|&arm| is_unguarded(arm)) - .flat_map(|arm| arm.pats.iter()) - .map(|pat| vec!(pat.clone())) - .collect(); - check_exhaustive(cx, ex.span, &m); - } - _ => () + // Second, check for unreachable arms. + check_arms(cx, arms.as_slice()); + + // Finally, check if the whole match expression is exhaustive. + // Check for empty enum, because is_useful only works on inhabited types. + let pat_ty = node_id_to_type(cx.tcx, scrut.id); + if (*arms).is_empty() { + if !type_is_empty(cx.tcx, pat_ty) { + // We know the type is inhabited, so this must be wrong + cx.tcx.sess.span_err(ex.span, format!("non-exhaustive patterns: \ + type {} is non-empty", + ty_to_str(cx.tcx, pat_ty)).as_slice()); + } + // If the type *is* empty, it's vacuously exhaustive + return; + } + let m: Matrix = arms + .iter() + .filter(|&arm| is_unguarded(arm)) + .flat_map(|arm| arm.pats.iter()) + .map(|pat| vec!(pat.clone())) + .collect(); + check_exhaustive(cx, ex.span, &m); + }, + _ => () } } @@ -98,7 +133,6 @@ fn check_arms(cx: &MatchCheckCtxt, arms: &[Arm]) { let mut seen = Vec::new(); for arm in arms.iter() { for pat in arm.pats.iter() { - // Check that we do not match against a static NaN (#6804) let pat_matches_nan: |&Pat| -> bool = |p| { let opt_def = cx.tcx.def_map.borrow().find_copy(&p.id); @@ -123,11 +157,9 @@ fn check_arms(cx: &MatchCheckCtxt, arms: &[Arm]) { }); let v = vec!(*pat); - match is_useful(cx, &seen, v.as_slice()) { - not_useful => { - cx.tcx.sess.span_err(pat.span, "unreachable pattern"); - } - _ => () + match is_useful(cx, &seen, v.as_slice(), LeaveOutWitness) { + NotUseful => cx.tcx.sess.span_err(pat.span, "unreachable pattern"), + _ => () } if arm.guard.is_none() { seen.push(v); } } @@ -136,80 +168,163 @@ fn check_arms(cx: &MatchCheckCtxt, arms: &[Arm]) { fn raw_pat(p: Gc) -> Gc { match p.node { - PatIdent(_, _, Some(s)) => { raw_pat(s) } - _ => { p } + PatIdent(_, _, Some(s)) => { raw_pat(s) } + _ => { p } } } -fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, m: &matrix) { - let ext = match is_useful(cx, m, [wild()]) { - not_useful => { +fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, m: &Matrix) { + match is_useful(cx, m, [wild()], ConstructWitness) { + NotUseful => { // This is good, wildcard pattern isn't reachable return; } - useful_ => None, - useful(ty, ref ctor) => { + Useful(pats) => { + let witness = match pats.as_slice() { + [witness] => witness, + [] => wild(), + _ => unreachable!() + }; + let msg = format!("non-exhaustive patterns: `{0}` not covered", pat_to_str(&*witness)); + cx.tcx.sess.span_err(sp, msg.as_slice()); + } + } +} + +#[deriving(Clone, PartialEq)] +enum ctor { + single, + variant(DefId /* variant */, bool /* is_structure */), + val(const_val), + range(const_val, const_val), + vec(uint) +} + +fn const_val_to_expr(value: &const_val) -> Gc { + let node = match value { + &const_bool(b) => LitBool(b), + _ => unreachable!() + }; + box(GC) Expr { + id: 0, + node: ExprLit(box(GC) Spanned { node: node, span: DUMMY_SP }), + span: DUMMY_SP + } +} + +fn construct_witness(cx: &MatchCheckCtxt, ctor: &ctor, pats: Vec>, lty: ty::t) -> Gc { + let pat = match ty::get(lty).sty { + ty::ty_tup(_) => PatTup(pats), + + ty::ty_enum(cid, _) | ty::ty_struct(cid, _) => { + let (vid, is_structure) = match ctor { + &variant(vid, is_structure) => (vid, is_structure), + _ => (cid, true) + }; + if is_structure { + let fields = ty::lookup_struct_fields(cx.tcx, vid); + let field_pats = fields.move_iter() + .zip(pats.iter()) + .map(|(field, pat)| FieldPat { + ident: Ident::new(field.name), + pat: pat.clone() + }).collect(); + PatStruct(def_to_path(cx.tcx, vid), field_pats, false) + } else { + PatEnum(def_to_path(cx.tcx, vid), Some(pats)) + } + }, + + ty::ty_rptr(_, ty::mt { ty: ty, .. }) => { match ty::get(ty).sty { - ty::ty_bool => { - match *ctor { - val(const_bool(true)) => Some("true".to_string()), - val(const_bool(false)) => Some("false".to_string()), - _ => None - } - } - ty::ty_enum(id, _) => { - let vid = match *ctor { - variant(id) => id, - _ => fail!("check_exhaustive: non-variant ctor"), - }; - let variants = ty::enum_variants(cx.tcx, id); - - match variants.iter().find(|v| v.id == vid) { - Some(v) => { - Some(token::get_ident(v.name).get() - .to_str() - .into_string()) - } - None => { - fail!("check_exhaustive: bad variant in ctor") - } - } - } - ty::ty_vec(..) | ty::ty_rptr(..) => { - match *ctor { - vec(n) => { - Some(format!("vectors of length {}", n)) - } - _ => None - } + ty::ty_vec(_, None) => match ctor { + &vec(_) => PatVec(pats, None, vec!()), + _ => unreachable!() + }, + ty::ty_str => PatWild, + _ => { + assert_eq!(pats.len(), 1); + PatRegion(pats.get(0).clone()) } - _ => None + } + }, + + ty::ty_box(_) => { + assert_eq!(pats.len(), 1); + PatBox(pats.get(0).clone()) + }, + + _ => { + match ctor { + &vec(_) => PatVec(pats, None, vec!()), + &val(ref v) => PatLit(const_val_to_expr(v)), + _ => PatWild } } }; - let msg = format!("non-exhaustive patterns{}", match ext { - Some(ref s) => format!(": {} not covered", *s), - None => "".to_string() - }); - cx.tcx.sess.span_err(sp, msg.as_slice()); + + box(GC) Pat { + id: 0, + node: pat, + span: DUMMY_SP + } } -type matrix = Vec>>; +fn missing_constructor(cx: &MatchCheckCtxt, m: &Matrix, left_ty: ty::t) -> Option { + let used_constructors: Vec = m.iter() + .filter_map(|r| pat_ctor_id(cx, left_ty, *r.get(0))) + .collect(); -#[deriving(Clone)] -enum useful { - useful(ty::t, ctor), - useful_, - not_useful, + all_constructors(cx, m, left_ty) + .move_iter() + .find(|c| !used_constructors.contains(c)) } -#[deriving(Clone, PartialEq)] -enum ctor { - single, - variant(DefId), - val(const_val), - range(const_val, const_val), - vec(uint) +fn all_constructors(cx: &MatchCheckCtxt, m: &Matrix, left_ty: ty::t) -> Vec { + fn vec_constructors(m: &Matrix) -> Vec { + let max_vec_len = m.iter().map(|r| match r.get(0).node { + PatVec(ref before, _, ref after) => before.len() + after.len(), + _ => 0u + }).max().unwrap_or(0u); + let contains_slice = m.iter().any(|r| match r.get(0).node { + PatVec(_, ref slice, _) => slice.is_some(), + _ => false + }); + let lengths = iter::range_inclusive(0u, if contains_slice { + max_vec_len + } else { + max_vec_len + 1 + }); + lengths.map(|len| vec(len)).collect() + } + + match ty::get(left_ty).sty { + ty::ty_bool => + [true, false].iter().map(|b| val(const_bool(*b))).collect(), + + ty::ty_rptr(_, ty::mt { ty: ty, .. }) => match ty::get(ty).sty { + ty::ty_vec(_, None) => vec_constructors(m), + _ => vec!(single) + }, + + ty::ty_enum(eid, _) => + ty::enum_variants(cx.tcx, eid) + .iter() + .map(|va| variant(va.id, va.arg_names.is_some())) + .collect(), + + ty::ty_vec(_, None) => + vec_constructors(m), + + ty::ty_vec(_, Some(n)) => + vec!(vec(n)), + + ty::ty_nil if !m.iter().all(|r| is_wild(cx, *r.get(0))) => + vec!(), + + _ => + vec!(single) + } } // Algorithm from http://moscova.inria.fr/~maranget/papers/warn/index.html @@ -225,12 +340,13 @@ enum ctor { // Note: is_useful doesn't work on empty types, as the paper notes. // So it assumes that v is non-empty. -fn is_useful(cx: &MatchCheckCtxt, m: &matrix, v: &[Gc]) -> useful { +fn is_useful(cx: &MatchCheckCtxt, m: &Matrix, v: &[Gc], + witness: WitnessPreference) -> Usefulness { if m.len() == 0u { - return useful_; + return Useful(vec!()); } if m.get(0).len() == 0u { - return not_useful + return NotUseful; } let real_pat = match m.iter().find(|r| r.get(0).id != 0) { Some(r) => { @@ -241,310 +357,156 @@ fn is_useful(cx: &MatchCheckCtxt, m: &matrix, v: &[Gc]) -> useful { _ => *r.get(0) } } - None if v.len() == 0 => return not_useful, + None if v.len() == 0 => return NotUseful, None => v[0] }; - let left_ty = if real_pat.id == 0 { ty::mk_nil() } - else { ty::node_id_to_type(cx.tcx, real_pat.id) }; - - match pat_ctor_id(cx, v[0]) { - None => { - match missing_ctor(cx, m, left_ty) { - None => { - match ty::get(left_ty).sty { - ty::ty_bool => { - match is_useful_specialized(cx, m, v, - val(const_bool(true)), - 0u, left_ty){ - not_useful => { - is_useful_specialized(cx, m, v, - val(const_bool(false)), - 0u, left_ty) - } - u => u, - } - } - ty::ty_enum(eid, _) => { - for va in (*ty::enum_variants(cx.tcx, eid)).iter() { - match is_useful_specialized(cx, m, v, variant(va.id), - va.args.len(), left_ty) { - not_useful => (), - u => return u, - } - } - not_useful - } - ty::ty_vec(_, Some(n)) => { - is_useful_specialized(cx, m, v, vec(n), n, left_ty) - } - ty::ty_vec(..) => fail!("impossible case"), - ty::ty_rptr(_, ty::mt{ty: ty, ..}) | ty::ty_uniq(ty) => match ty::get(ty).sty { - ty::ty_vec(_, None) => { - let max_len = m.iter().rev().fold(0, |max_len, r| { - match r.get(0).node { - PatVec(ref before, _, ref after) => { - cmp::max(before.len() + after.len(), max_len) - } - _ => max_len - } - }); - for n in iter::range(0u, max_len + 1) { - match is_useful_specialized(cx, m, v, vec(n), n, left_ty) { - not_useful => (), - u => return u, - } - } - not_useful - } - _ => { - let arity = ctor_arity(cx, &single, left_ty); - is_useful_specialized(cx, m, v, single, arity, left_ty) - } - }, - _ => { - let arity = ctor_arity(cx, &single, left_ty); - is_useful_specialized(cx, m, v, single, arity, left_ty) - } - } - } - Some(ctor) => { - match is_useful(cx, - &m.iter().filter_map(|r| { - default(cx, r.as_slice()) - }).collect::(), - v.tail()) { - useful_ => useful(left_ty, ctor), - u => u, + let left_ty = if real_pat.id == 0 { + ty::mk_nil() + } else { + ty::pat_ty(cx.tcx, &*real_pat) + }; + + match pat_ctor_id(cx, left_ty, v[0]) { + None => match missing_constructor(cx, m, left_ty) { + None => { + all_constructors(cx, m, left_ty).move_iter().filter_map(|c| { + is_useful_specialized(cx, m, v, c.clone(), + left_ty, witness).useful().map(|pats| { + Useful(match witness { + ConstructWitness => { + let arity = constructor_arity(cx, &c, left_ty); + let subpats = { + let pat_slice = pats.as_slice(); + Vec::from_fn(arity, |i| { + pat_slice.get(i).map(|p| p.clone()) + .unwrap_or_else(|| wild()) + }) + }; + let mut result = vec!(construct_witness(cx, &c, subpats, left_ty)); + result.extend(pats.move_iter().skip(arity)); + result + } + LeaveOutWitness => vec!() + }) + }) + }).nth(0).unwrap_or(NotUseful) + }, + + Some(ctor) => { + let matrix = m.iter().filter_map(|r| default(cx, r.as_slice())).collect(); + match is_useful(cx, &matrix, v.tail(), witness) { + Useful(pats) => Useful(match witness { + ConstructWitness => { + let arity = constructor_arity(cx, &ctor, left_ty); + let wild_pats = Vec::from_elem(arity, wild()); + let enum_pat = construct_witness(cx, &ctor, wild_pats, left_ty); + (vec!(enum_pat)).append(pats.as_slice()) + } + LeaveOutWitness => vec!() + }), + result => result + } } - } - } - } - Some(v0_ctor) => { - let arity = ctor_arity(cx, &v0_ctor, left_ty); - is_useful_specialized(cx, m, v, v0_ctor, arity, left_ty) - } + }, + + Some(v0_ctor) => is_useful_specialized(cx, m, v, v0_ctor, left_ty, witness) } } -fn is_useful_specialized(cx: &MatchCheckCtxt, - m: &matrix, - v: &[Gc], - ctor: ctor, - arity: uint, - lty: ty::t) - -> useful { - let ms = m.iter().filter_map(|r| { - specialize(cx, r.as_slice(), &ctor, arity, lty) - }).collect::(); - let could_be_useful = match specialize(cx, v, &ctor, arity, lty) { - Some(v) => is_useful(cx, &ms, v.as_slice()), - None => return not_useful, - }; - match could_be_useful { - useful_ => useful(lty, ctor), - u => u, +fn is_useful_specialized(cx: &MatchCheckCtxt, m: &Matrix, v: &[Gc], + ctor: ctor, lty: ty::t, witness: WitnessPreference) -> Usefulness { + let arity = constructor_arity(cx, &ctor, lty); + let matrix = m.iter().filter_map(|r| { + specialize(cx, r.as_slice(), &ctor, arity) + }).collect(); + match specialize(cx, v, &ctor, arity) { + Some(v) => is_useful(cx, &matrix, v.as_slice(), witness), + None => NotUseful } } -fn pat_ctor_id(cx: &MatchCheckCtxt, p: Gc) -> Option { +fn pat_ctor_id(cx: &MatchCheckCtxt, left_ty: ty::t, p: Gc) -> Option { let pat = raw_pat(p); match pat.node { - PatWild | PatWildMulti => { None } - PatIdent(_, _, _) | PatEnum(_, _) => { - let opt_def = cx.tcx.def_map.borrow().find_copy(&pat.id); - match opt_def { - Some(DefVariant(_, id, _)) => Some(variant(id)), - Some(DefStatic(did, false)) => { - let const_expr = lookup_const_by_id(cx.tcx, did).unwrap(); - Some(val(eval_const_expr(cx.tcx, &*const_expr))) - } - _ => None - } - } - PatLit(ref expr) => { Some(val(eval_const_expr(cx.tcx, &**expr))) } - PatRange(ref lo, ref hi) => { - Some(range(eval_const_expr(cx.tcx, &**lo), eval_const_expr(cx.tcx, &**hi))) - } - PatStruct(..) => { - match cx.tcx.def_map.borrow().find(&pat.id) { - Some(&DefVariant(_, id, _)) => Some(variant(id)), - _ => Some(single) - } - } - PatBox(_) | PatTup(_) | PatRegion(..) => { - Some(single) - } - PatVec(ref before, slice, ref after) => { - match slice { - Some(_) => None, - None => Some(vec(before.len() + after.len())) - } - } - PatMac(_) => cx.tcx.sess.bug("unexpanded macro"), + PatIdent(..) => + match cx.tcx.def_map.borrow().find(&pat.id) { + Some(&DefStatic(did, false)) => { + let const_expr = lookup_const_by_id(cx.tcx, did).unwrap(); + Some(val(eval_const_expr(cx.tcx, &*const_expr))) + }, + Some(&DefVariant(_, id, is_structure)) => Some(variant(id, is_structure)), + _ => None + }, + PatEnum(..) => + match cx.tcx.def_map.borrow().find(&pat.id) { + Some(&DefStatic(did, false)) => { + let const_expr = lookup_const_by_id(cx.tcx, did).unwrap(); + Some(val(eval_const_expr(cx.tcx, &*const_expr))) + }, + Some(&DefVariant(_, id, is_structure)) => Some(variant(id, is_structure)), + _ => Some(single) + }, + PatStruct(..) => + match cx.tcx.def_map.borrow().find(&pat.id) { + Some(&DefVariant(_, id, is_structure)) => Some(variant(id, is_structure)), + _ => Some(single) + }, + PatLit(expr) => + Some(val(eval_const_expr(cx.tcx, &*expr))), + PatRange(lo, hi) => + Some(range(eval_const_expr(cx.tcx, &*lo), eval_const_expr(cx.tcx, &*hi))), + PatVec(ref before, _, ref after) => match ty::get(left_ty).sty { + ty::ty_vec(_, Some(n)) => + Some(vec(n)), + _ => + Some(vec(before.len() + after.len())) + }, + PatBox(_) | PatTup(_) | PatRegion(..) => + Some(single), + PatWild | PatWildMulti => + None, + PatMac(_) => + cx.tcx.sess.bug("unexpanded macro") } } fn is_wild(cx: &MatchCheckCtxt, p: Gc) -> bool { let pat = raw_pat(p); match pat.node { - PatWild | PatWildMulti => { true } - PatIdent(_, _, _) => { - match cx.tcx.def_map.borrow().find(&pat.id) { - Some(&DefVariant(_, _, _)) | Some(&DefStatic(..)) => { false } - _ => { true } - } - } - _ => { false } - } -} - -fn missing_ctor(cx: &MatchCheckCtxt, - m: &matrix, - left_ty: ty::t) - -> Option { - return match ty::get(left_ty).sty { - ty::ty_box(_) | ty::ty_tup(_) | - ty::ty_struct(..) => check_matrix_for_wild(cx, m), - ty::ty_uniq(ty) | ty::ty_rptr(_, ty::mt{ty: ty, ..}) => match ty::get(ty).sty { - ty::ty_vec(_, None) => ctor_for_slice(m), - ty::ty_str => Some(single), - _ => check_matrix_for_wild(cx, m), - }, - ty::ty_enum(eid, _) => { - let pat_ctors: Vec = m - .iter() - .filter_map(|r| pat_ctor_id(cx, *r.get(0))) - .collect(); - let variants = ty::enum_variants(cx.tcx, eid); - variants.iter().map(|v| variant(v.id)).find(|c| !pat_ctors.contains(c)) - } - ty::ty_nil => None, - ty::ty_bool => { - let mut true_found = false; - let mut false_found = false; - for r in m.iter() { - match pat_ctor_id(cx, *r.get(0)) { - None => (), - Some(val(const_bool(true))) => true_found = true, - Some(val(const_bool(false))) => false_found = true, - _ => fail!("impossible case") + PatWild | PatWildMulti => true, + PatIdent(_, _, _) => { + match cx.tcx.def_map.borrow().find(&pat.id) { + Some(&DefVariant(_, _, _)) | Some(&DefStatic(..)) => false, + _ => true } } - if true_found && false_found { None } - else if true_found { Some(val(const_bool(false))) } - else { Some(val(const_bool(true))) } - } - ty::ty_vec(_, Some(n)) => { - let mut missing = true; - let mut wrong = false; - for r in m.iter() { - match r.get(0).node { - PatVec(ref before, ref slice, ref after) => { - let count = before.len() + after.len(); - if (count < n && slice.is_none()) || count > n { - wrong = true; - } - if count == n || (count < n && slice.is_some()) { - missing = false; - } - } - _ => {} - } - } - match (wrong, missing) { - (true, _) => Some(vec(n)), // should be compile-time error - (_, true) => Some(vec(n)), - _ => None - } - } - ty::ty_vec(..) => fail!("impossible case"), - _ => Some(single) - }; - - fn check_matrix_for_wild(cx: &MatchCheckCtxt, m: &matrix) -> Option { - for r in m.iter() { - if !is_wild(cx, *r.get(0)) { return None; } - } - return Some(single); - } - - // For slice and ~[T]. - fn ctor_for_slice(m: &matrix) -> Option { - // Find the lengths and slices of all vector patterns. - let mut vec_pat_lens = m.iter().filter_map(|r| { - match r.get(0).node { - PatVec(ref before, ref slice, ref after) => { - Some((before.len() + after.len(), slice.is_some())) - } - _ => None - } - }).collect:: >(); - - // Sort them by length such that for patterns of the same length, - // those with a destructured slice come first. - vec_pat_lens.sort_by(|&(len1, slice1), &(len2, slice2)| { - if len1 == len2 { - slice2.cmp(&slice1) - } else { - len1.cmp(&len2) - } - }); - vec_pat_lens.dedup(); - - let mut found_slice = false; - let mut next = 0; - let mut missing = None; - for &(length, slice) in vec_pat_lens.iter() { - if length != next { - missing = Some(next); - break; - } - if slice { - found_slice = true; - break; - } - next += 1; - } - - // We found patterns of all lengths within <0, next), yet there was no - // pattern with a slice - therefore, we report vec(next) as missing. - if !found_slice { - missing = Some(next); - } - match missing { - Some(k) => Some(vec(k)), - None => None - } + _ => false } } -fn ctor_arity(cx: &MatchCheckCtxt, ctor: &ctor, ty: ty::t) -> uint { - fn vec_ctor_arity(ctor: &ctor) -> uint { - match *ctor { - vec(n) => n, - _ => 0u - } - } - +fn constructor_arity(cx: &MatchCheckCtxt, ctor: &ctor, ty: ty::t) -> uint { match ty::get(ty).sty { ty::ty_tup(ref fs) => fs.len(), - ty::ty_box(_) => 1u, - ty::ty_uniq(ty) | ty::ty_rptr(_, ty::mt{ty: ty, ..}) => match ty::get(ty).sty { - ty::ty_vec(_, None) => vec_ctor_arity(ctor), - _ => 1u, + ty::ty_box(_) | ty::ty_uniq(_) => 1u, + ty::ty_rptr(_, ty::mt { ty: ty, .. }) => match ty::get(ty).sty { + ty::ty_vec(_, None) => match *ctor { + vec(n) => n, + _ => 0u + }, + ty::ty_str => 0u, + _ => 1u }, ty::ty_enum(eid, _) => { - let id = match *ctor { - variant(id) => id, - _ => fail!("impossible case") - }; - match ty::enum_variants(cx.tcx, eid).iter().find(|v| v.id == id ) { - Some(v) => v.args.len(), - None => fail!("impossible case") + match *ctor { + variant(id, _) => enum_variant_with_id(cx.tcx, eid, id).args.len(), + _ => unreachable!() } } ty::ty_struct(cid, _) => ty::lookup_struct_fields(cx.tcx, cid).len(), - ty::ty_vec(_, Some(_)) => vec_ctor_arity(ctor), + ty::ty_vec(_, _) => match *ctor { + vec(n) => n, + _ => 0u + }, _ => 0u } } @@ -553,10 +515,6 @@ fn wild() -> Gc { box(GC) Pat {id: 0, node: PatWild, span: DUMMY_SP} } -fn wild_multi() -> Gc { - box(GC) Pat {id: 0, node: PatWildMulti, span: DUMMY_SP} -} - fn range_covered_by_constructor(ctor_id: &ctor, from: &const_val, to: &const_val) -> Option { let (c_from, c_to) = match *ctor_id { val(ref value) => (value, value), @@ -572,164 +530,150 @@ fn range_covered_by_constructor(ctor_id: &ctor, from: &const_val, to: &const_val } } -fn specialize(cx: &MatchCheckCtxt, - r: &[Gc], - ctor_id: &ctor, - arity: uint, - left_ty: ty::t) - -> Option>> { - let &Pat{id: ref pat_id, node: ref n, span: ref pat_span} = &(*raw_pat(r[0])); +fn specialize(cx: &MatchCheckCtxt, r: &[Gc], + ctor_id: &ctor, arity: uint) -> Option>> { + let &Pat { + id: ref pat_id, node: ref n, span: ref pat_span + } = &(*raw_pat(r[0])); let head: Option>> = match n { - &PatWild => { - Some(Vec::from_elem(arity, wild())) - } - &PatWildMulti => { - Some(Vec::from_elem(arity, wild_multi())) - } - &PatIdent(_, _, _) => { - let opt_def = cx.tcx.def_map.borrow().find_copy(pat_id); - match opt_def { - Some(DefVariant(_, id, _)) => { - if variant(id) == *ctor_id { - Some(vec!()) - } else { + &PatWild => { + Some(Vec::from_elem(arity, wild())) + } + &PatWildMulti => { + Some(Vec::from_elem(arity, wild())) + } + &PatIdent(_, _, _) => { + let opt_def = cx.tcx.def_map.borrow().find_copy(pat_id); + match opt_def { + Some(DefVariant(_, id, _)) => match *ctor_id { + variant(vid, _) if vid == id => Some(vec!()), + _ => None + }, + Some(DefStatic(did, _)) => { + let const_expr = lookup_const_by_id(cx.tcx, did).unwrap(); + let e_v = eval_const_expr(cx.tcx, &*const_expr); + match range_covered_by_constructor(ctor_id, &e_v, &e_v) { + Some(true) => Some(vec!()), + Some(false) => None, + None => { + cx.tcx.sess.span_err(*pat_span, "mismatched types between arms"); None } } - Some(DefStatic(did, _)) => { - let const_expr = lookup_const_by_id(cx.tcx, did).unwrap(); - let e_v = eval_const_expr(cx.tcx, &*const_expr); - match range_covered_by_constructor(ctor_id, &e_v, &e_v) { - Some(true) => Some(vec!()), - Some(false) => None, - None => { - cx.tcx.sess.span_err(*pat_span, "mismatched types between arms"); - None - } - } - } - _ => { - Some(Vec::from_elem(arity, wild())) - } + } + _ => { + Some(Vec::from_elem(arity, wild())) } } - &PatEnum(_, ref args) => { - let def = cx.tcx.def_map.borrow().get_copy(pat_id); - match def { - DefStatic(did, _) => { - let const_expr = lookup_const_by_id(cx.tcx, did).unwrap(); - let e_v = eval_const_expr(cx.tcx, &*const_expr); - match range_covered_by_constructor(ctor_id, &e_v, &e_v) { - Some(true) => Some(vec!()), - Some(false) => None, - None => { - cx.tcx.sess.span_err(*pat_span, "mismatched types between arms"); - None - } + } + &PatEnum(_, ref args) => { + let def = cx.tcx.def_map.borrow().get_copy(pat_id); + match def { + DefStatic(did, _) => { + let const_expr = lookup_const_by_id(cx.tcx, did).unwrap(); + let e_v = eval_const_expr(cx.tcx, &*const_expr); + match range_covered_by_constructor(ctor_id, &e_v, &e_v) { + Some(true) => Some(vec!()), + Some(false) => None, + None => { + cx.tcx.sess.span_err(*pat_span, "mismatched types between arms"); + None } } - DefVariant(_, id, _) if variant(id) != *ctor_id => None, - DefVariant(..) | DefFn(..) | DefStruct(..) => { - Some(match args { - &Some(ref args) => args.clone(), - &None => Vec::from_elem(arity, wild()) - }) - } - _ => None } + DefVariant(_, id, _) if variant(id, false) != *ctor_id => None, + DefVariant(..) | DefFn(..) | DefStruct(..) => { + Some(match args { + &Some(ref args) => args.clone(), + &None => Vec::from_elem(arity, wild()) + }) + } + _ => None } - &PatStruct(_, ref pattern_fields, _) => { - // Is this a struct or an enum variant? - let def = cx.tcx.def_map.borrow().get_copy(pat_id); - let class_id = match def { - DefVariant(_, variant_id, _) => { - if variant(variant_id) == *ctor_id { - Some(variant_id) - } else { - None - } - } - _ => { - match ty::get(left_ty).sty { - ty::ty_struct(cid, _) => Some(cid), - _ => { - cx.tcx.sess.span_bug( - *pat_span, - format!("struct pattern resolved to {}, \ - not a struct", - ty_to_str(cx.tcx, - left_ty)).as_slice()); - } - } + } + + &PatStruct(_, ref pattern_fields, _) => { + // Is this a struct or an enum variant? + let def = cx.tcx.def_map.borrow().get_copy(pat_id); + let class_id = match def { + DefVariant(_, variant_id, _) => if *ctor_id == variant(variant_id, true) { + Some(variant_id) + } else { + None + }, + DefStruct(struct_id) => Some(struct_id), + _ => None + }; + class_id.map(|variant_id| { + let struct_fields = ty::lookup_struct_fields(cx.tcx, variant_id); + let args = struct_fields.iter().map(|sf| { + match pattern_fields.iter().find(|f| f.ident.name == sf.name) { + Some(f) => f.pat, + _ => wild() } - }; - class_id.map(|variant_id| { - let struct_fields = ty::lookup_struct_fields(cx.tcx, variant_id); - let args = struct_fields.iter().map(|sf| { - match pattern_fields.iter().find(|f| f.ident.name == sf.name) { - Some(f) => f.pat, - _ => wild() - } - }).collect(); - args - }) + }).collect(); + args + }) + } - } - &PatTup(ref args) => { - Some(args.clone()) - } - &PatBox(ref inner) | &PatRegion(ref inner) => { - Some(vec!(inner.clone())) - } - &PatLit(ref expr) => { - let expr_value = eval_const_expr(cx.tcx, &**expr); - match range_covered_by_constructor(ctor_id, &expr_value, &expr_value) { - Some(true) => Some(vec!()), - Some(false) => None, - None => { + &PatTup(ref args) => + Some(args.clone()), + + &PatBox(ref inner) | &PatRegion(ref inner) => + Some(vec!(inner.clone())), + + &PatLit(ref expr) => { + let expr_value = eval_const_expr(cx.tcx, &**expr); + match range_covered_by_constructor(ctor_id, &expr_value, &expr_value) { + Some(true) => Some(vec!()), + Some(false) => None, + None => { cx.tcx.sess.span_err(*pat_span, "mismatched types between arms"); None - } - } + } } - &PatRange(ref from, ref to) => { - let from_value = eval_const_expr(cx.tcx, &**from); - let to_value = eval_const_expr(cx.tcx, &**to); - match range_covered_by_constructor(ctor_id, &from_value, &to_value) { - Some(true) => Some(vec!()), - Some(false) => None, - None => { + } + + &PatRange(ref from, ref to) => { + let from_value = eval_const_expr(cx.tcx, &**from); + let to_value = eval_const_expr(cx.tcx, &**to); + match range_covered_by_constructor(ctor_id, &from_value, &to_value) { + Some(true) => Some(vec!()), + Some(false) => None, + None => { cx.tcx.sess.span_err(*pat_span, "mismatched types between arms"); None - } - } + } } - &PatVec(ref before, ref slice, ref after) => { - match *ctor_id { - vec(_) => { - let num_elements = before.len() + after.len(); - if num_elements < arity && slice.is_some() { - let mut result = Vec::new(); - result.push_all(before.as_slice()); - result.grow_fn(arity - num_elements, |_| wild()); - result.push_all(after.as_slice()); - Some(result) - } else if num_elements == arity { - let mut result = Vec::new(); - result.push_all(before.as_slice()); - result.push_all(after.as_slice()); - Some(result) - } else { - None - } + } + + &PatVec(ref before, ref slice, ref after) => { + match *ctor_id { + vec(_) => { + let num_elements = before.len() + after.len(); + if num_elements < arity && slice.is_some() { + let mut result = Vec::new(); + result.push_all(before.as_slice()); + result.grow_fn(arity - num_elements, |_| wild()); + result.push_all(after.as_slice()); + Some(result) + } else if num_elements == arity { + let mut result = Vec::new(); + result.push_all(before.as_slice()); + result.push_all(after.as_slice()); + Some(result) + } else { + None } - _ => None } + _ => None } - &PatMac(_) => { - cx.tcx.sess.span_err(*pat_span, "unexpanded macro"); - None - } + } + + &PatMac(_) => { + cx.tcx.sess.span_err(*pat_span, "unexpanded macro"); + None + } }; head.map(|head| head.append(r.tail())) } @@ -750,12 +694,15 @@ fn check_local(cx: &mut MatchCheckCtxt, loc: &Local) { LocalFor => "`for` loop" }; - let mut spans = vec![]; - find_refutable(cx, &*loc.pat, &mut spans); - - for span in spans.iter() { - cx.tcx.sess.span_err(*span, - format!("refutable pattern in {} binding", name).as_slice()); + match is_refutable(cx, loc.pat) { + Some(pat) => { + let msg = format!( + "refutable pattern in {} binding: `{}` not covered", + name, pat_to_str(&*pat) + ); + cx.tcx.sess.span_err(loc.pat.span, msg.as_slice()); + }, + None => () } // Check legality of move bindings. @@ -769,67 +716,27 @@ fn check_fn(cx: &mut MatchCheckCtxt, sp: Span) { visit::walk_fn(cx, kind, decl, body, sp, ()); for input in decl.inputs.iter() { - let mut spans = vec![]; - find_refutable(cx, &*input.pat, &mut spans); - - for span in spans.iter() { - cx.tcx.sess.span_err(*span, - "refutable pattern in function argument"); + match is_refutable(cx, input.pat) { + Some(pat) => { + let msg = format!( + "refutable pattern in function argument: `{}` not covered", + pat_to_str(&*pat) + ); + cx.tcx.sess.span_err(input.pat.span, msg.as_slice()); + }, + None => () } } } -fn find_refutable(cx: &MatchCheckCtxt, pat: &Pat, spans: &mut Vec) { - macro_rules! this_pattern { - () => { - { - spans.push(pat.span); - return - } - } - } - let opt_def = cx.tcx.def_map.borrow().find_copy(&pat.id); - match opt_def { - Some(DefVariant(enum_id, _, _)) => { - if ty::enum_variants(cx.tcx, enum_id).len() != 1u { - this_pattern!() - } - } - Some(DefStatic(..)) => this_pattern!(), - _ => () - } - - match pat.node { - PatBox(ref sub) | PatRegion(ref sub) | PatIdent(_, _, Some(ref sub)) => { - find_refutable(cx, &**sub, spans) - } - PatWild | PatWildMulti | PatIdent(_, _, None) => {} - PatLit(lit) => { - match lit.node { - ExprLit(lit) => { - match lit.node { - LitNil => {} // `()` - _ => this_pattern!(), - } - } - _ => this_pattern!(), - } - } - PatRange(_, _) => { this_pattern!() } - PatStruct(_, ref fields, _) => { - for f in fields.iter() { - find_refutable(cx, &*f.pat, spans); - } - } - PatTup(ref elts) | PatEnum(_, Some(ref elts))=> { - for elt in elts.iter() { - find_refutable(cx, &**elt, spans) - } - } - PatEnum(_,_) => {} - PatVec(..) => { this_pattern!() } - PatMac(_) => cx.tcx.sess.bug("unexpanded macro"), - } +fn is_refutable(cx: &MatchCheckCtxt, pat: Gc) -> Option> { + let pats = vec!(vec!(pat)); + is_useful(cx, &pats, [wild()], ConstructWitness) + .useful() + .map(|pats| { + assert_eq!(pats.len(), 1); + pats.get(0).clone() + }) } // Legality of move bindings checking diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index ffd29ffeb8fb4..5b9c89f625093 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -988,8 +988,7 @@ fn extract_vec_elems<'a>( pat_id: ast::NodeId, elem_count: uint, slice: Option, - val: ValueRef, - count: ValueRef) + val: ValueRef) -> ExtractedBlock<'a> { let _icx = push_ctxt("match::extract_vec_elems"); let vec_datum = match_datum(bcx, val, pat_id); @@ -1003,7 +1002,7 @@ fn extract_vec_elems<'a>( Some(n) if i < n => GEPi(bcx, base, [i]), Some(n) if i > n => { InBoundsGEP(bcx, base, [ - Sub(bcx, count, + Sub(bcx, len, C_int(bcx.ccx(), (elem_count - i) as int))]) } _ => unsafe { llvm::LLVMGetUndef(vt.llunit_ty.to_ref()) } @@ -1765,7 +1764,7 @@ fn compile_submatch_continue<'a, 'b>( vec_len_eq => (n, None) }; let args = extract_vec_elems(opt_cx, pat_id, n, - slice, val, test_val); + slice, val); size = args.vals.len(); unpacked = args.vals.clone(); opt_cx = args.bcx; @@ -2264,9 +2263,21 @@ fn bind_irrefutable_pat<'a>( let loaded_val = Load(bcx, val); bcx = bind_irrefutable_pat(bcx, inner, loaded_val, binding_mode, cleanup_scope); } - ast::PatVec(..) => { - bcx.sess().span_bug(pat.span, - "vector patterns are never irrefutable!"); + ast::PatVec(ref before, ref slice, ref after) => { + let extracted = extract_vec_elems( + bcx, pat.id, before.len() + 1u + after.len(), + slice.map(|_| before.len()), val + ); + bcx = before + .iter().map(|v| Some(*v)) + .chain(Some(*slice).move_iter()) + .chain(after.iter().map(|v| Some(*v))) + .zip(extracted.vals.iter()) + .fold(bcx, |bcx, (inner, elem)| { + inner.map_or(bcx, |inner| { + bind_irrefutable_pat(bcx, inner, *elem, binding_mode, cleanup_scope) + }) + }); } ast::PatMac(..) => { bcx.sess().span_bug(pat.span, "unexpanded macro"); diff --git a/src/librustc/middle/typeck/check/_match.rs b/src/librustc/middle/typeck/check/_match.rs index edb77e04b3078..e198653165a61 100644 --- a/src/librustc/middle/typeck/check/_match.rs +++ b/src/librustc/middle/typeck/check/_match.rs @@ -632,9 +632,9 @@ pub fn check_pat(pcx: &pat_ctxt, pat: &ast::Pat, expected: ty::t) { fcx.infcx().next_region_var( infer::PatternRegion(pat.span)); - let check_err = || { - for elt in before.iter() { - check_pat(pcx, &**elt, ty::mk_err()); + let check_err = |found: String| { + for &elt in before.iter() { + check_pat(pcx, &*elt, ty::mk_err()); } for elt in slice.iter() { check_pat(pcx, &**elt, ty::mk_err()); @@ -653,15 +653,16 @@ pub fn check_pat(pcx: &pat_ctxt, pat: &ast::Pat, expected: ty::t) { }) }, Some(expected), - "a vector pattern".to_string(), + found, None); fcx.write_error(pat.id); }; - let (elt_type, region_var, mutbl) = match *structure_of(fcx, + let (elt_type, region_var, mutbl, fixed) = match *structure_of(fcx, pat.span, expected) { - ty::ty_vec(mt, Some(_)) => (mt.ty, default_region_var, ast::MutImmutable), + ty::ty_vec(mt, Some(fixed)) => + (mt.ty, default_region_var, ast::MutImmutable, Some(fixed)), ty::ty_uniq(t) => match ty::get(t).sty { ty::ty_vec(mt, None) => { fcx.type_error_message(pat.span, @@ -671,25 +672,37 @@ pub fn check_pat(pcx: &pat_ctxt, pat: &ast::Pat, expected: ty::t) { }, expected, None); - (mt.ty, default_region_var, ast::MutImmutable) + (mt.ty, default_region_var, ast::MutImmutable, None) } _ => { - check_err(); + check_err("a vector pattern".to_string()); return; } }, ty::ty_rptr(r, mt) => match ty::get(mt.ty).sty { - ty::ty_vec(mt, None) => (mt.ty, r, mt.mutbl), + ty::ty_vec(mt, None) => (mt.ty, r, mt.mutbl, None), _ => { - check_err(); + check_err("a vector pattern".to_string()); return; } }, _ => { - check_err(); + check_err("a vector pattern".to_string()); return; } }; + + let min_len = before.len() + after.len(); + fixed.and_then(|count| match slice { + Some(_) if count < min_len => + Some(format!("a fixed vector pattern of size at least {}", min_len)), + + None if count != min_len => + Some(format!("a fixed vector pattern of size {}", min_len)), + + _ => None + }).map(check_err); + for elt in before.iter() { check_pat(pcx, &**elt, elt_type); } diff --git a/src/test/compile-fail/issue-13482.rs b/src/test/compile-fail/issue-13482.rs new file mode 100644 index 0000000000000..2b769b9e499ea --- /dev/null +++ b/src/test/compile-fail/issue-13482.rs @@ -0,0 +1,18 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + let x = [1,2]; + let y = match x { + [] => None, +//~^ ERROR expected `[, .. 2]` but found a fixed vector pattern of size 0 + [a,_] => Some(a) + }; +} diff --git a/src/test/compile-fail/issue-2111.rs b/src/test/compile-fail/issue-2111.rs index 40010b203aa9b..3d9c7401ded24 100644 --- a/src/test/compile-fail/issue-2111.rs +++ b/src/test/compile-fail/issue-2111.rs @@ -9,7 +9,8 @@ // except according to those terms. fn foo(a: Option, b: Option) { - match (a,b) { //~ ERROR: non-exhaustive patterns: None not covered + match (a,b) { + //~^ ERROR: non-exhaustive patterns: `(None, None)` not covered (Some(a), Some(b)) if a == b => { } (Some(_), None) | (None, Some(_)) => { } diff --git a/src/test/compile-fail/issue-4321.rs b/src/test/compile-fail/issue-4321.rs new file mode 100644 index 0000000000000..d589680b0ecda --- /dev/null +++ b/src/test/compile-fail/issue-4321.rs @@ -0,0 +1,18 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + let tup = (true, true); + println!("foo {:}", match tup { //~ ERROR non-exhaustive patterns: `(true, false)` not covered + (false, false) => "foo", + (false, true) => "bar", + (true, true) => "baz" + }); +} diff --git a/src/test/compile-fail/non-exhaustive-match-nested.rs b/src/test/compile-fail/non-exhaustive-match-nested.rs index 102772f79d57f..483168bb8bcfa 100644 --- a/src/test/compile-fail/non-exhaustive-match-nested.rs +++ b/src/test/compile-fail/non-exhaustive-match-nested.rs @@ -8,13 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern: non-exhaustive patterns enum t { a(u), b } enum u { c, d } fn main() { let x = a(c); - match x { + match x { //~ ERROR non-exhaustive patterns: `a(c)` not covered a(d) => { fail!("hello"); } b => { fail!("goodbye"); } } diff --git a/src/test/compile-fail/non-exhaustive-match.rs b/src/test/compile-fail/non-exhaustive-match.rs index a07fec853fc52..cd78419439a96 100644 --- a/src/test/compile-fail/non-exhaustive-match.rs +++ b/src/test/compile-fail/non-exhaustive-match.rs @@ -12,21 +12,21 @@ enum t { a, b, } fn main() { let x = a; - match x { b => { } } //~ ERROR non-exhaustive patterns - match true { //~ ERROR non-exhaustive patterns + match x { b => { } } //~ ERROR non-exhaustive patterns: `a` not covered + match true { //~ ERROR non-exhaustive patterns: `false` not covered true => {} } - match Some(10) { //~ ERROR non-exhaustive patterns + match Some(10) { //~ ERROR non-exhaustive patterns: `Some(_)` not covered None => {} } - match (2, 3, 4) { //~ ERROR non-exhaustive patterns + match (2, 3, 4) { //~ ERROR non-exhaustive patterns: `(_, _, _)` not covered (_, _, 4) => {} } - match (a, a) { //~ ERROR non-exhaustive patterns + match (a, a) { //~ ERROR non-exhaustive patterns: `(a, a)` not covered (a, b) => {} (b, a) => {} } - match a { //~ ERROR b not covered + match a { //~ ERROR non-exhaustive patterns: `b` not covered a => {} } // This is exhaustive, though the algorithm got it wrong at one point @@ -37,8 +37,7 @@ fn main() { } let vec = vec!(Some(42), None, Some(21)); let vec: &[Option] = vec.as_slice(); - match vec { - //~^ ERROR non-exhaustive patterns: vectors of length 0 not covered + match vec { //~ ERROR non-exhaustive patterns: `[]` not covered [Some(..), None, ..tail] => {} [Some(..), Some(..), ..tail] => {} [None] => {} @@ -51,7 +50,7 @@ fn main() { } let vec = vec!(0.5); let vec: &[f32] = vec.as_slice(); - match vec { //~ ERROR non-exhaustive patterns: vectors of length 4 not covered + match vec { //~ ERROR non-exhaustive patterns: `[_, _, _, _]` not covered [0.1, 0.2, 0.3] => (), [0.1, 0.2] => (), [0.1] => (), diff --git a/src/test/compile-fail/non-exhaustive-pattern-witness.rs b/src/test/compile-fail/non-exhaustive-pattern-witness.rs new file mode 100644 index 0000000000000..22e93d70858e4 --- /dev/null +++ b/src/test/compile-fail/non-exhaustive-pattern-witness.rs @@ -0,0 +1,74 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(struct_variant)] + +struct Foo { + first: bool, + second: Option<[uint, ..4]> +} + +enum Color { + Red, + Green, + CustomRGBA { a: bool, r: u8, g: u8, b: u8 } +} + +fn struct_with_a_nested_enum_and_vector() { + match Foo { first: true, second: None } { + //~^ ERROR non-exhaustive patterns: `Foo{first: false, second: Some([_, _, _, _])}` not covered + Foo { first: true, second: None } => (), + Foo { first: true, second: Some(_) } => (), + Foo { first: false, second: None } => (), + Foo { first: false, second: Some([1u, 2u, 3u, 4u]) } => () + } +} + +fn enum_with_multiple_missing_variants() { + match Red { + //~^ ERROR non-exhaustive patterns: `Red` not covered + CustomRGBA { .. } => () + } +} + +fn enum_struct_variant() { + match Red { + //~^ ERROR non-exhaustive patterns: `CustomRGBA{a: true, r: _, g: _, b: _}` not covered + Red => (), + Green => (), + CustomRGBA { a: false, r: _, g: _, b: 0 } => (), + CustomRGBA { a: false, r: _, g: _, b: _ } => () + } +} + +enum Enum { + First, + Second(bool) +} + +fn vectors_with_nested_enums() { + let x: &'static [Enum] = [First, Second(false)]; + match x { + //~^ ERROR non-exhaustive patterns: `[Second(true), Second(false)]` not covered + [] => (), + [_] => (), + [First, _] => (), + [Second(true), First] => (), + [Second(true), Second(true)] => (), + [Second(false), _] => (), + [_, _, ..tail, _] => () + } +} + +fn main() { + struct_with_a_nested_enum_and_vector(); + enum_with_multiple_missing_variants(); + enum_struct_variant(); +} \ No newline at end of file diff --git a/src/test/compile-fail/precise-refutable-pattern-errors.rs b/src/test/compile-fail/precise-refutable-pattern-errors.rs deleted file mode 100644 index efa2dbad83fda..0000000000000 --- a/src/test/compile-fail/precise-refutable-pattern-errors.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - - -fn func( - ( - 1, //~ ERROR refutable pattern in function argument - ( - Some( //~ ERROR refutable pattern in function argument - 1), // nested, so no warning. - 2..3 //~ ERROR refutable pattern in function argument - ) - ): (int, (Option, int)) - ) {} - -fn main() { - let ( - 1, //~ ERROR refutable pattern in local binding - ( - Some( //~ ERROR refutable pattern in local binding - 1), // nested, so no warning. - 2..3 //~ ERROR refutable pattern in local binding - ) - ) = (1, (None, 2)); -} diff --git a/src/test/compile-fail/refutable-pattern-errors.rs b/src/test/compile-fail/refutable-pattern-errors.rs new file mode 100644 index 0000000000000..9128ee68e26c5 --- /dev/null +++ b/src/test/compile-fail/refutable-pattern-errors.rs @@ -0,0 +1,18 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +fn func((1, (Some(1), 2..3)): (int, (Option, int))) { } +//~^ ERROR refutable pattern in function argument: `(_, _)` not covered + +fn main() { + let (1, (Some(1), 2..3)) = (1, (None, 2)); + //~^ ERROR refutable pattern in local binding: `(_, _)` not covered +} diff --git a/src/test/compile-fail/refutable-pattern-in-fn-arg.rs b/src/test/compile-fail/refutable-pattern-in-fn-arg.rs index d5489b6a852e4..954d4b23e30fa 100644 --- a/src/test/compile-fail/refutable-pattern-in-fn-arg.rs +++ b/src/test/compile-fail/refutable-pattern-in-fn-arg.rs @@ -9,6 +9,7 @@ // except according to those terms. fn main() { - let f = |3: int| println!("hello"); //~ ERROR refutable pattern + let f = |3: int| println!("hello"); + //~^ ERROR refutable pattern in function argument: `_` not covered f(4); } diff --git a/src/test/run-pass/issue-14393.rs b/src/test/run-pass/issue-14393.rs new file mode 100644 index 0000000000000..87108294ff362 --- /dev/null +++ b/src/test/run-pass/issue-14393.rs @@ -0,0 +1,19 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-win32: FIXME #13793 + +fn main() { + match ("", 1u) { + (_, 42u) => (), + ("", _) => (), + _ => () + } +} diff --git a/src/test/run-pass/issue-7784.rs b/src/test/run-pass/issue-7784.rs new file mode 100644 index 0000000000000..52c2d57753a14 --- /dev/null +++ b/src/test/run-pass/issue-7784.rs @@ -0,0 +1,36 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn foo + Clone>([x, y, z]: [T, ..3]) -> (T, T, T) { + (x.clone(), x.clone() + y.clone(), x + y + z) +} +fn bar(a: &'static str, b: &'static str) -> [&'static str, ..4] { + [a, b, b, a] +} + +fn main() { + assert_eq!(foo([1, 2, 3]), (1, 3, 6)); + + let [a, b, c, d] = bar("foo", "bar"); + assert_eq!(a, "foo"); + assert_eq!(b, "bar"); + assert_eq!(c, "bar"); + assert_eq!(d, "foo"); + + let [a, _, _, d] = bar("baz", "foo"); + assert_eq!(a, "baz"); + assert_eq!(d, "baz"); + + let out = bar("baz", "foo"); + let [a, ..xs, d] = out; + assert_eq!(a, "baz"); + assert!(xs == ["foo", "foo"]); + assert_eq!(d, "baz"); +} \ No newline at end of file