Skip to content

Commit 20953bb

Browse files
committed
Merge branch 'match' of https://github.com/msullivan/rust into rollup
2 parents f007a46 + 437a4c2 commit 20953bb

File tree

4 files changed

+196
-61
lines changed

4 files changed

+196
-61
lines changed

src/librustc/middle/trans/_match.rs

+165-50
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,51 @@
145145
* - `store_non_ref_bindings()`
146146
* - `insert_lllocals()`
147147
*
148+
*
149+
* ## Notes on vector pattern matching.
150+
*
151+
* Vector pattern matching is surprisingly tricky. The problem is that
152+
* the structure of the vector isn't fully known, and slice matches
153+
* can be done on subparts of it.
154+
*
155+
* The way that vector pattern matches are dealt with, then, is as
156+
* follows. First, we make the actual condition associated with a
157+
* vector pattern simply a vector length comparison. So the pattern
158+
* [1, .. x] gets the condition "vec len >= 1", and the pattern
159+
* [.. x] gets the condition "vec len >= 0". The problem here is that
160+
* having the condition "vec len >= 1" hold clearly does not mean that
161+
* only a pattern that has exactly that condition will match. This
162+
* means that it may well be the case that a condition holds, but none
163+
* of the patterns matching that condition match; to deal with this,
164+
* when doing vector length matches, we have match failures proceed to
165+
* the next condition to check.
166+
*
167+
* There are a couple more subtleties to deal with. While the "actual"
168+
* condition associated with vector length tests is simply a test on
169+
* the vector length, the actual vec_len Opt entry contains more
170+
* information used to restrict which matches are associated with it.
171+
* So that all matches in a submatch are matching against the same
172+
* values from inside the vector, they are split up by how many
173+
* elements they match at the front and at the back of the vector. In
174+
* order to make sure that arms are properly checked in order, even
175+
* with the overmatching conditions, each vec_len Opt entry is
176+
* associated with a range of matches.
177+
* Consider the following:
178+
*
179+
* match &[1, 2, 3] {
180+
* [1, 1, .. _] => 0,
181+
* [1, 2, 2, .. _] => 1,
182+
* [1, 2, 3, .. _] => 2,
183+
* [1, 2, .. _] => 3,
184+
* _ => 4
185+
* }
186+
* The proper arm to match is arm 2, but arms 0 and 3 both have the
187+
* condition "len >= 2". If arm 3 was lumped in with arm 0, then the
188+
* wrong branch would be taken. Instead, vec_len Opts are associated
189+
* with a contiguous range of matches that have the same "shape".
190+
* This is sort of ugly and requires a bunch of special handling of
191+
* vec_len options.
192+
*
148193
*/
149194

150195

@@ -189,14 +234,19 @@ enum Lit {
189234
ConstLit(ast::def_id), // the def ID of the constant
190235
}
191236

237+
#[deriving(Eq)]
238+
pub enum VecLenOpt {
239+
vec_len_eq,
240+
vec_len_ge(/* length of prefix */uint)
241+
}
242+
192243
// An option identifying a branch (either a literal, a enum variant or a
193244
// range)
194245
enum Opt {
195246
lit(Lit),
196247
var(/* disr val */ uint, @adt::Repr),
197248
range(@ast::expr, @ast::expr),
198-
vec_len_eq(uint),
199-
vec_len_ge(uint, /* slice */uint)
249+
vec_len(/* length */ uint, VecLenOpt, /*range of matches*/(uint, uint))
200250
}
201251

202252
fn opt_eq(tcx: ty::ctxt, a: &Opt, b: &Opt) -> bool {
@@ -247,9 +297,9 @@ fn opt_eq(tcx: ty::ctxt, a: &Opt, b: &Opt) -> bool {
247297
}
248298
}
249299
(&var(a, _), &var(b, _)) => a == b,
250-
(&vec_len_eq(a), &vec_len_eq(b)) => a == b,
251-
(&vec_len_ge(a, _), &vec_len_ge(b, _)) => a == b,
252-
_ => false
300+
(&vec_len(a1, a2, _), &vec_len(b1, b2, _)) =>
301+
a1 == b1 && a2 == b2,
302+
_ => false
253303
}
254304
}
255305

