auto merge of #14873 : pnkfelix/rust/fsk-dataflow-revisions, r=nikomatsakis

Fix #6298.  Fix  #13767.

This also includes some drive by fixes for some other issues, noted in the commits.

I still need to integrate regression tests for some cases that I noticed were missing from our unit test suite (i.e. things that compiling rustc exposes that should have been exposed when doing `make check-stage1`).  So do not land this yet, until I get the chance to add those tests.

I just wanted to get the review process started soon, since this has been long in the coming.
This commit is contained in:
bors 2014-06-18 16:16:42 +00:00
commit 4416b32768
23 changed files with 981 additions and 643 deletions

View File

@ -242,7 +242,7 @@ impl<'a> CheckLoanCtxt<'a> {
let mut loan_path = loan_path;
loop {
match *loan_path {
LpVar(_) => {
LpVar(_) | LpUpvar(_) => {
break;
}
LpExtend(ref lp_base, _, _) => {
@ -632,7 +632,7 @@ impl<'a> CheckLoanCtxt<'a> {
*/
match **lp {
LpVar(_) => {
LpVar(_) | LpUpvar(_) => {
// assigning to `x` does not require that `x` is initialized
}
LpExtend(ref lp_base, _, LpInterior(_)) => {

View File

@ -948,7 +948,7 @@ The borrow checker is also in charge of ensuring that:
These are two separate dataflow analyses built on the same
framework. Let's look at checking that memory is initialized first;
the checking of immutable local variabe assignments works in a very
the checking of immutable local variable assignments works in a very
similar way.
To track the initialization of memory, we actually track all the

View File

@ -395,7 +395,8 @@ impl<'a> GatherLoanCtxt<'a> {
//! from a local variable, mark the mutability decl as necessary.
match *loan_path {
LpVar(local_id) => {
LpVar(local_id) |
LpUpvar(ty::UpvarId{ var_id: local_id, closure_expr_id: _ }) => {
self.tcx().used_mut_nodes.borrow_mut().insert(local_id);
}
LpExtend(ref base, mc::McInherited, _) => {
@ -445,8 +446,8 @@ impl<'a> GatherLoanCtxt<'a> {
//! with immutable `&` pointers, because borrows of such pointers
//! do not require restrictions and hence do not cause a loan.
let lexical_scope = lp.kill_scope(self.bccx.tcx);
let rm = &self.bccx.tcx.region_maps;
let lexical_scope = rm.var_scope(lp.node_id());
if rm.is_subscope_of(lexical_scope, loan_scope) {
lexical_scope
} else {

View File

@ -67,13 +67,23 @@ impl<'a> RestrictionsContext<'a> {
}
mc::cat_local(local_id) |
mc::cat_arg(local_id) |
mc::cat_upvar(ty::UpvarId {var_id: local_id, ..}, _) => {
// R-Variable
mc::cat_arg(local_id) => {
// R-Variable, locally declared
let lp = Rc::new(LpVar(local_id));
SafeIf(lp.clone(), vec!(lp))
}
mc::cat_upvar(upvar_id, _) => {
// R-Variable, captured into closure
let lp = Rc::new(LpUpvar(upvar_id));
SafeIf(lp.clone(), vec!(lp))
}
mc::cat_copied_upvar(..) => {
// FIXME(#2152) allow mutation of upvars
Safe
}
mc::cat_downcast(cmt_base) => {
// When we borrow the interior of an enum, we have to
// ensure the enum itself is not mutated, because that
@ -107,7 +117,6 @@ impl<'a> RestrictionsContext<'a> {
self.extend(result, cmt.mutbl, LpDeref(pk))
}
mc::cat_copied_upvar(..) | // FIXME(#2152) allow mutation of upvars
mc::cat_static_item(..) => {
Safe
}

View File

@ -12,7 +12,9 @@
#![allow(non_camel_case_types)]
use middle::cfg;
use middle::dataflow::DataFlowContext;
use middle::dataflow::BitwiseOperator;
use middle::dataflow::DataFlowOperator;
use middle::def;
use euv = middle::expr_use_visitor;
@ -126,8 +128,13 @@ fn borrowck_fn(this: &mut BorrowckCtxt,
let id_range = ast_util::compute_id_range_for_fn_body(fk, decl, body, sp, id);
let (all_loans, move_data) =
gather_loans::gather_loans_in_fn(this, decl, body);
let cfg = cfg::CFG::new(this.tcx, body);
let mut loan_dfcx =
DataFlowContext::new(this.tcx,
"borrowck",
Some(decl),
&cfg,
LoanDataFlowOperator,
id_range,
all_loans.len());
@ -135,11 +142,14 @@ fn borrowck_fn(this: &mut BorrowckCtxt,
loan_dfcx.add_gen(loan.gen_scope, loan_idx);
loan_dfcx.add_kill(loan.kill_scope, loan_idx);
}
loan_dfcx.propagate(body);
loan_dfcx.add_kills_from_flow_exits(&cfg);
loan_dfcx.propagate(&cfg, body);
let flowed_moves = move_data::FlowedMoveData::new(move_data,
this.tcx,
&cfg,
id_range,
decl,
body);
check_loans::check_loans(this, &loan_dfcx, flowed_moves,
@ -191,6 +201,7 @@ pub struct Loan {
#[deriving(PartialEq, Eq, Hash)]
pub enum LoanPath {
LpVar(ast::NodeId), // `x` in doc.rs
LpUpvar(ty::UpvarId), // `x` captured by-value into closure
LpExtend(Rc<LoanPath>, mc::MutabilityCategory, LoanPathElem)
}
@ -200,11 +211,25 @@ pub enum LoanPathElem {
LpInterior(mc::InteriorKind) // `LV.f` in doc.rs
}
pub fn closure_to_block(closure_id: ast::NodeId,
tcx: &ty::ctxt) -> ast::NodeId {
match tcx.map.get(closure_id) {
ast_map::NodeExpr(expr) => match expr.node {
ast::ExprProc(_decl, block) |
ast::ExprFnBlock(_decl, block) => { block.id }
_ => fail!("encountered non-closure id: {}", closure_id)
},
_ => fail!("encountered non-expr id: {}", closure_id)
}
}
impl LoanPath {
pub fn node_id(&self) -> ast::NodeId {
pub fn kill_scope(&self, tcx: &ty::ctxt) -> ast::NodeId {
match *self {
LpVar(local_id) => local_id,
LpExtend(ref base, _, _) => base.node_id()
LpVar(local_id) => tcx.region_maps.var_scope(local_id),
LpUpvar(upvar_id) =>
closure_to_block(upvar_id.closure_expr_id, tcx),
LpExtend(ref base, _, _) => base.kill_scope(tcx),
}
}
}
@ -224,12 +249,18 @@ pub fn opt_loan_path(cmt: &mc::cmt) -> Option<Rc<LoanPath>> {
}
mc::cat_local(id) |
mc::cat_arg(id) |
mc::cat_copied_upvar(mc::CopiedUpvar { upvar_id: id, .. }) |
mc::cat_upvar(ty::UpvarId {var_id: id, ..}, _) => {
mc::cat_arg(id) => {
Some(Rc::new(LpVar(id)))
}
mc::cat_upvar(ty::UpvarId {var_id: id, closure_expr_id: proc_id}, _) |
mc::cat_copied_upvar(mc::CopiedUpvar { upvar_id: id,
onceness: _,
capturing_proc: proc_id }) => {
let upvar_id = ty::UpvarId{ var_id: id, closure_expr_id: proc_id };
Some(Rc::new(LpUpvar(upvar_id)))
}
mc::cat_deref(ref cmt_base, _, pk) => {
opt_loan_path(cmt_base).map(|lp| {
Rc::new(LpExtend(lp, cmt.mutbl, LpDeref(pk)))
@ -683,6 +714,7 @@ impl<'a> BorrowckCtxt<'a> {
loan_path: &LoanPath,
out: &mut String) {
match *loan_path {
LpUpvar(ty::UpvarId{ var_id: id, closure_expr_id: _ }) |
LpVar(id) => {
out.push_str(ty::local_var_name_str(self.tcx, id).get());
}
@ -724,7 +756,7 @@ impl<'a> BorrowckCtxt<'a> {
self.append_autoderefd_loan_path_to_str(&**lp_base, out)
}
LpVar(..) | LpExtend(_, _, LpInterior(..)) => {
LpVar(..) | LpUpvar(..) | LpExtend(_, _, LpInterior(..)) => {
self.append_loan_path_to_str(loan_path, out)
}
}
@ -753,16 +785,18 @@ fn is_statement_scope(tcx: &ty::ctxt, region: ty::Region) -> bool {
}
}
impl BitwiseOperator for LoanDataFlowOperator {
#[inline]
fn join(&self, succ: uint, pred: uint) -> uint {
succ | pred // loans from both preds are in scope
}
}
impl DataFlowOperator for LoanDataFlowOperator {
#[inline]
fn initial_value(&self) -> bool {
false // no loans in scope by default
}
#[inline]
fn join(&self, succ: uint, pred: uint) -> uint {
succ | pred // loans from both preds are in scope
}
}
impl Repr for Loan {
@ -784,6 +818,12 @@ impl Repr for LoanPath {
(format!("$({})", tcx.map.node_to_str(id))).to_string()
}
&LpUpvar(ty::UpvarId{ var_id, closure_expr_id }) => {
let s = tcx.map.node_to_str(var_id);
let s = format!("$({} captured by id={})", s, closure_expr_id);
s.to_string()
}
&LpExtend(ref lp, _, LpDeref(_)) => {
(format!("{}.*", lp.repr(tcx))).to_string()
}

View File

@ -20,7 +20,9 @@ use std::rc::Rc;
use std::uint;
use std::collections::{HashMap, HashSet};
use middle::borrowck::*;
use middle::cfg;
use middle::dataflow::DataFlowContext;
use middle::dataflow::BitwiseOperator;
use middle::dataflow::DataFlowOperator;
use euv = middle::expr_use_visitor;
use middle::ty;
@ -229,7 +231,7 @@ impl MoveData {
}
let index = match *lp {
LpVar(..) => {
LpVar(..) | LpUpvar(..) => {
let index = MovePathIndex(self.paths.borrow().len());
self.paths.borrow_mut().push(MovePath {
@ -300,7 +302,7 @@ impl MoveData {
}
None => {
match **lp {
LpVar(..) => { }
LpVar(..) | LpUpvar(..) => { }
LpExtend(ref b, _, _) => {
self.add_existing_base_paths(b, result);
}
@ -416,6 +418,11 @@ impl MoveData {
let path = *self.path_map.borrow().get(&path.loan_path);
self.kill_moves(path, kill_id, dfcx_moves);
}
LpUpvar(ty::UpvarId { var_id: _, closure_expr_id }) => {
let kill_id = closure_to_block(closure_expr_id, tcx);
let path = *self.path_map.borrow().get(&path.loan_path);
self.kill_moves(path, kill_id, dfcx_moves);
}
LpExtend(..) => {}
}
}
@ -428,6 +435,10 @@ impl MoveData {
let kill_id = tcx.region_maps.var_scope(id);
dfcx_assign.add_kill(kill_id, assignment_index);
}
LpUpvar(ty::UpvarId { var_id: _, closure_expr_id }) => {
let kill_id = closure_to_block(closure_expr_id, tcx);
dfcx_assign.add_kill(kill_id, assignment_index);
}
LpExtend(..) => {
tcx.sess.bug("var assignment for non var path");
}
@ -499,22 +510,33 @@ impl MoveData {
impl<'a> FlowedMoveData<'a> {
pub fn new(move_data: MoveData,
tcx: &'a ty::ctxt,
cfg: &'a cfg::CFG,
id_range: ast_util::IdRange,
decl: &ast::FnDecl,
body: &ast::Block)
-> FlowedMoveData<'a> {
let mut dfcx_moves =
DataFlowContext::new(tcx,
"flowed_move_data_moves",
Some(decl),
cfg,
MoveDataFlowOperator,
id_range,
move_data.moves.borrow().len());
let mut dfcx_assign =
DataFlowContext::new(tcx,
"flowed_move_data_assigns",
Some(decl),
cfg,
AssignDataFlowOperator,
id_range,
move_data.var_assignments.borrow().len());
move_data.add_gen_kills(tcx, &mut dfcx_moves, &mut dfcx_assign);
dfcx_moves.propagate(body);
dfcx_assign.propagate(body);
dfcx_moves.add_kills_from_flow_exits(cfg);
dfcx_assign.add_kills_from_flow_exits(cfg);
dfcx_moves.propagate(cfg, body);
dfcx_assign.propagate(cfg, body);
FlowedMoveData {
move_data: move_data,
dfcx_moves: dfcx_moves,
@ -659,12 +681,21 @@ impl<'a> FlowedMoveData<'a> {
}
}
impl BitwiseOperator for MoveDataFlowOperator {
#[inline]
fn join(&self, succ: uint, pred: uint) -> uint {
succ | pred // moves from both preds are in scope
}
}
impl DataFlowOperator for MoveDataFlowOperator {
#[inline]
fn initial_value(&self) -> bool {
false // no loans in scope by default
}
}
impl BitwiseOperator for AssignDataFlowOperator {
#[inline]
fn join(&self, succ: uint, pred: uint) -> uint {
succ | pred // moves from both preds are in scope
@ -676,9 +707,4 @@ impl DataFlowOperator for AssignDataFlowOperator {
fn initial_value(&self) -> bool {
false // no assignments in scope by default
}
#[inline]
fn join(&self, succ: uint, pred: uint) -> uint {
succ | pred // moves from both preds are in scope
}
}

View File

@ -254,6 +254,7 @@ impl<'a> CFGBuilder<'a> {
});
let body_exit = self.block(&**body, cond_exit); // 4
self.add_contained_edge(body_exit, loopback); // 5
self.loop_scopes.pop();
expr_exit
}
@ -427,8 +428,22 @@ impl<'a> CFGBuilder<'a> {
self.straightline(expr, pred, [e])
}
ast::ExprInlineAsm(ref inline_asm) => {
let inputs = inline_asm.inputs.iter();
let outputs = inline_asm.outputs.iter();
fn extract_expr<A>(&(_, expr): &(A, Gc<ast::Expr>)) -> Gc<ast::Expr> { expr }
let post_inputs = self.exprs(inputs.map(|a| {
debug!("cfg::construct InlineAsm id:{} input:{:?}", expr.id, a);
extract_expr(a)
}), pred);
let post_outputs = self.exprs(outputs.map(|a| {
debug!("cfg::construct InlineAsm id:{} output:{:?}", expr.id, a);
extract_expr(a)
}), post_inputs);
self.add_node(expr.id, [post_outputs])
}
ast::ExprMac(..) |
ast::ExprInlineAsm(..) |
ast::ExprFnBlock(..) |
ast::ExprProc(..) |
ast::ExprLit(..) |
@ -444,15 +459,22 @@ impl<'a> CFGBuilder<'a> {
func_or_rcvr: Gc<ast::Expr>,
args: &[Gc<ast::Expr>]) -> CFGIndex {
let func_or_rcvr_exit = self.expr(func_or_rcvr, pred);
self.straightline(call_expr, func_or_rcvr_exit, args)
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
}
}
fn exprs(&mut self,
exprs: &[Gc<ast::Expr>],
pred: CFGIndex) -> CFGIndex {
fn exprs<I:Iterator<Gc<ast::Expr>>>(&mut self,
mut exprs: I,
pred: CFGIndex) -> CFGIndex {
//! Constructs graph for `exprs` evaluated in order
exprs.iter().fold(pred, |p, &e| self.expr(e, p))
exprs.fold(pred, |p, e| self.expr(e, p))
}
fn opt_expr(&mut self,
@ -469,7 +491,7 @@ impl<'a> CFGBuilder<'a> {
subexprs: &[Gc<ast::Expr>]) -> CFGIndex {
//! Handles case of an expression that evaluates `subexprs` in order
let subexprs_exit = self.exprs(subexprs, pred);
let subexprs_exit = self.exprs(subexprs.iter().map(|&e|e), pred);
self.add_node(expr.id, [subexprs_exit])
}

View File

@ -15,8 +15,6 @@ Uses `Graph` as the underlying representation.
*/
#![allow(dead_code)] // still a WIP, #6298
use middle::graph;
use middle::ty;
use syntax::ast;
@ -48,11 +46,6 @@ pub type CFGNode = graph::Node<CFGNodeData>;
pub type CFGEdge = graph::Edge<CFGEdgeData>;
pub struct CFGIndices {
entry: CFGIndex,
exit: CFGIndex,
}
impl CFG {
pub fn new(tcx: &ty::ctxt,
blk: &ast::Block) -> CFG {

File diff suppressed because it is too large Load Diff

View File

@ -186,24 +186,7 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> {
let cmt = return_if_err!(self.mc.cat_expr(expr));
self.delegate_consume(expr.id, expr.span, cmt);
match expr.node {
ast::ExprParen(ref subexpr) => {
// Argh but is ExprParen horrible. So, if we consume
// `(x)`, that generally is also consuming `x`, UNLESS
// there are adjustments on the `(x)` expression
// (e.g., autoderefs and autorefs).
if self.typer.adjustments().borrow().contains_key(&expr.id) {
self.walk_expr(expr);
} else {
self.consume_expr(&**subexpr);
}
}
_ => {
self.walk_expr(expr)
}
}
self.walk_expr(expr);
}
fn mutate_expr(&mut self,

View File

@ -55,7 +55,7 @@ pub struct Edge<E> {
pub data: E,
}
#[deriving(PartialEq)]
#[deriving(Clone, PartialEq, Show)]
pub struct NodeIndex(pub uint);
pub static InvalidNodeIndex: NodeIndex = NodeIndex(uint::MAX);

View File

@ -97,6 +97,7 @@ pub enum categorization {
pub struct CopiedUpvar {
pub upvar_id: ast::NodeId,
pub onceness: ast::Onceness,
pub capturing_proc: ast::NodeId,
}
// different kinds of pointers:
@ -559,7 +560,9 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> {
span:span,
cat:cat_copied_upvar(CopiedUpvar {
upvar_id: var_id,
onceness: closure_ty.onceness}),
onceness: closure_ty.onceness,
capturing_proc: fn_node_id,
}),
mutbl:McImmutable,
ty:expr_ty
}))

View File

@ -12,7 +12,7 @@ fn a() {
let mut v = vec!(1, 2, 3);
let vb: &mut [int] = v.as_mut_slice();
match vb {
[_a, ..tail] => { //~ ERROR cannot use `vb[..]` because it was mutably borrowed
[_a, ..tail] => {
v.push(tail[0] + tail[1]); //~ ERROR cannot borrow
}
_ => {}

View File

@ -2,7 +2,8 @@
FILES=f00.rs f01.rs f02.rs f03.rs f04.rs f05.rs f06.rs f07.rs \
f08.rs f09.rs f10.rs f11.rs f12.rs f13.rs f14.rs f15.rs \
f16.rs f17.rs f18.rs f19.rs f20.rs f21.rs f22.rs
f16.rs f17.rs f18.rs f19.rs f20.rs f21.rs f22.rs f23.rs \
f24.rs f25.rs
# all: $(patsubst %.rs,$(TMPDIR)/%.dot,$(FILES)) $(patsubst %.rs,$(TMPDIR)/%.pp,$(FILES))

View File

@ -0,0 +1,93 @@
digraph block {
N0[label="entry"];
N1[label="exit"];
N2[label="expr 23"];
N3[label="local mut x"];
N4[label="expr 23"];
N5[label="local mut y"];
N6[label="expr 23"];
N7[label="local mut z"];
N8[label="(dummy_node)"];
N9[label="expr x"];
N10[label="expr 0"];
N11[label="expr x > 0"];
N12[label="expr while x > 0 {\l x -= 1;\l while y > 0 {\l y -= 1;\l while z > 0 { z -= 1; }\l if x > 10 { return; \"unreachable\"; }\l }\l}\l"];
N13[label="expr 1"];
N14[label="expr x"];
N15[label="expr x -= 1"];
N16[label="(dummy_node)"];
N17[label="expr y"];
N18[label="expr 0"];
N19[label="expr y > 0"];
N20[label="expr while y > 0 {\l y -= 1;\l while z > 0 { z -= 1; }\l if x > 10 { return; \"unreachable\"; }\l}\l"];
N21[label="expr 1"];
N22[label="expr y"];
N23[label="expr y -= 1"];
N24[label="(dummy_node)"];
N25[label="expr z"];
N26[label="expr 0"];
N27[label="expr z > 0"];
N28[label="expr while z > 0 { z -= 1; }"];
N29[label="expr 1"];
N30[label="expr z"];
N31[label="expr z -= 1"];
N32[label="block { z -= 1; }"];
N33[label="expr x"];
N34[label="expr 10"];
N35[label="expr x > 10"];
N36[label="expr return"];
N37[label="(dummy_node)"];
N38[label="expr \"unreachable\""];
N39[label="block { return; \"unreachable\"; }"];
N40[label="expr if x > 10 { return; \"unreachable\"; }"];
N41[label="block { y -= 1; while z > 0 { z -= 1; } if x > 10 { return; \"unreachable\"; } }"];
N42[label="block {\l x -= 1;\l while y > 0 {\l y -= 1;\l while z > 0 { z -= 1; }\l if x > 10 { return; \"unreachable\"; }\l }\l}\l"];
N43[label="block {\l let mut x = 23;\l let mut y = 23;\l let mut z = 23;\l while x > 0 {\l x -= 1;\l while y > 0 {\l y -= 1;\l while z > 0 { z -= 1; }\l if x > 10 { return; \"unreachable\"; }\l }\l }\l}\l"];
N0 -> N2;
N2 -> N3;
N3 -> N4;
N4 -> N5;
N5 -> N6;
N6 -> N7;
N7 -> N8;
N8 -> N9;
N9 -> N10;
N10 -> N11;
N11 -> N12;
N11 -> N13;
N13 -> N14;
N14 -> N15;
N15 -> N16;
N16 -> N17;
N17 -> N18;
N18 -> N19;
N19 -> N20;
N19 -> N21;
N21 -> N22;
N22 -> N23;
N23 -> N24;
N24 -> N25;
N25 -> N26;
N26 -> N27;
N27 -> N28;
N27 -> N29;
N29 -> N30;
N30 -> N31;
N31 -> N32;
N32 -> N24;
N28 -> N33;
N33 -> N34;
N34 -> N35;
N35 -> N36;
N36 -> N1[label="exiting scope_0 expr while y > 0 {\l y -= 1;\l while z > 0 { z -= 1; }\l if x > 10 { return; \"unreachable\"; }\l}\l,\lexiting scope_1 expr while x > 0 {\l x -= 1;\l while y > 0 {\l y -= 1;\l while z > 0 { z -= 1; }\l if x > 10 { return; \"unreachable\"; }\l }\l}\l"];
N37 -> N38;
N38 -> N39;
N35 -> N40;
N39 -> N40;
N40 -> N41;
N41 -> N16;
N20 -> N42;
N42 -> N8;
N12 -> N43;
N43 -> N1;
}

View File

@ -0,0 +1,31 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// 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.
#[allow(unreachable_code)]
pub fn expr_while_23() {
let mut x = 23;
let mut y = 23;
let mut z = 23;
while x > 0 {
x -= 1;
while y > 0 {
y -= 1;
while z > 0 { z -= 1; }
if x > 10 {
return;
"unreachable";
}
}
}
}

View File

@ -0,0 +1,123 @@
digraph block {
N0[label="entry"];
N1[label="exit"];
N2[label="expr 24"];
N3[label="local mut x"];
N4[label="expr 24"];
N5[label="local mut y"];
N6[label="expr 24"];
N7[label="local mut z"];
N8[label="(dummy_node)"];
N9[label="expr loop {\l if x == 0 { break ; \"unreachable\"; }\l x -= 1;\l loop {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { return; \"unreachable\"; }\l }\l}\l"];
N10[label="expr x"];
N11[label="expr 0"];
N12[label="expr x == 0"];
N13[label="expr break"];
N14[label="(dummy_node)"];
N15[label="expr \"unreachable\""];
N16[label="block { break ; \"unreachable\"; }"];
N17[label="expr if x == 0 { break ; \"unreachable\"; }"];
N18[label="expr 1"];
N19[label="expr x"];
N20[label="expr x -= 1"];
N21[label="(dummy_node)"];
N22[label="expr loop {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { return; \"unreachable\"; }\l}\l"];
N23[label="expr y"];
N24[label="expr 0"];
N25[label="expr y == 0"];
N26[label="expr break"];
N27[label="(dummy_node)"];
N28[label="expr \"unreachable\""];
N29[label="block { break ; \"unreachable\"; }"];
N30[label="expr if y == 0 { break ; \"unreachable\"; }"];
N31[label="expr 1"];
N32[label="expr y"];
N33[label="expr y -= 1"];
N34[label="(dummy_node)"];
N35[label="expr loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }"];
N36[label="expr z"];
N37[label="expr 0"];
N38[label="expr z == 0"];
N39[label="expr break"];
N40[label="(dummy_node)"];
N41[label="expr \"unreachable\""];
N42[label="block { break ; \"unreachable\"; }"];
N43[label="expr if z == 0 { break ; \"unreachable\"; }"];
N44[label="expr 1"];
N45[label="expr z"];
N46[label="expr z -= 1"];
N47[label="block { if z == 0 { break ; \"unreachable\"; } z -= 1; }"];
N48[label="expr x"];
N49[label="expr 10"];
N50[label="expr x > 10"];
N51[label="expr return"];
N52[label="(dummy_node)"];
N53[label="expr \"unreachable\""];
N54[label="block { return; \"unreachable\"; }"];
N55[label="expr if x > 10 { return; \"unreachable\"; }"];
N56[label="block {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { return; \"unreachable\"; }\l}\l"];
N57[label="block {\l if x == 0 { break ; \"unreachable\"; }\l x -= 1;\l loop {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { return; \"unreachable\"; }\l }\l}\l"];
N58[label="block {\l let mut x = 24;\l let mut y = 24;\l let mut z = 24;\l loop {\l if x == 0 { break ; \"unreachable\"; }\l x -= 1;\l loop {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { return; \"unreachable\"; }\l }\l }\l}\l"];
N0 -> N2;
N2 -> N3;
N3 -> N4;
N4 -> N5;
N5 -> N6;
N6 -> N7;
N7 -> N8;
N8 -> N10;
N10 -> N11;
N11 -> N12;
N12 -> N13;
N13 -> N9[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if x == 0 { break ; \"unreachable\"; },\lexiting scope_4 stmt if x == 0 { break ; \"unreachable\"; },\lexiting scope_5 block {\l if x == 0 { break ; \"unreachable\"; }\l x -= 1;\l loop {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { return; \"unreachable\"; }\l }\l}\l"];
N14 -> N15;
N15 -> N16;
N12 -> N17;
N16 -> N17;
N17 -> N18;
N18 -> N19;
N19 -> N20;
N20 -> N21;
N21 -> N23;
N23 -> N24;
N24 -> N25;
N25 -> N26;
N26 -> N22[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if y == 0 { break ; \"unreachable\"; },\lexiting scope_4 stmt if y == 0 { break ; \"unreachable\"; },\lexiting scope_5 block {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { return; \"unreachable\"; }\l}\l"];
N27 -> N28;
N28 -> N29;
N25 -> N30;
N29 -> N30;
N30 -> N31;
N31 -> N32;
N32 -> N33;
N33 -> N34;
N34 -> N36;
N36 -> N37;
N37 -> N38;
N38 -> N39;
N39 -> N35[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if z == 0 { break ; \"unreachable\"; },\lexiting scope_4 stmt if z == 0 { break ; \"unreachable\"; },\lexiting scope_5 block { if z == 0 { break ; \"unreachable\"; } z -= 1; }"];
N40 -> N41;
N41 -> N42;
N38 -> N43;
N42 -> N43;
N43 -> N44;
N44 -> N45;
N45 -> N46;
N46 -> N47;
N47 -> N34;
N35 -> N48;
N48 -> N49;
N49 -> N50;
N50 -> N51;
N51 -> N1[label="exiting scope_0 expr loop {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { return; \"unreachable\"; }\l}\l,\lexiting scope_1 expr loop {\l if x == 0 { break ; \"unreachable\"; }\l x -= 1;\l loop {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { return; \"unreachable\"; }\l }\l}\l"];
N52 -> N53;
N53 -> N54;
N50 -> N55;
N54 -> N55;
N55 -> N56;
N56 -> N21;
N22 -> N57;
N57 -> N8;
N9 -> N58;
N58 -> N1;
}

View File

@ -0,0 +1,36 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// 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.
#[allow(unreachable_code)]
pub fn expr_while_24() {
let mut x = 24;
let mut y = 24;
let mut z = 24;
loop {
if x == 0 { break; "unreachable"; }
x -= 1;
loop {
if y == 0 { break; "unreachable"; }
y -= 1;
loop {
if z == 0 { break; "unreachable"; }
z -= 1;
}
if x > 10 {
return;
"unreachable";
}
}
}
}

View File

@ -0,0 +1,123 @@
digraph block {
N0[label="entry"];
N1[label="exit"];
N2[label="expr 25"];
N3[label="local mut x"];
N4[label="expr 25"];
N5[label="local mut y"];
N6[label="expr 25"];
N7[label="local mut z"];
N8[label="(dummy_node)"];
N9[label="expr \'a:\l loop {\l if x == 0 { break ; \"unreachable\"; }\l x -= 1;\l \'a:\l loop {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l \'a: loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { continue \'a ; \"unreachable\"; }\l }\l }\l"];
N10[label="expr x"];
N11[label="expr 0"];
N12[label="expr x == 0"];
N13[label="expr break"];
N14[label="(dummy_node)"];
N15[label="expr \"unreachable\""];
N16[label="block { break ; \"unreachable\"; }"];
N17[label="expr if x == 0 { break ; \"unreachable\"; }"];
N18[label="expr 1"];
N19[label="expr x"];
N20[label="expr x -= 1"];
N21[label="(dummy_node)"];
N22[label="expr \'a:\l loop {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l \'a: loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { continue \'a ; \"unreachable\"; }\l }\l"];
N23[label="expr y"];
N24[label="expr 0"];
N25[label="expr y == 0"];
N26[label="expr break"];
N27[label="(dummy_node)"];
N28[label="expr \"unreachable\""];
N29[label="block { break ; \"unreachable\"; }"];
N30[label="expr if y == 0 { break ; \"unreachable\"; }"];
N31[label="expr 1"];
N32[label="expr y"];
N33[label="expr y -= 1"];
N34[label="(dummy_node)"];
N35[label="expr \'a: loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }"];
N36[label="expr z"];
N37[label="expr 0"];
N38[label="expr z == 0"];
N39[label="expr break"];
N40[label="(dummy_node)"];
N41[label="expr \"unreachable\""];
N42[label="block { break ; \"unreachable\"; }"];
N43[label="expr if z == 0 { break ; \"unreachable\"; }"];
N44[label="expr 1"];
N45[label="expr z"];
N46[label="expr z -= 1"];
N47[label="block { if z == 0 { break ; \"unreachable\"; } z -= 1; }"];
N48[label="expr x"];
N49[label="expr 10"];
N50[label="expr x > 10"];
N51[label="expr continue \'a"];
N52[label="(dummy_node)"];
N53[label="expr \"unreachable\""];
N54[label="block { continue \'a ; \"unreachable\"; }"];
N55[label="expr if x > 10 { continue \'a ; \"unreachable\"; }"];
N56[label="block {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l \'a: loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { continue \'a ; \"unreachable\"; }\l}\l"];
N57[label="block {\l if x == 0 { break ; \"unreachable\"; }\l x -= 1;\l \'a:\l loop {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l \'a: loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { continue \'a ; \"unreachable\"; }\l }\l}\l"];
N58[label="block {\l let mut x = 25;\l let mut y = 25;\l let mut z = 25;\l \'a:\l loop {\l if x == 0 { break ; \"unreachable\"; }\l x -= 1;\l \'a:\l loop {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l \'a: loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { continue \'a ; \"unreachable\"; }\l }\l }\l}\l"];
N0 -> N2;
N2 -> N3;
N3 -> N4;
N4 -> N5;
N5 -> N6;
N6 -> N7;
N7 -> N8;
N8 -> N10;
N10 -> N11;
N11 -> N12;
N12 -> N13;
N13 -> N9[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if x == 0 { break ; \"unreachable\"; },\lexiting scope_4 stmt if x == 0 { break ; \"unreachable\"; },\lexiting scope_5 block {\l if x == 0 { break ; \"unreachable\"; }\l x -= 1;\l \'a:\l loop {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l \'a: loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { continue \'a ; \"unreachable\"; }\l }\l}\l"];
N14 -> N15;
N15 -> N16;
N12 -> N17;
N16 -> N17;
N17 -> N18;
N18 -> N19;
N19 -> N20;
N20 -> N21;
N21 -> N23;
N23 -> N24;
N24 -> N25;
N25 -> N26;
N26 -> N22[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if y == 0 { break ; \"unreachable\"; },\lexiting scope_4 stmt if y == 0 { break ; \"unreachable\"; },\lexiting scope_5 block {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l \'a: loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { continue \'a ; \"unreachable\"; }\l}\l"];
N27 -> N28;
N28 -> N29;
N25 -> N30;
N29 -> N30;
N30 -> N31;
N31 -> N32;
N32 -> N33;
N33 -> N34;
N34 -> N36;
N36 -> N37;
N37 -> N38;
N38 -> N39;
N39 -> N35[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if z == 0 { break ; \"unreachable\"; },\lexiting scope_4 stmt if z == 0 { break ; \"unreachable\"; },\lexiting scope_5 block { if z == 0 { break ; \"unreachable\"; } z -= 1; }"];
N40 -> N41;
N41 -> N42;
N38 -> N43;
N42 -> N43;
N43 -> N44;
N44 -> N45;
N45 -> N46;
N46 -> N47;
N47 -> N34;
N35 -> N48;
N48 -> N49;
N49 -> N50;
N50 -> N51;
N51 -> N21[label="exiting scope_0 expr continue \'a,\lexiting scope_1 stmt continue \'a ;,\lexiting scope_2 block { continue \'a ; \"unreachable\"; },\lexiting scope_3 expr if x > 10 { continue \'a ; \"unreachable\"; },\lexiting scope_4 block {\l if y == 0 { break ; \"unreachable\"; }\l y -= 1;\l \'a: loop { if z == 0 { break ; \"unreachable\"; } z -= 1; }\l if x > 10 { continue \'a ; \"unreachable\"; }\l}\l"];
N52 -> N53;
N53 -> N54;
N50 -> N55;
N54 -> N55;
N55 -> N56;
N56 -> N21;
N22 -> N57;
N57 -> N8;
N9 -> N58;
N58 -> N1;
}

View File

@ -0,0 +1,36 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// 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.
#[allow(unreachable_code)]
pub fn expr_while_25() {
let mut x = 25;
let mut y = 25;
let mut z = 25;
'a: loop {
if x == 0 { break; "unreachable"; }
x -= 1;
'a: loop {
if y == 0 { break; "unreachable"; }
y -= 1;
'a: loop {
if z == 0 { break; "unreachable"; }
z -= 1;
}
if x > 10 {
continue 'a;
"unreachable";
}
}
}
}

View File

@ -0,0 +1,41 @@
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// 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.
struct S;
// Ensure S is moved, not copied, on assignment.
impl Drop for S { fn drop(&mut self) { } }
// user-defined function "returning" bottom (i.e. no return at all).
fn my_fail() -> ! { loop {} }
pub fn step(f: bool) {
let mut g = S;
let mut i = 0;
loop
{
if i > 10 { break; } else { i += 1; }
let _g = g;
if f {
// re-initialize g, but only before restarting loop.
g = S;
continue;
}
my_fail();
// we never get here, so we do not need to re-initialize g.
}
}
pub fn main() {
step(true);
}

View File

@ -0,0 +1,30 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// 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.
#[deriving(PartialEq, Show)]
struct Partial<T> { x: T, y: T }
#[deriving(PartialEq, Show)]
struct S { val: int }
impl S { fn new(v: int) -> S { S { val: v } } }
impl Drop for S { fn drop(&mut self) { } }
pub fn f<T>((b1, b2): (T, T), f: |T| -> T) -> Partial<T> {
let p = Partial { x: b1, y: b2 };
// Move of `p` is legal even though we are also moving `p.y`; the
// `..p` moves all fields *except* `p.y` in this context.
Partial { y: f(p.y), ..p }
}
pub fn main() {
let p = f((S::new(3), S::new(4)), |S { val: z }| S::new(z+1));
assert_eq!(p, Partial { x: S::new(3), y: S::new(5) });
}

View File

@ -0,0 +1,37 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// 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.
#[deriving(PartialEq, Show)]
struct Partial<T> { x: T, y: T }
#[deriving(PartialEq, Show)]
struct S { val: int }
impl S { fn new(v: int) -> S { S { val: v } } }
impl Drop for S { fn drop(&mut self) { } }
type Two<T> = (Partial<T>, Partial<T>);
pub fn f<T>((b1, b2): (T, T), (b3, b4): (T, T), f: |T| -> T) -> Two<T> {
let p = Partial { x: b1, y: b2 };
let q = Partial { x: b3, y: b4 };
// Move of `q` is legal even though we have already moved `q.y`;
// the `..q` moves all fields *except* `q.y` in this context.
// Likewise, the move of `p.x` is legal for similar reasons.
(Partial { x: f(q.y), ..p }, Partial { y: f(p.x), ..q })
}
pub fn main() {
let two = f((S::new(1), S::new(3)),
(S::new(5), S::new(7)),
|S { val: z }| S::new(z+1));
assert_eq!(two, (Partial { x: S::new(8), y: S::new(3) },
Partial { x: S::new(5), y: S::new(2) }));
}