Change:
The following code prints instead of : Removing the first arm () makes it print as expected.
Based on reading PR and the code, I think the bug is a bug in , which doesn't properly consider guards and the possible overlap between literals and ranges. However, I don't think the fix in PR is quite right -- is not meant to be a general "might match" check. What seems to currently do is to iterate over the list of patterns, given that the given option matched. So, in the example test case, the state would be that the remaining patterns are: (1) with guard (1, 2) and the opt here is . What incorrectly does is to consider 1 and 3 a match but not 2. I'm not....100% sure what the intended answer is here actually, and the introduction of a guard makes things more complicated. In particular I don't quite understand where the precedence of earlier matches comes into play. Imagine that there were no guard: in that case, I could imagine the correct answer being just 1, because that match has the highest precedence. But the code doesn't appear to be setup that way, it seems to test each option independently with no regard for order. Therefore I gather that it would yield (in the case with no guard) 1 and 3. And then I think in the next recursion call to , we would not consider option 3 because it is a "default" match. (Ah, I see. What was confusing me is how a match like this would work . It seems like we would infinitely recurse because would always yield both options. But the answer is that we rule these cases out by an earlier check, which reports that the second 1 is unreachable.) Note: may shed some light on the intended workings of the code. For some reason I haven't read it. I'm a glutton for punishment I guess. I really ought to, particularly since he writes so well. Based on all this, the right fix is... not immediately obvious to me. One option is to have consider guards and precedence, but as I said that doesn't seem to be what the code is setup to do. Another option is to handle guards early -- right now, if the next option in the list has a guard, we will execute it, but only if the pattern is fully matched. I could imagine trying to test the remaining bits of pattern imperatively and test the guard, but that'd be a whole other code path (it might be one that we want, actually, if we are going to permit overloadable deref in that's a separate bug). Having unconditionally consider literals as matching against ranges feels a bit creepy. I think the code would fail to terminate for a test case like . Perhaps if the exhaustiveness check reported an error for this case, but it doesn't. Also, I think there is a related bug that the current patch would not solve, though if we patched up more thoroughly it might: My conclusion is thus that the best thing would be to change to consider guards and precedence, but I want to read marijn's article maybe and see if it offers a 3rd way.
I think I understand the logic of now. It translates the following: to something like: By the virtue of the fact that we are in already, assumes m1 is "stricter" than m2 because otherwise compiler should've bailed out with "unreachable pattern" error. Unfortunately, "m1 + guard" is not necessarily stricter than m2 since guard function may return false. Note, is translated correctly to: because we have exact equality of two here. So, we probably need to change the semantic of to do range overlap, single value + range overlap and range + single value overlap check after all. A possible optimization is that the extended check is only necessary if there's a guard.
I've revised . It now only touches and passes a more comprehensive test. It also Please review.