@@ -283,10 +333,10 @@ fn trans_opt(bcx: @mut Block, o: &Opt) -> opt_result {
283333
return range_result(rslt(bcx, consts::const_expr(ccx, l1)),
284334
rslt(bcx, consts::const_expr(ccx, l2)));
285335
}
286-
vec_len_eq(n) => {
336+
vec_len(n, vec_len_eq, _) => {
287337
return single_result(rslt(bcx, C_int(ccx, n as int)));
288338
}
289-
vec_len_ge(n, _) => {
339+
vec_len(n, vec_len_ge(_), _) => {
290340
return lower_bound(rslt(bcx, C_int(ccx, n as int)));
291341
}
292342
}
@@ -471,10 +521,11 @@ fn enter_match<'r>(bcx: @mut Block,
471521
}
472522

473523
fn enter_default<'r>(bcx: @mut Block,
474-
dm: DefMap,
475-
m: &[Match<'r>],
476-
col: uint,
477-
val: ValueRef)
524+
dm: DefMap,
525+
m: &[Match<'r>],
526+
col: uint,
527+
val: ValueRef,
528+
chk: Option<mk_fail>)
478529
-> ~[Match<'r>] {
479530
debug!("enter_default(bcx=%s, m=%s, col=%u, val=%s)",
480531
bcx.to_str(),
@@ -483,13 +534,36 @@ fn enter_default<'r>(bcx: @mut Block,
483534
bcx.val_to_str(val));
484535
let _indenter = indenter();
485536

486-
do enter_match(bcx, dm, m, col, val) |p| {
537+
// Collect all of the matches that can match against anything.
538+
let matches = do enter_match(bcx, dm, m, col, val) |p| {
487539
match p.node {
488540
ast::pat_wild | ast::pat_tup(_) => Some(~[]),
489541
ast::pat_ident(_, _, None) if pat_is_binding(dm, p) => Some(~[]),
490542
_ => None
491543
}
492-
}
544+
};
545+
546+
// Ok, now, this is pretty subtle. A "default" match is a match
547+
// that needs to be considered if none of the actual checks on the
548+
// value being considered succeed. The subtlety lies in that sometimes
549+
// identifier/wildcard matches are *not* default matches. Consider:
550+
// "match x { _ if something => foo, true => bar, false => baz }".
551+
// There is a wildcard match, but it is *not* a default case. The boolean
552+
// case on the value being considered is exhaustive. If the case is
553+
// exhaustive, then there are no defaults.
554+
//
555+
// We detect whether the case is exhaustive in the following
556+
// somewhat kludgy way: if the last wildcard/binding match has a
557+
// guard, then by non-redundancy, we know that there aren't any
558+
// non guarded matches, and thus by exhaustiveness, we know that
559+
// we don't need any default cases. If the check *isn't* nonexhaustive
560+
// (because chk is Some), then we need the defaults anyways.
561+
let is_exhaustive = match matches.last_opt() {
562+
Some(m) if m.data.arm.guard.is_some() && chk.is_none() => true,
563+
_ => false
564+
};
565+
566+
if is_exhaustive { ~[] } else { matches }
493567
}
494568

