diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index 1b61e2bfb08..7206f3f03f5 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -526,7 +526,7 @@ fn enter_default<'a, 'b>( // 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 } @@ -712,18 +712,7 @@ fn enter_opt<'a, 'b>( } _ => { 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; @@ -1363,7 +1352,8 @@ fn compile_guard<'a, 'b>( 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(), @@ -1394,7 +1384,17 @@ fn compile_guard<'a, 'b>( // 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 }); @@ -1418,7 +1418,8 @@ fn compile_submatch<'a, 'b>( 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()), @@ -1448,7 +1449,8 @@ fn compile_submatch<'a, 'b>( m[0].data, m.slice(1, m.len()), vals, - chk); + chk, + has_genuine_default); } _ => () } @@ -1466,9 +1468,10 @@ fn compile_submatch<'a, 'b>( 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) } } @@ -1478,7 +1481,8 @@ fn compile_submatch_continue<'a, 'b>( 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; @@ -1512,7 +1516,7 @@ fn compile_submatch_continue<'a, 'b>( rec_fields.as_slice(), val).as_slice(), rec_vals.append(vals_left.as_slice()).as_slice(), - chk); + chk, has_genuine_default); }); return; } @@ -1537,7 +1541,7 @@ fn compile_submatch_continue<'a, 'b>( val, n_tup_elts).as_slice(), tup_vals.append(vals_left.as_slice()).as_slice(), - chk); + chk, has_genuine_default); return; } @@ -1562,7 +1566,7 @@ fn compile_submatch_continue<'a, 'b>( 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; } @@ -1571,7 +1575,7 @@ fn compile_submatch_continue<'a, 'b>( 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; } @@ -1580,7 +1584,7 @@ fn compile_submatch_continue<'a, 'b>( 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; } @@ -1637,9 +1641,9 @@ fn compile_submatch_continue<'a, 'b>( // 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; @@ -1689,6 +1693,16 @@ fn compile_submatch_continue<'a, 'b>( } }; 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 => { @@ -1726,8 +1740,10 @@ fn compile_submatch_continue<'a, 'b>( 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); } _ => () @@ -1767,27 +1783,38 @@ fn compile_submatch_continue<'a, 'b>( 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); + } } } } @@ -1892,7 +1919,22 @@ fn trans_match_inner<'a>(scope_cx: &'a Block<'a>, })); } - 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() { diff --git a/src/test/run-pass/issue-13027.rs b/src/test/run-pass/issue-13027.rs index f2f0418a33f..fc3b1c7cb1a 100644 --- a/src/test/run-pass/issue-13027.rs +++ b/src/test/run-pass/issue-13027.rs @@ -23,6 +23,7 @@ pub fn main() { multi_pats_shadow_range(); lit_shadow_multi_pats(); range_shadow_multi_pats(); + misc(); } fn lit_shadow_range() { @@ -168,3 +169,18 @@ fn range_shadow_multi_pats() { _ => 3, }); } + +fn misc() { + enum Foo { + Bar(uint, bool) + } + // This test basically mimics how trace_macros! macro is implemented, + // which is a rare combination of vector patterns, multiple wild-card + // patterns and guard functions. + let r = match [Bar(0, false)].as_slice() { + [Bar(_, pred)] if pred => 1, + [Bar(_, pred)] if !pred => 2, + _ => 0, + }; + assert_eq!(2, r); +} diff --git a/src/test/run-pass/issue-13867.rs b/src/test/run-pass/issue-13867.rs new file mode 100644 index 00000000000..fb76dbf2f69 --- /dev/null +++ b/src/test/run-pass/issue-13867.rs @@ -0,0 +1,57 @@ +// 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 or the MIT license +// , 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); +}