From 1eafb5c8abf49377dbfd2c52849cb799c58b4a15 Mon Sep 17 00:00:00 2001 From: Jakub Wieczorek Date: Thu, 29 Nov 2012 21:27:45 +0000 Subject: [PATCH 01/13] Vector destructuring --- src/librustc/middle/check_alt.rs | 3 + src/librustc/middle/mem_categorization.rs | 4 +- src/librustc/middle/trans/alt.rs | 149 +++++++++++++++++- src/librustc/middle/typeck/check/alt.rs | 26 +++ src/libsyntax/ast.rs | 1 + src/libsyntax/ast_util.rs | 8 + src/libsyntax/fold.rs | 6 +- src/libsyntax/parse/parser.rs | 36 +++++ src/libsyntax/print/pprust.rs | 10 ++ src/libsyntax/visit.rs | 8 +- src/test/compile-fail/alt-vec-mismatch-2.rs | 6 + src/test/compile-fail/alt-vec-mismatch.rs | 6 + src/test/compile-fail/alt-vec-unreachable.rs | 13 ++ .../compile-fail/let-destruct-refutable.rs | 3 + src/test/compile-fail/non-exhaustive-match.rs | 11 ++ src/test/run-pass/vec-matching.rs | 33 ++++ src/test/run-pass/vec-tail-matching.rs | 35 ++++ 17 files changed, 349 insertions(+), 9 deletions(-) create mode 100644 src/test/compile-fail/alt-vec-mismatch-2.rs create mode 100644 src/test/compile-fail/alt-vec-mismatch.rs create mode 100644 src/test/compile-fail/alt-vec-unreachable.rs create mode 100644 src/test/run-pass/vec-matching.rs create mode 100644 src/test/run-pass/vec-tail-matching.rs diff --git a/src/librustc/middle/check_alt.rs b/src/librustc/middle/check_alt.rs index e7716ad984eb3..3fcd604936d49 100644 --- a/src/librustc/middle/check_alt.rs +++ b/src/librustc/middle/check_alt.rs @@ -250,6 +250,7 @@ fn pat_ctor_id(tcx: ty::ctxt, p: @pat) -> Option { pat_region(*) => { Some(single) } + pat_vec(*) => None } } @@ -469,6 +470,7 @@ fn specialize(tcx: ty::ctxt, r: ~[@pat], ctor_id: ctor, arity: uint, compare_const_vals(c_hi, v_hi) <= 0; if match_ { Some(vec::tail(r)) } else { None } } + pat_vec(*) => None } } @@ -534,6 +536,7 @@ fn is_refutable(tcx: ty::ctxt, pat: &pat) -> bool { args.any(|a| is_refutable(tcx, *a)) } pat_enum(_,_) => { false } + pat_vec(*) => { true } } } diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index fd5735e78deff..ee3bc0ab47fc5 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -972,7 +972,9 @@ impl &mem_categorization_ctxt { 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*/ + } } } diff --git a/src/librustc/middle/trans/alt.rs b/src/librustc/middle/trans/alt.rs index 03501521129b7..27953977a62dd 100644 --- a/src/librustc/middle/trans/alt.rs +++ b/src/librustc/middle/trans/alt.rs @@ -167,7 +167,9 @@ enum Lit { 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 { @@ -211,12 +213,15 @@ 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 { @@ -244,6 +249,12 @@ 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))); + } } } @@ -531,6 +542,24 @@ fn enter_opt(bcx: block, m: &[@Match/&r], opt: &Opt, col: uint, 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)) @@ -741,6 +770,13 @@ fn get_options(ccx: @crate_ctxt, m: &[@Match], col: uint) -> ~[Opt] { 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); + } _ => {} } } @@ -776,6 +812,41 @@ fn extract_variant_args(bcx: block, pat_id: ast::node_id, 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 slice_offset = Mul(bcx, vt.llunit_size, + C_int(bcx.ccx(), elem_count as int) + ); + let slice_begin = tvec::pointer_add(bcx, base, slice_offset); + let tail_len = Sub(bcx, len, slice_offset); + let slice_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, slice_ty, false); + Store(bcx, slice_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] { @@ -904,7 +975,7 @@ fn pick_col(m: &[@Match]) -> uint { 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 { @@ -1244,6 +1315,15 @@ fn compile_submatch(bcx: block, 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; } } } @@ -1299,6 +1379,12 @@ fn compile_submatch(bcx: block, 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}) => { @@ -1318,9 +1404,47 @@ fn compile_submatch(bcx: block, 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); } @@ -1333,6 +1457,16 @@ fn compile_submatch(bcx: block, 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); @@ -1342,7 +1476,9 @@ fn compile_submatch(bcx: block, // 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); } @@ -1607,7 +1743,8 @@ fn bind_irrefutable_pat(bcx: block, 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; } diff --git a/src/librustc/middle/typeck/check/alt.rs b/src/librustc/middle/typeck/check/alt.rs index 6131fb531249b..f75a966bacfbb 100644 --- a/src/librustc/middle/typeck/check/alt.rs +++ b/src/librustc/middle/typeck/check/alt.rs @@ -576,7 +576,33 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) { } } } + 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.ty, + _ => { + 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); + } + fcx.write_ty(pat.id, expected); + match tail { + Some(tail_pat) => { + let slice_ty = ty::mk_evec(tcx, + {ty: elt_type, mutbl: ast::m_imm}, + ty::vstore_slice(ty::re_static) + ); + check_pat(pcx, tail_pat, slice_ty); + } + None => () + } + } } } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 1ffe741d255fd..714a3c1e9f137 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -372,6 +372,7 @@ enum pat_ { pat_region(@pat), // borrowed pointer pattern pat_lit(@expr), pat_range(@expr, @expr), + pat_vec(~[@pat], Option<@pat>) } #[auto_serialize] diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index e4e29d1fb4550..7472fe33d1c37 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -583,6 +583,14 @@ fn walk_pat(pat: @pat, it: fn(@pat)) { 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(_, _) => { } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 9a1f3e7f04e80..267a6bfd46fa2 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -356,7 +356,11 @@ fn noop_fold_pat(p: pat_, fld: ast_fold) -> pat_ { 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)) + ) }; } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index d8fd58b3d5081..3a01e71b5dcbc 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1851,6 +1851,35 @@ impl Parser { }; } + 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 subpat = self.parse_pat(refutable); + + if self.token == token::ELLIPSIS { + self.bump(); + 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; @@ -1988,6 +2017,13 @@ impl Parser { 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); + } tok => { if !is_ident_or_path(tok) || self.is_keyword(~"true") diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 5ad3c051c5977..81d59089a3770 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1589,6 +1589,16 @@ fn print_pat(s: ps, &&pat: @ast::pat) { word(s.s, ~".."); print_expr(s, end); } + ast::pat_vec(elts, tail) => { + word(s.s, ~"["); + commasep(s, inconsistent, elts, print_pat); + do option::iter(&tail) |tail| { + if vec::len(elts) != 0u { word_space(s, ~","); } + print_pat(s, *tail); + word(s.s, ~"..."); + } + word(s.s, ~"]"); + } } (s.ann.post)(ann_node); } diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index fbb1bc9117211..5246e2078e1d0 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -241,7 +241,13 @@ fn visit_pat(p: @pat, e: E, v: vt) { (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); + } + } } } diff --git a/src/test/compile-fail/alt-vec-mismatch-2.rs b/src/test/compile-fail/alt-vec-mismatch-2.rs new file mode 100644 index 0000000000000..fe3a305eeb5b9 --- /dev/null +++ b/src/test/compile-fail/alt-vec-mismatch-2.rs @@ -0,0 +1,6 @@ +fn main() { + match () { + [()] => { } //~ ERROR mismatched type: expected `()` but found vector + } +} + diff --git a/src/test/compile-fail/alt-vec-mismatch.rs b/src/test/compile-fail/alt-vec-mismatch.rs new file mode 100644 index 0000000000000..d29bf37799bd3 --- /dev/null +++ b/src/test/compile-fail/alt-vec-mismatch.rs @@ -0,0 +1,6 @@ +fn main() { + match ~"foo" { + ['f', 'o', _...] => { } //~ ERROR mismatched type: expected `~str` but found vector + _ => { } + } +} diff --git a/src/test/compile-fail/alt-vec-unreachable.rs b/src/test/compile-fail/alt-vec-unreachable.rs new file mode 100644 index 0000000000000..500ae376b01b9 --- /dev/null +++ b/src/test/compile-fail/alt-vec-unreachable.rs @@ -0,0 +1,13 @@ +fn main() { + match [~"foo", ~"bar", ~"baz"] { + [a, _, _, _...] => { io::println(a); } + [~"foo", ~"bar"] => { } //~ ERROR unreachable pattern + _ => { } + } + + match ['a', 'b', 'c'] { + ['a', 'b', 'c', _tail...] => {} + ['a', 'b', 'c'] => {} //~ ERROR unreachable pattern + _ => {} + } +} diff --git a/src/test/compile-fail/let-destruct-refutable.rs b/src/test/compile-fail/let-destruct-refutable.rs index 1d80654781f4e..849ef6ba632ff 100644 --- a/src/test/compile-fail/let-destruct-refutable.rs +++ b/src/test/compile-fail/let-destruct-refutable.rs @@ -1,8 +1,11 @@ // 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]; } diff --git a/src/test/compile-fail/non-exhaustive-match.rs b/src/test/compile-fail/non-exhaustive-match.rs index 0e9615c7bd9a3..ab6d81e24eb4f 100644 --- a/src/test/compile-fail/non-exhaustive-match.rs +++ b/src/test/compile-fail/non-exhaustive-match.rs @@ -25,4 +25,15 @@ fn main() { (_, a) => {} (b, b) => {} } + match ~[Some(42), None, Some(21)] { //~ ERROR non-exhaustive patterns + [Some(*), None, tail...] => {} + [Some(*), Some(*), tail...] => {} + [None] => {} + } + match ~[Some(42), None, Some(21)] { + [Some(*), None, tail...] => {} + [Some(*), Some(*), tail...] => {} + [None] => {} + [] => {} + } } diff --git a/src/test/run-pass/vec-matching.rs b/src/test/run-pass/vec-matching.rs new file mode 100644 index 0000000000000..28ad305926062 --- /dev/null +++ b/src/test/run-pass/vec-matching.rs @@ -0,0 +1,33 @@ +fn foldl( + 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; +} diff --git a/src/test/run-pass/vec-tail-matching.rs b/src/test/run-pass/vec-tail-matching.rs new file mode 100644 index 0000000000000..3c12792ac0625 --- /dev/null +++ b/src/test/run-pass/vec-tail-matching.rs @@ -0,0 +1,35 @@ +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(); + } + } +} From b4b1590e9404606d8b9417691ec88ca09987c0b7 Mon Sep 17 00:00:00 2001 From: Jakub Wieczorek Date: Mon, 3 Dec 2012 23:54:30 +0000 Subject: [PATCH 02/13] Change the vector pattern to use leading .. for the tail --- src/libsyntax/parse/parser.rs | 10 +++++++--- src/libsyntax/print/pprust.rs | 2 +- src/test/compile-fail/alt-vec-mismatch.rs | 2 +- src/test/compile-fail/alt-vec-unreachable.rs | 4 ++-- src/test/compile-fail/non-exhaustive-match.rs | 8 ++++---- src/test/run-pass/vec-matching.rs | 2 +- src/test/run-pass/vec-tail-matching.rs | 4 ++-- 7 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 3a01e71b5dcbc..970483ff4ec87 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1860,10 +1860,14 @@ impl Parser { if first { first = false; } else { self.expect(token::COMMA); } - let subpat = self.parse_pat(refutable); - - if self.token == token::ELLIPSIS { + 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(_, _, _), _ } => (), diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 81d59089a3770..311294e0e77e5 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1594,8 +1594,8 @@ fn print_pat(s: ps, &&pat: @ast::pat) { commasep(s, inconsistent, elts, print_pat); do option::iter(&tail) |tail| { if vec::len(elts) != 0u { word_space(s, ~","); } + word(s.s, ~".."); print_pat(s, *tail); - word(s.s, ~"..."); } word(s.s, ~"]"); } diff --git a/src/test/compile-fail/alt-vec-mismatch.rs b/src/test/compile-fail/alt-vec-mismatch.rs index d29bf37799bd3..ef4d92ea4913b 100644 --- a/src/test/compile-fail/alt-vec-mismatch.rs +++ b/src/test/compile-fail/alt-vec-mismatch.rs @@ -1,6 +1,6 @@ fn main() { match ~"foo" { - ['f', 'o', _...] => { } //~ ERROR mismatched type: expected `~str` but found vector + ['f', 'o', .._] => { } //~ ERROR mismatched type: expected `~str` but found vector _ => { } } } diff --git a/src/test/compile-fail/alt-vec-unreachable.rs b/src/test/compile-fail/alt-vec-unreachable.rs index 500ae376b01b9..4ed9b9846d5db 100644 --- a/src/test/compile-fail/alt-vec-unreachable.rs +++ b/src/test/compile-fail/alt-vec-unreachable.rs @@ -1,12 +1,12 @@ fn main() { match [~"foo", ~"bar", ~"baz"] { - [a, _, _, _...] => { io::println(a); } + [a, _, _, .._] => { io::println(a); } [~"foo", ~"bar"] => { } //~ ERROR unreachable pattern _ => { } } match ['a', 'b', 'c'] { - ['a', 'b', 'c', _tail...] => {} + ['a', 'b', 'c', .._tail] => {} ['a', 'b', 'c'] => {} //~ ERROR unreachable pattern _ => {} } diff --git a/src/test/compile-fail/non-exhaustive-match.rs b/src/test/compile-fail/non-exhaustive-match.rs index ab6d81e24eb4f..3a00e959a1090 100644 --- a/src/test/compile-fail/non-exhaustive-match.rs +++ b/src/test/compile-fail/non-exhaustive-match.rs @@ -26,13 +26,13 @@ fn main() { (b, b) => {} } match ~[Some(42), None, Some(21)] { //~ ERROR non-exhaustive patterns - [Some(*), None, tail...] => {} - [Some(*), Some(*), tail...] => {} + [Some(*), None, ..tail] => {} + [Some(*), Some(*), ..tail] => {} [None] => {} } match ~[Some(42), None, Some(21)] { - [Some(*), None, tail...] => {} - [Some(*), Some(*), tail...] => {} + [Some(*), None, ..tail] => {} + [Some(*), Some(*), ..tail] => {} [None] => {} [] => {} } diff --git a/src/test/run-pass/vec-matching.rs b/src/test/run-pass/vec-matching.rs index 28ad305926062..a3840c9f561f8 100644 --- a/src/test/run-pass/vec-matching.rs +++ b/src/test/run-pass/vec-matching.rs @@ -4,7 +4,7 @@ fn foldl( function: &fn(partial: U, element: &T) -> U ) -> U { match values { - [head, tail...] => + [head, ..tail] => foldl(tail, function(initial, &head), function), _ => copy initial } diff --git a/src/test/run-pass/vec-tail-matching.rs b/src/test/run-pass/vec-tail-matching.rs index 3c12792ac0625..7a7010f079d04 100644 --- a/src/test/run-pass/vec-tail-matching.rs +++ b/src/test/run-pass/vec-tail-matching.rs @@ -9,14 +9,14 @@ fn main() { Foo { string: ~"baz" } ]; match x { - [first, tail...] => { + [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...] => { + [Foo { _ }, _, Foo { _ }, ..tail] => { core::util::unreachable(); } [Foo { string: a }, Foo { string: b }] => { From db7d2215b2034a12a52d5026b2a8f88c2642ce07 Mon Sep 17 00:00:00 2001 From: Jakub Wieczorek Date: Mon, 3 Dec 2012 23:55:43 +0000 Subject: [PATCH 03/13] Add two tests for parse errors --- src/test/compile-fail/alt-vec-invalid-2.rs | 6 ++++++ src/test/compile-fail/alt-vec-invalid.rs | 7 +++++++ 2 files changed, 13 insertions(+) create mode 100644 src/test/compile-fail/alt-vec-invalid-2.rs create mode 100644 src/test/compile-fail/alt-vec-invalid.rs diff --git a/src/test/compile-fail/alt-vec-invalid-2.rs b/src/test/compile-fail/alt-vec-invalid-2.rs new file mode 100644 index 0000000000000..4174120b291c6 --- /dev/null +++ b/src/test/compile-fail/alt-vec-invalid-2.rs @@ -0,0 +1,6 @@ +fn main() { + match ~[] { + [_, ..tail, _] => {}, //~ ERROR: expected `]` but found `,` + _ => () + } +} diff --git a/src/test/compile-fail/alt-vec-invalid.rs b/src/test/compile-fail/alt-vec-invalid.rs new file mode 100644 index 0000000000000..b35731c2e4acd --- /dev/null +++ b/src/test/compile-fail/alt-vec-invalid.rs @@ -0,0 +1,7 @@ +fn main() { + let a = ~[]; + match a { + [1, ..tail, ..tail] => {}, //~ ERROR: expected `]` but found `,` + _ => () + } +} From ca963251d0f459a7236c8acac74e688b73cdebd8 Mon Sep 17 00:00:00 2001 From: Jakub Wieczorek Date: Mon, 3 Dec 2012 23:57:06 +0000 Subject: [PATCH 04/13] Add one more test for determining unreachability --- src/test/compile-fail/alt-vec-unreachable.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/test/compile-fail/alt-vec-unreachable.rs b/src/test/compile-fail/alt-vec-unreachable.rs index 4ed9b9846d5db..f783536e4faf5 100644 --- a/src/test/compile-fail/alt-vec-unreachable.rs +++ b/src/test/compile-fail/alt-vec-unreachable.rs @@ -1,7 +1,14 @@ fn main() { + let x: ~[(int, int)] = ~[]; + match x { + [(1, 2), (2, 3), _] => (), + [a, _, (3, 4)] => (), //~ ERROR unreachable pattern + _ => () + } + match [~"foo", ~"bar", ~"baz"] { [a, _, _, .._] => { io::println(a); } - [~"foo", ~"bar"] => { } //~ ERROR unreachable pattern + [~"foo", ~"bar", ~"baz", ~"foo", ~"bar"] => { } //~ ERROR unreachable pattern _ => { } } From 48b33bc2b7b605aabc7c3aee7a1ae9c3e57ff904 Mon Sep 17 00:00:00 2001 From: Jakub Wieczorek Date: Tue, 4 Dec 2012 01:54:37 +0000 Subject: [PATCH 05/13] Add arm reachability and exhaustiveness detection --- src/librustc/middle/check_alt.rs | 99 ++++++++++++++++++- src/test/compile-fail/non-exhaustive-match.rs | 3 + 2 files changed, 99 insertions(+), 3 deletions(-) diff --git a/src/librustc/middle/check_alt.rs b/src/librustc/middle/check_alt.rs index 3fcd604936d49..0c606567e7aa5 100644 --- a/src/librustc/middle/check_alt.rs +++ b/src/librustc/middle/check_alt.rs @@ -10,6 +10,7 @@ use syntax::visit; use middle::ty; use middle::ty::*; use std::map::HashMap; +use std::sort; fn check_crate(tcx: ty::ctxt, crate: @crate) { visit::visit_crate(*crate, (), visit::mk_vt(@{ @@ -103,6 +104,12 @@ fn check_exhaustive(tcx: ty::ctxt, sp: span, pats: ~[@pat]) { 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 } } @@ -123,6 +130,8 @@ enum ctor { variant(def_id), val(const_val), range(const_val, const_val), + vec(uint), + vec_with_tail(uint) } impl ctor : cmp::Eq { @@ -134,7 +143,10 @@ impl ctor : cmp::Eq { (range(cv0_self, cv1_self), range(cv0_other, 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 } } @@ -250,7 +262,12 @@ fn pat_ctor_id(tcx: ty::ctxt, p: @pat) -> Option { pat_region(*) => { Some(single) } - pat_vec(*) => None + pat_vec(elems, tail) => { + match tail { + Some(_) => Some(vec_with_tail(elems.len())), + None => Some(vec(elems.len())) + } + } } } @@ -311,6 +328,53 @@ fn missing_ctor(tcx: ty::ctxt, m: matrix, left_ty: ty::t) -> Option { 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; + } + }; + match missing { + Some(k) => Some(vec(k)), + None => None + } + } _ => Some(single) } } @@ -329,6 +393,12 @@ fn ctor_arity(tcx: ty::ctxt, ctor: ctor, ty: ty::t) -> uint { } } ty::ty_class(cid, _) => ty::lookup_class_fields(tcx, cid).len(), + ty::ty_unboxed_vec(*) | ty::ty_evec(*) => { + match ctor { + vec(n) | vec_with_tail(n) => n, + _ => 0u + } + } _ => 0u } } @@ -470,7 +540,30 @@ fn specialize(tcx: ty::ctxt, r: ~[@pat], ctor_id: ctor, arity: uint, compare_const_vals(c_hi, v_hi) <= 0; if match_ { Some(vec::tail(r)) } else { None } } - pat_vec(*) => 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 + } + } } } diff --git a/src/test/compile-fail/non-exhaustive-match.rs b/src/test/compile-fail/non-exhaustive-match.rs index 3a00e959a1090..94619ac8b3b45 100644 --- a/src/test/compile-fail/non-exhaustive-match.rs +++ b/src/test/compile-fail/non-exhaustive-match.rs @@ -33,6 +33,9 @@ fn main() { match ~[Some(42), None, Some(21)] { [Some(*), None, ..tail] => {} [Some(*), Some(*), ..tail] => {} + [None, None, ..tail] => {} + [None, Some(*), ..tail] => {} + [Some(_)] => {} [None] => {} [] => {} } From 941931926bf8debb2a8dcc636878243bbcb6d3f1 Mon Sep 17 00:00:00 2001 From: Jakub Wieczorek Date: Tue, 4 Dec 2012 02:01:08 +0000 Subject: [PATCH 06/13] Rename some of the variables in extract_vec_elements() for clarity --- src/librustc/middle/trans/alt.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/librustc/middle/trans/alt.rs b/src/librustc/middle/trans/alt.rs index 27953977a62dd..9ee93333debd9 100644 --- a/src/librustc/middle/trans/alt.rs +++ b/src/librustc/middle/trans/alt.rs @@ -825,17 +825,17 @@ fn extract_vec_elems(bcx: block, pat_id: ast::node_id, GEPi(bcx, base, ~[i]) }; if tail { - let slice_offset = Mul(bcx, vt.llunit_size, + let tail_offset = Mul(bcx, vt.llunit_size, C_int(bcx.ccx(), elem_count as int) ); - let slice_begin = tvec::pointer_add(bcx, base, slice_offset); - let tail_len = Sub(bcx, len, slice_offset); - let slice_ty = ty::mk_evec(bcx.tcx(), + 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, slice_ty, false); - Store(bcx, slice_begin, + 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, From 9332c76dd8b52356f1e989f4b730bee32afb6e1d Mon Sep 17 00:00:00 2001 From: Jakub Wieczorek Date: Tue, 4 Dec 2012 02:04:22 +0000 Subject: [PATCH 07/13] Add the right error message to the non-exhaustive-match test --- src/test/compile-fail/non-exhaustive-match.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/compile-fail/non-exhaustive-match.rs b/src/test/compile-fail/non-exhaustive-match.rs index 94619ac8b3b45..fafb4d51f444f 100644 --- a/src/test/compile-fail/non-exhaustive-match.rs +++ b/src/test/compile-fail/non-exhaustive-match.rs @@ -25,7 +25,7 @@ fn main() { (_, a) => {} (b, b) => {} } - match ~[Some(42), None, Some(21)] { //~ ERROR non-exhaustive patterns + match ~[Some(42), None, Some(21)] { //~ ERROR non-exhaustive patterns: vectors of length 0 not covered [Some(*), None, ..tail] => {} [Some(*), Some(*), ..tail] => {} [None] => {} From a5bb1b7465e91426b8667b822ed80acc700fd878 Mon Sep 17 00:00:00 2001 From: Jakub Wieczorek Date: Tue, 4 Dec 2012 02:16:42 +0000 Subject: [PATCH 08/13] Fix the first test case in alt-vec-unreachable.rs --- src/test/compile-fail/alt-vec-unreachable.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/compile-fail/alt-vec-unreachable.rs b/src/test/compile-fail/alt-vec-unreachable.rs index f783536e4faf5..2719d84b2dd25 100644 --- a/src/test/compile-fail/alt-vec-unreachable.rs +++ b/src/test/compile-fail/alt-vec-unreachable.rs @@ -1,8 +1,8 @@ fn main() { let x: ~[(int, int)] = ~[]; match x { - [(1, 2), (2, 3), _] => (), - [a, _, (3, 4)] => (), //~ ERROR unreachable pattern + [a, (2, 3), _] => (), + [(1, 2), (2, 3), b] => (), //~ ERROR unreachable pattern _ => () } From 05d096f3b7dd7520c0ef31bf735526908a4fd660 Mon Sep 17 00:00:00 2001 From: Jakub Wieczorek Date: Tue, 4 Dec 2012 02:48:32 +0000 Subject: [PATCH 09/13] Complete the exhaustiveness check --- src/librustc/middle/check_alt.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/librustc/middle/check_alt.rs b/src/librustc/middle/check_alt.rs index 0c606567e7aa5..1f8b315b9c80d 100644 --- a/src/librustc/middle/check_alt.rs +++ b/src/librustc/middle/check_alt.rs @@ -201,6 +201,21 @@ fn is_useful(tcx: ty::ctxt, m: matrix, v: ~[@pat]) -> useful { } 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(tcx, m, v, vec(n), n, left_ty) { + not_useful => (), + u => return u + } + } + not_useful + } _ => { let arity = ctor_arity(tcx, single, left_ty); is_useful_specialized(tcx, m, v, single, arity, left_ty) @@ -370,6 +385,9 @@ fn missing_ctor(tcx: ty::ctxt, m: matrix, left_ty: ty::t) -> Option { 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 From f7a0cd89b18bf99c02d32e9e6c7ae293f144bce8 Mon Sep 17 00:00:00 2001 From: Jakub Wieczorek Date: Tue, 4 Dec 2012 02:48:56 +0000 Subject: [PATCH 10/13] Add two more cases to non-exhaustive-match --- src/test/compile-fail/non-exhaustive-match.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/test/compile-fail/non-exhaustive-match.rs b/src/test/compile-fail/non-exhaustive-match.rs index fafb4d51f444f..2cddf9a198bc2 100644 --- a/src/test/compile-fail/non-exhaustive-match.rs +++ b/src/test/compile-fail/non-exhaustive-match.rs @@ -30,6 +30,16 @@ fn main() { [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] => {} From 7bd069810c096f87e24300545c7301b61b5f6c7c Mon Sep 17 00:00:00 2001 From: Jakub Wieczorek Date: Tue, 4 Dec 2012 20:55:12 +0000 Subject: [PATCH 11/13] Fix style --- src/librustc/middle/check_alt.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/librustc/middle/check_alt.rs b/src/librustc/middle/check_alt.rs index 1f8b315b9c80d..ff1c426580766 100644 --- a/src/librustc/middle/check_alt.rs +++ b/src/librustc/middle/check_alt.rs @@ -144,7 +144,9 @@ impl ctor : cmp::Eq { cv0_self == cv0_other && cv1_self == cv1_other } (vec(n_self), vec(n_other)) => n_self == n_other, - (vec_with_tail(n_self), vec_with_tail(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 @@ -570,7 +572,9 @@ fn specialize(tcx: ty::ctxt, r: ~[@pat], ctor_id: ctor, arity: uint, vec(_) => { if elems.len() < arity && tail.is_some() { Some(vec::append( - vec::append(elems, vec::from_elem(arity - elems.len(), wild())), + vec::append(elems, vec::from_elem( + arity - elems.len(), wild()) + ), vec::tail(r) )) } else if elems.len() == arity { From df4966b387c3288ec3b17e4e9216900f9f060adc Mon Sep 17 00:00:00 2001 From: Jakub Wieczorek Date: Thu, 6 Dec 2012 08:29:08 +0000 Subject: [PATCH 12/13] Fix the vec pattern visitor --- src/libsyntax/visit.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 5246e2078e1d0..6926bb900e1ae 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -242,11 +242,13 @@ fn visit_pat(p: @pat, e: E, v: vt) { (v.visit_expr)(e2, e, v); } 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); - } + 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); + } } } } From 1a49c44647a5c00c44f605529ee227b4e318681b Mon Sep 17 00:00:00 2001 From: Jakub Wieczorek Date: Thu, 6 Dec 2012 23:24:41 +0000 Subject: [PATCH 13/13] Mark the right region on the vector tail type --- src/librustc/middle/typeck/check/alt.rs | 11 +++++++---- .../compile-fail/alt-vec-illegal-tail-loan.rs | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 src/test/compile-fail/alt-vec-illegal-tail-loan.rs diff --git a/src/librustc/middle/typeck/check/alt.rs b/src/librustc/middle/typeck/check/alt.rs index f75a966bacfbb..2acb3703b5d4f 100644 --- a/src/librustc/middle/typeck/check/alt.rs +++ b/src/librustc/middle/typeck/check/alt.rs @@ -578,7 +578,7 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) { } 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.ty, + ty::ty_evec(mt, _) | ty::ty_unboxed_vec(mt) => mt, _ => { tcx.sess.span_fatal( pat.span, @@ -588,15 +588,18 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) { } }; for elts.each |elt| { - check_pat(pcx, *elt, elt_type); + 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, mutbl: ast::m_imm}, - ty::vstore_slice(ty::re_static) + {ty: elt_type.ty, mutbl: elt_type.mutbl}, + ty::vstore_slice(region_var) ); check_pat(pcx, tail_pat, slice_ty); } diff --git a/src/test/compile-fail/alt-vec-illegal-tail-loan.rs b/src/test/compile-fail/alt-vec-illegal-tail-loan.rs new file mode 100644 index 0000000000000..69bc910669de9 --- /dev/null +++ b/src/test/compile-fail/alt-vec-illegal-tail-loan.rs @@ -0,0 +1,15 @@ +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)); + } +}