// Collect all of the matches that can match against anything.
let matches = enter_match(bcx, dm, m, col, val, |p| {
match p.node {
- ast::PatWild | ast::PatWildMulti | ast::PatTup(_) => Some(Vec::new()),
+ ast::PatWild | ast::PatWildMulti => Some(Vec::new()),
ast::PatIdent(_, _, None) if pat_is_binding(dm, p) => Some(Vec::new()),
_ => None
}
}
_ => {
assert_is_binding_or_wild(bcx, p);
- // In most cases, a binding/wildcard match be
- // considered to match against any Opt. However, when
- // doing vector pattern matching, submatches are
- // considered even if the eventual match might be from
- // a different submatch. Thus, when a submatch fails
- // when doing a vector match, we proceed to the next
- // submatch. Thus, including a default match would
- // cause the default match to fire spuriously.
- match *opt {
- vec_len(..) => None,
- _ => Some(Vec::from_elem(variant_size, dummy))
- }
+ Some(Vec::from_elem(variant_size, dummy))
}
};
i += 1;
data: &ArmData,
m: &'a [Match<'a, 'b>],
vals: &[ValueRef],
- chk: &FailureHandler)
+ chk: &FailureHandler,
+ has_genuine_default: bool)
-> &'b Block<'b> {
debug!("compile_guard(bcx={}, guard_expr={}, m={}, vals={})",
bcx.to_str(),
// Guard does not match: free the values we copied,
// and remove all bindings from the lllocals table
let bcx = drop_bindings(bcx, data);
- compile_submatch(bcx, m, vals, chk);
+ match chk {
+ // If the default arm is the only one left, move on to the next
+ // condition explicitly rather than (possibly) falling back to
+ // the default arm.
+ &JumpToBasicBlock(_) if m.len() == 1 && has_genuine_default => {
+ Br(bcx, chk.handle_fail());
+ }
+ _ => {
+ compile_submatch(bcx, m, vals, chk, has_genuine_default);
+ }
+ };
bcx
});
bcx: &'b Block<'b>,
m: &'a [Match<'a, 'b>],
vals: &[ValueRef],
- chk: &FailureHandler) {
+ chk: &FailureHandler,
+ has_genuine_default: bool) {
debug!("compile_submatch(bcx={}, m={}, vals={})",
bcx.to_str(),
m.repr(bcx.tcx()),
m[0].data,
m.slice(1, m.len()),
vals,
- chk);
+ chk,
+ has_genuine_default);
}
_ => ()
}
vals,
chk,
col,
- val)
+ val,
+ has_genuine_default)
} else {
- compile_submatch_continue(bcx, m, vals, chk, col, val)
+ compile_submatch_continue(bcx, m, vals, chk, col, val, has_genuine_default)
}
}
vals: &[ValueRef],
chk: &FailureHandler,
col: uint,
- val: ValueRef) {
+ val: ValueRef,
+ has_genuine_default: bool) {
let fcx = bcx.fcx;
let tcx = bcx.tcx();
let dm = &tcx.def_map;
rec_fields.as_slice(),
val).as_slice(),
rec_vals.append(vals_left.as_slice()).as_slice(),
- chk);
+ chk, has_genuine_default);
});
return;
}
val,
n_tup_elts).as_slice(),
tup_vals.append(vals_left.as_slice()).as_slice(),
- chk);
+ chk, has_genuine_default);
return;
}
enter_tuple_struct(bcx, dm, m, col, val,
struct_element_count).as_slice(),
llstructvals.append(vals_left.as_slice()).as_slice(),
- chk);
+ chk, has_genuine_default);
return;
}
compile_submatch(bcx,
enter_uniq(bcx, dm, m, col, val).as_slice(),
(vec!(llbox)).append(vals_left.as_slice()).as_slice(),
- chk);
+ chk, has_genuine_default);
return;
}
compile_submatch(bcx,
enter_region(bcx, dm, m, col, val).as_slice(),
(vec!(loaded_val)).append(vals_left.as_slice()).as_slice(),
- chk);
+ chk, has_genuine_default);
return;
}
// Compile subtrees for each option
for (i, opt) in opts.iter().enumerate() {
- // In some cases in vector pattern matching, we need to override
- // the failure case so that instead of failing, it proceeds to
- // try more matching. branch_chk, then, is the proper failure case
+ // In some cases of range and vector pattern matching, we need to
+ // override the failure case so that instead of failing, it proceeds
+ // to try more matching. branch_chk, then, is the proper failure case
// for the current conditional branch.
let mut branch_chk = None;
let mut opt_cx = else_cx;
}
};
bcx = fcx.new_temp_block("compare_next");
+
+ // If none of the sub-cases match, and the current condition
+ // is guarded or has multiple patterns, move on to the next
+ // condition, if there is any, rather than falling back to
+ // the default.
+ let guarded = m[i].data.arm.guard.is_some();
+ let multi_pats = m[i].pats.len() > 1;
+ if i+1 < len && (guarded || multi_pats) {
+ branch_chk = Some(JumpToBasicBlock(bcx.llbb));
+ }
CondBr(after_cx, matches, opt_cx.llbb, bcx.llbb);
}
compare_vec_len => {
bcx = fcx.new_temp_block("compare_vec_len_next");
// If none of these subcases match, move on to the
- // next condition.
- branch_chk = Some(JumpToBasicBlock(bcx.llbb));
+ // next condition if there is any.
+ if i+1 < len {
+ branch_chk = Some(JumpToBasicBlock(bcx.llbb));
+ }
CondBr(after_cx, matches, opt_cx.llbb, bcx.llbb);
}
_ => ()
compile_submatch(opt_cx,
opt_ms.as_slice(),
opt_vals.as_slice(),
- chk)
+ chk,
+ has_genuine_default)
}
Some(branch_chk) => {
compile_submatch(opt_cx,
opt_ms.as_slice(),
opt_vals.as_slice(),
- &branch_chk)
+ &branch_chk,
+ has_genuine_default)
}
}
}
// Compile the fall-through case, if any
- if !exhaustive {
+ if !exhaustive && kind != single {
if kind == compare || kind == compare_vec_len {
Br(bcx, else_cx.llbb);
}
- if kind != single {
- compile_submatch(else_cx,
- defaults.as_slice(),
- vals_left.as_slice(),
- chk);
+ match chk {
+ // If there is only one default arm left, move on to the next
+ // condition explicitly rather than (eventually) falling back to
+ // the last default arm.
+ &JumpToBasicBlock(_) if defaults.len() == 1 && has_genuine_default => {
+ Br(else_cx, chk.handle_fail());
+ }
+ _ => {
+ compile_submatch(else_cx,
+ defaults.as_slice(),
+ vals_left.as_slice(),
+ chk,
+ has_genuine_default);
+ }
}
}
}
}));
}
- compile_submatch(bcx, matches.as_slice(), [discr_datum.val], &chk);
+ // `compile_submatch` works one column of arm patterns a time and
+ // then peels that column off. So as we progress, it may become
+ // impossible to know whether we have a genuine default arm, i.e.
+ // `_ => foo` or not. Sometimes it is important to know that in order
+ // to decide whether moving on to the next condition or falling back
+ // to the default arm.
+ let has_default = arms.len() > 0 && {
+ let ref pats = arms.last().unwrap().pats;
+
+ pats.len() == 1
+ && match pats.last().unwrap().node {
+ ast::PatWild => true, _ => false
+ }
+ };
+
+ compile_submatch(bcx, matches.as_slice(), [discr_datum.val], &chk, has_default);
let mut arm_cxs = Vec::new();
for arm_data in arm_datas.iter() {
--- /dev/null
+// Copyright 2012-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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that codegen works correctly when there are multiple refutable
+// patterns in match expression.
+
+enum Foo {
+ FooUint(uint),
+ FooNullary,
+}
+
+fn main() {
+ let r = match (FooNullary, 'a') {
+ (FooUint(..), 'a'..'z') => 1,
+ (FooNullary, 'x') => 2,
+ _ => 0
+ };
+ assert_eq!(r, 0);
+
+ let r = match (FooUint(0), 'a') {
+ (FooUint(1), 'a'..'z') => 1,
+ (FooUint(..), 'x') => 2,
+ (FooNullary, 'a') => 3,
+ _ => 0
+ };
+ assert_eq!(r, 0);
+
+ let r = match ('a', FooUint(0)) {
+ ('a'..'z', FooUint(1)) => 1,
+ ('x', FooUint(..)) => 2,
+ ('a', FooNullary) => 3,
+ _ => 0
+ };
+ assert_eq!(r, 0);
+
+ let r = match ('a', 'a') {
+ ('a'..'z', 'b') => 1,
+ ('x', 'a'..'z') => 2,
+ _ => 0
+ };
+ assert_eq!(r, 0);
+
+ let r = match ('a', 'a') {
+ ('a'..'z', 'b') => 1,
+ ('x', 'a'..'z') => 2,
+ ('a', 'a') => 3,
+ _ => 0
+ };
+ assert_eq!(r, 3);
+}