Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 6635fe7

Browse files
committedJul 29, 2014
auto merge of #15989 : pcwalton/rust/borrowck-pattern-guards, r=pnkfelix
the CFG for match statements. There were two bugs in issue #14684. One was simply that the borrow check didn't know about the correct CFG for match statements: the pattern must be a predecessor of the guard. This disallows the bad behavior if there are bindings in the pattern. But it isn't enough to prevent the memory safety problem, because of wildcards; thus, this patch introduces a more restrictive rule, which disallows assignments and mutable borrows inside guards outright. I discussed this with Niko and we decided this was the best plan of action. This breaks code that performs mutable borrows in pattern guards. Most commonly, the code looks like this: impl Foo { fn f(&mut self, ...) {} fn g(&mut self, ...) { match bar { Baz if self.f(...) => { ... } _ => { ... } } } } Change this code to not use a guard. For example: impl Foo { fn f(&mut self, ...) {} fn g(&mut self, ...) { match bar { Baz => { if self.f(...) { ... } else { ... } } _ => { ... } } } } Sometimes this can result in code duplication, but often it illustrates a hidden memory safety problem. Closes #14684. [breaking-change] r? @pnkfelix
2 parents b92536f + b2eb888 commit 6635fe7

File tree

10 files changed

+383
-271
lines changed

10 files changed

+383
-271
lines changed
 

‎src/libfmt_macros/lib.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -352,8 +352,13 @@ impl<'a> Parser<'a> {
352352
None => {
353353
let tmp = self.cur.clone();
354354
match self.word() {
355-
word if word.len() > 0 && self.consume('$') => {
356-
CountIsName(word)
355+
word if word.len() > 0 => {
356+
if self.consume('$') {
357+
CountIsName(word)
358+
} else {
359+
self.cur = tmp;
360+
CountImplied
361+
}
357362
}
358363
_ => {
359364
self.cur = tmp;

‎src/librustc/middle/cfg/construct.rs

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -333,32 +333,37 @@ impl<'a> CFGBuilder<'a> {
333333
// [discr]
334334
// |
335335
// v 2
336-
// [guard1]
336+
// [cond1]
337337
// / \
338338
// | \
339-
// v 3 |
340-
// [pat1] |
341-
// |
342-
// v 4 |
343-
// [body1] v
344-
// | [guard2]
345-
// | / \
346-
// | [body2] \
347-
// | | ...
348-
// | | |
349-
// v 5 v v
350-
// [....expr....]
339+
// v 3 \
340+
// [pat1] \
341+
// | |
342+
// v 4 |
343+
// [guard1] |
344+
// | |
345+
// | |
346+
// v 5 v
347+
// [body1] [cond2]
348+
// | / \
349+
// | ... ...
350+
// | | |
351+
// v 6 v v
352+
// [.....expr.....]
351353
//
352354
let discr_exit = self.expr(discr.clone(), pred); // 1
353355

354356
let expr_exit = self.add_node(expr.id, []);
355-
let mut guard_exit = discr_exit;
357+
let mut cond_exit = discr_exit;
356358
for arm in arms.iter() {
357-
guard_exit = self.opt_expr(arm.guard, guard_exit); // 2
359+
cond_exit = self.add_dummy_node([cond_exit]); // 2
358360
let pats_exit = self.pats_any(arm.pats.as_slice(),
359-
guard_exit); // 3
360-
let body_exit = self.expr(arm.body.clone(), pats_exit); // 4
361-
self.add_contained_edge(body_exit, expr_exit); // 5
361+
cond_exit); // 3
362+
let guard_exit = self.opt_expr(arm.guard,
363+
pats_exit); // 4
364+
let body_exit = self.expr(arm.body.clone(),
365+
guard_exit); // 5
366+
self.add_contained_edge(body_exit, expr_exit); // 6
362367
}
363368
expr_exit
364369
}

‎src/librustc/middle/check_match.rs

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
use middle::const_eval::{compare_const_vals, const_bool, const_float, const_nil, const_val};
1212
use middle::const_eval::{const_expr_to_pat, eval_const_expr, lookup_const_by_id};
1313
use middle::def::*;
14+
use middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Init};
15+
use middle::expr_use_visitor::{JustWrite, LoanCause, MutateMode};
16+
use middle::expr_use_visitor::{WriteAndRead};
17+
use middle::mem_categorization::cmt;
1418
use middle::pat_util::*;
1519
use middle::ty::*;
1620
use middle::ty;
@@ -143,7 +147,16 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) {
143147
arm.pats.as_slice());
144148
}
145149

146-
// Second, check for unreachable arms.
150+
// Second, if there is a guard on each arm, make sure it isn't
151+
// assigning or borrowing anything mutably.
152+
for arm in arms.iter() {
153+
match arm.guard {
154+
Some(guard) => check_for_mutation_in_guard(cx, &*guard),
155+
None => {}
156+
}
157+
}
158+
159+
// Third, check for unreachable arms.
147160
check_arms(cx, arms.as_slice());
148161

149162
// Finally, check if the whole match expression is exhaustive.
@@ -903,3 +916,53 @@ fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
903916
});
904917
}
905918
}
919+
920+
/// Ensures that a pattern guard doesn't borrow by mutable reference or
921+
/// assign.
922+
fn check_for_mutation_in_guard<'a>(cx: &'a MatchCheckCtxt<'a>, guard: &Expr) {
923+
let mut checker = MutationChecker {
924+
cx: cx,
925+
};
926+
let mut visitor = ExprUseVisitor::new(&mut checker, checker.cx.tcx);
927+
visitor.walk_expr(guard);
928+
}
929+
930+
struct MutationChecker<'a> {
931+
cx: &'a MatchCheckCtxt<'a>,
932+
}
933+
934+
impl<'a> Delegate for MutationChecker<'a> {
935+
fn consume(&mut self, _: NodeId, _: Span, _: cmt, _: ConsumeMode) {}
936+
fn consume_pat(&mut self, _: &Pat, _: cmt, _: ConsumeMode) {}
937+
fn borrow(&mut self,
938+
_: NodeId,
939+
span: Span,
940+
_: cmt,
941+
_: Region,
942+
kind: BorrowKind,
943+
_: LoanCause) {
944+
match kind {
945+
MutBorrow => {
946+
self.cx
947+
.tcx
948+
.sess
949+
.span_err(span,
950+
"cannot mutably borrow in a pattern guard")
951+
}
952+
ImmBorrow | UniqueImmBorrow => {}
953+
}
954+
}
955+
fn decl_without_init(&mut self, _: NodeId, _: Span) {}
956+
fn mutate(&mut self, _: NodeId, span: Span, _: cmt, mode: MutateMode) {
957+
match mode {
958+
JustWrite | WriteAndRead => {
959+
self.cx
960+
.tcx
961+
.sess
962+
.span_err(span, "cannot assign in a pattern guard")
963+
}
964+
Init => {}
965+
}
966+
}
967+
}
968+

