Skip to content

Commit 90449ab

Browse files
committed
Adjust codegen logic for range and guarded arms
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 rust-lang#12582 and rust-lang#13027 without causing regressions such as rust-lang#13867. Closes rust-lang#13867
1 parent 7adc485 commit 90449ab

File tree

3 files changed

+154
-39
lines changed

3 files changed

+154
-39
lines changed

src/librustc/middle/trans/_match.rs

Lines changed: 81 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,7 @@ fn enter_default<'a, 'b>(
526526
// Collect all of the matches that can match against anything.
527527
let matches = enter_match(bcx, dm, m, col, val, |p| {
528528
match p.node {
529-
ast::PatWild | ast::PatWildMulti | ast::PatTup(_) => Some(Vec::new()),
529+
ast::PatWild | ast::PatWildMulti => Some(Vec::new()),
530530
ast::PatIdent(_, _, None) if pat_is_binding(dm, p) => Some(Vec::new()),
531531
_ => None
532532
}
@@ -712,18 +712,7 @@ fn enter_opt<'a, 'b>(
712712
}
713713
_ => {
714714
assert_is_binding_or_wild(bcx, p);
715-
// In most cases, a binding/wildcard match be
716-
// considered to match against any Opt. However, when
717-
// doing vector pattern matching, submatches are
718-
// considered even if the eventual match might be from
719-
// a different submatch. Thus, when a submatch fails
720-
// when doing a vector match, we proceed to the next
721-
// submatch. Thus, including a default match would
722-
// cause the default match to fire spuriously.
723-
match *opt {
724-
vec_len(..) => None,
725-
_ => Some(Vec::from_elem(variant_size, dummy))
726-
}
715+
Some(Vec::from_elem(variant_size, dummy))
727716
}
728717
};
729718
i += 1;
@@ -1363,7 +1352,8 @@ fn compile_guard<'a, 'b>(
13631352
data: &ArmData,
13641353
m: &'a [Match<'a, 'b>],
13651354
vals: &[ValueRef],
1366-
chk: &FailureHandler)
1355+
chk: &FailureHandler,
1356+
has_genuine_default: bool)
13671357
-> &'b Block<'b> {
13681358
debug!("compile_guard(bcx={}, guard_expr={}, m={}, vals={})",
13691359
bcx.to_str(),
@@ -1394,7 +1384,17 @@ fn compile_guard<'a, 'b>(
13941384
// Guard does not match: free the values we copied,
13951385
// and remove all bindings from the lllocals table
13961386
let bcx = drop_bindings(bcx, data);
1397-
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+
};
13981398
bcx
13991399
});
14001400

@@ -1418,7 +1418,8 @@ fn compile_submatch<'a, 'b>(
14181418
bcx: &'b Block<'b>,
14191419
m: &'a [Match<'a, 'b>],
14201420
vals: &[ValueRef],
1421-
chk: &FailureHandler) {
1421+
chk: &FailureHandler,
1422+
has_genuine_default: bool) {
14221423
debug!("compile_submatch(bcx={}, m={}, vals={})",
14231424
bcx.to_str(),
14241425
m.repr(bcx.tcx()),
@@ -1448,7 +1449,8 @@ fn compile_submatch<'a, 'b>(
14481449
m[0].data,
14491450
m.slice(1, m.len()),
14501451
vals,
1451-
chk);
1452+
chk,
1453+
has_genuine_default);
14521454
}
14531455
_ => ()
14541456
}
@@ -1466,9 +1468,10 @@ fn compile_submatch<'a, 'b>(
14661468
vals,
14671469
chk,
14681470
col,
1469-
val)
1471+
val,
1472+
has_genuine_default)
14701473
} else {
1471-
compile_submatch_continue(bcx, m, vals, chk, col, val)
1474+
compile_submatch_continue(bcx, m, vals, chk, col, val, has_genuine_default)
14721475
}
14731476
}
14741477

@@ -1478,7 +1481,8 @@ fn compile_submatch_continue<'a, 'b>(
14781481
vals: &[ValueRef],
14791482
chk: &FailureHandler,
14801483
col: uint,
1481-
val: ValueRef) {
1484+
val: ValueRef,
1485+
has_genuine_default: bool) {
14821486
let fcx = bcx.fcx;
14831487
let tcx = bcx.tcx();
14841488
let dm = &tcx.def_map;
@@ -1512,7 +1516,7 @@ fn compile_submatch_continue<'a, 'b>(
15121516
rec_fields.as_slice(),
15131517
val).as_slice(),
15141518
rec_vals.append(vals_left.as_slice()).as_slice(),
1515-
chk);
1519+
chk, has_genuine_default);
15161520
});
15171521
return;
15181522
}
@@ -1537,7 +1541,7 @@ fn compile_submatch_continue<'a, 'b>(
15371541
val,
15381542
n_tup_elts).as_slice(),
15391543
tup_vals.append(vals_left.as_slice()).as_slice(),
1540-
chk);
1544+
chk, has_genuine_default);
15411545
return;
15421546
}
15431547

