Skip to content

Commit acf9d42

Browse files
committed
auto merge of #13940 : edwardw/rust/refutable-match, r=pcwalton
By carefully distinguishing falling back to the default arm from moving on to the next pattern, this patch adjusts the codegen logic for range and guarded arms of pattern matching expression. It is a more appropriate way of fixing #12582 and #13027 without causing regressions such as #13867. Closes #13867
2 parents bd3fb81 + 90449ab commit acf9d42

File tree

3 files changed

+162
-105
lines changed

3 files changed

+162
-105
lines changed

src/librustc/middle/trans/_match.rs

Lines changed: 89 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -289,42 +289,6 @@ fn opt_eq(tcx: &ty::ctxt, a: &Opt, b: &Opt) -> bool {
289289
}
290290
}
291291

292-
fn opt_overlap(tcx: &ty::ctxt, a: &Opt, b: &Opt) -> bool {
293-
match (a, b) {
294-
(&lit(a), &lit(b)) => {
295-
let a_expr = lit_to_expr(tcx, &a);
296-
let b_expr = lit_to_expr(tcx, &b);
297-
match const_eval::compare_lit_exprs(tcx, a_expr, b_expr) {
298-
Some(val1) => val1 == 0,
299-
None => fail!("opt_overlap: type mismatch"),
300-
}
301-
}
302-
303-
(&range(a1, a2), &range(b1, b2)) => {
304-
let m1 = const_eval::compare_lit_exprs(tcx, a1, b2);
305-
let m2 = const_eval::compare_lit_exprs(tcx, b1, a2);
306-
match (m1, m2) {
307-
// two ranges [a1, a2] and [b1, b2] overlap iff:
308-
// a1 <= b2 && b1 <= a2
309-
(Some(val1), Some(val2)) => (val1 <= 0 && val2 <= 0),
310-
_ => fail!("opt_overlap: type mismatch"),
311-
}
312-
}
313-
314-
(&range(a1, a2), &lit(b)) | (&lit(b), &range(a1, a2)) => {
315-
let b_expr = lit_to_expr(tcx, &b);
316-
let m1 = const_eval::compare_lit_exprs(tcx, a1, b_expr);
317-
let m2 = const_eval::compare_lit_exprs(tcx, a2, b_expr);
318-
match (m1, m2) {
319-
// b is in range [a1, a2] iff a1 <= b and b <= a2
320-
(Some(val1), Some(val2)) => (val1 <= 0 && 0 <= val2),
321-
_ => fail!("opt_overlap: type mismatch"),
322-
}
323-
}
324-
_ => fail!("opt_overlap: expect lit or range")
325-
}
326-
}
327-
328292
pub enum opt_result<'a> {
329293
single_result(Result<'a>),
330294
lower_bound(Result<'a>),
@@ -562,7 +526,7 @@ fn enter_default<'a, 'b>(
562526
// Collect all of the matches that can match against anything.
563527
let matches = enter_match(bcx, dm, m, col, val, |p| {
564528
match p.node {
565-
ast::PatWild | ast::PatWildMulti | ast::PatTup(_) => Some(Vec::new()),
529+
ast::PatWild | ast::PatWildMulti => Some(Vec::new()),
566530
ast::PatIdent(_, _, None) if pat_is_binding(dm, p) => Some(Vec::new()),
567531
_ => None
568532
}
@@ -634,30 +598,16 @@ fn enter_opt<'a, 'b>(
634598
let tcx = bcx.tcx();
635599
let dummy = @ast::Pat {id: 0, node: ast::PatWild, span: DUMMY_SP};
636600
let mut i = 0;
637-
// By the virtue of fact that we are in `trans` already, `enter_opt` is able
638-
// to prune sub-match tree aggressively based on exact equality. But when it
639-
// comes to literal or range, that strategy may lead to wrong result if there
640-
// are guard function or multiple patterns inside tuple; in that case, pruning
641-
// based on the overlap of patterns is required.
642-
//
643-
// Ideally, when constructing the sub-match tree for certain arm, only those
644-
// arms beneath it matter. But that isn't how algorithm works right now and
645-
// all other arms are taken into consideration when computing `guarded` below.
646-
// That is ok since each round of `compile_submatch` guarantees to trim one
647-
// "column" of arm patterns and the algorithm will converge.
648-
let guarded = m.iter().any(|x| x.data.arm.guard.is_some());
649-
let multi_pats = m.len() > 0 && m[0].pats.len() > 1;
650601
enter_match(bcx, &tcx.def_map, m, col, val, |p| {
651602
let answer = match p.node {
652603
ast::PatEnum(..) |
653604
ast::PatIdent(_, _, None) if pat_is_const(&tcx.def_map, p) => {
654605
let const_def = tcx.def_map.borrow().get_copy(&p.id);
655606
let const_def_id = ast_util::def_id_of_def(const_def);
656-
let konst = lit(ConstLit(const_def_id));
657-
match guarded || multi_pats {
658-
false if opt_eq(tcx, &konst, opt) => Some(Vec::new()),
659-
true if opt_overlap(tcx, &konst, opt) => Some(Vec::new()),
660-
_ => None,
607+
if opt_eq(tcx, &lit(ConstLit(const_def_id)), opt) {
608+
Some(Vec::new())
609+
} else {
610+
None
661611
}
662612
}
663613
ast::PatEnum(_, ref subpats) => {
@@ -682,20 +632,12 @@ fn enter_opt<'a, 'b>(
682632
}
683633
}
684634
ast::PatLit(l) => {
685-
let lit_expr = lit(ExprLit(l));
686-
match guarded || multi_pats {
687-
false if opt_eq(tcx, &lit_expr, opt) => Some(Vec::new()),
688-
true if opt_overlap(tcx, &lit_expr, opt) => Some(Vec::new()),
689-
_ => None,
690-
}
635+
if opt_eq(tcx, &lit(ExprLit(l)), opt) { Some(Vec::new()) }
636+
else { None }
691637
}
692638
ast::PatRange(l1, l2) => {
693-
let rng = range(l1, l2);
694-
match guarded || multi_pats {
695-
false if opt_eq(tcx, &rng, opt) => Some(Vec::new()),
696-
true if opt_overlap(tcx, &rng, opt) => Some(Vec::new()),
697-
_ => None,
698-
}
639+
if opt_eq(tcx, &range(l1, l2), opt) { Some(Vec::new()) }
640+
else { None }
699641
}
700642
ast::PatStruct(_, ref field_pats, _) => {
701643
if opt_eq(tcx, &variant_opt(bcx, p.id), opt) {
@@ -770,18 +712,7 @@ fn enter_opt<'a, 'b>(
770712
}
771713
_ => {
772714
assert_is_binding_or_wild(bcx, p);
773-
// In most cases, a binding/wildcard match be
774-
// considered to match against any Opt. However, when
775-
// doing vector pattern matching, submatches are
776-
// considered even if the eventual match might be from
777-
// a different submatch. Thus, when a submatch fails
778-
// when doing a vector match, we proceed to the next
779-
// submatch. Thus, including a default match would
780-
// cause the default match to fire spuriously.
781-
match *opt {
782-
vec_len(..) => None,
783-
_ => Some(Vec::from_elem(variant_size, dummy))
784-
}
715+
Some(Vec::from_elem(variant_size, dummy))
785716
}
786717
};
787718
i += 1;
@@ -1421,7 +1352,8 @@ fn compile_guard<'a, 'b>(
14211352
data: &ArmData,
14221353
m: &'a [Match<'a, 'b>],
14231354
vals: &[ValueRef],
1424-
chk: &FailureHandler)
1355+
chk: &FailureHandler,
1356+
has_genuine_default: bool)
14251357
-> &'b Block<'b> {
14261358
debug!("compile_guard(bcx={}, guard_expr={}, m={}, vals={})",
14271359
bcx.to_str(),
@@ -1452,7 +1384,17 @@ fn compile_guard<'a, 'b>(
14521384
// Guard does not match: free the values we copied,
14531385
// and remove all bindings from the lllocals table
14541386
let bcx = drop_bindings(bcx, data);
1455-
compile_submatch(bcx, m, vals, chk);
1387+
match chk {
1388+
// If the default arm is the only one left, move on to the next
1389+
// condition explicitly rather than (possibly) falling back to
1390+
// the default arm.
1391+
&JumpToBasicBlock(_) if m.len() == 1 && has_genuine_default => {
1392+
Br(bcx, chk.handle_fail());
1393+
}
1394+
_ => {
1395+
compile_submatch(bcx, m, vals, chk, has_genuine_default);
1396+
}
1397+
};
14561398
bcx
14571399
});
14581400

@@ -1476,7 +1418,8 @@ fn compile_submatch<'a, 'b>(
14761418
bcx: &'b Block<'b>,
14771419
m: &'a [Match<'a, 'b>],
14781420
vals: &[ValueRef],
1479-
chk: &FailureHandler) {
1421+
chk: &FailureHandler,
1422+
has_genuine_default: bool) {
14801423
debug!("compile_submatch(bcx={}, m={}, vals={})",
14811424
bcx.to_str(),
14821425
m.repr(bcx.tcx()),
@@ -1506,7 +1449,8 @@ fn compile_submatch<'a, 'b>(
15061449
m[0].data,
15071450
m.slice(1, m.len()),
15081451
vals,
1509-
chk);
1452+
chk,
1453+
has_genuine_default);
15101454
}
15111455
_ => ()
15121456
}
@@ -1524,9 +1468,10 @@ fn compile_submatch<'a, 'b>(
15241468
vals,
15251469
chk,
15261470
col,
1527-
val)
1471+
val,
1472+
has_genuine_default)
15281473
} else {
1529-
compile_submatch_continue(bcx, m, vals, chk, col, val)
1474+
compile_submatch_continue(bcx, m, vals, chk, col, val, has_genuine_default)
15301475
}
15311476
}
15321477

@@ -1536,7 +1481,8 @@ fn compile_submatch_continue<'a, 'b>(
15361481
vals: &[ValueRef],
15371482
chk: &FailureHandler,
15381483
col: uint,
1539-
val: ValueRef) {
1484+
val: ValueRef,
1485+
has_genuine_default: bool) {
15401486
let fcx = bcx.fcx;
15411487
let tcx = bcx.tcx();
15421488
let dm = &tcx.def_map;
@@ -1570,7 +1516,7 @@ fn compile_submatch_continue<'a, 'b>(
15701516
rec_fields.as_slice(),
15711517
val).as_slice(),
15721518
rec_vals.append(vals_left.as_slice()).as_slice(),
1573-
chk);
1519+
chk, has_genuine_default);
15741520
});
15751521
return;
15761522
}
@@ -1595,7 +1541,7 @@ fn compile_submatch_continue<'a, 'b>(
15951541
val,
15961542
n_tup_elts).as_slice(),
15971543
tup_vals.append(vals_left.as_slice()).as_slice(),
1598-
chk);
1544+
chk, has_genuine_default);
15991545
return;
16001546
}
16011547

