2014-07-27 06:50:46 -05:00
|
|
|
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
2013-05-10 12:10:35 -05:00
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
use middle::cfg::*;
|
2014-05-14 14:31:30 -05:00
|
|
|
use middle::def;
|
2013-05-10 12:10:35 -05:00
|
|
|
use middle::graph;
|
|
|
|
use middle::typeck;
|
|
|
|
use middle::ty;
|
|
|
|
use syntax::ast;
|
|
|
|
use syntax::ast_util;
|
2014-02-28 16:34:26 -06:00
|
|
|
use util::nodemap::NodeMap;
|
2013-05-10 12:10:35 -05:00
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
use std::gc::Gc;
|
|
|
|
|
2014-04-22 07:56:37 -05:00
|
|
|
struct CFGBuilder<'a, 'tcx: 'a> {
|
|
|
|
tcx: &'a ty::ctxt<'tcx>,
|
2014-02-28 16:34:26 -06:00
|
|
|
exit_map: NodeMap<CFGIndex>,
|
2013-05-10 12:10:35 -05:00
|
|
|
graph: CFGGraph,
|
2014-05-08 17:07:57 -05:00
|
|
|
fn_exit: CFGIndex,
|
|
|
|
loop_scopes: Vec<LoopScope>,
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
struct LoopScope {
|
2013-07-27 03:25:59 -05:00
|
|
|
loop_id: ast::NodeId, // id of loop/while node
|
2013-05-10 12:10:35 -05:00
|
|
|
continue_index: CFGIndex, // where to go on a `loop`
|
|
|
|
break_index: CFGIndex, // where to go on a `break
|
|
|
|
}
|
|
|
|
|
2014-03-05 21:07:47 -06:00
|
|
|
pub fn construct(tcx: &ty::ctxt,
|
2013-07-19 00:38:55 -05:00
|
|
|
blk: &ast::Block) -> CFG {
|
2014-05-08 17:07:57 -05:00
|
|
|
let mut graph = graph::Graph::new();
|
|
|
|
let entry = add_initial_dummy_node(&mut graph);
|
|
|
|
|
|
|
|
// `fn_exit` is target of return exprs, which lies somewhere
|
|
|
|
// outside input `blk`. (Distinguishing `fn_exit` and `block_exit`
|
|
|
|
// also resolves chicken-and-egg problem that arises if you try to
|
|
|
|
// have return exprs jump to `block_exit` during construction.)
|
|
|
|
let fn_exit = add_initial_dummy_node(&mut graph);
|
|
|
|
let block_exit;
|
|
|
|
|
2013-05-10 12:10:35 -05:00
|
|
|
let mut cfg_builder = CFGBuilder {
|
2014-02-28 16:34:26 -06:00
|
|
|
exit_map: NodeMap::new(),
|
2014-05-08 17:07:57 -05:00
|
|
|
graph: graph,
|
|
|
|
fn_exit: fn_exit,
|
2013-05-10 12:10:35 -05:00
|
|
|
tcx: tcx,
|
2014-03-04 12:02:49 -06:00
|
|
|
loop_scopes: Vec::new()
|
2013-05-10 12:10:35 -05:00
|
|
|
};
|
2014-05-08 17:07:57 -05:00
|
|
|
block_exit = cfg_builder.block(blk, entry);
|
|
|
|
cfg_builder.add_contained_edge(block_exit, fn_exit);
|
2013-11-28 14:22:53 -06:00
|
|
|
let CFGBuilder {exit_map, graph, ..} = cfg_builder;
|
2013-05-10 12:10:35 -05:00
|
|
|
CFG {exit_map: exit_map,
|
|
|
|
graph: graph,
|
|
|
|
entry: entry,
|
2014-05-08 17:07:57 -05:00
|
|
|
exit: fn_exit}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn add_initial_dummy_node(g: &mut CFGGraph) -> CFGIndex {
|
|
|
|
g.add_node(CFGNodeData { id: ast::DUMMY_NODE_ID })
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
|
2014-04-22 07:56:37 -05:00
|
|
|
impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
|
2013-07-19 00:38:55 -05:00
|
|
|
fn block(&mut self, blk: &ast::Block, pred: CFGIndex) -> CFGIndex {
|
2013-05-10 12:10:35 -05:00
|
|
|
let mut stmts_exit = pred;
|
2014-05-16 12:15:33 -05:00
|
|
|
for stmt in blk.stmts.iter() {
|
|
|
|
stmts_exit = self.stmt(stmt.clone(), stmts_exit);
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
let expr_exit = self.opt_expr(blk.expr.clone(), stmts_exit);
|
2013-05-10 12:10:35 -05:00
|
|
|
|
2013-07-16 13:08:35 -05:00
|
|
|
self.add_node(blk.id, [expr_exit])
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
fn stmt(&mut self, stmt: Gc<ast::Stmt>, pred: CFGIndex) -> CFGIndex {
|
2013-05-10 12:10:35 -05:00
|
|
|
match stmt.node {
|
2014-08-27 20:46:52 -05:00
|
|
|
ast::StmtDecl(ref decl, id) => {
|
|
|
|
let exit = self.decl(&**decl, pred);
|
|
|
|
self.add_node(id, [exit])
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
|
2014-08-27 20:46:52 -05:00
|
|
|
ast::StmtExpr(ref expr, id) | ast::StmtSemi(ref expr, id) => {
|
|
|
|
let exit = self.expr(expr.clone(), pred);
|
|
|
|
self.add_node(id, [exit])
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
|
2013-11-28 14:22:53 -06:00
|
|
|
ast::StmtMac(..) => {
|
2013-05-10 12:10:35 -05:00
|
|
|
self.tcx.sess.span_bug(stmt.span, "unexpanded macro");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
fn decl(&mut self, decl: &ast::Decl, pred: CFGIndex) -> CFGIndex {
|
2013-05-10 12:10:35 -05:00
|
|
|
match decl.node {
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::DeclLocal(ref local) => {
|
|
|
|
let init_exit = self.opt_expr(local.init.clone(), pred);
|
|
|
|
self.pat(&*local.pat, init_exit)
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
|
2013-09-01 20:45:37 -05:00
|
|
|
ast::DeclItem(_) => {
|
2013-05-10 12:10:35 -05:00
|
|
|
pred
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
fn pat(&mut self, pat: &ast::Pat, pred: CFGIndex) -> CFGIndex {
|
2013-05-10 12:10:35 -05:00
|
|
|
match pat.node {
|
2013-09-01 20:45:37 -05:00
|
|
|
ast::PatIdent(_, _, None) |
|
|
|
|
ast::PatEnum(_, None) |
|
2013-11-28 14:22:53 -06:00
|
|
|
ast::PatLit(..) |
|
|
|
|
ast::PatRange(..) |
|
2014-08-06 10:04:44 -05:00
|
|
|
ast::PatWild(_) => {
|
2013-05-10 12:10:35 -05:00
|
|
|
self.add_node(pat.id, [pred])
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::PatBox(ref subpat) |
|
|
|
|
ast::PatRegion(ref subpat) |
|
|
|
|
ast::PatIdent(_, _, Some(ref subpat)) => {
|
|
|
|
let subpat_exit = self.pat(&**subpat, pred);
|
2013-05-10 12:10:35 -05:00
|
|
|
self.add_node(pat.id, [subpat_exit])
|
|
|
|
}
|
|
|
|
|
2013-09-01 20:45:37 -05:00
|
|
|
ast::PatEnum(_, Some(ref subpats)) |
|
|
|
|
ast::PatTup(ref subpats) => {
|
2013-05-10 12:10:35 -05:00
|
|
|
let pats_exit =
|
2014-05-16 12:15:33 -05:00
|
|
|
self.pats_all(subpats.iter().map(|p| p.clone()), pred);
|
2013-05-10 12:10:35 -05:00
|
|
|
self.add_node(pat.id, [pats_exit])
|
|
|
|
}
|
|
|
|
|
2013-09-01 20:45:37 -05:00
|
|
|
ast::PatStruct(_, ref subpats, _) => {
|
2013-05-10 12:10:35 -05:00
|
|
|
let pats_exit =
|
2014-05-16 12:15:33 -05:00
|
|
|
self.pats_all(subpats.iter().map(|f| f.pat.clone()), pred);
|
2013-05-10 12:10:35 -05:00
|
|
|
self.add_node(pat.id, [pats_exit])
|
|
|
|
}
|
|
|
|
|
2013-09-01 20:45:37 -05:00
|
|
|
ast::PatVec(ref pre, ref vec, ref post) => {
|
2013-05-10 12:10:35 -05:00
|
|
|
let pre_exit =
|
2013-08-09 22:09:47 -05:00
|
|
|
self.pats_all(pre.iter().map(|p| *p), pred);
|
2013-05-10 12:10:35 -05:00
|
|
|
let vec_exit =
|
2013-08-09 22:09:47 -05:00
|
|
|
self.pats_all(vec.iter().map(|p| *p), pre_exit);
|
2013-05-10 12:10:35 -05:00
|
|
|
let post_exit =
|
2013-08-09 22:09:47 -05:00
|
|
|
self.pats_all(post.iter().map(|p| *p), vec_exit);
|
2013-05-10 12:10:35 -05:00
|
|
|
self.add_node(pat.id, [post_exit])
|
|
|
|
}
|
2014-05-19 15:29:41 -05:00
|
|
|
|
|
|
|
ast::PatMac(_) => {
|
|
|
|
self.tcx.sess.span_bug(pat.span, "unexpanded macro");
|
|
|
|
}
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
fn pats_all<I: Iterator<Gc<ast::Pat>>>(&mut self,
|
2013-05-10 12:10:35 -05:00
|
|
|
pats: I,
|
|
|
|
pred: CFGIndex) -> CFGIndex {
|
|
|
|
//! Handles case where all of the patterns must match.
|
|
|
|
let mut pats = pats;
|
2014-05-16 12:15:33 -05:00
|
|
|
pats.fold(pred, |pred, pat| self.pat(&*pat, pred))
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn pats_any(&mut self,
|
2014-05-16 12:15:33 -05:00
|
|
|
pats: &[Gc<ast::Pat>],
|
2013-05-10 12:10:35 -05:00
|
|
|
pred: CFGIndex) -> CFGIndex {
|
|
|
|
//! Handles case where just one of the patterns must match.
|
|
|
|
|
|
|
|
if pats.len() == 1 {
|
2014-05-16 12:15:33 -05:00
|
|
|
self.pat(&*pats[0], pred)
|
2013-05-10 12:10:35 -05:00
|
|
|
} else {
|
|
|
|
let collect = self.add_dummy_node([]);
|
2013-08-03 11:45:23 -05:00
|
|
|
for &pat in pats.iter() {
|
2014-05-16 12:15:33 -05:00
|
|
|
let pat_exit = self.pat(&*pat, pred);
|
2013-05-10 12:10:35 -05:00
|
|
|
self.add_contained_edge(pat_exit, collect);
|
|
|
|
}
|
|
|
|
collect
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
fn expr(&mut self, expr: Gc<ast::Expr>, pred: CFGIndex) -> CFGIndex {
|
2013-05-10 12:10:35 -05:00
|
|
|
match expr.node {
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprBlock(ref blk) => {
|
|
|
|
let blk_exit = self.block(&**blk, pred);
|
2013-05-10 12:10:35 -05:00
|
|
|
self.add_node(expr.id, [blk_exit])
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprIf(ref cond, ref then, None) => {
|
2013-05-10 12:10:35 -05:00
|
|
|
//
|
|
|
|
// [pred]
|
|
|
|
// |
|
|
|
|
// v 1
|
|
|
|
// [cond]
|
|
|
|
// |
|
|
|
|
// / \
|
|
|
|
// / \
|
|
|
|
// v 2 *
|
|
|
|
// [then] |
|
|
|
|
// | |
|
|
|
|
// v 3 v 4
|
|
|
|
// [..expr..]
|
|
|
|
//
|
2014-05-16 12:15:33 -05:00
|
|
|
let cond_exit = self.expr(cond.clone(), pred); // 1
|
|
|
|
let then_exit = self.block(&**then, cond_exit); // 2
|
|
|
|
self.add_node(expr.id, [cond_exit, then_exit]) // 3,4
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprIf(ref cond, ref then, Some(ref otherwise)) => {
|
2013-05-10 12:10:35 -05:00
|
|
|
//
|
|
|
|
// [pred]
|
|
|
|
// |
|
|
|
|
// v 1
|
|
|
|
// [cond]
|
|
|
|
// |
|
|
|
|
// / \
|
|
|
|
// / \
|
|
|
|
// v 2 v 3
|
|
|
|
// [then][otherwise]
|
|
|
|
// | |
|
|
|
|
// v 4 v 5
|
|
|
|
// [..expr..]
|
|
|
|
//
|
2014-05-16 12:15:33 -05:00
|
|
|
let cond_exit = self.expr(cond.clone(), pred); // 1
|
|
|
|
let then_exit = self.block(&**then, cond_exit); // 2
|
|
|
|
let else_exit = self.expr(otherwise.clone(), cond_exit); // 3
|
|
|
|
self.add_node(expr.id, [then_exit, else_exit]) // 4, 5
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
|
2014-07-25 19:12:51 -05:00
|
|
|
ast::ExprWhile(ref cond, ref body, _) => {
|
2013-05-10 12:10:35 -05:00
|
|
|
//
|
|
|
|
// [pred]
|
|
|
|
// |
|
|
|
|
// v 1
|
|
|
|
// [loopback] <--+ 5
|
|
|
|
// | |
|
|
|
|
// v 2 |
|
|
|
|
// +-----[cond] |
|
|
|
|
// | | |
|
|
|
|
// | v 4 |
|
|
|
|
// | [body] -----+
|
|
|
|
// v 3
|
|
|
|
// [expr]
|
|
|
|
//
|
2014-07-21 22:54:28 -05:00
|
|
|
// Note that `break` and `continue` statements
|
2013-05-10 12:10:35 -05:00
|
|
|
// may cause additional edges.
|
|
|
|
|
2013-07-30 18:47:22 -05:00
|
|
|
// Is the condition considered part of the loop?
|
2014-05-16 12:15:33 -05:00
|
|
|
let loopback = self.add_dummy_node([pred]); // 1
|
|
|
|
let cond_exit = self.expr(cond.clone(), loopback); // 2
|
|
|
|
let expr_exit = self.add_node(expr.id, [cond_exit]); // 3
|
2013-05-10 12:10:35 -05:00
|
|
|
self.loop_scopes.push(LoopScope {
|
|
|
|
loop_id: expr.id,
|
|
|
|
continue_index: loopback,
|
|
|
|
break_index: expr_exit
|
|
|
|
});
|
2014-05-16 12:15:33 -05:00
|
|
|
let body_exit = self.block(&**body, cond_exit); // 4
|
|
|
|
self.add_contained_edge(body_exit, loopback); // 5
|
2014-05-20 11:49:19 -05:00
|
|
|
self.loop_scopes.pop();
|
2013-05-10 12:10:35 -05:00
|
|
|
expr_exit
|
|
|
|
}
|
|
|
|
|
2014-07-21 22:54:28 -05:00
|
|
|
ast::ExprForLoop(ref pat, ref head, ref body, _) => {
|
|
|
|
//
|
|
|
|
// [pred]
|
|
|
|
// |
|
|
|
|
// v 1
|
|
|
|
// [head]
|
|
|
|
// |
|
|
|
|
// v 2
|
|
|
|
// [loopback] <--+ 7
|
|
|
|
// | |
|
|
|
|
// v 3 |
|
|
|
|
// +------[cond] |
|
|
|
|
// | | |
|
|
|
|
// | v 5 |
|
|
|
|
// | [pat] |
|
|
|
|
// | | |
|
|
|
|
// | v 6 |
|
|
|
|
// v 4 [body] -----+
|
|
|
|
// [expr]
|
|
|
|
//
|
|
|
|
// Note that `break` and `continue` statements
|
|
|
|
// may cause additional edges.
|
|
|
|
|
|
|
|
let head = self.expr(head.clone(), pred); // 1
|
|
|
|
let loopback = self.add_dummy_node([head]); // 2
|
|
|
|
let cond = self.add_dummy_node([loopback]); // 3
|
|
|
|
let expr_exit = self.add_node(expr.id, [cond]); // 4
|
|
|
|
self.loop_scopes.push(LoopScope {
|
|
|
|
loop_id: expr.id,
|
|
|
|
continue_index: loopback,
|
|
|
|
break_index: expr_exit,
|
|
|
|
});
|
|
|
|
let pat = self.pat(&**pat, cond); // 5
|
|
|
|
let body = self.block(&**body, pat); // 6
|
|
|
|
self.add_contained_edge(body, loopback); // 7
|
|
|
|
self.loop_scopes.pop();
|
|
|
|
expr_exit
|
|
|
|
}
|
2013-07-29 19:25:00 -05:00
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprLoop(ref body, _) => {
|
2013-05-10 12:10:35 -05:00
|
|
|
//
|
|
|
|
// [pred]
|
|
|
|
// |
|
|
|
|
// v 1
|
|
|
|
// [loopback] <---+
|
|
|
|
// | 4 |
|
|
|
|
// v 3 |
|
|
|
|
// [body] ------+
|
|
|
|
//
|
|
|
|
// [expr] 2
|
|
|
|
//
|
|
|
|
// Note that `break` and `loop` statements
|
|
|
|
// may cause additional edges.
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
let loopback = self.add_dummy_node([pred]); // 1
|
|
|
|
let expr_exit = self.add_node(expr.id, []); // 2
|
2013-05-10 12:10:35 -05:00
|
|
|
self.loop_scopes.push(LoopScope {
|
|
|
|
loop_id: expr.id,
|
|
|
|
continue_index: loopback,
|
|
|
|
break_index: expr_exit,
|
|
|
|
});
|
2014-05-16 12:15:33 -05:00
|
|
|
let body_exit = self.block(&**body, loopback); // 3
|
|
|
|
self.add_contained_edge(body_exit, loopback); // 4
|
2013-05-10 12:10:35 -05:00
|
|
|
self.loop_scopes.pop();
|
|
|
|
expr_exit
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprMatch(ref discr, ref arms) => {
|
2013-05-10 12:10:35 -05:00
|
|
|
//
|
|
|
|
// [pred]
|
|
|
|
// |
|
|
|
|
// v 1
|
|
|
|
// [discr]
|
|
|
|
// |
|
|
|
|
// v 2
|
librustc: Disallow mutation and assignment in pattern guards, and modify
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]
2014-07-25 17:18:19 -05:00
|
|
|
// [cond1]
|
2013-05-10 12:10:35 -05:00
|
|
|
// / \
|
|
|
|
// | \
|
librustc: Disallow mutation and assignment in pattern guards, and modify
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]
2014-07-25 17:18:19 -05:00
|
|
|
// v 3 \
|
|
|
|
// [pat1] \
|
|
|
|
// | |
|
|
|
|
// v 4 |
|
|
|
|
// [guard1] |
|
|
|
|
// | |
|
|
|
|
// | |
|
|
|
|
// v 5 v
|
|
|
|
// [body1] [cond2]
|
|
|
|
// | / \
|
|
|
|
// | ... ...
|
|
|
|
// | | |
|
|
|
|
// v 6 v v
|
|
|
|
// [.....expr.....]
|
2013-05-10 12:10:35 -05:00
|
|
|
//
|
2014-05-16 12:15:33 -05:00
|
|
|
let discr_exit = self.expr(discr.clone(), pred); // 1
|
2013-05-10 12:10:35 -05:00
|
|
|
|
|
|
|
let expr_exit = self.add_node(expr.id, []);
|
librustc: Disallow mutation and assignment in pattern guards, and modify
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]
2014-07-25 17:18:19 -05:00
|
|
|
let mut cond_exit = discr_exit;
|
2013-08-03 11:45:23 -05:00
|
|
|
for arm in arms.iter() {
|
librustc: Disallow mutation and assignment in pattern guards, and modify
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]
2014-07-25 17:18:19 -05:00
|
|
|
cond_exit = self.add_dummy_node([cond_exit]); // 2
|
2014-02-28 17:25:15 -06:00
|
|
|
let pats_exit = self.pats_any(arm.pats.as_slice(),
|
librustc: Disallow mutation and assignment in pattern guards, and modify
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]
2014-07-25 17:18:19 -05:00
|
|
|
cond_exit); // 3
|
|
|
|
let guard_exit = self.opt_expr(arm.guard,
|
|
|
|
pats_exit); // 4
|
|
|
|
let body_exit = self.expr(arm.body.clone(),
|
|
|
|
guard_exit); // 5
|
|
|
|
self.add_contained_edge(body_exit, expr_exit); // 6
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
expr_exit
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprBinary(op, ref l, ref r) if ast_util::lazy_binop(op) => {
|
2013-05-10 12:10:35 -05:00
|
|
|
//
|
|
|
|
// [pred]
|
|
|
|
// |
|
|
|
|
// v 1
|
|
|
|
// [l]
|
|
|
|
// |
|
|
|
|
// / \
|
|
|
|
// / \
|
|
|
|
// v 2 *
|
|
|
|
// [r] |
|
|
|
|
// | |
|
|
|
|
// v 3 v 4
|
|
|
|
// [..exit..]
|
|
|
|
//
|
2014-05-16 12:15:33 -05:00
|
|
|
let l_exit = self.expr(l.clone(), pred); // 1
|
|
|
|
let r_exit = self.expr(r.clone(), l_exit); // 2
|
2013-05-10 12:10:35 -05:00
|
|
|
self.add_node(expr.id, [l_exit, r_exit]) // 3,4
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprRet(ref v) => {
|
|
|
|
let v_exit = self.opt_expr(v.clone(), pred);
|
2014-05-08 17:07:57 -05:00
|
|
|
let b = self.add_node(expr.id, [v_exit]);
|
|
|
|
self.add_returning_edge(expr, b);
|
|
|
|
self.add_node(ast::DUMMY_NODE_ID, [])
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
|
2013-09-01 20:45:37 -05:00
|
|
|
ast::ExprBreak(label) => {
|
2013-05-10 12:10:35 -05:00
|
|
|
let loop_scope = self.find_scope(expr, label);
|
2014-05-08 17:07:57 -05:00
|
|
|
let b = self.add_node(expr.id, [pred]);
|
|
|
|
self.add_exiting_edge(expr, b,
|
2013-05-10 12:10:35 -05:00
|
|
|
loop_scope, loop_scope.break_index);
|
2014-05-08 17:07:57 -05:00
|
|
|
self.add_node(ast::DUMMY_NODE_ID, [])
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
|
2013-09-01 20:45:37 -05:00
|
|
|
ast::ExprAgain(label) => {
|
2013-05-10 12:10:35 -05:00
|
|
|
let loop_scope = self.find_scope(expr, label);
|
2014-05-08 17:07:57 -05:00
|
|
|
let a = self.add_node(expr.id, [pred]);
|
|
|
|
self.add_exiting_edge(expr, a,
|
2013-05-10 12:10:35 -05:00
|
|
|
loop_scope, loop_scope.continue_index);
|
2014-05-08 17:07:57 -05:00
|
|
|
self.add_node(ast::DUMMY_NODE_ID, [])
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
|
2014-04-04 05:12:18 -05:00
|
|
|
ast::ExprVec(ref elems) => {
|
2014-02-28 17:25:15 -06:00
|
|
|
self.straightline(expr, pred, elems.as_slice())
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprCall(ref func, ref args) => {
|
|
|
|
self.call(expr, pred, func.clone(), args.as_slice())
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
|
2014-02-26 08:06:45 -06:00
|
|
|
ast::ExprMethodCall(_, _, ref args) => {
|
2014-02-28 17:25:15 -06:00
|
|
|
self.call(expr, pred, *args.get(0), args.slice_from(1))
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprIndex(ref l, ref r) |
|
|
|
|
ast::ExprBinary(_, ref l, ref r) if self.is_method_call(&*expr) => {
|
|
|
|
self.call(expr, pred, l.clone(), [r.clone()])
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprUnary(_, ref e) if self.is_method_call(&*expr) => {
|
|
|
|
self.call(expr, pred, e.clone(), [])
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
|
2013-09-01 20:45:37 -05:00
|
|
|
ast::ExprTup(ref exprs) => {
|
2014-02-28 17:25:15 -06:00
|
|
|
self.straightline(expr, pred, exprs.as_slice())
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
|
2013-09-01 20:45:37 -05:00
|
|
|
ast::ExprStruct(_, ref fields, base) => {
|
2013-05-10 12:10:35 -05:00
|
|
|
let base_exit = self.opt_expr(base, pred);
|
2014-05-16 12:15:33 -05:00
|
|
|
let field_exprs: Vec<Gc<ast::Expr>> =
|
2013-08-09 22:09:47 -05:00
|
|
|
fields.iter().map(|f| f.expr).collect();
|
2014-03-08 14:36:22 -06:00
|
|
|
self.straightline(expr, base_exit, field_exprs.as_slice())
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
|
2014-04-04 05:12:18 -05:00
|
|
|
ast::ExprRepeat(elem, count) => {
|
2013-05-10 12:10:35 -05:00
|
|
|
self.straightline(expr, pred, [elem, count])
|
|
|
|
}
|
|
|
|
|
2013-09-01 20:45:37 -05:00
|
|
|
ast::ExprAssign(l, r) |
|
2014-02-26 08:06:45 -06:00
|
|
|
ast::ExprAssignOp(_, l, r) => {
|
2013-05-10 12:10:35 -05:00
|
|
|
self.straightline(expr, pred, [r, l])
|
|
|
|
}
|
|
|
|
|
2014-02-26 08:06:45 -06:00
|
|
|
ast::ExprIndex(l, r) |
|
|
|
|
ast::ExprBinary(_, l, r) => { // NB: && and || handled earlier
|
2013-05-10 12:10:35 -05:00
|
|
|
self.straightline(expr, pred, [l, r])
|
|
|
|
}
|
|
|
|
|
2013-12-17 18:46:18 -06:00
|
|
|
ast::ExprBox(p, e) => {
|
|
|
|
self.straightline(expr, pred, [p, e])
|
|
|
|
}
|
|
|
|
|
2013-09-01 20:45:37 -05:00
|
|
|
ast::ExprAddrOf(_, e) |
|
|
|
|
ast::ExprCast(e, _) |
|
2014-02-26 08:06:45 -06:00
|
|
|
ast::ExprUnary(_, e) |
|
2013-09-01 20:45:37 -05:00
|
|
|
ast::ExprParen(e) |
|
2014-08-09 22:54:33 -05:00
|
|
|
ast::ExprField(e, _, _) |
|
|
|
|
ast::ExprTupField(e, _, _) => {
|
2013-05-10 12:10:35 -05:00
|
|
|
self.straightline(expr, pred, [e])
|
|
|
|
}
|
|
|
|
|
2014-05-20 11:49:19 -05:00
|
|
|
ast::ExprInlineAsm(ref inline_asm) => {
|
|
|
|
let inputs = inline_asm.inputs.iter();
|
|
|
|
let outputs = inline_asm.outputs.iter();
|
|
|
|
let post_inputs = self.exprs(inputs.map(|a| {
|
|
|
|
debug!("cfg::construct InlineAsm id:{} input:{:?}", expr.id, a);
|
2014-08-19 14:39:26 -05:00
|
|
|
let &(_, expr) = a;
|
|
|
|
expr
|
2014-05-20 11:49:19 -05:00
|
|
|
}), pred);
|
|
|
|
let post_outputs = self.exprs(outputs.map(|a| {
|
|
|
|
debug!("cfg::construct InlineAsm id:{} output:{:?}", expr.id, a);
|
2014-08-19 14:39:26 -05:00
|
|
|
let &(_, expr, _) = a;
|
|
|
|
expr
|
2014-05-20 11:49:19 -05:00
|
|
|
}), post_inputs);
|
|
|
|
self.add_node(expr.id, [post_outputs])
|
|
|
|
}
|
|
|
|
|
2013-11-28 14:22:53 -06:00
|
|
|
ast::ExprMac(..) |
|
|
|
|
ast::ExprFnBlock(..) |
|
|
|
|
ast::ExprProc(..) |
|
2014-05-29 00:26:56 -05:00
|
|
|
ast::ExprUnboxedFn(..) |
|
2013-11-28 14:22:53 -06:00
|
|
|
ast::ExprLit(..) |
|
|
|
|
ast::ExprPath(..) => {
|
2013-05-10 12:10:35 -05:00
|
|
|
self.straightline(expr, pred, [])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn call(&mut self,
|
2014-05-16 12:15:33 -05:00
|
|
|
call_expr: Gc<ast::Expr>,
|
2013-05-10 12:10:35 -05:00
|
|
|
pred: CFGIndex,
|
2014-05-16 12:15:33 -05:00
|
|
|
func_or_rcvr: Gc<ast::Expr>,
|
|
|
|
args: &[Gc<ast::Expr>]) -> CFGIndex {
|
2013-05-10 12:10:35 -05:00
|
|
|
let func_or_rcvr_exit = self.expr(func_or_rcvr, pred);
|
2014-05-20 11:49:19 -05:00
|
|
|
let ret = self.straightline(call_expr, func_or_rcvr_exit, args);
|
|
|
|
|
|
|
|
let return_ty = ty::node_id_to_type(self.tcx, call_expr.id);
|
|
|
|
let fails = ty::type_is_bot(return_ty);
|
|
|
|
if fails {
|
|
|
|
self.add_node(ast::DUMMY_NODE_ID, [])
|
|
|
|
} else {
|
|
|
|
ret
|
|
|
|
}
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
|
2014-05-20 11:49:19 -05:00
|
|
|
fn exprs<I:Iterator<Gc<ast::Expr>>>(&mut self,
|
|
|
|
mut exprs: I,
|
|
|
|
pred: CFGIndex) -> CFGIndex {
|
2013-05-10 12:10:35 -05:00
|
|
|
//! Constructs graph for `exprs` evaluated in order
|
2014-05-20 11:49:19 -05:00
|
|
|
exprs.fold(pred, |p, e| self.expr(e, p))
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn opt_expr(&mut self,
|
2014-05-16 12:15:33 -05:00
|
|
|
opt_expr: Option<Gc<ast::Expr>>,
|
2013-05-10 12:10:35 -05:00
|
|
|
pred: CFGIndex) -> CFGIndex {
|
|
|
|
//! Constructs graph for `opt_expr` evaluated, if Some
|
|
|
|
|
|
|
|
opt_expr.iter().fold(pred, |p, &e| self.expr(e, p))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn straightline(&mut self,
|
2014-05-16 12:15:33 -05:00
|
|
|
expr: Gc<ast::Expr>,
|
2013-05-10 12:10:35 -05:00
|
|
|
pred: CFGIndex,
|
2014-05-16 12:15:33 -05:00
|
|
|
subexprs: &[Gc<ast::Expr>]) -> CFGIndex {
|
2013-05-10 12:10:35 -05:00
|
|
|
//! Handles case of an expression that evaluates `subexprs` in order
|
|
|
|
|
2014-05-20 11:49:19 -05:00
|
|
|
let subexprs_exit = self.exprs(subexprs.iter().map(|&e|e), pred);
|
2013-05-10 12:10:35 -05:00
|
|
|
self.add_node(expr.id, [subexprs_exit])
|
|
|
|
}
|
|
|
|
|
|
|
|
fn add_dummy_node(&mut self, preds: &[CFGIndex]) -> CFGIndex {
|
2014-05-08 17:07:57 -05:00
|
|
|
self.add_node(ast::DUMMY_NODE_ID, preds)
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
|
2013-07-27 03:25:59 -05:00
|
|
|
fn add_node(&mut self, id: ast::NodeId, preds: &[CFGIndex]) -> CFGIndex {
|
2013-05-10 12:10:35 -05:00
|
|
|
assert!(!self.exit_map.contains_key(&id));
|
|
|
|
let node = self.graph.add_node(CFGNodeData {id: id});
|
2014-05-08 17:07:57 -05:00
|
|
|
if id != ast::DUMMY_NODE_ID {
|
|
|
|
assert!(!self.exit_map.contains_key(&id));
|
|
|
|
self.exit_map.insert(id, node);
|
|
|
|
}
|
2013-08-03 11:45:23 -05:00
|
|
|
for &pred in preds.iter() {
|
2013-05-10 12:10:35 -05:00
|
|
|
self.add_contained_edge(pred, node);
|
|
|
|
}
|
|
|
|
node
|
|
|
|
}
|
|
|
|
|
|
|
|
fn add_contained_edge(&mut self,
|
|
|
|
source: CFGIndex,
|
|
|
|
target: CFGIndex) {
|
2014-03-19 07:16:56 -05:00
|
|
|
let data = CFGEdgeData {exiting_scopes: vec!() };
|
2013-05-10 12:10:35 -05:00
|
|
|
self.graph.add_edge(source, target, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn add_exiting_edge(&mut self,
|
2014-05-16 12:15:33 -05:00
|
|
|
from_expr: Gc<ast::Expr>,
|
2013-05-10 12:10:35 -05:00
|
|
|
from_index: CFGIndex,
|
|
|
|
to_loop: LoopScope,
|
|
|
|
to_index: CFGIndex) {
|
2014-03-19 07:16:56 -05:00
|
|
|
let mut data = CFGEdgeData {exiting_scopes: vec!() };
|
2013-05-10 12:10:35 -05:00
|
|
|
let mut scope_id = from_expr.id;
|
|
|
|
while scope_id != to_loop.loop_id {
|
2014-03-19 07:16:56 -05:00
|
|
|
|
2013-05-10 12:10:35 -05:00
|
|
|
data.exiting_scopes.push(scope_id);
|
|
|
|
scope_id = self.tcx.region_maps.encl_scope(scope_id);
|
|
|
|
}
|
|
|
|
self.graph.add_edge(from_index, to_index, data);
|
|
|
|
}
|
|
|
|
|
2014-05-08 17:07:57 -05:00
|
|
|
fn add_returning_edge(&mut self,
|
2014-05-16 12:15:33 -05:00
|
|
|
_from_expr: Gc<ast::Expr>,
|
2014-05-08 17:07:57 -05:00
|
|
|
from_index: CFGIndex) {
|
2014-05-16 12:45:16 -05:00
|
|
|
let mut data = CFGEdgeData {
|
|
|
|
exiting_scopes: vec!(),
|
|
|
|
};
|
2014-05-08 17:07:57 -05:00
|
|
|
for &LoopScope { loop_id: id, .. } in self.loop_scopes.iter().rev() {
|
|
|
|
data.exiting_scopes.push(id);
|
|
|
|
}
|
|
|
|
self.graph.add_edge(from_index, self.fn_exit, data);
|
|
|
|
}
|
|
|
|
|
2013-05-10 12:10:35 -05:00
|
|
|
fn find_scope(&self,
|
2014-05-16 12:15:33 -05:00
|
|
|
expr: Gc<ast::Expr>,
|
2014-02-15 02:54:32 -06:00
|
|
|
label: Option<ast::Ident>) -> LoopScope {
|
2013-05-10 12:10:35 -05:00
|
|
|
match label {
|
|
|
|
None => {
|
2013-12-23 08:08:23 -06:00
|
|
|
return *self.loop_scopes.last().unwrap();
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
Some(_) => {
|
2014-03-20 21:49:20 -05:00
|
|
|
match self.tcx.def_map.borrow().find(&expr.id) {
|
2014-05-14 14:31:30 -05:00
|
|
|
Some(&def::DefLabel(loop_id)) => {
|
2013-08-03 11:45:23 -05:00
|
|
|
for l in self.loop_scopes.iter() {
|
2013-05-10 12:10:35 -05:00
|
|
|
if l.loop_id == loop_id {
|
|
|
|
return *l;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.tcx.sess.span_bug(
|
|
|
|
expr.span,
|
2014-05-16 12:45:16 -05:00
|
|
|
format!("no loop scope for id {:?}",
|
|
|
|
loop_id).as_slice());
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
r => {
|
|
|
|
self.tcx.sess.span_bug(
|
|
|
|
expr.span,
|
2014-05-16 12:45:16 -05:00
|
|
|
format!("bad entry `{:?}` in def_map for label",
|
|
|
|
r).as_slice());
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-01 20:45:37 -05:00
|
|
|
fn is_method_call(&self, expr: &ast::Expr) -> bool {
|
2014-03-06 11:24:11 -06:00
|
|
|
let method_call = typeck::MethodCall::expr(expr.id);
|
2014-04-17 14:00:08 -05:00
|
|
|
self.tcx.method_map.borrow().contains_key(&method_call)
|
2013-05-10 12:10:35 -05:00
|
|
|
}
|
2013-07-09 21:32:09 -05:00
|
|
|
}
|