use syntax::codemap::span;
use syntax::print::pprust::pat_to_str;
use syntax::visit;
+use std::sort;
struct AltCheckCtxt {
tcx: ty::ctxt,
None => fail ~"check_exhaustive: bad variant in ctor"
}
}
+ ty::ty_unboxed_vec(*) | ty::ty_evec(*) => {
+ match (*ctor) {
+ vec(n) => Some(fmt!("vectors of length %u", n)),
+ _ => None
+ }
+ }
_ => None
}
}
variant(def_id),
val(const_val),
range(const_val, const_val),
+ vec(uint),
+ vec_with_tail(uint)
}
impl ctor : cmp::Eq {
range(ref cv0_other, ref cv1_other)) => {
(*cv0_self) == (*cv0_other) && (*cv1_self) == (*cv1_other)
}
- (single, _) | (variant(_), _) | (val(_), _) | (range(*), _) => {
+ (vec(n_self), vec(n_other)) => n_self == n_other,
+ (vec_with_tail(n_self), vec_with_tail(n_other)) => {
+ n_self == n_other
+ }
+ (single, _) | (variant(_), _) | (val(_), _) |
+ (range(*), _) | (vec(*), _) | (vec_with_tail(*), _) => {
false
}
}
}
not_useful
}
+ ty::ty_unboxed_vec(*) | ty::ty_evec(*) => {
+ let max_len = do m.foldr(0) |r, max_len| {
+ match r[0].node {
+ pat_vec(elems, _) => uint::max(elems.len(), max_len),
+ _ => max_len
+ }
+ };
+ for uint::range(0, max_len + 1) |n| {
+ match is_useful_specialized(cx, m, v, vec(n), n, left_ty) {
+ not_useful => (),
+ ref u => return (*u)
+ }
+ }
+ not_useful
+ }
_ => {
let arity = ctor_arity(cx, single, left_ty);
is_useful_specialized(cx, m, v, single, arity, left_ty)
pat_region(*) => {
Some(single)
}
+ pat_vec(elems, tail) => {
+ match tail {
+ Some(_) => Some(vec_with_tail(elems.len())),
+ None => Some(vec(elems.len()))
+ }
+ }
}
}
else if true_found { Some(val(const_bool(false))) }
else { Some(val(const_bool(true))) }
}
+ ty::ty_unboxed_vec(*) | ty::ty_evec(*) => {
+ let max_len = do m.foldr(0) |r, max_len| {
+ match r[0].node {
+ pat_vec(elems, _) => uint::max(elems.len(), max_len),
+ _ => max_len
+ }
+ };
+ let min_len_with_tail = do m.foldr(max_len + 1) |r, min_len| {
+ match r[0].node {
+ pat_vec(elems, tail) => {
+ if tail.is_some() && elems.len() < min_len {
+ elems.len()
+ } else {
+ min_len
+ }
+ }
+ _ => min_len
+ }
+ };
+ let vec_lens = do m.filter_map |r| {
+ match r[0].node {
+ pat_vec(elems, tail) => {
+ match tail {
+ None if elems.len() < min_len_with_tail => Some(elems.len()),
+ _ => None
+ }
+ }
+ _ => None
+ }
+ };
+ let mut sorted_vec_lens = do sort::merge_sort(vec_lens) |a, b| {
+ a < b
+ };
+ vec::dedup(&mut sorted_vec_lens);
+
+ let mut missing = None;
+ for uint::range(0, min_len_with_tail) |i| {
+ if i >= sorted_vec_lens.len() || i != sorted_vec_lens[i] {
+ missing = Some(i);
+ break;
+ }
+ };
+ if missing.is_none() && min_len_with_tail > max_len {
+ missing = Some(min_len_with_tail);
+ }
+ match missing {
+ Some(k) => Some(vec(k)),
+ None => None
+ }
+ }
_ => Some(single)
}
}
}
}
ty::ty_struct(cid, _) => ty::lookup_struct_fields(cx.tcx, cid).len(),
+ ty::ty_unboxed_vec(*) | ty::ty_evec(*) => {
+ match ctor {
+ vec(n) | vec_with_tail(n) => n,
+ _ => 0u
+ }
+ }
_ => 0u
}
}
compare_const_vals(c_hi, v_hi) <= 0;
if match_ { Some(vec::tail(r)) } else { None }
}
+ pat_vec(elems, tail) => {
+ match ctor_id {
+ vec_with_tail(_) => {
+ if elems.len() >= arity {
+ Some(vec::append(elems.slice(0, arity), vec::tail(r)))
+ } else {
+ None
+ }
+ }
+ vec(_) => {
+ if elems.len() < arity && tail.is_some() {
+ Some(vec::append(
+ vec::append(elems, vec::from_elem(
+ arity - elems.len(), wild())
+ ),
+ vec::tail(r)
+ ))
+ } else if elems.len() == arity {
+ Some(vec::append(elems, vec::tail(r)))
+ } else {
+ None
+ }
+ }
+ _ => None
+ }
+ }
}
}
args.any(|a| is_refutable(cx, *a))
}
pat_enum(_,_) => { false }
+ pat_vec(*) => { true }
}
}
self.cat_pattern(subcmt, subpat, op);
}
- ast::pat_lit(_) | ast::pat_range(_, _) => { /*always ok*/ }
+ ast::pat_vec(*) | ast::pat_lit(_) | ast::pat_range(_, _) => {
+ /*always ok*/
+ }
}
}
enum Opt {
lit(Lit),
var(/* disr val */int, /* variant dids */{enm: def_id, var: def_id}),
- range(@ast::expr, @ast::expr)
+ range(@ast::expr, @ast::expr),
+ vec_len_eq(uint),
+ vec_len_ge(uint)
}
fn opt_eq(tcx: ty::ctxt, a: &Opt, b: &Opt) -> bool {
const_eval::compare_lit_exprs(tcx, a2, b2) == 0
}
(var(a, _), var(b, _)) => a == b,
+ (vec_len_eq(a), vec_len_eq(b)) => a == b,
+ (vec_len_ge(a), vec_len_ge(b)) => a == b,
_ => false
}
}
enum opt_result {
single_result(Result),
+ lower_bound(Result),
range_result(Result, Result),
}
fn trans_opt(bcx: block, o: &Opt) -> opt_result {
return range_result(rslt(bcx, consts::const_expr(ccx, l1)),
rslt(bcx, consts::const_expr(ccx, l2)));
}
+ vec_len_eq(n) => {
+ return single_result(rslt(bcx, C_int(ccx, n as int)));
+ }
+ vec_len_ge(n) => {
+ return lower_bound(rslt(bcx, C_int(ccx, n as int)));
+ }
}
}
None
}
}
+ ast::pat_vec(elems, tail) => {
+ match tail {
+ Some(_) => {
+ if opt_eq(tcx, &vec_len_ge(elems.len()), opt) {
+ Some(vec::append_one(elems, tail.get()))
+ } else {
+ None
+ }
+ }
+ None => {
+ if opt_eq(tcx, &vec_len_eq(elems.len()), opt) {
+ Some(copy elems)
+ } else {
+ None
+ }
+ }
+ }
+ }
_ => {
assert_is_binding_or_wild(bcx, p);
Some(vec::from_elem(variant_size, dummy))
ast::pat_range(l1, l2) => {
add_to_set(ccx.tcx, &found, range(l1, l2));
}
+ ast::pat_vec(elems, tail) => {
+ let opt = match tail {
+ None => vec_len_eq(elems.len()),
+ Some(_) => vec_len_ge(elems.len())
+ };
+ add_to_set(ccx.tcx, &found, opt);
+ }
_ => {}
}
}
return {vals: args, bcx: bcx};
}
+fn extract_vec_elems(bcx: block, pat_id: ast::node_id,
+ elem_count: uint, tail: bool, val: ValueRef)
+ -> {vals: ~[ValueRef], bcx: block}
+{
+ let _icx = bcx.insn_ctxt("alt::extract_vec_elems");
+ let vt = tvec::vec_types(bcx, node_id_type(bcx, pat_id));
+ let unboxed = load_if_immediate(bcx, val, vt.vec_ty);
+ let (base, len) = tvec::get_base_and_len(bcx, unboxed, vt.vec_ty);
+
+ let mut elems = do vec::from_fn(elem_count) |i| {
+ GEPi(bcx, base, ~[i])
+ };
+ if tail {
+ let tail_offset = Mul(bcx, vt.llunit_size,
+ C_int(bcx.ccx(), elem_count as int)
+ );
+ let tail_begin = tvec::pointer_add(bcx, base, tail_offset);
+ let tail_len = Sub(bcx, len, tail_offset);
+ let tail_ty = ty::mk_evec(bcx.tcx(),
+ {ty: vt.unit_ty, mutbl: ast::m_imm},
+ ty::vstore_slice(ty::re_static)
+ );
+ let scratch = scratch_datum(bcx, tail_ty, false);
+ Store(bcx, tail_begin,
+ GEPi(bcx, scratch.val, [0u, abi::slice_elt_base])
+ );
+ Store(bcx, tail_len,
+ GEPi(bcx, scratch.val, [0u, abi::slice_elt_len])
+ );
+ elems.push(scratch.val);
+ scratch.add_clean(bcx);
+ }
+ return {vals: elems, bcx: bcx};
+}
+
// NB: This function does not collect fields from struct-like enum variants.
fn collect_record_or_struct_fields(bcx: block, m: &[@Match], col: uint) ->
~[ast::ident] {
return best_col;
}
-enum branch_kind { no_branch, single, switch, compare, }
+enum branch_kind { no_branch, single, switch, compare, compare_vec_len, }
impl branch_kind : cmp::Eq {
pure fn eq(&self, other: &branch_kind) -> bool {
range(_, _) => {
test_val = Load(bcx, val);
kind = compare;
+ },
+ vec_len_eq(_) | vec_len_ge(_) => {
+ let vt = tvec::vec_types(bcx, node_id_type(bcx, pat_id));
+ let unboxed = load_if_immediate(bcx, val, vt.vec_ty);
+ let (_, len) = tvec::get_base_and_len(
+ bcx, unboxed, vt.vec_ty
+ );
+ test_val = SDiv(bcx, len, vt.llunit_size);
+ kind = compare_vec_len;
}
}
}
Result {bcx, val}) => {
compare_values(bcx, test_val, val, t)
}
+ lower_bound(
+ Result {bcx, val}) => {
+ compare_scalar_types(
+ bcx, test_val, val,
+ t, ast::ge)
+ }
range_result(
Result {val: vbegin, _},
Result {bcx, val: vend}) => {
bcx = sub_block(after_cx, ~"compare_next");
CondBr(after_cx, matches, opt_cx.llbb, bcx.llbb);
}
- _ => ()
+ compare_vec_len => {
+ let Result {bcx: after_cx, val: matches} = {
+ do with_scope_result(bcx, None,
+ ~"compare_vec_len_scope") |bcx| {
+ match trans_opt(bcx, opt) {
+ single_result(
+ Result {bcx, val}) => {
+ let value = compare_scalar_values(
+ bcx, test_val, val,
+ signed_int, ast::eq);
+ rslt(bcx, value)
+ }
+ lower_bound(
+ Result {bcx, val: val}) => {
+ let value = compare_scalar_values(
+ bcx, test_val, val,
+ signed_int, ast::ge);
+ rslt(bcx, value)
+ }
+ range_result(
+ Result {val: vbegin, _},
+ Result {bcx, val: vend}) => {
+ let llge =
+ compare_scalar_values(
+ bcx, test_val,
+ vbegin, signed_int, ast::ge);
+ let llle =
+ compare_scalar_values(
+ bcx, test_val, vend,
+ signed_int, ast::le);
+ rslt(bcx, And(bcx, llge, llle))
+ }
+ }
+ }
+ };
+ bcx = sub_block(after_cx, ~"compare_vec_len_next");
+ CondBr(after_cx, matches, opt_cx.llbb, bcx.llbb);
+ }
+ _ => ()
}
- } else if kind == compare {
+ } else if kind == compare || kind == compare_vec_len {
Br(bcx, else_cx.llbb);
}
unpacked = args.vals;
opt_cx = args.bcx;
}
+ vec_len_eq(n) | vec_len_ge(n) => {
+ let tail = match *opt {
+ vec_len_ge(_) => true,
+ _ => false
+ };
+ let args = extract_vec_elems(opt_cx, pat_id, n, tail, val);
+ size = args.vals.len();
+ unpacked = args.vals;
+ opt_cx = args.bcx;
+ }
lit(_) | range(_, _) => ()
}
let opt_ms = enter_opt(opt_cx, m, opt, col, size, val);
// Compile the fall-through case, if any
if !exhaustive {
- if kind == compare { Br(bcx, else_cx.llbb); }
+ if kind == compare || kind == compare_vec_len {
+ Br(bcx, else_cx.llbb);
+ }
if kind != single {
compile_submatch(else_cx, defaults, vals_left, chk);
}
true,
binding_mode);
}
- ast::pat_wild | ast::pat_lit(_) | ast::pat_range(_, _) => ()
+ ast::pat_wild | ast::pat_lit(_) | ast::pat_range(_, _) |
+ ast::pat_vec(*) => ()
}
return bcx;
}
}
}
}
+ ast::pat_vec(elts, tail) => {
+ let elt_type = match structure_of(fcx, pat.span, expected) {
+ ty::ty_evec(mt, _) | ty::ty_unboxed_vec(mt) => mt,
+ _ => {
+ tcx.sess.span_fatal(
+ pat.span,
+ fmt!("mismatched type: expected `%s` but found vector",
+ fcx.infcx().ty_to_str(expected))
+ );
+ }
+ };
+ for elts.each |elt| {
+ check_pat(pcx, *elt, elt_type.ty);
+ }
+ fcx.write_ty(pat.id, expected);
+ match tail {
+ Some(tail_pat) => {
+ let region_var = fcx.infcx().next_region_var_with_lb(
+ pat.span, pcx.block_region
+ );
+ let slice_ty = ty::mk_evec(tcx,
+ {ty: elt_type.ty, mutbl: elt_type.mutbl},
+ ty::vstore_slice(region_var)
+ );
+ check_pat(pcx, tail_pat, slice_ty);
+ }
+ None => ()
+ }
+ }
}
}
pat_region(@pat), // borrowed pointer pattern
pat_lit(@expr),
pat_range(@expr, @expr),
+ pat_vec(~[@pat], Option<@pat>)
}
#[auto_serialize]
pat_box(s) | pat_uniq(s) | pat_region(s) => {
walk_pat(s, it)
}
+ pat_vec(elts, tail) => {
+ for elts.each |p| {
+ walk_pat(*p, it)
+ }
+ do option::iter(&tail) |tail| {
+ walk_pat(*tail, it)
+ }
+ }
pat_wild | pat_lit(_) | pat_range(_, _) | pat_ident(_, _, _) |
pat_enum(_, _) => {
}
pat_region(inner) => pat_region(fld.fold_pat(inner)),
pat_range(e1, e2) => {
pat_range(fld.fold_expr(e1), fld.fold_expr(e2))
- }
+ },
+ pat_vec(elts, tail) => pat_vec(
+ vec::map(elts, |x| fld.fold_pat(*x)),
+ option::map(&tail, |tail| fld.fold_pat(*tail))
+ )
};
}
};
}
+ fn parse_pat_vec_elements(refutable: bool) -> (~[@pat], Option<@pat>) {
+ let mut elements = ~[];
+ let mut tail = None;
+ let mut first = true;
+
+ while self.token != token::RBRACKET {
+ if first { first = false; }
+ else { self.expect(token::COMMA); }
+
+ let mut is_tail = false;
+ if self.token == token::DOTDOT {
+ self.bump();
+ is_tail = true;
+ }
+
+ let subpat = self.parse_pat(refutable);
+ if is_tail {
+ match subpat {
+ @{ node: pat_wild, _ } => (),
+ @{ node: pat_ident(_, _, _), _ } => (),
+ @{ span, _ } => self.span_fatal(
+ span, ~"expected an identifier or `_`"
+ )
+ }
+ tail = Some(subpat);
+ break;
+ }
+
+ elements.push(subpat);
+ }
+ return (elements, tail);
+ }
+
fn parse_pat_fields(refutable: bool) -> (~[ast::field_pat], bool) {
let mut fields = ~[];
let mut etc = false;
pat = pat_tup(fields);
}
}
+ token::LBRACKET => {
+ self.bump();
+ let (elements, tail) = self.parse_pat_vec_elements(refutable);
+ hi = self.span.hi;
+ self.expect(token::RBRACKET);
+ pat = ast::pat_vec(elements, tail);
+ }
copy tok => {
if !is_ident_or_path(tok)
|| self.is_keyword(~"true")
word(s.s, ~"..");
print_expr(s, end);
}
+ ast::pat_vec(elts, tail) => {
+ word(s.s, ~"[");
+ commasep(s, inconsistent, elts, |s, p| print_pat(s, p, refutable));
+ do option::iter(&tail) |tail| {
+ if vec::len(elts) != 0u { word_space(s, ~","); }
+ word(s.s, ~"..");
+ print_pat(s, *tail, refutable);
+ }
+ word(s.s, ~"]");
+ }
}
(s.ann.post)(ann_node);
}
(v.visit_expr)(e1, e, v);
(v.visit_expr)(e2, e, v);
}
- pat_wild => ()
+ pat_wild => (),
+ pat_vec(elts, tail) => {
+ for elts.each |elt| {
+ (v.visit_pat)(*elt, e, v);
+ }
+ do option::iter(&tail) |tail| {
+ (v.visit_pat)(*tail, e, v);
+ }
+ }
}
}
--- /dev/null
+fn a() -> &[int] {
+ let vec = [1, 2, 3, 4];
+ let tail = match vec {
+ [a, ..tail] => tail, //~ ERROR illegal borrow
+ _ => fail ~"foo"
+ };
+ move tail
+}
+
+fn main() {
+ let tail = a();
+ for tail.each |n| {
+ io::println(fmt!("%d", *n));
+ }
+}
--- /dev/null
+fn main() {
+ match ~[] {
+ [_, ..tail, _] => {}, //~ ERROR: expected `]` but found `,`
+ _ => ()
+ }
+}
--- /dev/null
+fn main() {
+ let a = ~[];
+ match a {
+ [1, ..tail, ..tail] => {}, //~ ERROR: expected `]` but found `,`
+ _ => ()
+ }
+}
--- /dev/null
+fn main() {
+ match () {
+ [()] => { } //~ ERROR mismatched type: expected `()` but found vector
+ }
+}
--- /dev/null
+fn main() {
+ match ~"foo" {
+ ['f', 'o', .._] => { } //~ ERROR mismatched type: expected `~str` but found vector
+ _ => { }
+ }
+}
--- /dev/null
+fn main() {
+ let x: ~[(int, int)] = ~[];
+ match x {
+ [a, (2, 3), _] => (),
+ [(1, 2), (2, 3), b] => (), //~ ERROR unreachable pattern
+ _ => ()
+ }
+
+ match [~"foo", ~"bar", ~"baz"] {
+ [a, _, _, .._] => { io::println(a); }
+ [~"foo", ~"bar", ~"baz", ~"foo", ~"bar"] => { } //~ ERROR unreachable pattern
+ _ => { }
+ }
+
+ match ['a', 'b', 'c'] {
+ ['a', 'b', 'c', .._tail] => {}
+ ['a', 'b', 'c'] => {} //~ ERROR unreachable pattern
+ _ => {}
+ }
+}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+// error-pattern:refutable pattern
// error-pattern:refutable pattern
enum xx { xx(int), yy, }
fn main() {
let @{x: xx(x), y: y} = @{x: xx(10), y: 20};
assert (x + y == 30);
+
+ let [a, b] = ~[1, 2];
}
(_, a) => {}
(b, b) => {}
}
+ match ~[Some(42), None, Some(21)] { //~ ERROR non-exhaustive patterns: vectors of length 0 not covered
+ [Some(*), None, ..tail] => {}
+ [Some(*), Some(*), ..tail] => {}
+ [None] => {}
+ }
+ match ~[1] {
+ [_, ..tail] => (),
+ [] => ()
+ }
+ match ~[0.5] { //~ ERROR non-exhaustive patterns: vectors of length 4 not covered
+ [0.1, 0.2, 0.3] => (),
+ [0.1, 0.2] => (),
+ [0.1] => (),
+ [] => ()
+ }
+ match ~[Some(42), None, Some(21)] {
+ [Some(*), None, ..tail] => {}
+ [Some(*), Some(*), ..tail] => {}
+ [None, None, ..tail] => {}
+ [None, Some(*), ..tail] => {}
+ [Some(_)] => {}
+ [None] => {}
+ [] => {}
+ }
}
--- /dev/null
+fn foldl<T, U: Copy>(
+ values: &[T],
+ initial: U,
+ function: &fn(partial: U, element: &T) -> U
+) -> U {
+ match values {
+ [head, ..tail] =>
+ foldl(tail, function(initial, &head), function),
+ _ => copy initial
+ }
+}
+
+fn main() {
+ let x = [1, 2, 3, 4, 5];
+ match x {
+ [a, b, c, d, e, f] => {
+ core::util::unreachable();
+ }
+ [a, b, c, d, e] => {
+ assert a == 1;
+ assert b == 2;
+ assert c == 3;
+ assert d == 4;
+ assert e == 5;
+ }
+ _ => {
+ core::util::unreachable();
+ }
+ }
+
+ let product = foldl(x, 1, |a, b| a * *b);
+ assert product == 120;
+}
--- /dev/null
+struct Foo {
+ string: ~str
+}
+
+fn main() {
+ let x = [
+ Foo { string: ~"foo" },
+ Foo { string: ~"bar" },
+ Foo { string: ~"baz" }
+ ];
+ match x {
+ [first, ..tail] => {
+ assert first.string == ~"foo";
+ assert tail.len() == 2;
+ assert tail[0].string == ~"bar";
+ assert tail[1].string == ~"baz";
+
+ match tail {
+ [Foo { _ }, _, Foo { _ }, ..tail] => {
+ core::util::unreachable();
+ }
+ [Foo { string: a }, Foo { string: b }] => {
+ assert a == ~"bar";
+ assert b == ~"baz";
+ }
+ _ => {
+ core::util::unreachable();
+ }
+ }
+ }
+ _ => {
+ core::util::unreachable();
+ }
+ }
+}