@@ -1562,7 +1566,7 @@ fn compile_submatch_continue<'a, 'b>(
15621566
enter_tuple_struct(bcx, dm, m, col, val,
15631567
struct_element_count).as_slice(),
15641568
llstructvals.append(vals_left.as_slice()).as_slice(),
1565-
chk);
1569+
chk, has_genuine_default);
15661570
return;
15671571
}
15681572

@@ -1571,7 +1575,7 @@ fn compile_submatch_continue<'a, 'b>(
15711575
compile_submatch(bcx,
15721576
enter_uniq(bcx, dm, m, col, val).as_slice(),
15731577
(vec!(llbox)).append(vals_left.as_slice()).as_slice(),
1574-
chk);
1578+
chk, has_genuine_default);
15751579
return;
15761580
}
15771581

@@ -1580,7 +1584,7 @@ fn compile_submatch_continue<'a, 'b>(
15801584
compile_submatch(bcx,
15811585
enter_region(bcx, dm, m, col, val).as_slice(),
15821586
(vec!(loaded_val)).append(vals_left.as_slice()).as_slice(),
1583-
chk);
1587+
chk, has_genuine_default);
15841588
return;
15851589
}
15861590

@@ -1637,9 +1641,9 @@ fn compile_submatch_continue<'a, 'b>(
16371641

16381642
// Compile subtrees for each option
16391643
for (i, opt) in opts.iter().enumerate() {
1640-
// In some cases in vector pattern matching, we need to override
1641-
// the failure case so that instead of failing, it proceeds to
1642-
// 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
16431647
// for the current conditional branch.
16441648
let mut branch_chk = None;
16451649
let mut opt_cx = else_cx;
@@ -1689,6 +1693,16 @@ fn compile_submatch_continue<'a, 'b>(
16891693
}
16901694
};
16911695
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+
}
16921706
CondBr(after_cx, matches, opt_cx.llbb, bcx.llbb);
16931707
}
16941708
compare_vec_len => {
@@ -1726,8 +1740,10 @@ fn compile_submatch_continue<'a, 'b>(
17261740
bcx = fcx.new_temp_block("compare_vec_len_next");
17271741

17281742
// If none of these subcases match, move on to the
1729-
// next condition.
1730-
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+
}
17311747
CondBr(after_cx, matches, opt_cx.llbb, bcx.llbb);
17321748
}
17331749
_ => ()
@@ -1767,27 +1783,38 @@ fn compile_submatch_continue<'a, 'b>(
17671783
compile_submatch(opt_cx,
17681784
opt_ms.as_slice(),
17691785
opt_vals.as_slice(),
1770-
chk)
1786+
chk,
1787+
has_genuine_default)
17711788
}
17721789
Some(branch_chk) => {
17731790
compile_submatch(opt_cx,
17741791
opt_ms.as_slice(),
17751792
opt_vals.as_slice(),
1776-
&branch_chk)
1793+
&branch_chk,
1794+
has_genuine_default)
17771795
}
17781796
}
17791797
}
17801798

17811799
// Compile the fall-through case, if any
1782-
if !exhaustive {
1800+
if !exhaustive && kind != single {
17831801
if kind == compare || kind == compare_vec_len {
17841802
Br(bcx, else_cx.llbb);
17851803
}
1786-
if kind != single {
1787-
compile_submatch(else_cx,
1788-
defaults.as_slice(),
1789-
vals_left.as_slice(),
1790-
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+
}
17911818
}
17921819
}
17931820
}
@@ -1892,7 +1919,22 @@ fn trans_match_inner<'a>(scope_cx: &'a Block<'a>,
18921919
}));
18931920
}
18941921

1895-
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);
18961938

18971939
let mut arm_cxs = Vec::new();
18981940
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+
}

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

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that codegen works correctly when there are multiple refutable
12+
// patterns in match expression.
13+
14+
enum Foo {
15+
FooUint(uint),
16+
FooNullary,
17+
}
18+
19+
fn main() {
20+
let r = match (FooNullary, 'a') {
21+
(FooUint(..), 'a'..'z') => 1,
22+
(FooNullary, 'x') => 2,
23+
_ => 0
24+
};
25+
assert_eq!(r, 0);
26+
27+
let r = match (FooUint(0), 'a') {
28+
(FooUint(1), 'a'..'z') => 1,
29+
(FooUint(..), 'x') => 2,
30+
(FooNullary, 'a') => 3,
31+
_ => 0
32+
};
33+
assert_eq!(r, 0);
34+
35+
let r = match ('a', FooUint(0)) {
36+
('a'..'z', FooUint(1)) => 1,
37+
('x', FooUint(..)) => 2,
38+
('a', FooNullary) => 3,
39+
_ => 0
40+
};
41+
assert_eq!(r, 0);
42+
43+
let r = match ('a', 'a') {
44+
('a'..'z', 'b') => 1,
45+
('x', 'a'..'z') => 2,
46+
_ => 0
47+
};
48+
assert_eq!(r, 0);
49+
50+
let r = match ('a', 'a') {
51+
('a'..'z', 'b') => 1,
52+
('x', 'a'..'z') => 2,
53+
('a', 'a') => 3,
54+
_ => 0
55+
};
56+
assert_eq!(r, 3);
57+
}

0 commit comments

Comments
 (0)