‎src/librustc/middle/expr_use_visitor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> {
292292
self.walk_expr(expr)
293293
}
294294

295-
fn walk_expr(&mut self, expr: &ast::Expr) {
295+
pub fn walk_expr(&mut self, expr: &ast::Expr) {
296296
debug!("walk_expr(expr={})", expr.repr(self.tcx()));
297297

298298
self.walk_adjustment(expr);

‎src/librustdoc/html/render.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1734,17 +1734,19 @@ fn item_struct(w: &mut fmt::Formatter, it: &clean::Item,
17341734
}
17351735
}).peekable();
17361736
match s.struct_type {
1737-
doctree::Plain if fields.peek().is_some() => {
1738-
try!(write!(w, "<h2 class='fields'>Fields</h2>\n<table>"));
1739-
for field in fields {
1740-
try!(write!(w, "<tr><td id='structfield.{name}'>\
1741-
{stab}<code>{name}</code></td><td>",
1742-
stab = ConciseStability(&field.stability),
1743-
name = field.name.get_ref().as_slice()));
1744-
try!(document(w, field));
1745-
try!(write!(w, "</td></tr>"));
1737+
doctree::Plain => {
1738+
if fields.peek().is_some() {
1739+
try!(write!(w, "<h2 class='fields'>Fields</h2>\n<table>"));
1740+
for field in fields {
1741+
try!(write!(w, "<tr><td id='structfield.{name}'>\
1742+
{stab}<code>{name}</code></td><td>",
1743+
stab = ConciseStability(&field.stability),
1744+
name = field.name.get_ref().as_slice()));
1745+
try!(document(w, field));
1746+
try!(write!(w, "</td></tr>"));
1747+
}
1748+
try!(write!(w, "</table>"));
17461749
}
1747-
try!(write!(w, "</table>"));
17481750
}
17491751
_ => {}
17501752
}

‎src/libsyntax/parse/parser.rs

Lines changed: 208 additions & 183 deletions
Large diffs are not rendered by default.

‎src/test/compile-fail/borrowck-lend-flow-match.rs

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@
1111
#![allow(unused_variable)]
1212
#![allow(dead_assignment)]
1313

14-
fn cond() -> bool { fail!() }
15-
fn link<'a>(v: &'a uint, w: &mut &'a uint) -> bool { *w = v; true }
16-
1714
fn separate_arms() {
1815
// Here both arms perform assignments, but only is illegal.
1916

@@ -31,28 +28,4 @@ fn separate_arms() {
3128
x.clone(); // just to prevent liveness warnings
3229
}
3330

34-
fn guard() {
35-
// Here the guard performs a borrow. This borrow "infects" all
36-
// subsequent arms (but not the prior ones).
37-
38-
let mut a = box 3u;
39-
let mut b = box 4u;
40-
let mut w = &*a;
41-
match 22i {
42-
_ if cond() => {
43-
b = box 5u;
44-
}
45-
46-
_ if link(&*b, &mut w) => {
47-
b = box 6u; //~ ERROR cannot assign
48-
}
49-
50-
_ => {
51-
b = box 7u; //~ ERROR cannot assign
52-
}
53-
}
54-
55-
b = box 8; //~ ERROR cannot assign
56-
}
57-
5831
fn main() {}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 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+
enum Enum<'a> {
12+
A(&'a int),
13+
B(bool),
14+
}
15+
16+
fn foo() -> int {
17+
let mut n = 42;
18+
let mut x = A(&mut n);
19+
match x {
20+
A(_) if { x = B(false); false } => 1,
21+
//~^ ERROR cannot assign in a pattern guard
22+
A(_) if { let y = &mut x; *y = B(false); false } => 1,
23+
//~^ ERROR cannot mutably borrow in a pattern guard
24+
//~^^ ERROR cannot assign in a pattern guard
25+
A(p) => *p,
26+
B(_) => 2,
27+
}
28+
}
29+
30+
fn main() {
31+
foo();
32+
}
33+

‎src/test/run-make/graphviz-flowgraph/f07.dot-expected.dot

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ digraph block {
77
N5[label="expr 7777i"];
88
N6[label="expr [7i, 77i, 777i, 7777i]"];
99
N7[label="expr match [7i, 77i, 777i, 7777i] { [x, y, ..] => x + y }"];
10-
N8[label="local x"];
11-
N9[label="local y"];
12-
N10[label="pat .."];
13-
N11[label="pat [x, y, ..]"];
14-
N12[label="expr x"];
15-
N13[label="expr y"];
16-
N14[label="expr x + y"];
17-
N15[label="block { match [7i, 77i, 777i, 7777i] { [x, y, ..] => x + y }; }"];
10+
N8[label="(dummy_node)"];
11+
N9[label="local x"];
12+
N10[label="local y"];
13+
N11[label="pat .."];
14+
N12[label="pat [x, y, ..]"];
15+
N13[label="expr x"];
16+
N14[label="expr y"];
17+
N15[label="expr x + y"];
18+
N16[label="block { match [7i, 77i, 777i, 7777i] { [x, y, ..] => x + y }; }"];
1819
N0 -> N2;
1920
N2 -> N3;
2021
N3 -> N4;
@@ -27,7 +28,8 @@ digraph block {
2728
N11 -> N12;
2829
N12 -> N13;
2930
N13 -> N14;
30-
N14 -> N7;
31-
N7 -> N15;
32-
N15 -> N1;
31+
N14 -> N15;
32+
N15 -> N7;
33+
N7 -> N16;
34+
N16 -> N1;
3335
}

‎src/test/run-make/graphviz-flowgraph/f13.dot-expected.dot

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,20 @@ digraph block {
88
N6[label="local _y"];
99
N7[label="expr x"];
1010
N8[label="expr match x { E13a => _y = 1, E13b(v) => _y = v + 1 }"];
11-
N9[label="local E13a"];
12-
N10[label="expr 1"];
13-
N11[label="expr _y"];
14-
N12[label="expr _y = 1"];
15-
N13[label="local v"];
16-
N14[label="pat E13b(v)"];
17-
N15[label="expr v"];
18-
N16[label="expr 1"];
19-
N17[label="expr v + 1"];
20-
N18[label="expr _y"];
21-
N19[label="expr _y = v + 1"];
22-
N20[label="block {\l let x = E13b(13);\l let _y;\l match x { E13a => _y = 1, E13b(v) => _y = v + 1 }\l}\l"];
11+
N9[label="(dummy_node)"];
12+
N10[label="local E13a"];
13+
N11[label="expr 1"];
14+
N12[label="expr _y"];
15+
N13[label="expr _y = 1"];
16+
N14[label="(dummy_node)"];
17+
N15[label="local v"];
18+
N16[label="pat E13b(v)"];
19+
N17[label="expr v"];
20+
N18[label="expr 1"];
21+
N19[label="expr v + 1"];
22+
N20[label="expr _y"];
23+
N21[label="expr _y = v + 1"];
24+
N22[label="block {\l let x = E13b(13);\l let _y;\l match x { E13a => _y = 1, E13b(v) => _y = v + 1 }\l}\l"];
2325
N0 -> N2;
2426
N2 -> N3;
2527
N3 -> N4;
@@ -30,15 +32,17 @@ digraph block {
3032
N9 -> N10;
3133
N10 -> N11;
3234
N11 -> N12;
33-
N12 -> N8;
34-
N7 -> N13;
35-
N13 -> N14;
35+
N12 -> N13;
36+
N13 -> N8;
37+
N9 -> N14;
3638
N14 -> N15;
3739
N15 -> N16;
3840
N16 -> N17;
3941
N17 -> N18;
4042
N18 -> N19;
41-
N19 -> N8;
42-
N8 -> N20;
43-
N20 -> N1;
43+
N19 -> N20;
44+
N20 -> N21;
45+
N21 -> N8;
46+
N8 -> N22;
47+
N22 -> N1;
4448
}

0 commit comments

Comments
 (0)
Please sign in to comment.