@@ -256,43 +256,23 @@ enum Opt {
256256 vec_len( /* length */ uint , VecLenOpt , /*range of matches*/ ( uint , uint ) )
257257}
258258
259+ fn lit_to_expr ( tcx : & ty:: ctxt , a : & Lit ) -> @ast:: Expr {
260+ match * a {
261+ ExprLit ( existing_a_expr) => existing_a_expr,
262+ ConstLit ( a_const) => const_eval:: lookup_const_by_id ( tcx, a_const) . unwrap ( ) ,
263+ UnitLikeStructLit ( _) => fail ! ( "lit_to_expr: unexpected struct lit" ) ,
264+ }
265+ }
266+
259267fn opt_eq ( tcx : & ty:: ctxt , a : & Opt , b : & Opt ) -> bool {
260268 match ( a, b) {
269+ ( & lit( UnitLikeStructLit ( a) ) , & lit( UnitLikeStructLit ( b) ) ) => a == b,
261270 ( & lit( a) , & lit( b) ) => {
262- match ( a, b) {
263- ( UnitLikeStructLit ( a) , UnitLikeStructLit ( b) ) => a == b,
264- _ => {
265- let a_expr;
266- match a {
267- ExprLit ( existing_a_expr) => a_expr = existing_a_expr,
268- ConstLit ( a_const) => {
269- let e = const_eval:: lookup_const_by_id ( tcx, a_const) ;
270- a_expr = e. unwrap ( ) ;
271- }
272- UnitLikeStructLit ( _) => {
273- fail ! ( "UnitLikeStructLit should have been handled \
274- above")
275- }
276- }
277-
278- let b_expr;
279- match b {
280- ExprLit ( existing_b_expr) => b_expr = existing_b_expr,
281- ConstLit ( b_const) => {
282- let e = const_eval:: lookup_const_by_id ( tcx, b_const) ;
283- b_expr = e. unwrap ( ) ;
284- }
285- UnitLikeStructLit ( _) => {
286- fail ! ( "UnitLikeStructLit should have been handled \
287- above")
288- }
289- }
290-
291- match const_eval:: compare_lit_exprs ( tcx, a_expr, b_expr) {
292- Some ( val1) => val1 == 0 ,
293- None => fail ! ( "compare_list_exprs: type mismatch" ) ,
294- }
295- }
271+ let a_expr = lit_to_expr ( tcx, & a) ;
272+ let b_expr = lit_to_expr ( tcx, & b) ;
273+ match const_eval:: compare_lit_exprs ( tcx, a_expr, b_expr) {
274+ Some ( val1) => val1 == 0 ,
275+ None => fail ! ( "compare_list_exprs: type mismatch" ) ,
296276 }
297277 }
298278 ( & range( a1, a2) , & range( b1, b2) ) => {
@@ -310,6 +290,42 @@ fn opt_eq(tcx: &ty::ctxt, a: &Opt, b: &Opt) -> bool {
310290 }
311291}
312292
293+ fn opt_overlap ( tcx : & ty:: ctxt , a : & Opt , b : & Opt ) -> bool {
294+ match ( a, b) {
295+ ( & lit( a) , & lit( b) ) => {
296+ let a_expr = lit_to_expr ( tcx, & a) ;
297+ let b_expr = lit_to_expr ( tcx, & b) ;
298+ match const_eval:: compare_lit_exprs ( tcx, a_expr, b_expr) {
299+ Some ( val1) => val1 == 0 ,
300+ None => fail ! ( "opt_overlap: type mismatch" ) ,
301+ }
302+ }
303+
304+ ( & range( a1, a2) , & range( b1, b2) ) => {
305+ let m1 = const_eval:: compare_lit_exprs ( tcx, a1, b2) ;
306+ let m2 = const_eval:: compare_lit_exprs ( tcx, b1, a2) ;
307+ match ( m1, m2) {
308+ // two ranges [a1, a2] and [b1, b2] overlap iff:
309+ // a1 <= b2 && b1 <= a2
310+ ( Some ( val1) , Some ( val2) ) => ( val1 <= 0 && val2 <= 0 ) ,
311+ _ => fail ! ( "opt_overlap: type mismatch" ) ,
312+ }
313+ }
314+
315+ ( & range( a1, a2) , & lit( b) ) | ( & lit( b) , & range( a1, a2) ) => {
316+ let b_expr = lit_to_expr ( tcx, & b) ;
317+ let m1 = const_eval:: compare_lit_exprs ( tcx, a1, b_expr) ;
318+ let m2 = const_eval:: compare_lit_exprs ( tcx, a2, b_expr) ;
319+ match ( m1, m2) {
320+ // b is in range [a1, a2] iff a1 <= b and b <= a2
321+ ( Some ( val1) , Some ( val2) ) => ( val1 <= 0 && 0 <= val2) ,
322+ _ => fail ! ( "opt_overlap: type mismatch" ) ,
323+ }
324+ }
325+ _ => fail ! ( "opt_overlap: expect lit or range" )
326+ }
327+ }
328+
313329pub enum opt_result < ' a > {
314330 single_result( Result < ' a > ) ,
315331 lower_bound( Result < ' a > ) ,
@@ -490,7 +506,7 @@ fn assert_is_binding_or_wild(bcx: &Block, p: @ast::Pat) {
490506 }
491507}
492508
493- type enter_pat < ' a > = ' a |@ast:: Pat | -> Option < Vec < @ast:: Pat > > ;
509+ type enter_pat < ' a > = ' a |@ast:: Pat | -> Option < Vec < @ast:: Pat > > ;
494510
495511fn enter_match < ' r , ' b > (
496512 bcx : & ' b Block < ' b > ,
@@ -632,16 +648,30 @@ fn enter_opt<'r,'b>(
632648 let tcx = bcx. tcx ( ) ;
633649 let dummy = @ast:: Pat { id : 0 , node : ast:: PatWild , span : DUMMY_SP } ;
634650 let mut i = 0 ;
651+ // By the virtue of fact that we are in `trans` already, `enter_opt` is able
652+ // to prune sub-match tree aggressively based on exact equality. But when it
653+ // comes to literal or range, that strategy may lead to wrong result if there
654+ // are guard function or multiple patterns inside tuple; in that case, pruning
655+ // based on the overlap of patterns is required.
656+ //
657+ // Ideally, when constructing the sub-match tree for certain arm, only those
658+ // arms beneath it matter. But that isn't how algorithm works right now and
659+ // all other arms are taken into consideration when computing `guarded` below.
660+ // That is ok since each round of `compile_submatch` guarantees to trim one
661+ // "column" of arm patterns and the algorithm will converge.
662+ let guarded = m. iter ( ) . any ( |x| x. data . arm . guard . is_some ( ) ) ;
663+ let multi_pats = m. len ( ) > 0 && m[ 0 ] . pats . len ( ) > 1 ;
635664 enter_match ( bcx, tcx. def_map , m, col, val, |p| {
636665 let answer = match p. node {
637666 ast:: PatEnum ( ..) |
638667 ast:: PatIdent ( _, _, None ) if pat_is_const ( tcx. def_map , p) => {
639668 let const_def = tcx. def_map . borrow ( ) . get_copy ( & p. id ) ;
640669 let const_def_id = ast_util:: def_id_of_def ( const_def) ;
641- if opt_eq ( tcx, & lit ( ConstLit ( const_def_id) ) , opt) {
642- Some ( Vec :: new ( ) )
643- } else {
644- None
670+ let konst = lit ( ConstLit ( const_def_id) ) ;
671+ match guarded || multi_pats {
672+ false if opt_eq ( tcx, & konst, opt) => Some ( Vec :: new ( ) ) ,
673+ true if opt_overlap ( tcx, & konst, opt) => Some ( Vec :: new ( ) ) ,
674+ _ => None ,
645675 }
646676 }
647677 ast:: PatEnum ( _, ref subpats) => {
@@ -666,10 +696,20 @@ fn enter_opt<'r,'b>(
666696 }
667697 }
668698 ast:: PatLit ( l) => {
669- if opt_eq ( tcx, & lit ( ExprLit ( l) ) , opt) { Some ( Vec :: new ( ) ) } else { None }
699+ let lit_expr = lit ( ExprLit ( l) ) ;
700+ match guarded || multi_pats {
701+ false if opt_eq ( tcx, & lit_expr, opt) => Some ( Vec :: new ( ) ) ,
702+ true if opt_overlap ( tcx, & lit_expr, opt) => Some ( Vec :: new ( ) ) ,
703+ _ => None ,
704+ }
670705 }
671706 ast:: PatRange ( l1, l2) => {
672- if opt_eq ( tcx, & range ( l1, l2) , opt) { Some ( Vec :: new ( ) ) } else { None }
707+ let rng = range ( l1, l2) ;
708+ match guarded || multi_pats {
709+ false if opt_eq ( tcx, & rng, opt) => Some ( Vec :: new ( ) ) ,
710+ true if opt_overlap ( tcx, & rng, opt) => Some ( Vec :: new ( ) ) ,
711+ _ => None ,
712+ }
673713 }
674714 ast:: PatStruct ( _, ref field_pats, _) => {
675715 if opt_eq ( tcx, & variant_opt ( bcx, p. id ) , opt) {
0 commit comments