@@ -1620,7 +1566,7 @@ fn compile_submatch_continue<'a, 'b>(
16201566
enter_tuple_struct(bcx, dm, m, col, val,
16211567
struct_element_count).as_slice(),
16221568
llstructvals.append(vals_left.as_slice()).as_slice(),
1623-
chk);
1569+
chk, has_genuine_default);
16241570
return;
16251571
}
16261572

@@ -1629,7 +1575,7 @@ fn compile_submatch_continue<'a, 'b>(
16291575
compile_submatch(bcx,
16301576
enter_uniq(bcx, dm, m, col, val).as_slice(),
16311577
(vec!(llbox)).append(vals_left.as_slice()).as_slice(),
1632-
chk);
1578+
chk, has_genuine_default);
16331579
return;
16341580
}
16351581

@@ -1638,7 +1584,7 @@ fn compile_submatch_continue<'a, 'b>(
16381584
compile_submatch(bcx,
16391585
enter_region(bcx, dm, m, col, val).as_slice(),
16401586
(vec!(loaded_val)).append(vals_left.as_slice()).as_slice(),
1641-
chk);
1587+
chk, has_genuine_default);
16421588
return;
16431589
}
16441590

@@ -1695,9 +1641,9 @@ fn compile_submatch_continue<'a, 'b>(
16951641

16961642
// Compile subtrees for each option
16971643
for (i, opt) in opts.iter().enumerate() {
1698-
// In some cases in vector pattern matching, we need to override
1699-
// the failure case so that instead of failing, it proceeds to
1700-
// try more matching. branch_chk, then, is the proper failure case
1644+
// In some cases of range and vector pattern matching, we need to
1645+
// override the failure case so that instead of failing, it proceeds
1646+
// to try more matching. branch_chk, then, is the proper failure case
17011647
// for the current conditional branch.
17021648
let mut branch_chk = None;
17031649
let mut opt_cx = else_cx;
@@ -1747,6 +1693,16 @@ fn compile_submatch_continue<'a, 'b>(
17471693
}
17481694
};
17491695
bcx = fcx.new_temp_block("compare_next");
1696+
1697+
// If none of the sub-cases match, and the current condition
1698+
// is guarded or has multiple patterns, move on to the next
1699+
// condition, if there is any, rather than falling back to
1700+
// the default.
1701+
let guarded = m[i].data.arm.guard.is_some();
1702+
let multi_pats = m[i].pats.len() > 1;
1703+
if i+1 < len && (guarded || multi_pats) {
1704+
branch_chk = Some(JumpToBasicBlock(bcx.llbb));
1705+
}
17501706
CondBr(after_cx, matches, opt_cx.llbb, bcx.llbb);
17511707
}
17521708
compare_vec_len => {
@@ -1784,8 +1740,10 @@ fn compile_submatch_continue<'a, 'b>(
17841740
bcx = fcx.new_temp_block("compare_vec_len_next");
17851741

17861742
// If none of these subcases match, move on to the
1787-
// next condition.
1788-
branch_chk = Some(JumpToBasicBlock(bcx.llbb));
1743+
// next condition if there is any.
1744+
if i+1 < len {
1745+
branch_chk = Some(JumpToBasicBlock(bcx.llbb));
1746+
}
17891747
CondBr(after_cx, matches, opt_cx.llbb, bcx.llbb);
17901748
}
17911749
_ => ()
@@ -1825,27 +1783,38 @@ fn compile_submatch_continue<'a, 'b>(
18251783
compile_submatch(opt_cx,
18261784
opt_ms.as_slice(),
18271785
opt_vals.as_slice(),
1828-
chk)
1786+
chk,
1787+
has_genuine_default)
18291788
}
18301789
Some(branch_chk) => {
18311790
compile_submatch(opt_cx,
18321791
opt_ms.as_slice(),
18331792
opt_vals.as_slice(),
1834-
&branch_chk)
1793+
&branch_chk,
1794+
has_genuine_default)
18351795
}
18361796
}
18371797
}
18381798

