refactor the targeted_by_break field

In master, this field was an arbitrary node-id (in fact, an id for
something that doesn't even exist in the HIR -- the `catch` node).
Breaks targeting this block used that id. In the newer system, this
field is a boolean, and any breaks targeted this block will use the
id of the block.
This commit is contained in:
Niko Matsakis 2017-03-22 11:40:29 -04:00
parent 6a47fa1d3d
commit 066d44bc0d
9 changed files with 144 additions and 132 deletions

View file

@ -74,11 +74,11 @@ pub fn construct<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
fn block(&mut self, blk: &hir::Block, pred: CFGIndex) -> CFGIndex {
if let Some(break_to_expr_id) = blk.break_to_expr_id {
if blk.targeted_by_break {
let expr_exit = self.add_ast_node(blk.id, &[]);
self.breakable_block_scopes.push(BlockScope {
block_expr_id: break_to_expr_id,
block_expr_id: blk.id,
break_index: expr_exit,
});

View file

@ -1156,7 +1156,7 @@ impl<'a> LoweringContext<'a> {
bounds.iter().map(|bound| self.lower_ty_param_bound(bound)).collect()
}
fn lower_block(&mut self, b: &Block, break_to: Option<NodeId>) -> P<hir::Block> {
fn lower_block(&mut self, b: &Block, targeted_by_break: bool) -> P<hir::Block> {
let mut expr = None;
let mut stmts = vec![];
@ -1179,7 +1179,7 @@ impl<'a> LoweringContext<'a> {
expr: expr,
rules: self.lower_block_check_mode(&b.rules),
span: b.span,
break_to_expr_id: break_to,
targeted_by_break: targeted_by_break,
})
}
@ -1274,7 +1274,7 @@ impl<'a> LoweringContext<'a> {
}
ItemKind::Fn(ref decl, unsafety, constness, abi, ref generics, ref body) => {
self.with_new_scopes(|this| {
let body = this.lower_block(body, None);
let body = this.lower_block(body, false);
let body = this.expr_block(body, ThinVec::new());
let body_id = this.record_body(body, Some(decl));
hir::ItemFn(this.lower_fn_decl(decl),
@ -1368,7 +1368,7 @@ impl<'a> LoweringContext<'a> {
hir::TraitMethod::Required(names))
}
TraitItemKind::Method(ref sig, Some(ref body)) => {
let body = this.lower_block(body, None);
let body = this.lower_block(body, false);
let expr = this.expr_block(body, ThinVec::new());
let body_id = this.record_body(expr, Some(&sig.decl));
hir::TraitItemKind::Method(this.lower_method_sig(sig),
@ -1424,7 +1424,7 @@ impl<'a> LoweringContext<'a> {
hir::ImplItemKind::Const(this.lower_ty(ty), body_id)
}
ImplItemKind::Method(ref sig, ref body) => {
let body = this.lower_block(body, None);
let body = this.lower_block(body, false);
let expr = this.expr_block(body, ThinVec::new());
let body_id = this.record_body(expr, Some(&sig.decl));
hir::ImplItemKind::Method(this.lower_method_sig(sig), body_id)
@ -1848,7 +1848,7 @@ impl<'a> LoweringContext<'a> {
id: id,
rules: hir::DefaultBlock,
span: span,
break_to_expr_id: None,
targeted_by_break: false,
});
P(self.expr_block(blk, ThinVec::new()))
}
@ -1856,7 +1856,7 @@ impl<'a> LoweringContext<'a> {
}
});
let then_blk = self.lower_block(blk, None);
let then_blk = self.lower_block(blk, false);
let then_expr = self.expr_block(then_blk, ThinVec::new());
hir::ExprIf(P(self.lower_expr(cond)), P(then_expr), else_opt)
@ -1865,18 +1865,18 @@ impl<'a> LoweringContext<'a> {
self.with_loop_scope(e.id, |this|
hir::ExprWhile(
this.with_loop_condition_scope(|this| P(this.lower_expr(cond))),
this.lower_block(body, None),
this.lower_block(body, false),
this.lower_opt_sp_ident(opt_ident)))
}
ExprKind::Loop(ref body, opt_ident) => {
self.with_loop_scope(e.id, |this|
hir::ExprLoop(this.lower_block(body, None),
hir::ExprLoop(this.lower_block(body, false),
this.lower_opt_sp_ident(opt_ident),
hir::LoopSource::Loop))
}
ExprKind::Catch(ref body) => {
self.with_catch_scope(e.id, |this|
hir::ExprBlock(this.lower_block(body, Some(e.id))))
self.with_catch_scope(body.id, |this|
hir::ExprBlock(this.lower_block(body, true)))
}
ExprKind::Match(ref expr, ref arms) => {
hir::ExprMatch(P(self.lower_expr(expr)),
@ -1894,7 +1894,7 @@ impl<'a> LoweringContext<'a> {
})
})
}
ExprKind::Block(ref blk) => hir::ExprBlock(self.lower_block(blk, None)),
ExprKind::Block(ref blk) => hir::ExprBlock(self.lower_block(blk, false)),
ExprKind::Assign(ref el, ref er) => {
hir::ExprAssign(P(self.lower_expr(el)), P(self.lower_expr(er)))
}
@ -2040,7 +2040,7 @@ impl<'a> LoweringContext<'a> {
// `<pat> => <body>`
{
let body = self.lower_block(body, None);
let body = self.lower_block(body, false);
let body_expr = P(self.expr_block(body, ThinVec::new()));
let pat = self.lower_pat(pat);
arms.push(self.arm(hir_vec![pat], body_expr));
@ -2112,7 +2112,7 @@ impl<'a> LoweringContext<'a> {
let (guard, body) = if let ExprKind::If(ref cond,
ref then,
_) = else_expr.node {
let then = self.lower_block(then, None);
let then = self.lower_block(then, false);
(Some(cond),
self.expr_block(then, ThinVec::new()))
} else {
@ -2162,7 +2162,7 @@ impl<'a> LoweringContext<'a> {
// Note that the block AND the condition are evaluated in the loop scope.
// This is done to allow `break` from inside the condition of the loop.
let (body, break_expr, sub_expr) = self.with_loop_scope(e.id, |this| (
this.lower_block(body, None),
this.lower_block(body, false),
this.expr_break(e.span, ThinVec::new()),
this.with_loop_condition_scope(|this| P(this.lower_expr(sub_expr))),
));
@ -2223,7 +2223,7 @@ impl<'a> LoweringContext<'a> {
// `::std::option::Option::Some(<pat>) => <body>`
let pat_arm = {
let body_block = self.with_loop_scope(e.id,
|this| this.lower_block(body, None));
|this| this.lower_block(body, false));
let body_expr = P(self.expr_block(body_block, ThinVec::new()));
let pat = self.lower_pat(pat);
let some_pat = self.pat_some(e.span, pat);
@ -2655,7 +2655,7 @@ impl<'a> LoweringContext<'a> {
id: self.next_id(),
rules: hir::DefaultBlock,
span: span,
break_to_expr_id: None,
targeted_by_break: false,
}
}
@ -2763,7 +2763,7 @@ impl<'a> LoweringContext<'a> {
id: id,
stmts: stmts,
expr: Some(expr),
break_to_expr_id: None,
targeted_by_break: false,
});
self.expr_block(block, attrs)
}

View file

@ -549,9 +549,11 @@ pub struct Block {
/// Distinguishes between `unsafe { ... }` and `{ ... }`
pub rules: BlockCheckMode,
pub span: Span,
/// The id of the expression that `break` breaks to if the block can be broken out of.
/// Currently only `Some(_)` for `catch {}` blocks
pub break_to_expr_id: Option<NodeId>,
/// If true, then there may exist `break 'a` values that aim to
/// break out of this block early. As of this writing, this is not
/// currently permitted in Rust itself, but it is generated as
/// part of `catch` statements.
pub targeted_by_break: bool,
}
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash)]

View file

@ -821,8 +821,8 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
fn propagate_through_block(&mut self, blk: &hir::Block, succ: LiveNode)
-> LiveNode {
if let Some(break_to_expr_id) = blk.break_to_expr_id {
self.breakable_block_ln.insert(break_to_expr_id, succ);
if blk.targeted_by_break {
self.breakable_block_ln.insert(blk.id, succ);
}
let succ = self.propagate_through_opt_expr(blk.expr.as_ref().map(|e| &**e), succ);
blk.stmts.iter().rev().fold(succ, |succ, stmt| {

View file

@ -12,90 +12,116 @@ use build::{BlockAnd, BlockAndExtension, Builder};
use hair::*;
use rustc::mir::*;
use rustc::hir;
use syntax_pos::Span;
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
pub fn ast_block(&mut self,
destination: &Lvalue<'tcx>,
mut block: BasicBlock,
ast_block: &'tcx hir::Block)
block: BasicBlock,
ast_block: &'tcx hir::Block,
source_info: SourceInfo)
-> BlockAnd<()> {
let Block { extent, span, stmts, expr } = self.hir.mirror(ast_block);
let Block { extent, span, stmts, expr, targeted_by_break } = self.hir.mirror(ast_block);
self.in_scope(extent, block, move |this| {
// This convoluted structure is to avoid using recursion as we walk down a list
// of statements. Basically, the structure we get back is something like:
//
// let x = <init> in {
// expr1;
// let y = <init> in {
// expr2;
// expr3;
// ...
// }
// }
//
// The let bindings are valid till the end of block so all we have to do is to pop all
// the let-scopes at the end.
//
// First we build all the statements in the block.
let mut let_extent_stack = Vec::with_capacity(8);
let outer_visibility_scope = this.visibility_scope;
for stmt in stmts {
let Stmt { span: _, kind } = this.hir.mirror(stmt);
match kind {
StmtKind::Expr { scope, expr } => {
unpack!(block = this.in_scope(scope, block, |this| {
let expr = this.hir.mirror(expr);
this.stmt_expr(block, expr)
if targeted_by_break {
// This is a `break`-able block (currently only `catch { ... }`)
let exit_block = this.cfg.start_new_block();
let block_exit = this.in_breakable_scope(None, exit_block,
destination.clone(), |this| {
this.ast_block_stmts(destination, block, span, stmts, expr)
});
this.cfg.terminate(unpack!(block_exit), source_info,
TerminatorKind::Goto { target: exit_block });
exit_block.unit()
} else {
this.ast_block_stmts(destination, block, span, stmts, expr)
}
})
}
fn ast_block_stmts(&mut self,
destination: &Lvalue<'tcx>,
mut block: BasicBlock,
span: Span,
stmts: Vec<StmtRef<'tcx>>,
expr: Option<ExprRef<'tcx>>)
-> BlockAnd<()> {
let this = self;
// This convoluted structure is to avoid using recursion as we walk down a list
// of statements. Basically, the structure we get back is something like:
//
// let x = <init> in {
// expr1;
// let y = <init> in {
// expr2;
// expr3;
// ...
// }
// }
//
// The let bindings are valid till the end of block so all we have to do is to pop all
// the let-scopes at the end.
//
// First we build all the statements in the block.
let mut let_extent_stack = Vec::with_capacity(8);
let outer_visibility_scope = this.visibility_scope;
for stmt in stmts {
let Stmt { span: _, kind } = this.hir.mirror(stmt);
match kind {
StmtKind::Expr { scope, expr } => {
unpack!(block = this.in_scope(scope, block, |this| {
let expr = this.hir.mirror(expr);
this.stmt_expr(block, expr)
}));
}
StmtKind::Let { remainder_scope, init_scope, pattern, initializer } => {
let tcx = this.hir.tcx();
// Enter the remainder scope, i.e. the bindings' destruction scope.
this.push_scope(remainder_scope);
let_extent_stack.push(remainder_scope);
// Declare the bindings, which may create a visibility scope.
let remainder_span = remainder_scope.span(&tcx.region_maps, &tcx.hir);
let remainder_span = remainder_span.unwrap_or(span);
let scope = this.declare_bindings(None, remainder_span, &pattern);
// Evaluate the initializer, if present.
if let Some(init) = initializer {
unpack!(block = this.in_scope(init_scope, block, move |this| {
// FIXME #30046 ^~~~
this.expr_into_pattern(block, pattern, init)
}));
} else {
this.visit_bindings(&pattern, &mut |this, _, _, node, span, _| {
this.storage_live_binding(block, node, span);
this.schedule_drop_for_binding(node, span);
})
}
StmtKind::Let { remainder_scope, init_scope, pattern, initializer } => {
let tcx = this.hir.tcx();
// Enter the remainder scope, i.e. the bindings' destruction scope.
this.push_scope(remainder_scope);
let_extent_stack.push(remainder_scope);
// Declare the bindings, which may create a visibility scope.
let remainder_span = remainder_scope.span(&tcx.region_maps, &tcx.hir);
let remainder_span = remainder_span.unwrap_or(span);
let scope = this.declare_bindings(None, remainder_span, &pattern);
// Evaluate the initializer, if present.
if let Some(init) = initializer {
unpack!(block = this.in_scope(init_scope, block, move |this| {
// FIXME #30046 ^~~~
this.expr_into_pattern(block, pattern, init)
}));
} else {
this.visit_bindings(&pattern, &mut |this, _, _, node, span, _| {
this.storage_live_binding(block, node, span);
this.schedule_drop_for_binding(node, span);
})
}
// Enter the visibility scope, after evaluating the initializer.
if let Some(visibility_scope) = scope {
this.visibility_scope = visibility_scope;
}
// Enter the visibility scope, after evaluating the initializer.
if let Some(visibility_scope) = scope {
this.visibility_scope = visibility_scope;
}
}
}
// Then, the block may have an optional trailing expression which is a “return” value
// of the block.
if let Some(expr) = expr {
unpack!(block = this.into(destination, block, expr));
} else {
let source_info = this.source_info(span);
this.cfg.push_assign_unit(block, source_info, destination);
}
// Finally, we pop all the let scopes before exiting out from the scope of block
// itself.
for extent in let_extent_stack.into_iter().rev() {
unpack!(block = this.pop_scope(extent, block));
}
// Restore the original visibility scope.
this.visibility_scope = outer_visibility_scope;
block.unit()
})
}
// Then, the block may have an optional trailing expression which is a “return” value
// of the block.
if let Some(expr) = expr {
unpack!(block = this.into(destination, block, expr));
} else {
let source_info = this.source_info(span);
this.cfg.push_assign_unit(block, source_info, destination);
}
// Finally, we pop all the let scopes before exiting out from the scope of block
// itself.
for extent in let_extent_stack.into_iter().rev() {
unpack!(block = this.pop_scope(extent, block));
}
// Restore the original visibility scope.
this.visibility_scope = outer_visibility_scope;
block.unit()
}
}

View file

@ -40,19 +40,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
this.in_scope(extent, block, |this| this.into(destination, block, value))
}
ExprKind::Block { body: ast_block } => {
if let Some(_) = ast_block.break_to_expr_id {
// This is a `break`-able block (currently only `catch { ... }`)
let exit_block = this.cfg.start_new_block();
let block_exit = this.in_breakable_scope(None, exit_block,
destination.clone(), |this| {
this.ast_block(destination, block, ast_block)
});
this.cfg.terminate(unpack!(block_exit), source_info,
TerminatorKind::Goto { target: exit_block });
exit_block.unit()
} else {
this.ast_block(destination, block, ast_block)
}
this.ast_block(destination, block, ast_block, source_info)
}
ExprKind::Match { discriminant, arms } => {
this.match_expr(destination, expr_span, block, discriminant, arms)

View file

@ -23,6 +23,7 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Block {
// in order to get the lexical scoping correctly.
let stmts = mirror_stmts(cx, self.id, &*self.stmts);
Block {
targeted_by_break: self.targeted_by_break,
extent: cx.tcx.region_maps.node_extent(self.id),
span: self.span,
stmts: stmts,

View file

@ -31,6 +31,7 @@ pub use rustc_const_eval::pattern::{BindingMode, Pattern, PatternKind, FieldPatt
#[derive(Clone, Debug)]
pub struct Block<'tcx> {
pub targeted_by_break: bool,
pub extent: CodeExtent,
pub span: Span,
pub stmts: Vec<StmtRef<'tcx>>,

View file

@ -416,15 +416,11 @@ pub struct EnclosingBreakables<'gcx, 'tcx> {
}
impl<'gcx, 'tcx> EnclosingBreakables<'gcx, 'tcx> {
fn find_breakable(&mut self, target: hir::ScopeTarget)
-> Option<&mut BreakableCtxt<'gcx, 'tcx>>
{
let opt_index = target.opt_id().and_then(|id| self.by_id.get(&id).cloned());
if let Some(ix) = opt_index {
Some(&mut self.stack[ix])
} else {
None
}
fn find_breakable(&mut self, target_id: ast::NodeId) -> &mut BreakableCtxt<'gcx, 'tcx> {
let ix = *self.by_id.get(&target_id).unwrap_or_else(|| {
bug!("could not find enclosing breakable with id {}", target_id);
});
&mut self.stack[ix]
}
}
@ -3472,12 +3468,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
tcx.mk_nil()
}
hir::ExprBreak(destination, ref expr_opt) => {
let coerce_to = {
let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
enclosing_breakables
.find_breakable(destination.target_id).map(|ctxt| ctxt.coerce_to)
};
if let Some(coerce_to) = coerce_to {
if let Some(target_id) = destination.target_id.opt_id() {
let coerce_to = {
let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
enclosing_breakables.find_breakable(target_id).coerce_to
};
let e_ty;
let cause;
if let Some(ref e) = *expr_opt {
@ -3492,7 +3488,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}
let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
let ctxt = enclosing_breakables.find_breakable(destination.target_id).unwrap();
let ctxt = enclosing_breakables.find_breakable(target_id);
let result = if let Some(ref e) = *expr_opt {
// Special-case the first element, as it has no "previous expressions".
@ -4024,7 +4020,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
replace(&mut *fcx_ps, unsafety_state)
};
let mut ty = if let Some(break_to_expr_id) = blk.break_to_expr_id {
let mut ty = if blk.targeted_by_break {
let unified = self.next_ty_var(TypeVariableOrigin::TypeInference(blk.span));
let coerce_to = expected.only_has_type(self).unwrap_or(unified);
let ctxt = BreakableCtxt {
@ -4034,15 +4030,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
may_break: false,
};
let (mut ctxt, (e_ty, cause)) = self.with_breakable_ctxt(break_to_expr_id, ctxt, || {
let (mut ctxt, (e_ty, cause)) = self.with_breakable_ctxt(blk.id, ctxt, || {
for s in &blk.stmts {
self.check_stmt(s);
}
let coerce_to = {
let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
enclosing_breakables.find_breakable(
hir::ScopeTarget::Block(break_to_expr_id)
).unwrap().coerce_to
enclosing_breakables.find_breakable(blk.id).coerce_to
};
let e_ty;
let cause;