@@ -256,43 +256,23 @@ enum Opt {
256
256
vec_len( /* length */ uint , VecLenOpt , /*range of matches*/ ( uint , uint ) )
257
257
}
258
258
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
+
259
267
fn opt_eq ( tcx : & ty:: ctxt , a : & Opt , b : & Opt ) -> bool {
260
268
match ( a, b) {
269
+ ( & lit( UnitLikeStructLit ( a) ) , & lit( UnitLikeStructLit ( b) ) ) => a == b,
261
270
( & 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" ) ,
296
276
}
297
277
}
298
278
( & range( a1, a2) , & range( b1, b2) ) => {
@@ -310,6 +290,42 @@ fn opt_eq(tcx: &ty::ctxt, a: &Opt, b: &Opt) -> bool {
310
290
}
311
291
}
312
292
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
+
313
329
pub enum opt_result < ' a > {
314
330
single_result( Result < ' a > ) ,
315
331
lower_bound( Result < ' a > ) ,
@@ -490,7 +506,7 @@ fn assert_is_binding_or_wild(bcx: &Block, p: @ast::Pat) {
490
506
}
491
507
}
492
508
493
- type enter_pat < ' a > = ' a |@ast:: Pat | -> Option < Vec < @ast:: Pat > > ;
509
+ type enter_pat < ' a > = ' a |@ast:: Pat | -> Option < Vec < @ast:: Pat > > ;
494
510
495
511
fn enter_match < ' r , ' b > (
496
512
bcx : & ' b Block < ' b > ,
@@ -632,16 +648,30 @@ fn enter_opt<'r,'b>(
632
648
let tcx = bcx. tcx ( ) ;
633
649
let dummy = @ast:: Pat { id : 0 , node : ast:: PatWild , span : DUMMY_SP } ;
634
650
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 ;
635
664
enter_match ( bcx, tcx. def_map , m, col, val, |p| {
636
665
let answer = match p. node {
637
666
ast:: PatEnum ( ..) |
638
667
ast:: PatIdent ( _, _, None ) if pat_is_const ( tcx. def_map , p) => {
639
668
let const_def = tcx. def_map . borrow ( ) . get_copy ( & p. id ) ;
640
669
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 ,
645
675
}
646
676
}
647
677
ast:: PatEnum ( _, ref subpats) => {
@@ -666,10 +696,20 @@ fn enter_opt<'r,'b>(
666
696
}
667
697
}
668
698
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
+ }
670
705
}
671
706
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
+ }
673
713
}
674
714
ast:: PatStruct ( _, ref field_pats, _) => {
675
715
if opt_eq ( tcx, & variant_opt ( bcx, p. id ) , opt) {
0 commit comments