18391799
// Compile the fall-through case, if any
1840-
if !exhaustive {
1800+
if !exhaustive && kind != single {
18411801
if kind == compare || kind == compare_vec_len {
18421802
Br(bcx, else_cx.llbb);
18431803
}
1844-
if kind != single {
1845-
compile_submatch(else_cx,
1846-
defaults.as_slice(),
1847-
vals_left.as_slice(),
1848-
chk);
1804+
match chk {
1805+
// If there is only one default arm left, move on to the next
1806+
// condition explicitly rather than (eventually) falling back to
1807+
// the last default arm.
1808+
&JumpToBasicBlock(_) if defaults.len() == 1 && has_genuine_default => {
1809+
Br(else_cx, chk.handle_fail());
1810+
}
1811+
_ => {
1812+
compile_submatch(else_cx,
1813+
defaults.as_slice(),
1814+
vals_left.as_slice(),
1815+
chk,
1816+
has_genuine_default);
1817+
}
18491818
}
18501819
}
18511820
}
@@ -1950,7 +1919,22 @@ fn trans_match_inner<'a>(scope_cx: &'a Block<'a>,
19501919
}));
19511920
}
19521921

1953-
compile_submatch(bcx, matches.as_slice(), [discr_datum.val], &chk);
1922+
// `compile_submatch` works one column of arm patterns a time and
1923+
// then peels that column off. So as we progress, it may become
1924+
// impossible to know whether we have a genuine default arm, i.e.
1925+
// `_ => foo` or not. Sometimes it is important to know that in order
1926+
// to decide whether moving on to the next condition or falling back
1927+
// to the default arm.
1928+
let has_default = arms.len() > 0 && {
1929+
let ref pats = arms.last().unwrap().pats;
1930+
1931+
pats.len() == 1
1932+
&& match pats.last().unwrap().node {
1933+
ast::PatWild => true, _ => false
1934+
}
1935+
};
1936+
1937+
compile_submatch(bcx, matches.as_slice(), [discr_datum.val], &chk, has_default);
19541938

19551939
let mut arm_cxs = Vec::new();
19561940
for arm_data in arm_datas.iter() {

src/test/run-pass/issue-13027.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ pub fn main() {
2323
multi_pats_shadow_range();
2424
lit_shadow_multi_pats();
2525
range_shadow_multi_pats();
26+
misc();
2627
}
2728

2829
fn lit_shadow_range() {
@@ -168,3 +169,18 @@ fn range_shadow_multi_pats() {
168169
_ => 3,
169170
});
170171
}
172+
173+
fn misc() {
174+
enum Foo {
175+
Bar(uint, bool)
176+
}
177+
// This test basically mimics how trace_macros! macro is implemented,
178+
// which is a rare combination of vector patterns, multiple wild-card
179+
// patterns and guard functions.
180+
let r = match [Bar(0, false)].as_slice() {
181+
[Bar(_, pred)] if pred => 1,
182+
[Bar(_, pred)] if !pred => 2,
183+
_ => 0,
184+
};
185+
assert_eq!(2, r);
186+
}

0 commit comments

Comments
 (0)