diff --git a/src/librustc/cfg/construct.rs b/src/librustc/cfg/construct.rs index 388d019f654a..20b322ec1895 100644 --- a/src/librustc/cfg/construct.rs +++ b/src/librustc/cfg/construct.rs @@ -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, }); diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index fbc8a97dacc4..17185a6ab69f 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -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) -> P { + fn lower_block(&mut self, b: &Block, targeted_by_break: bool) -> P { 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> { // ` => ` { - 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() => ` 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) } diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 560fa18a4050..d5000ac9c186 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -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, + /// 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)] diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index e146663d4c8d..7cae08efc0de 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -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| { diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs index 3305cfc0dfe1..7739766182cf 100644 --- a/src/librustc_mir/build/block.rs +++ b/src/librustc_mir/build/block.rs @@ -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 = in { - // expr1; - // let y = 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>, + expr: Option>) + -> 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 = in { + // expr1; + // let y = 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() } } diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index e1b0c6a6f042..a5a114c61bcf 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -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) diff --git a/src/librustc_mir/hair/cx/block.rs b/src/librustc_mir/hair/cx/block.rs index ba6b9361a83f..d2465331df35 100644 --- a/src/librustc_mir/hair/cx/block.rs +++ b/src/librustc_mir/hair/cx/block.rs @@ -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, diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs index 2ee375dee08a..a3982efd2d69 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -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>, diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 54da0b2b0047..000398a4bce9 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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;