Discussion:
vec_len(/* length */ uint, VecLenOpt, /*range of matches*/(uint, uint)) } <ins> fn lit_to_expr(tcx: &ty::ctxt, a: &Lit) -> @ast::Expr { match *a { ExprLit(existing_a_expr) => existing_a_expr, ConstLit(a_const) => const_eval::lookup_const_by_id(tcx, a_const).unwrap(), UnitLikeStructLit(_) => fail!("lit_to_expr: unexpected struct lit"), } } </ins> fn opt_eq(tcx: &ty::ctxt, a: &Opt, b: &Opt) -> bool { match (a, b) { <ins> (&lit(UnitLikeStructLit(a)), &lit(UnitLikeStructLit(b))) => a == b, </ins> (&lit(a), &lit(b)) => { <del> match (a, b) { (UnitLikeStructLit(a), UnitLikeStructLit(b)) => a == b, _ => { let a_expr; match a { ExprLit(existing_a_expr) => a_expr = existing_a_expr, ConstLit(a_const) => { let e = const_eval::lookup_const_by_id(tcx, a_const); a_expr = e.unwrap(); } UnitLikeStructLit(_) => { fail!("UnitLikeStructLit should have been handled  above") } } let b_expr; match b { ExprLit(existing_b_expr) => b_expr = existing_b_expr, ConstLit(b_const) => { let e = const_eval::lookup_const_by_id(tcx, b_const); b_expr = e.unwrap(); } UnitLikeStructLit(_) => { fail!("UnitLikeStructLit should have been handled  above") } } match const_eval::compare_lit_exprs(tcx, a_expr, b_expr) { Some(val1) => val1 == 0, None => fail!("compare_list_exprs: type mismatch"), } } </del> <ins> let a_expr = lit_to_expr(tcx, &a); let b_expr = lit_to_expr(tcx, &b); match const_eval::compare_lit_exprs(tcx, a_expr, b_expr) { Some(val1) => val1 == 0, None => fail!("compare_list_exprs: type mismatch"), </ins> } } (&range(a1, a2), &range(b1, b2)) => {

} } <ins> fn opt_overlap(tcx: &ty::ctxt, a: &Opt, b: &Opt) -> bool { match (a, b) { (&lit(a), &lit(b)) => { let a_expr = lit_to_expr(tcx, &a); let b_expr = lit_to_expr(tcx, &b); match const_eval::compare_lit_exprs(tcx, a_expr, b_expr) { Some(val1) => val1 == 0, None => fail!("opt_overlap: type mismatch"), } } (&range(a1, a2), &range(b1, b2)) => { let m1 = const_eval::compare_lit_exprs(tcx, a1, b2); let m2 = const_eval::compare_lit_exprs(tcx, b1, a2); match (m1, m2) { // two ranges [a1, a2] and [b1, b2] overlap iff: //      a1 <= b2 && b1 <= a2 (Some(val1), Some(val2)) => (val1 <= 0 && val2 <= 0), _ => fail!("opt_overlap: type mismatch"), } } (&range(a1, a2), &lit(b)) | (&lit(b), &range(a1, a2)) => { let b_expr = lit_to_expr(tcx, &b); let m1 = const_eval::compare_lit_exprs(tcx, a1, b_expr); let m2 = const_eval::compare_lit_exprs(tcx, a2, b_expr); match (m1, m2) { // b is in range [a1, a2] iff a1 <= b and b <= a2 (Some(val1), Some(val2)) => (val1 <= 0 && 0 <= val2), _ => fail!("opt_overlap: type mismatch"), } } _ => fail!("opt_overlap: expect lit or range") } } </ins> pub enum opt_result<'a> { single_result(Result<'a>), lower_bound(Result<'a>),

} } <del> type enter_pat<'a> = 'a |@ast::Pat| -> Option<Vec<@ast::Pat> >; </del> <ins> type enter_pat<'a> = 'a |@ast::Pat| -> Option<Vec<@ast::Pat>>; </ins> fn enter_match<'r,'b>( bcx: &'b Block<'b>,

let tcx = bcx.tcx(); let dummy = @ast::Pat {id: 0, node: ast::PatWild, span: DUMMY_SP}; let mut i = 0; <ins> // By the virtue of fact that we are in `trans` already, `enter_opt` is able // to prune sub-match tree aggressively based on exact equality. But when it // comes to literal or range, that strategy may lead to wrong result if there // are guard function or multiple patterns inside tuple; in that case, pruning // based on the overlap of patterns is required. // // Ideally, when constructing the sub-match tree for certain arm, only those // arms beneath it matter. But that isn't how algorithm works right now and // all other arms are taken into consideration when computing `guarded` below. // That is ok since each round of `compile_submatch` guarantees to trim one // "column" of arm patterns and the algorithm will converge. let guarded = m.iter().any(|x| x.data.arm.guard.is_some()); let multi_pats = m.len() > 0 && m[0].pats.len() > 1; </ins> enter_match(bcx, tcx.def_map, m, col, val, |p| { let answer = match p.node { ast::PatEnum(..) | ast::PatIdent(_, _, None) if pat_is_const(tcx.def_map, p) => { let const_def = tcx.def_map.borrow().get_copy(&p.id); let const_def_id = ast_util::def_id_of_def(const_def); <del> if opt_eq(tcx, &lit(ConstLit(const_def_id)), opt) { Some(Vec::new()) } else { None </del> <ins> let konst = lit(ConstLit(const_def_id)); match guarded || multi_pats { false if opt_eq(tcx, &konst, opt) => Some(Vec::new()), true if opt_overlap(tcx, &konst, opt) => Some(Vec::new()), _ => None, </ins> } } ast::PatEnum(_, ref subpats) => {

} } ast::PatLit(l) => { <del> if opt_eq(tcx, &lit(ExprLit(l)), opt) {Some(Vec::new())} else {None} </del> <ins> let lit_expr = lit(ExprLit(l)); match guarded || multi_pats { false if opt_eq(tcx, &lit_expr, opt) => Some(Vec::new()), true if opt_overlap(tcx, &lit_expr, opt) => Some(Vec::new()), _ => None, } </ins> } ast::PatRange(l1, l2) => { <del> if opt_eq(tcx, &range(l1, l2), opt) {Some(Vec::new())} else {None} </del> <ins> let rng = range(l1, l2); match guarded || multi_pats { false if opt_eq(tcx, &rng, opt) => Some(Vec::new()), true if opt_overlap(tcx, &rng, opt) => Some(Vec::new()), _ => None, } </ins> } ast::PatStruct(_, ref field_pats, _) => { if opt_eq(tcx, &variant_opt(bcx, p.id), opt) {

<ins> // 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. pub fn main() { let x = 1; let y = 2; assert_eq!(3, match (x, y) { (1, 1) => 1, (2, 2) => 2, (1..2, 2) => 3, _ => 4, }); // nested tuple assert_eq!(3, match ((x, y),) { ((1, 1),) => 1, ((2, 2),) => 2, ((1..2, 2),) => 3, _ => 4, }); } </ins>

<ins> // 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. // Tests that match expression handles overlapped literal and range // properly in the presence of guard function. fn val() -> uint { 1 } static CONST: uint = 1; pub fn main() { lit_shadow_range(); range_shadow_lit(); range_shadow_range(); multi_pats_shadow_lit(); multi_pats_shadow_range(); lit_shadow_multi_pats(); range_shadow_multi_pats(); } fn lit_shadow_range() { assert_eq!(2, match 1 { 1 if false => 1, 1..2 => 2, _ => 3 }); let x = 0; assert_eq!(2, match x+1 { 0 => 0, 1 if false => 1, 1..2 => 2, _ => 3 }); assert_eq!(2, match val() { 1 if false => 1, 1..2 => 2, _ => 3 }); assert_eq!(2, match CONST { 0 => 0, 1 if false => 1, 1..2 => 2, _ => 3 }); // value is out of the range of second arm, should match wildcard pattern assert_eq!(3, match 3 { 1 if false => 1, 1..2 => 2, _ => 3 }); } fn range_shadow_lit() { assert_eq!(2, match 1 { 1..2 if false => 1, 1 => 2, _ => 3 }); let x = 0; assert_eq!(2, match x+1 { 0 => 0, 1..2 if false => 1, 1 => 2, _ => 3 }); assert_eq!(2, match val() { 1..2 if false => 1, 1 => 2, _ => 3 }); assert_eq!(2, match CONST { 0 => 0, 1..2 if false => 1, 1 => 2, _ => 3 }); // ditto assert_eq!(3, match 3 { 1..2 if false => 1, 1 => 2, _ => 3 }); } fn range_shadow_range() { assert_eq!(2, match 1 { 0..2 if false => 1, 1..3 => 2, _ => 3, }); let x = 0; assert_eq!(2, match x+1 { 100 => 0, 0..2 if false => 1, 1..3 => 2, _ => 3, }); assert_eq!(2, match val() { 0..2 if false => 1, 1..3 => 2, _ => 3, }); assert_eq!(2, match CONST { 100 => 0, 0..2 if false => 1, 1..3 => 2, _ => 3, }); // ditto assert_eq!(3, match 5 { 0..2 if false => 1, 1..3 => 2, _ => 3, }); } fn multi_pats_shadow_lit() { assert_eq!(2, match 1 { 100 => 0, 0 | 1..10 if false => 1, 1 => 2, _ => 3, }); } fn multi_pats_shadow_range() { assert_eq!(2, match 1 { 100 => 0, 0 | 1..10 if false => 1, 1..3 => 2, _ => 3, }); } fn lit_shadow_multi_pats() { assert_eq!(2, match 1 { 100 => 0, 1 if false => 1, 0 | 1..10 => 2, _ => 3, }); } fn range_shadow_multi_pats() { assert_eq!(2, match 1 { 100 => 0, 1..3 if false => 1, 0 | 1..10 => 2, _ => 3, }); } </ins>