495569
// <pcwalton> nmatsakis: what does enter_opt do?
@@ -523,17 +597,19 @@ fn enter_opt<'r>(bcx: @mut Block,
523597
variant_size: uint,
524598
val: ValueRef)
525599
-> ~[Match<'r>] {
526-
debug!("enter_opt(bcx=%s, m=%s, col=%u, val=%s)",
600+
debug!("enter_opt(bcx=%s, m=%s, opt=%?, col=%u, val=%s)",
527601
bcx.to_str(),
528602
m.repr(bcx.tcx()),
603+
*opt,
529604
col,
530605
bcx.val_to_str(val));
531606
let _indenter = indenter();
532607

533608
let tcx = bcx.tcx();
534609
let dummy = @ast::pat {id: 0, node: ast::pat_wild, span: dummy_sp()};
610+
let mut i = 0;
535611
do enter_match(bcx, tcx.def_map, m, col, val) |p| {
536-
match p.node {
612+
let answer = match p.node {
537613
ast::pat_enum(*) |
538614
ast::pat_ident(_, _, None) if pat_is_const(tcx.def_map, p) => {
539615
let const_def = tcx.def_map.get_copy(&p.id);
@@ -599,32 +675,53 @@ fn enter_opt<'r>(bcx: @mut Block,
599675
}
600676
}
601677
ast::pat_vec(ref before, slice, ref after) => {
678+
let (lo, hi) = match *opt {
679+
vec_len(_, _, (lo, hi)) => (lo, hi),
680+
_ => tcx.sess.span_bug(p.span,
681+
"vec pattern but not vec opt")
682+
};
683+
602684
match slice {
603-
Some(slice) => {
685+
Some(slice) if i >= lo && i <= hi => {
604686
let n = before.len() + after.len();
605-
let i = before.len();
606-
if opt_eq(tcx, &vec_len_ge(n, i), opt) {
687+
let this_opt = vec_len(n, vec_len_ge(before.len()),
688+
(lo, hi));
689+
if opt_eq(tcx, &this_opt, opt) {
607690
Some(vec::append_one((*before).clone(), slice) +
608691
*after)
609692
} else {
610693
None
611694
}
612695
}
613-
None => {
696+
None if i >= lo && i <= hi => {
614697
let n = before.len();
615-
if opt_eq(tcx, &vec_len_eq(n), opt) {
698+
if opt_eq(tcx, &vec_len(n, vec_len_eq, (lo,hi)), opt) {
616699
Some((*before).clone())
617700
} else {
618701
None
619702
}
620703
}
704+
_ => None
621705
}
622706
}
623707
_ => {
624708
assert_is_binding_or_wild(bcx, p);
625-
Some(vec::from_elem(variant_size, dummy))
709+
// In most cases, a binding/wildcard match be
710+
// considered to match against any Opt. However, when
711+
// doing vector pattern matching, submatches are
712+
// considered even if the eventual match might be from
713+
// a different submatch. Thus, when a submatch fails
714+
// when doing a vector match, we proceed to the next
715+
// submatch. Thus, including a default match would
716+
// cause the default match to fire spuriously.
717+
match *opt {
718+
vec_len(*) => None,
719+
_ => Some(vec::from_elem(variant_size, dummy))
720+
}
626721
}
627-
}
722+
};
723+
i += 1;
724+
answer
628725
}
629726
}
630727

@@ -805,9 +902,25 @@ fn get_options(bcx: @mut Block, m: &[Match], col: uint) -> ~[Opt] {
805902
if set.iter().any(|l| opt_eq(tcx, l, &val)) {return;}
806903
set.push(val);
807904
}
905+
// Vector comparisions are special in that since the actual
906+
// conditions over-match, we need to be careful about them. This
907+
// means that in order to properly handle things in order, we need
908+
// to not always merge conditions.
909+
fn add_veclen_to_set(set: &mut ~[Opt], i: uint,
910+
len: uint, vlo: VecLenOpt) {
911+
match set.last_opt() {
912+
// If the last condition in the list matches the one we want
913+
// to add, then extend its range. Otherwise, make a new
914+
// vec_len with a range just covering the new entry.
915+
Some(&vec_len(len2, vlo2, (start, end)))
916+
if len == len2 && vlo == vlo2 =>
917+
set[set.len() - 1] = vec_len(len, vlo, (start, end+1)),
918+
_ => set.push(vec_len(len, vlo, (i, i)))
919+
}
920+
}
808921

809922
let mut found = ~[];
810-
for br in m.iter() {
923+
for (i, br) in m.iter().enumerate() {
811924
let cur = br.pats[col];
812925
match cur.node {
813926
ast::pat_lit(l) => {
@@ -852,12 +965,12 @@ fn get_options(bcx: @mut Block, m: &[Match], col: uint) -> ~[Opt] {
852965
add_to_set(ccx.tcx, &mut found, range(l1, l2));
853966
}
854967
ast::pat_vec(ref before, slice, ref after) => {
855-
let opt = match slice {
856-
None => vec_len_eq(before.len()),
857-
Some(_) => vec_len_ge(before.len() + after.len(),
858-
before.len())
968+
let (len, vec_opt) = match slice {
969+
None => (before.len(), vec_len_eq),
970+
Some(_) => (before.len() + after.len(),
971+
vec_len_ge(before.len()))
859972
};
860-
add_to_set(ccx.tcx, &mut found, opt);
973+
add_veclen_to_set(&mut found, i, len, vec_opt);
861974
}
862975
_ => {}
863976
}
@@ -1075,13 +1188,13 @@ fn pick_col(m: &[Match]) -> uint {
10751188
}
10761189
let mut scores = vec::from_elem(m[0].pats.len(), 0u);
10771190
for br in m.iter() {
1078-
let mut i = 0u;
1079-
for p in br.pats.iter() { scores[i] += score(*p); i += 1u; }
1191+
for (i, p) in br.pats.iter().enumerate() {
1192+
scores[i] += score(*p);
1193+
}
10801194
}
10811195
let mut max_score = 0u;
10821196
let mut best_col = 0u;
1083-
let mut i = 0u;
1084-
for score in scores.iter() {
1197+
for (i, score) in scores.iter().enumerate() {
10851198
let score = *score;
10861199

10871200
// Irrefutable columns always go first, they'd only be duplicated in
@@ -1090,7 +1203,6 @@ fn pick_col(m: &[Match]) -> uint {
10901203
// If no irrefutable ones are found, we pick the one with the biggest
10911204
// branching factor.
10921205
if score > max_score { max_score = score; best_col = i; }
1093-
i += 1u;
10941206
}
10951207
return best_col;
10961208
}
@@ -1460,7 +1572,7 @@ fn compile_submatch_continue(mut bcx: @mut Block,
14601572
test_val = Load(bcx, val);
14611573
kind = compare;
14621574
},
1463-
vec_len_eq(*) | vec_len_ge(*) => {
1575+
vec_len(*) => {
14641576
let vt = tvec::vec_types(bcx, node_id_type(bcx, pat_id));
14651577
let unboxed = load_if_immediate(bcx, val, vt.vec_ty);
14661578
let (_, len) = tvec::get_base_and_len(
@@ -1487,16 +1599,19 @@ fn compile_submatch_continue(mut bcx: @mut Block,
14871599
C_int(ccx, 0) // Placeholder for when not using a switch
14881600
};
14891601

1490-
let defaults = enter_default(else_cx, dm, m, col, val);
1602+
let defaults = enter_default(else_cx, dm, m, col, val, chk);
14911603
let exhaustive = chk.is_none() && defaults.len() == 0u;
14921604
let len = opts.len();
1493-
let mut i = 0u;
14941605

14951606
// Compile subtrees for each option
1496-
for opt in opts.iter() {
1497-
i += 1u;
1607+
for (i, opt) in opts.iter().enumerate() {
1608+
// In some cases in vector pattern matching, we need to override
1609+
// the failure case so that instead of failing, it proceeds to
1610+
// try more matching. branch_chk, then, is the proper failure case
1611+
// for the current conditional branch.
1612+
let mut branch_chk = chk;
14981613
let mut opt_cx = else_cx;
1499-
if !exhaustive || i < len {
1614+
if !exhaustive || i+1 < len {
15001615
opt_cx = sub_block(bcx, "match_case");
15011616
match kind {
15021617
single => Br(bcx, opt_cx.llbb),
@@ -1586,6 +1701,10 @@ fn compile_submatch_continue(mut bcx: @mut Block,
15861701
}
15871702
};
15881703
bcx = sub_block(after_cx, "compare_vec_len_next");
1704+
1705+
// If none of these subcases match, move on to the
1706+
// next condition.
1707+
branch_chk = Some::<mk_fail>(|| bcx.llbb);
15891708
CondBr(after_cx, matches, opt_cx.llbb, bcx.llbb);
15901709
}
15911710
_ => ()
@@ -1604,17 +1723,13 @@ fn compile_submatch_continue(mut bcx: @mut Block,
16041723
unpacked = argvals;
16051724
opt_cx = new_bcx;
16061725
}
1607-
vec_len_eq(n) | vec_len_ge(n, _) => {
1608-
let n = match *opt {
1609-
vec_len_ge(*) => n + 1u,
1610-
_ => n
1611-
};
1612-
let slice = match *opt {
1613-
vec_len_ge(_, i) => Some(i),
1614-
_ => None
1726+
vec_len(n, vt, _) => {
1727+
let (n, slice) = match vt {
1728+
vec_len_ge(i) => (n + 1u, Some(i)),
1729+
vec_len_eq => (n, None)
16151730
};
1616-
let args = extract_vec_elems(opt_cx, pat_span, pat_id, n, slice,
1617-
val, test_val);
1731+
let args = extract_vec_elems(opt_cx, pat_span, pat_id, n,
1732+
slice, val, test_val);
16181733
size = args.vals.len();
16191734
unpacked = args.vals.clone();
16201735
opt_cx = args.bcx;
@@ -1623,7 +1738,7 @@ fn compile_submatch_continue(mut bcx: @mut Block,
16231738
}
16241739
let opt_ms = enter_opt(opt_cx, m, opt, col, size, val);
16251740
let opt_vals = vec::append(unpacked, vals_left);
1626-
compile_submatch(opt_cx, opt_ms, opt_vals, chk);
1741+
compile_submatch(opt_cx, opt_ms, opt_vals, branch_chk);
16271742
}
16281743

16291744
// Compile the fall-through case, if any

src/test/run-pass/issue-3121.rs

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
// xfail-test
1211
enum side { mayo, catsup, vinegar }
1312
enum order { hamburger, fries(side), shake }
1413
enum meal { to_go(order), for_here(order) }

0 commit comments

Comments
 (0)