]> git.lizzy.rs Git - rust.git/commitdiff
Rollup merge of #40229 - cramertj:break-to-blocks, r=nikomatsakis
authorCorey Farwell <coreyf@rwell.org>
Tue, 21 Mar 2017 03:44:55 +0000 (23:44 -0400)
committerGitHub <noreply@github.com>
Tue, 21 Mar 2017 03:44:55 +0000 (23:44 -0400)
Implement `?` in catch expressions

Builds on #39921. Final part of #39849.

r? @nikomatsakis

17 files changed:
src/librustc/cfg/construct.rs
src/librustc/hir/intravisit.rs
src/librustc/hir/lowering.rs
src/librustc/hir/mod.rs
src/librustc/middle/liveness.rs
src/librustc_mir/build/expr/into.rs
src/librustc_mir/build/expr/stmt.rs
src/librustc_mir/build/mod.rs
src/librustc_mir/build/scope.rs
src/librustc_mir/hair/cx/expr.rs
src/librustc_passes/loops.rs
src/librustc_typeck/check/mod.rs
src/test/compile-fail/catch-bad-lifetime.rs [new file with mode: 0644]
src/test/compile-fail/catch-bad-type.rs [new file with mode: 0644]
src/test/compile-fail/catch-maybe-bad-lifetime.rs [new file with mode: 0644]
src/test/compile-fail/catch-opt-init.rs [new file with mode: 0644]
src/test/run-pass/catch-expr.rs

index 7a70eda955b69dec324b78be14b67ddf8181d592..189a7344c313058432770a3ce7175470a0fcfc3e 100644 (file)
@@ -22,13 +22,20 @@ struct CFGBuilder<'a, 'tcx: 'a> {
     graph: CFGGraph,
     fn_exit: CFGIndex,
     loop_scopes: Vec<LoopScope>,
+    breakable_block_scopes: Vec<BlockScope>,
+}
+
+#[derive(Copy, Clone)]
+struct BlockScope {
+    block_expr_id: ast::NodeId, // id of breakable block expr node
+    break_index: CFGIndex, // where to go on `break`
 }
 
 #[derive(Copy, Clone)]
 struct LoopScope {
     loop_id: ast::NodeId,     // id of loop/while node
     continue_index: CFGIndex, // where to go on a `loop`
-    break_index: CFGIndex,    // where to go on a `break
+    break_index: CFGIndex,    // where to go on a `break`
 }
 
 pub fn construct<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
@@ -53,6 +60,7 @@ pub fn construct<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         graph: graph,
         fn_exit: fn_exit,
         loop_scopes: Vec::new(),
+        breakable_block_scopes: Vec::new(),
     };
     body_exit = cfg_builder.expr(&body.value, entry);
     cfg_builder.add_contained_edge(body_exit, fn_exit);
@@ -66,14 +74,34 @@ 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 {
-        let mut stmts_exit = pred;
-        for stmt in &blk.stmts {
-            stmts_exit = self.stmt(stmt, stmts_exit);
-        }
+        if let Some(break_to_expr_id) = blk.break_to_expr_id {
+            let expr_exit = self.add_ast_node(blk.id, &[]);
+
+            self.breakable_block_scopes.push(BlockScope {
+                block_expr_id: break_to_expr_id,
+                break_index: expr_exit,
+            });
+
+            let mut stmts_exit = pred;
+            for stmt in &blk.stmts {
+                stmts_exit = self.stmt(stmt, stmts_exit);
+            }
+            let blk_expr_exit = self.opt_expr(&blk.expr, stmts_exit);
+            self.add_contained_edge(blk_expr_exit, expr_exit);
+
+            self.breakable_block_scopes.pop();
+
+            expr_exit
+        } else {
+            let mut stmts_exit = pred;
+            for stmt in &blk.stmts {
+                stmts_exit = self.stmt(stmt, stmts_exit);
+            }
 
-        let expr_exit = self.opt_expr(&blk.expr, stmts_exit);
+            let expr_exit = self.opt_expr(&blk.expr, stmts_exit);
 
-        self.add_ast_node(blk.id, &[expr_exit])
+            self.add_ast_node(blk.id, &[expr_exit])
+        }
     }
 
     fn stmt(&mut self, stmt: &hir::Stmt, pred: CFGIndex) -> CFGIndex {
@@ -295,18 +323,18 @@ fn expr(&mut self, expr: &hir::Expr, pred: CFGIndex) -> CFGIndex {
 
             hir::ExprBreak(destination, ref opt_expr) => {
                 let v = self.opt_expr(opt_expr, pred);
-                let loop_scope = self.find_scope(expr, destination);
+                let (scope_id, break_dest) =
+                    self.find_scope_edge(expr, destination, ScopeCfKind::Break);
                 let b = self.add_ast_node(expr.id, &[v]);
-                self.add_exiting_edge(expr, b,
-                                      loop_scope, loop_scope.break_index);
+                self.add_exiting_edge(expr, b, scope_id, break_dest);
                 self.add_unreachable_node()
             }
 
             hir::ExprAgain(destination) => {
-                let loop_scope = self.find_scope(expr, destination);
+                let (scope_id, cont_dest) =
+                    self.find_scope_edge(expr, destination, ScopeCfKind::Continue);
                 let a = self.add_ast_node(expr.id, &[pred]);
-                self.add_exiting_edge(expr, a,
-                                      loop_scope, loop_scope.continue_index);
+                self.add_exiting_edge(expr, a, scope_id, cont_dest);
                 self.add_unreachable_node()
             }
 
@@ -552,11 +580,11 @@ fn add_contained_edge(&mut self,
     fn add_exiting_edge(&mut self,
                         from_expr: &hir::Expr,
                         from_index: CFGIndex,
-                        to_loop: LoopScope,
+                        scope_id: ast::NodeId,
                         to_index: CFGIndex) {
         let mut data = CFGEdgeData { exiting_scopes: vec![] };
         let mut scope = self.tcx.region_maps.node_extent(from_expr.id);
-        let target_scope = self.tcx.region_maps.node_extent(to_loop.loop_id);
+        let target_scope = self.tcx.region_maps.node_extent(scope_id);
         while scope != target_scope {
             data.exiting_scopes.push(scope.node_id(&self.tcx.region_maps));
             scope = self.tcx.region_maps.encl_scope(scope);
@@ -576,20 +604,42 @@ fn add_returning_edge(&mut self,
         self.graph.add_edge(from_index, self.fn_exit, data);
     }
 
-    fn find_scope(&self,
+    fn find_scope_edge(&self,
                   expr: &hir::Expr,
-                  destination: hir::Destination) -> LoopScope {
-
-        match destination.loop_id.into() {
-            Ok(loop_id) => {
+                  destination: hir::Destination,
+                  scope_cf_kind: ScopeCfKind) -> (ast::NodeId, CFGIndex) {
+
+        match destination.target_id {
+            hir::ScopeTarget::Block(block_expr_id) => {
+                for b in &self.breakable_block_scopes {
+                    if b.block_expr_id == block_expr_id {
+                        return (block_expr_id, match scope_cf_kind {
+                            ScopeCfKind::Break => b.break_index,
+                            ScopeCfKind::Continue => bug!("can't continue to block"),
+                        });
+                    }
+                }
+                span_bug!(expr.span, "no block expr for id {}", block_expr_id);
+            }
+            hir::ScopeTarget::Loop(hir::LoopIdResult::Ok(loop_id)) => {
                 for l in &self.loop_scopes {
                     if l.loop_id == loop_id {
-                        return *l;
+                        return (loop_id, match scope_cf_kind {
+                            ScopeCfKind::Break => l.break_index,
+                            ScopeCfKind::Continue => l.continue_index,
+                        });
                     }
                 }
                 span_bug!(expr.span, "no loop scope for id {}", loop_id);
             }
-            Err(err) => span_bug!(expr.span, "loop scope error: {}", err),
+            hir::ScopeTarget::Loop(hir::LoopIdResult::Err(err)) =>
+                span_bug!(expr.span, "loop scope error: {}",  err),
         }
     }
 }
+
+#[derive(Copy, Clone, Eq, PartialEq)]
+enum ScopeCfKind {
+    Break,
+    Continue,
+}
index fd6796ccc0bf2b0cd6b7957281a960c99322ff4c..f59b8b757f5cc4de61cb1aae6731a8ee023655e9 100644 (file)
@@ -1008,18 +1008,24 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
         }
         ExprBreak(label, ref opt_expr) => {
             label.ident.map(|ident| {
-                if let Ok(loop_id) = label.loop_id.into() {
-                    visitor.visit_def_mention(Def::Label(loop_id));
-                }
+                match label.target_id {
+                    ScopeTarget::Block(node_id) |
+                    ScopeTarget::Loop(LoopIdResult::Ok(node_id)) =>
+                        visitor.visit_def_mention(Def::Label(node_id)),
+                    ScopeTarget::Loop(LoopIdResult::Err(_)) => {},
+                };
                 visitor.visit_name(ident.span, ident.node.name);
             });
             walk_list!(visitor, visit_expr, opt_expr);
         }
         ExprAgain(label) => {
             label.ident.map(|ident| {
-                if let Ok(loop_id) = label.loop_id.into() {
-                    visitor.visit_def_mention(Def::Label(loop_id));
-                }
+                match label.target_id {
+                    ScopeTarget::Block(_) => bug!("can't `continue` to a non-loop block"),
+                    ScopeTarget::Loop(LoopIdResult::Ok(node_id)) =>
+                        visitor.visit_def_mention(Def::Label(node_id)),
+                    ScopeTarget::Loop(LoopIdResult::Err(_)) => {},
+                };
                 visitor.visit_name(ident.span, ident.node.name);
             });
         }
index d966f4899e50fc9c0556d1dd93f7751fc97644d1..81591a5650f07995d948ea2934de04dc460489f0 100644 (file)
@@ -356,22 +356,26 @@ fn lower_opt_sp_ident(&mut self, o_id: Option<Spanned<Ident>>) -> Option<Spanned
         o_id.map(|sp_ident| respan(sp_ident.span, sp_ident.node.name))
     }
 
-    fn lower_destination(&mut self, destination: Option<(NodeId, Spanned<Ident>)>)
+    fn lower_loop_destination(&mut self, destination: Option<(NodeId, Spanned<Ident>)>)
         -> hir::Destination
     {
         match destination {
-            Some((id, label_ident)) => hir::Destination {
-                ident: Some(label_ident),
-                loop_id: if let Def::Label(loop_id) = self.expect_full_def(id) {
+            Some((id, label_ident)) => {
+                let target = if let Def::Label(loop_id) = self.expect_full_def(id) {
                     hir::LoopIdResult::Ok(loop_id)
                 } else {
                     hir::LoopIdResult::Err(hir::LoopIdError::UnresolvedLabel)
+                };
+                hir::Destination {
+                    ident: Some(label_ident),
+                    target_id: hir::ScopeTarget::Loop(target),
                 }
             },
             None => hir::Destination {
                 ident: None,
-                loop_id: self.loop_scopes.last().map(|innermost_loop_id| Ok(*innermost_loop_id))
-                            .unwrap_or(Err(hir::LoopIdError::OutsideLoopScope)).into()
+                target_id: hir::ScopeTarget::Loop(
+                    self.loop_scopes.last().map(|innermost_loop_id| Ok(*innermost_loop_id))
+                            .unwrap_or(Err(hir::LoopIdError::OutsideLoopScope)).into())
             }
         }
     }
@@ -990,7 +994,7 @@ fn lower_bounds(&mut self, bounds: &[TyParamBound]) -> hir::TyParamBounds {
         bounds.iter().map(|bound| self.lower_ty_param_bound(bound)).collect()
     }
 
-    fn lower_block(&mut self, b: &Block) -> P<hir::Block> {
+    fn lower_block(&mut self, b: &Block, break_to: Option<NodeId>) -> P<hir::Block> {
         let mut expr = None;
 
         let mut stmts = b.stmts.iter().flat_map(|s| self.lower_stmt(s)).collect::<Vec<_>>();
@@ -1008,6 +1012,7 @@ fn lower_block(&mut self, b: &Block) -> P<hir::Block> {
             expr: expr,
             rules: self.lower_block_check_mode(&b.rules),
             span: b.span,
+            break_to_expr_id: break_to,
         })
     }
 
@@ -1085,7 +1090,7 @@ fn lower_item_kind(&mut self,
             }
             ItemKind::Fn(ref decl, unsafety, constness, abi, ref generics, ref body) => {
                 self.with_new_scopes(|this| {
-                    let body = this.lower_block(body);
+                    let body = this.lower_block(body, None);
                     let body = this.expr_block(body, ThinVec::new());
                     let body_id = this.record_body(body, Some(decl));
                     hir::ItemFn(this.lower_fn_decl(decl),
@@ -1179,7 +1184,7 @@ fn lower_trait_item(&mut self, i: &TraitItem) -> hir::TraitItem {
                                                    hir::TraitMethod::Required(names))
                     }
                     TraitItemKind::Method(ref sig, Some(ref body)) => {
-                        let body = this.lower_block(body);
+                        let body = this.lower_block(body, None);
                         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),
@@ -1235,7 +1240,7 @@ fn lower_impl_item(&mut self, i: &ImplItem) -> hir::ImplItem {
                         hir::ImplItemKind::Const(this.lower_ty(ty), body_id)
                     }
                     ImplItemKind::Method(ref sig, ref body) => {
-                        let body = this.lower_block(body);
+                        let body = this.lower_block(body, None);
                         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)
@@ -1662,6 +1667,7 @@ fn lower_expr(&mut self, e: &Expr) -> hir::Expr {
                                     id: id,
                                     rules: hir::DefaultBlock,
                                     span: span,
+                                    break_to_expr_id: None,
                                 });
                                 P(self.expr_block(blk, ThinVec::new()))
                             }
@@ -1669,24 +1675,24 @@ fn lower_expr(&mut self, e: &Expr) -> hir::Expr {
                         }
                     });
 
-                    hir::ExprIf(P(self.lower_expr(cond)), self.lower_block(blk), else_opt)
+                    hir::ExprIf(P(self.lower_expr(cond)), self.lower_block(blk, None), else_opt)
                 }
                 ExprKind::While(ref cond, ref body, opt_ident) => {
                     self.with_loop_scope(e.id, |this|
                         hir::ExprWhile(
                             this.with_loop_condition_scope(|this| P(this.lower_expr(cond))),
-                            this.lower_block(body),
+                            this.lower_block(body, None),
                             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),
+                        hir::ExprLoop(this.lower_block(body, None),
                                       this.lower_opt_sp_ident(opt_ident),
                                       hir::LoopSource::Loop))
                 }
                 ExprKind::Catch(ref body) => {
-                    // FIXME(cramertj): Add catch to HIR
-                    self.with_catch_scope(e.id, |this| hir::ExprBlock(this.lower_block(body)))
+                    self.with_catch_scope(e.id, |this|
+                        hir::ExprBlock(this.lower_block(body, Some(e.id))))
                 }
                 ExprKind::Match(ref expr, ref arms) => {
                     hir::ExprMatch(P(self.lower_expr(expr)),
@@ -1704,7 +1710,7 @@ fn lower_expr(&mut self, e: &Expr) -> hir::Expr {
                         })
                     })
                 }
-                ExprKind::Block(ref blk) => hir::ExprBlock(self.lower_block(blk)),
+                ExprKind::Block(ref blk) => hir::ExprBlock(self.lower_block(blk, None)),
                 ExprKind::Assign(ref el, ref er) => {
                     hir::ExprAssign(P(self.lower_expr(el)), P(self.lower_expr(er)))
                 }
@@ -1783,10 +1789,11 @@ fn make_struct(this: &mut LoweringContext,
                     let label_result = if self.is_in_loop_condition && opt_ident.is_none() {
                         hir::Destination {
                             ident: opt_ident,
-                            loop_id: Err(hir::LoopIdError::UnlabeledCfInWhileCondition).into(),
+                            target_id: hir::ScopeTarget::Loop(
+                                Err(hir::LoopIdError::UnlabeledCfInWhileCondition).into()),
                         }
                     } else {
-                        self.lower_destination(opt_ident.map(|ident| (e.id, ident)))
+                        self.lower_loop_destination(opt_ident.map(|ident| (e.id, ident)))
                     };
                     hir::ExprBreak(
                             label_result,
@@ -1797,11 +1804,11 @@ fn make_struct(this: &mut LoweringContext,
                         if self.is_in_loop_condition && opt_ident.is_none() {
                             hir::Destination {
                                 ident: opt_ident,
-                                loop_id: Err(
-                                    hir::LoopIdError::UnlabeledCfInWhileCondition).into(),
+                                target_id: hir::ScopeTarget::Loop(Err(
+                                    hir::LoopIdError::UnlabeledCfInWhileCondition).into()),
                             }
                         } else {
-                            self.lower_destination(opt_ident.map( |ident| (e.id, ident)))
+                            self.lower_loop_destination(opt_ident.map( |ident| (e.id, ident)))
                         }),
                 ExprKind::Ret(ref e) => hir::ExprRet(e.as_ref().map(|x| P(self.lower_expr(x)))),
                 ExprKind::InlineAsm(ref asm) => {
@@ -1859,7 +1866,7 @@ fn make_struct(this: &mut LoweringContext,
 
                     // `<pat> => <body>`
                     let pat_arm = {
-                        let body = self.lower_block(body);
+                        let body = self.lower_block(body, None);
                         let body_expr = P(self.expr_block(body, ThinVec::new()));
                         let pat = self.lower_pat(pat);
                         self.arm(hir_vec![pat], body_expr)
@@ -1946,7 +1953,7 @@ fn make_struct(this: &mut LoweringContext,
                     // 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),
+                        this.lower_block(body, None),
                         this.expr_break(e.span, ThinVec::new()),
                         this.with_loop_condition_scope(|this| P(this.lower_expr(sub_expr))),
                     ));
@@ -2007,7 +2014,8 @@ fn make_struct(this: &mut LoweringContext,
 
                     // `::std::option::Option::Some(<pat>) => <body>`
                     let pat_arm = {
-                        let body_block = self.with_loop_scope(e.id, |this| this.lower_block(body));
+                        let body_block = self.with_loop_scope(e.id,
+                                                              |this| this.lower_block(body, None));
                         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);
@@ -2090,14 +2098,12 @@ fn make_struct(this: &mut LoweringContext,
                     // match Carrier::translate(<expr>) {
                     //     Ok(val) => #[allow(unreachable_code)] val,
                     //     Err(err) => #[allow(unreachable_code)]
+                    //                 // If there is an enclosing `catch {...}`
+                    //                 break 'catch_target Carrier::from_error(From::from(err)),
+                    //                 // Otherwise
                     //                 return Carrier::from_error(From::from(err)),
                     // }
 
-                    // FIXME(cramertj): implement breaking to catch
-                    if !self.catch_scopes.is_empty() {
-                        bug!("`?` in catch scopes is unimplemented")
-                    }
-
                     let unstable_span = self.allow_internal_unstable("?", e.span);
 
                     // Carrier::translate(<expr>)
@@ -2157,9 +2163,24 @@ fn make_struct(this: &mut LoweringContext,
                             P(self.expr_call(e.span, from_err, hir_vec![from_expr]))
                         };
 
-                        let ret_expr = P(self.expr(e.span,
-                                                   hir::Expr_::ExprRet(Some(from_err_expr)),
-                                                                       ThinVec::from(attrs)));
+                        let thin_attrs = ThinVec::from(attrs);
+                        let catch_scope = self.catch_scopes.last().map(|x| *x);
+                        let ret_expr = if let Some(catch_node) = catch_scope {
+                            P(self.expr(
+                                e.span,
+                                hir::ExprBreak(
+                                    hir::Destination {
+                                        ident: None,
+                                        target_id: hir::ScopeTarget::Block(catch_node),
+                                    },
+                                    Some(from_err_expr)
+                                ),
+                                thin_attrs))
+                        } else {
+                            P(self.expr(e.span,
+                                        hir::Expr_::ExprRet(Some(from_err_expr)),
+                                        thin_attrs))
+                        };
 
                         let err_pat = self.pat_err(e.span, err_local);
                         self.arm(hir_vec![err_pat], ret_expr)
@@ -2302,7 +2323,7 @@ fn field(&mut self, name: Name, expr: P<hir::Expr>, span: Span) -> hir::Field {
     }
 
     fn expr_break(&mut self, span: Span, attrs: ThinVec<Attribute>) -> P<hir::Expr> {
-        let expr_break = hir::ExprBreak(self.lower_destination(None), None);
+        let expr_break = hir::ExprBreak(self.lower_loop_destination(None), None);
         P(self.expr(span, expr_break, attrs))
     }
 
@@ -2415,6 +2436,7 @@ fn block_all(&mut self, span: Span, stmts: hir::HirVec<hir::Stmt>, expr: Option<
             id: self.next_id(),
             rules: hir::DefaultBlock,
             span: span,
+            break_to_expr_id: None,
         }
     }
 
@@ -2519,6 +2541,7 @@ fn signal_block_expr(&mut self,
             id: id,
             stmts: stmts,
             expr: Some(expr),
+            break_to_expr_id: None,
         });
         self.expr_block(block, attrs)
     }
index 22bc28eb3fec0a00e90a08bdb4fc7af268c5fa1a..d5f496b90035dc526c6fb22de6f3737cf3bad5d8 100644 (file)
@@ -485,6 +485,9 @@ 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>,
 }
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash)]
@@ -1080,6 +1083,22 @@ fn from(res: Result<NodeId, LoopIdError>) -> Self {
     }
 }
 
+#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
+pub enum ScopeTarget {
+    Block(NodeId),
+    Loop(LoopIdResult),
+}
+
+impl ScopeTarget {
+    pub fn opt_id(self) -> Option<NodeId> {
+        match self {
+            ScopeTarget::Block(node_id) |
+            ScopeTarget::Loop(LoopIdResult::Ok(node_id)) => Some(node_id),
+            ScopeTarget::Loop(LoopIdResult::Err(_)) => None,
+        }
+    }
+}
+
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
 pub struct Destination {
     // This is `Some(_)` iff there is an explicit user-specified `label
@@ -1087,7 +1106,7 @@ pub struct Destination {
 
     // These errors are caught and then reported during the diagnostics pass in
     // librustc_passes/loops.rs
-    pub loop_id: LoopIdResult,
+    pub target_id: ScopeTarget,
 }
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
index a8c1559ae2373b309ca0072d0c0ec90790d1f917..769dc8aeb54ddce49744a685505a047dd6da0e41 100644 (file)
@@ -516,14 +516,15 @@ struct Liveness<'a, 'tcx: 'a> {
     s: Specials,
     successors: Vec<LiveNode>,
     users: Vec<Users>,
-    // The list of node IDs for the nested loop scopes
-    // we're in.
-    loop_scope: Vec<NodeId>,
+
     // mappings from loop node ID to LiveNode
     // ("break" label should map to loop node ID,
     // it probably doesn't now)
     break_ln: NodeMap<LiveNode>,
-    cont_ln: NodeMap<LiveNode>
+    cont_ln: NodeMap<LiveNode>,
+
+    // mappings from node ID to LiveNode for "breakable" blocks-- currently only `catch {...}`
+    breakable_block_ln: NodeMap<LiveNode>,
 }
 
 impl<'a, 'tcx> Liveness<'a, 'tcx> {
@@ -550,9 +551,9 @@ fn new(ir: &'a mut IrMaps<'a, 'tcx>, body: hir::BodyId) -> Liveness<'a, 'tcx> {
             s: specials,
             successors: vec![invalid_node(); num_live_nodes],
             users: vec![invalid_users(); num_live_nodes * num_vars],
-            loop_scope: Vec::new(),
             break_ln: NodeMap(),
             cont_ln: NodeMap(),
+            breakable_block_ln: NodeMap(),
         }
     }
 
@@ -793,15 +794,17 @@ fn compute(&mut self, body: &hir::Expr) -> LiveNode {
         debug!("compute: using id for body, {}", self.ir.tcx.hir.node_to_pretty_string(body.id));
 
         let exit_ln = self.s.exit_ln;
-        let entry_ln: LiveNode = self.with_loop_nodes(body.id, exit_ln, exit_ln, |this| {
-            // the fallthrough exit is only for those cases where we do not
-            // explicitly return:
-            let s = this.s;
-            this.init_from_succ(s.fallthrough_ln, s.exit_ln);
-            this.acc(s.fallthrough_ln, s.clean_exit_var, ACC_READ);
-
-            this.propagate_through_expr(body, s.fallthrough_ln)
-        });
+
+        self.break_ln.insert(body.id, exit_ln);
+        self.cont_ln.insert(body.id, exit_ln);
+
+        // the fallthrough exit is only for those cases where we do not
+        // explicitly return:
+        let s = self.s;
+        self.init_from_succ(s.fallthrough_ln, s.exit_ln);
+        self.acc(s.fallthrough_ln, s.clean_exit_var, ACC_READ);
+
+        let entry_ln = self.propagate_through_expr(body, s.fallthrough_ln);
 
         // hack to skip the loop unless debug! is enabled:
         debug!("^^ liveness computation results for body {} (entry={:?})",
@@ -818,6 +821,9 @@ fn compute(&mut self, body: &hir::Expr) -> LiveNode {
 
     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);
+        }
         let succ = self.propagate_through_opt_expr(blk.expr.as_ref().map(|e| &**e), succ);
         blk.stmts.iter().rev().fold(succ, |succ, stmt| {
             self.propagate_through_stmt(stmt, succ)
@@ -901,30 +907,32 @@ fn propagate_through_expr(&mut self, expr: &Expr, succ: LiveNode)
           }
 
           hir::ExprClosure(.., blk_id, _) => {
-              debug!("{} is an ExprClosure",
-                     self.ir.tcx.hir.node_to_pretty_string(expr.id));
+              debug!("{} is an ExprClosure", self.ir.tcx.hir.node_to_pretty_string(expr.id));
 
               /*
               The next-node for a break is the successor of the entire
               loop. The next-node for a continue is the top of this loop.
               */
               let node = self.live_node(expr.id, expr.span);
-              self.with_loop_nodes(blk_id.node_id, succ, node, |this| {
-
-                 // the construction of a closure itself is not important,
-                 // but we have to consider the closed over variables.
-                 let caps = match this.ir.capture_info_map.get(&expr.id) {
-                    Some(caps) => caps.clone(),
-                    None => {
-                        span_bug!(expr.span, "no registered caps");
-                     }
-                 };
-                 caps.iter().rev().fold(succ, |succ, cap| {
-                     this.init_from_succ(cap.ln, succ);
-                     let var = this.variable(cap.var_nid, expr.span);
-                     this.acc(cap.ln, var, ACC_READ | ACC_USE);
-                     cap.ln
-                 })
+
+              let break_ln = succ;
+              let cont_ln = node;
+              self.break_ln.insert(blk_id.node_id, break_ln);
+              self.cont_ln.insert(blk_id.node_id, cont_ln);
+
+              // the construction of a closure itself is not important,
+              // but we have to consider the closed over variables.
+              let caps = match self.ir.capture_info_map.get(&expr.id) {
+                  Some(caps) => caps.clone(),
+                  None => {
+                      span_bug!(expr.span, "no registered caps");
+                  }
+              };
+              caps.iter().rev().fold(succ, |succ, cap| {
+                  self.init_from_succ(cap.ln, succ);
+                  let var = self.variable(cap.var_nid, expr.span);
+                  self.acc(cap.ln, var, ACC_READ | ACC_USE);
+                  cap.ln
               })
           }
 
@@ -1003,28 +1011,33 @@ fn propagate_through_expr(&mut self, expr: &Expr, succ: LiveNode)
 
           hir::ExprBreak(label, ref opt_expr) => {
               // Find which label this break jumps to
-              let sc = match label.loop_id.into() {
-                  Ok(loop_id) => loop_id,
-                  Err(err) => span_bug!(expr.span, "loop scope error: {}", err),
-              };
+              let target = match label.target_id {
+                    hir::ScopeTarget::Block(node_id) =>
+                        self.breakable_block_ln.get(&node_id),
+                    hir::ScopeTarget::Loop(hir::LoopIdResult::Ok(node_id)) =>
+                        self.break_ln.get(&node_id),
+                    hir::ScopeTarget::Loop(hir::LoopIdResult::Err(err)) =>
+                        span_bug!(expr.span, "loop scope error: {}", err),
+              }.map(|x| *x);
 
               // Now that we know the label we're going to,
               // look it up in the break loop nodes table
 
-              match self.break_ln.get(&sc) {
-                  Some(&b) => self.propagate_through_opt_expr(opt_expr.as_ref().map(|e| &**e), b),
+              match target {
+                  Some(b) => self.propagate_through_opt_expr(opt_expr.as_ref().map(|e| &**e), b),
                   None => span_bug!(expr.span, "break to unknown label")
               }
           }
 
           hir::ExprAgain(label) => {
               // Find which label this expr continues to
-              let sc = match label.loop_id.into() {
-                  Ok(loop_id) => loop_id,
-                  Err(err) => span_bug!(expr.span, "loop scope error: {}", err),
+              let sc = match label.target_id {
+                    hir::ScopeTarget::Block(_) => bug!("can't `continue` to a non-loop block"),
+                    hir::ScopeTarget::Loop(hir::LoopIdResult::Ok(node_id)) => node_id,
+                    hir::ScopeTarget::Loop(hir::LoopIdResult::Err(err)) =>
+                        span_bug!(expr.span, "loop scope error: {}", err),
               };
 
-
               // Now that we know the label we're going to,
               // look it up in the continue loop nodes table
 
@@ -1287,14 +1300,16 @@ fn propagate_through_loop(&mut self,
         debug!("propagate_through_loop: using id for loop body {} {}",
                expr.id, self.ir.tcx.hir.node_to_pretty_string(body.id));
 
-        let (cond_ln, body_ln) = self.with_loop_nodes(expr.id, succ, ln, |this| {
-            let cond_ln = match kind {
-                LoopLoop => ln,
-                WhileLoop(ref cond) => this.propagate_through_expr(&cond, ln),
-            };
-            let body_ln = this.propagate_through_block(body, cond_ln);
-            (cond_ln, body_ln)
-        });
+        let break_ln = succ;
+        let cont_ln = ln;
+        self.break_ln.insert(expr.id, break_ln);
+        self.cont_ln.insert(expr.id, cont_ln);
+
+        let cond_ln = match kind {
+            LoopLoop => ln,
+            WhileLoop(ref cond) => self.propagate_through_expr(&cond, ln),
+        };
+        let body_ln = self.propagate_through_block(body, cond_ln);
 
         // repeat until fixed point is reached:
         while self.merge_from_succ(ln, body_ln, first_merge) {
@@ -1307,29 +1322,11 @@ fn propagate_through_loop(&mut self,
                 }
             };
             assert!(cond_ln == new_cond_ln);
-            assert!(body_ln == self.with_loop_nodes(expr.id, succ, ln,
-            |this| this.propagate_through_block(body, cond_ln)));
+            assert!(body_ln == self.propagate_through_block(body, cond_ln));
         }
 
         cond_ln
     }
-
-    fn with_loop_nodes<R, F>(&mut self,
-                             loop_node_id: NodeId,
-                             break_ln: LiveNode,
-                             cont_ln: LiveNode,
-                             f: F)
-                             -> R where
-        F: FnOnce(&mut Liveness<'a, 'tcx>) -> R,
-    {
-        debug!("with_loop_nodes: {} {}", loop_node_id, break_ln.get());
-        self.loop_scope.push(loop_node_id);
-        self.break_ln.insert(loop_node_id, break_ln);
-        self.cont_ln.insert(loop_node_id, cont_ln);
-        let r = f(self);
-        self.loop_scope.pop();
-        r
-    }
 }
 
 // _______________________________________________________________________
index dab85106873299e2747b79e954f36c2c41941620..e1b0c6a6f042e0ee980b39d05688b45e4277958b 100644 (file)
@@ -40,7 +40,19 @@ pub fn into_expr(&mut self,
                 this.in_scope(extent, block, |this| this.into(destination, block, value))
             }
             ExprKind::Block { body: ast_block } => {
-                this.ast_block(destination, block, 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)
+                }
             }
             ExprKind::Match { discriminant, arms } => {
                 this.match_expr(destination, expr_span, block, discriminant, arms)
@@ -165,8 +177,8 @@ pub fn into_expr(&mut self,
                 this.cfg.terminate(block, source_info,
                                    TerminatorKind::Goto { target: loop_block });
 
-                this.in_loop_scope(
-                    loop_block, exit_block, destination.clone(),
+                this.in_breakable_scope(
+                    Some(loop_block), exit_block, destination.clone(),
                     move |this| {
                         // conduct the test, if necessary
                         let body_block;
index be39dcbf6d08d84a3940a32188dffb6838710cfb..7336da654c1840f550e82d5b66ffc125bd7454ac 100644 (file)
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use build::{BlockAnd, BlockAndExtension, Builder};
-use build::scope::LoopScope;
+use build::scope::BreakableScope;
 use hair::*;
 use rustc::mir::*;
 
@@ -77,19 +77,21 @@ pub fn stmt_expr(&mut self, mut block: BasicBlock, expr: Expr<'tcx>) -> BlockAnd
                 block.unit()
             }
             ExprKind::Continue { label } => {
-                let LoopScope { continue_block, extent, .. } =
-                    *this.find_loop_scope(expr_span, label);
+                let BreakableScope { continue_block, extent, .. } =
+                    *this.find_breakable_scope(expr_span, label);
+                let continue_block = continue_block.expect(
+                    "Attempted to continue in non-continuable breakable block");
                 this.exit_scope(expr_span, extent, block, continue_block);
                 this.cfg.start_new_block().unit()
             }
             ExprKind::Break { label, value } => {
                 let (break_block, extent, destination) = {
-                    let LoopScope {
+                    let BreakableScope {
                         break_block,
                         extent,
                         ref break_destination,
                         ..
-                    } = *this.find_loop_scope(expr_span, label);
+                    } = *this.find_breakable_scope(expr_span, label);
                     (break_block, extent, break_destination.clone())
                 };
                 if let Some(value) = value {
index 41374a0012327a4e78cb7d49e1b9682b682c2890..db03a1c68f7156023cde3ad4c0b631b71036fb41 100644 (file)
@@ -36,9 +36,9 @@ pub struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     /// see the `scope` module for more details
     scopes: Vec<scope::Scope<'tcx>>,
 
-    /// the current set of loops; see the `scope` module for more
+    /// the current set of breakables; see the `scope` module for more
     /// details
-    loop_scopes: Vec<scope::LoopScope<'tcx>>,
+    breakable_scopes: Vec<scope::BreakableScope<'tcx>>,
 
     /// the vector of all scopes that we have created thus far;
     /// we track this for debuginfo later
@@ -248,7 +248,7 @@ fn new(hir: Cx<'a, 'gcx, 'tcx>,
             scopes: vec![],
             visibility_scopes: IndexVec::new(),
             visibility_scope: ARGUMENT_VISIBILITY_SCOPE,
-            loop_scopes: vec![],
+            breakable_scopes: vec![],
             local_decls: IndexVec::from_elem_n(LocalDecl::new_return_pointer(return_ty), 1),
             var_indices: NodeMap(),
             unit_temp: None,
index 3dab1717f6b2e4a466534457c6eb971435eef903..1de5b9218564fdb73a96682b9fb3dc291c540392 100644 (file)
@@ -177,16 +177,16 @@ struct FreeData<'tcx> {
 }
 
 #[derive(Clone, Debug)]
-pub struct LoopScope<'tcx> {
+pub struct BreakableScope<'tcx> {
     /// Extent of the loop
     pub extent: CodeExtent,
-    /// Where the body of the loop begins
-    pub continue_block: BasicBlock,
-    /// Block to branch into when the loop terminates (either by being `break`-en out from, or by
-    /// having its condition to become false)
+    /// Where the body of the loop begins. `None` if block
+    pub continue_block: Option<BasicBlock>,
+    /// Block to branch into when the loop or block terminates (either by being `break`-en out
+    /// from, or by having its condition to become false)
     pub break_block: BasicBlock,
-    /// The destination of the loop expression itself (i.e. where to put the result of a `break`
-    /// expression)
+    /// The destination of the loop/block expression itself (i.e. where to put the result of a
+    /// `break` expression)
     pub break_destination: Lvalue<'tcx>,
 }
 
@@ -242,28 +242,29 @@ fn source_info(&self, span: Span) -> SourceInfo {
 impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     // Adding and removing scopes
     // ==========================
-    /// Start a loop scope, which tracks where `continue` and `break`
+    /// Start a breakable scope, which tracks where `continue` and `break`
     /// should branch to. See module comment for more details.
     ///
-    /// Returns the might_break attribute of the LoopScope used.
-    pub fn in_loop_scope<F>(&mut self,
-                            loop_block: BasicBlock,
+    /// Returns the might_break attribute of the BreakableScope used.
+    pub fn in_breakable_scope<F, R>(&mut self,
+                            loop_block: Option<BasicBlock>,
                             break_block: BasicBlock,
                             break_destination: Lvalue<'tcx>,
-                            f: F)
-        where F: FnOnce(&mut Builder<'a, 'gcx, 'tcx>)
+                            f: F) -> R
+        where F: FnOnce(&mut Builder<'a, 'gcx, 'tcx>) -> R
     {
         let extent = self.topmost_scope();
-        let loop_scope = LoopScope {
+        let scope = BreakableScope {
             extent: extent,
             continue_block: loop_block,
             break_block: break_block,
             break_destination: break_destination,
         };
-        self.loop_scopes.push(loop_scope);
-        f(self);
-        let loop_scope = self.loop_scopes.pop().unwrap();
-        assert!(loop_scope.extent == extent);
+        self.breakable_scopes.push(scope);
+        let res = f(self);
+        let breakable_scope = self.breakable_scopes.pop().unwrap();
+        assert!(breakable_scope.extent == extent);
+        res
     }
 
     /// Convenience wrapper that pushes a scope and then executes `f`
@@ -381,18 +382,18 @@ pub fn new_visibility_scope(&mut self, span: Span) -> VisibilityScope {
 
     // Finding scopes
     // ==============
-    /// Finds the loop scope for a given label. This is used for
+    /// Finds the breakable scope for a given label. This is used for
     /// resolving `break` and `continue`.
-    pub fn find_loop_scope(&mut self,
+    pub fn find_breakable_scope(&mut self,
                            span: Span,
                            label: CodeExtent)
-                           -> &mut LoopScope<'tcx> {
+                           -> &mut BreakableScope<'tcx> {
         // find the loop-scope with the correct id
-        self.loop_scopes.iter_mut()
+        self.breakable_scopes.iter_mut()
             .rev()
-            .filter(|loop_scope| loop_scope.extent == label)
+            .filter(|breakable_scope| breakable_scope.extent == label)
             .next()
-            .unwrap_or_else(|| span_bug!(span, "no enclosing loop scope found?"))
+            .unwrap_or_else(|| span_bug!(span, "no enclosing breakable scope found"))
     }
 
     /// Given a span and the current visibility scope, make a SourceInfo.
index c67bb8ec6c585346fc7d646addcecfaeda46df6f..da58a1ed1f49b2a90c5f72b1fd4f7da63cd0b9b5 100644 (file)
@@ -606,22 +606,25 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
             }
         }
         hir::ExprRet(ref v) => ExprKind::Return { value: v.to_ref() },
-        hir::ExprBreak(label, ref value) => {
-            match label.loop_id.into() {
-                Ok(loop_id) => ExprKind::Break {
-                    label: cx.tcx.region_maps.node_extent(loop_id),
+        hir::ExprBreak(dest, ref value) => {
+            match dest.target_id {
+                hir::ScopeTarget::Block(target_id) |
+                hir::ScopeTarget::Loop(hir::LoopIdResult::Ok(target_id)) => ExprKind::Break {
+                    label: cx.tcx.region_maps.node_extent(target_id),
                     value: value.to_ref(),
                 },
-                Err(err) => bug!("invalid loop id for break: {}", err)
+                hir::ScopeTarget::Loop(hir::LoopIdResult::Err(err)) =>
+                    bug!("invalid loop id for break: {}", err)
             }
-
         }
-        hir::ExprAgain(label) => {
-            match label.loop_id.into() {
-                Ok(loop_id) => ExprKind::Continue {
+        hir::ExprAgain(dest) => {
+            match dest.target_id {
+                hir::ScopeTarget::Block(_) => bug!("cannot continue to blocks"),
+                hir::ScopeTarget::Loop(hir::LoopIdResult::Ok(loop_id)) => ExprKind::Continue {
                     label: cx.tcx.region_maps.node_extent(loop_id),
                 },
-                Err(err) => bug!("invalid loop id for continue: {}", err)
+                hir::ScopeTarget::Loop(hir::LoopIdResult::Err(err)) =>
+                    bug!("invalid loop id for continue: {}", err)
             }
         }
         hir::ExprMatch(ref discr, ref arms, _) => {
index b2d51be5bf72095bc3b4199dec8af37c285ae3e9..421181c68c4cda90eef45bf85fe12ab59a27e424 100644 (file)
@@ -87,14 +87,19 @@ fn visit_expr(&mut self, e: &'hir hir::Expr) {
                 self.with_context(Closure, |v| v.visit_nested_body(b));
             }
             hir::ExprBreak(label, ref opt_expr) => {
-                let loop_id = match label.loop_id.into() {
-                    Ok(loop_id) => loop_id,
-                    Err(hir::LoopIdError::OutsideLoopScope) => ast::DUMMY_NODE_ID,
-                    Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => {
-                        self.emit_unlabled_cf_in_while_condition(e.span, "break");
-                        ast::DUMMY_NODE_ID
-                    },
-                    Err(hir::LoopIdError::UnresolvedLabel) => ast::DUMMY_NODE_ID,
+                let loop_id = match label.target_id {
+                    hir::ScopeTarget::Block(_) => return,
+                    hir::ScopeTarget::Loop(loop_res) => {
+                        match loop_res.into() {
+                            Ok(loop_id) => loop_id,
+                            Err(hir::LoopIdError::OutsideLoopScope) => ast::DUMMY_NODE_ID,
+                            Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => {
+                                self.emit_unlabled_cf_in_while_condition(e.span, "break");
+                                ast::DUMMY_NODE_ID
+                            },
+                            Err(hir::LoopIdError::UnresolvedLabel) => ast::DUMMY_NODE_ID,
+                        }
+                    }
                 };
 
                 if opt_expr.is_some() {
@@ -124,7 +129,9 @@ fn visit_expr(&mut self, e: &'hir hir::Expr) {
                 self.require_loop("break", e.span);
             }
             hir::ExprAgain(label) => {
-                if let Err(hir::LoopIdError::UnlabeledCfInWhileCondition) = label.loop_id.into() {
+                if let hir::ScopeTarget::Loop(
+                    hir::LoopIdResult::Err(
+                        hir::LoopIdError::UnlabeledCfInWhileCondition)) = label.target_id {
                     self.emit_unlabled_cf_in_while_condition(e.span, "continue");
                 }
                 self.require_loop("continue", e.span)
index f36254a8a10eaaa3794be3c1a10deaafb1879ac0..c857388106d5ad6c2fe89e628c3f3b657b158e3e 100644 (file)
@@ -402,7 +402,7 @@ fn always(self) -> bool {
 }
 
 #[derive(Clone)]
-pub struct LoopCtxt<'gcx, 'tcx> {
+pub struct BreakableCtxt<'gcx, 'tcx> {
     unified: Ty<'tcx>,
     coerce_to: Ty<'tcx>,
     break_exprs: Vec<&'gcx hir::Expr>,
@@ -410,15 +410,17 @@ pub struct LoopCtxt<'gcx, 'tcx> {
 }
 
 #[derive(Clone)]
-pub struct EnclosingLoops<'gcx, 'tcx> {
-    stack: Vec<LoopCtxt<'gcx, 'tcx>>,
+pub struct EnclosingBreakables<'gcx, 'tcx> {
+    stack: Vec<BreakableCtxt<'gcx, 'tcx>>,
     by_id: NodeMap<usize>,
 }
 
-impl<'gcx, 'tcx> EnclosingLoops<'gcx, 'tcx> {
-    fn find_loop(&mut self, id: hir::LoopIdResult) -> Option<&mut LoopCtxt<'gcx, 'tcx>> {
-        let id_res: Result<_,_> = id.into();
-        if let Some(ix) = id_res.ok().and_then(|id| self.by_id.get(&id).cloned()) {
+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
@@ -448,7 +450,7 @@ pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     /// Whether any child nodes have any type errors.
     has_errors: Cell<bool>,
 
-    enclosing_loops: RefCell<EnclosingLoops<'gcx, 'tcx>>,
+    enclosing_breakables: RefCell<EnclosingBreakables<'gcx, 'tcx>>,
 
     inh: &'a Inherited<'a, 'gcx, 'tcx>,
 }
@@ -1429,7 +1431,7 @@ pub fn new(inh: &'a Inherited<'a, 'gcx, 'tcx>,
                                                      ast::CRATE_NODE_ID)),
             diverges: Cell::new(Diverges::Maybe),
             has_errors: Cell::new(false),
-            enclosing_loops: RefCell::new(EnclosingLoops {
+            enclosing_breakables: RefCell::new(EnclosingBreakables {
                 stack: Vec::new(),
                 by_id: NodeMap(),
             }),
@@ -3480,10 +3482,11 @@ fn check_expr_kind(&self,
               }
               tcx.mk_nil()
           }
-          hir::ExprBreak(label, ref expr_opt) => {
+          hir::ExprBreak(destination, ref expr_opt) => {
             let coerce_to = {
-                let mut enclosing_loops = self.enclosing_loops.borrow_mut();
-                enclosing_loops.find_loop(label.loop_id).map(|ctxt| ctxt.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 {
                 let e_ty;
@@ -3499,8 +3502,8 @@ fn check_expr_kind(&self,
                     cause = self.misc(expr.span);
                 }
 
-                let mut enclosing_loops = self.enclosing_loops.borrow_mut();
-                let ctxt = enclosing_loops.find_loop(label.loop_id).unwrap();
+                let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
+                let ctxt = enclosing_breakables.find_breakable(destination.target_id).unwrap();
 
                 let result = if let Some(ref e) = *expr_opt {
                     // Special-case the first element, as it has no "previous expressions".
@@ -3530,8 +3533,9 @@ fn check_expr_kind(&self,
 
                 ctxt.may_break = true;
             }
-            // Otherwise, we failed to find the enclosing loop; this can only happen if the
-            // `break` was not inside a loop at all, which is caught by the loop-checking pass.
+            // Otherwise, we failed to find the enclosing breakable; this can only happen if the
+            // `break` target was not found, which is caught in HIR lowering and reported by the
+            // loop-checking pass.
             tcx.types.never
           }
           hir::ExprAgain(_) => { tcx.types.never }
@@ -3588,13 +3592,13 @@ fn check_expr_kind(&self,
           hir::ExprWhile(ref cond, ref body, _) => {
             let unified = self.tcx.mk_nil();
             let coerce_to = unified;
-            let ctxt = LoopCtxt {
+            let ctxt = BreakableCtxt {
                 unified: unified,
                 coerce_to: coerce_to,
                 break_exprs: vec![],
                 may_break: true,
             };
-            self.with_loop_ctxt(expr.id, ctxt, || {
+            self.with_breakable_ctxt(expr.id, ctxt, || {
                 self.check_expr_has_type(&cond, tcx.types.bool);
                 let cond_diverging = self.diverges.get();
                 self.check_block_no_value(&body);
@@ -3612,14 +3616,14 @@ fn check_expr_kind(&self,
           hir::ExprLoop(ref body, _, _) => {
             let unified = self.next_ty_var(TypeVariableOrigin::TypeInference(body.span));
             let coerce_to = expected.only_has_type(self).unwrap_or(unified);
-            let ctxt = LoopCtxt {
+            let ctxt = BreakableCtxt {
                 unified: unified,
                 coerce_to: coerce_to,
                 break_exprs: vec![],
                 may_break: false,
             };
 
-            let ctxt = self.with_loop_ctxt(expr.id, ctxt, || {
+            let (ctxt, ()) = self.with_breakable_ctxt(expr.id, ctxt, || {
                 self.check_block_no_value(&body);
             });
             if ctxt.may_break {
@@ -3638,8 +3642,8 @@ fn check_expr_kind(&self,
           hir::ExprClosure(capture, ref decl, body_id, _) => {
               self.check_expr_closure(expr, capture, &decl, body_id, expected)
           }
-          hir::ExprBlock(ref b) => {
-            self.check_block_with_expected(&b, expected)
+          hir::ExprBlock(ref body) => {
+            self.check_block_with_expected(&body, expected)
           }
           hir::ExprCall(ref callee, ref args) => {
               self.check_call(expr, &callee, args, expected)
@@ -4031,65 +4035,85 @@ fn check_block_with_expected(&self,
             replace(&mut *fcx_ps, unsafety_state)
         };
 
-        for s in &blk.stmts {
-            self.check_stmt(s);
-        }
+        let mut ty = if let Some(break_to_expr_id) = blk.break_to_expr_id {
+            let unified = self.next_ty_var(TypeVariableOrigin::TypeInference(blk.span));
+            let coerce_to = expected.only_has_type(self).unwrap_or(unified);
+            let ctxt = BreakableCtxt {
+                unified: unified,
+                coerce_to: coerce_to,
+                break_exprs: vec![],
+                may_break: false,
+            };
 
-        let mut ty = match blk.expr {
-            Some(ref e) => self.check_expr_with_expectation(e, expected),
-            None => self.tcx.mk_nil()
-        };
+            let (mut ctxt, (e_ty, cause)) = self.with_breakable_ctxt(break_to_expr_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
+                };
+                let e_ty;
+                let cause;
+                match blk.expr {
+                    Some(ref e) => {
+                        e_ty = self.check_expr_with_hint(e, coerce_to);
+                        cause = self.misc(e.span);
+                    },
+                    None => {
+                        e_ty = self.tcx.mk_nil();
+                        cause = self.misc(blk.span);
+                    }
+                };
+
+                (e_ty, cause)
+            });
 
-        if self.diverges.get().always() {
-            ty = self.next_diverging_ty_var(TypeVariableOrigin::DivergingBlockExpr(blk.span));
-        } else if let ExpectHasType(ety) = expected {
             if let Some(ref e) = blk.expr {
-                // Coerce the tail expression to the right type.
-                self.demand_coerce(e, ty, ety);
+                let result = if !ctxt.may_break {
+                    self.try_coerce(e, e_ty, ctxt.coerce_to)
+                } else {
+                    self.try_find_coercion_lub(&cause, || ctxt.break_exprs.iter().cloned(),
+                                               ctxt.unified, e, e_ty)
+                };
+                match result {
+                    Ok(ty) => ctxt.unified = ty,
+                    Err(err) =>
+                        self.report_mismatched_types(&cause, ctxt.unified, e_ty, err).emit(),
+                }
             } else {
-                // We're not diverging and there's an expected type, which,
-                // in case it's not `()`, could result in an error higher-up.
-                // We have a chance to error here early and be more helpful.
-                let cause = self.misc(blk.span);
-                let trace = TypeTrace::types(&cause, false, ty, ety);
-                match self.sub_types(false, &cause, ty, ety) {
-                    Ok(InferOk { obligations, .. }) => {
-                        // FIXME(#32730) propagate obligations
-                        assert!(obligations.is_empty());
-                    },
-                    Err(err) => {
-                        let mut err = self.report_and_explain_type_error(trace, &err);
-
-                        // Be helpful when the user wrote `{... expr;}` and
-                        // taking the `;` off is enough to fix the error.
-                        let mut extra_semi = None;
-                        if let Some(stmt) = blk.stmts.last() {
-                            if let hir::StmtSemi(ref e, _) = stmt.node {
-                                if self.can_sub_types(self.node_ty(e.id), ety).is_ok() {
-                                    extra_semi = Some(stmt);
-                                }
-                            }
-                        }
-                        if let Some(last_stmt) = extra_semi {
-                            let original_span = original_sp(self.tcx.sess.codemap(),
-                                                            last_stmt.span, blk.span);
-                            let span_semi = Span {
-                                lo: original_span.hi - BytePos(1),
-                                hi: original_span.hi,
-                                expn_id: original_span.expn_id
-                            };
-                            err.span_help(span_semi, "consider removing this semicolon:");
-                        }
+                self.check_block_no_expr(blk, self.tcx.mk_nil(), e_ty);
+            };
 
-                        err.emit();
-                    }
-                }
+            ctxt.unified
+        } else {
+            for s in &blk.stmts {
+                self.check_stmt(s);
             }
 
-            // We already applied the type (and potentially errored),
-            // use the expected type to avoid further errors out.
-            ty = ety;
-        }
+            let mut ty = match blk.expr {
+                Some(ref e) => self.check_expr_with_expectation(e, expected),
+                None => self.tcx.mk_nil()
+            };
+
+            if self.diverges.get().always() {
+                ty = self.next_diverging_ty_var(TypeVariableOrigin::DivergingBlockExpr(blk.span));
+            } else if let ExpectHasType(ety) = expected {
+                if let Some(ref e) = blk.expr {
+                    // Coerce the tail expression to the right type.
+                    self.demand_coerce(e, ty, ety);
+                } else {
+                    self.check_block_no_expr(blk, ty, ety);
+                }
+
+                // We already applied the type (and potentially errored),
+                // use the expected type to avoid further errors out.
+                ty = ety;
+            }
+            ty
+        };
 
         if self.has_errors.get() || ty.references_error() {
             ty = self.tcx.types.err
@@ -4101,6 +4125,46 @@ fn check_block_with_expected(&self,
         ty
     }
 
+    pub fn check_block_no_expr(&self, blk: &'gcx hir::Block, ty: Ty<'tcx>, ety: Ty<'tcx>) {
+        // We're not diverging and there's an expected type, which,
+        // in case it's not `()`, could result in an error higher-up.
+        // We have a chance to error here early and be more helpful.
+        let cause = self.misc(blk.span);
+        let trace = TypeTrace::types(&cause, false, ty, ety);
+        match self.sub_types(false, &cause, ty, ety) {
+            Ok(InferOk { obligations, .. }) => {
+                // FIXME(#32730) propagate obligations
+                assert!(obligations.is_empty());
+            },
+            Err(err) => {
+                let mut err = self.report_and_explain_type_error(trace, &err);
+
+                // Be helpful when the user wrote `{... expr;}` and
+                // taking the `;` off is enough to fix the error.
+                let mut extra_semi = None;
+                if let Some(stmt) = blk.stmts.last() {
+                    if let hir::StmtSemi(ref e, _) = stmt.node {
+                        if self.can_sub_types(self.node_ty(e.id), ety).is_ok() {
+                            extra_semi = Some(stmt);
+                        }
+                    }
+                }
+                if let Some(last_stmt) = extra_semi {
+                    let original_span = original_sp(self.tcx.sess.codemap(),
+                                                    last_stmt.span, blk.span);
+                    let span_semi = Span {
+                        lo: original_span.hi - BytePos(1),
+                        hi: original_span.hi,
+                        expn_id: original_span.expn_id
+                    };
+                    err.span_help(span_semi, "consider removing this semicolon:");
+                }
+
+                err.emit();
+            }
+        }
+    }
+
     // Instantiates the given path, which must refer to an item with the given
     // number of type parameters and type.
     pub fn instantiate_value_path(&self,
@@ -4498,22 +4562,24 @@ pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
         })
     }
 
-    fn with_loop_ctxt<F: FnOnce()>(&self, id: ast::NodeId, ctxt: LoopCtxt<'gcx, 'tcx>, f: F)
-                                   -> LoopCtxt<'gcx, 'tcx> {
+    fn with_breakable_ctxt<F: FnOnce() -> R, R>(&self, id: ast::NodeId,
+                                        ctxt: BreakableCtxt<'gcx, 'tcx>, f: F)
+                                   -> (BreakableCtxt<'gcx, 'tcx>, R) {
         let index;
         {
-            let mut enclosing_loops = self.enclosing_loops.borrow_mut();
-            index = enclosing_loops.stack.len();
-            enclosing_loops.by_id.insert(id, index);
-            enclosing_loops.stack.push(ctxt);
-        }
-        f();
-        {
-            let mut enclosing_loops = self.enclosing_loops.borrow_mut();
-            debug_assert!(enclosing_loops.stack.len() == index + 1);
-            enclosing_loops.by_id.remove(&id).expect("missing loop context");
-            (enclosing_loops.stack.pop().expect("missing loop context"))
+            let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
+            index = enclosing_breakables.stack.len();
+            enclosing_breakables.by_id.insert(id, index);
+            enclosing_breakables.stack.push(ctxt);
         }
+        let result = f();
+        let ctxt = {
+            let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
+            debug_assert!(enclosing_breakables.stack.len() == index + 1);
+            enclosing_breakables.by_id.remove(&id).expect("missing breakable context");
+            enclosing_breakables.stack.pop().expect("missing breakable context")
+        };
+        (ctxt, result)
     }
 }
 
diff --git a/src/test/compile-fail/catch-bad-lifetime.rs b/src/test/compile-fail/catch-bad-lifetime.rs
new file mode 100644 (file)
index 0000000..57242da
--- /dev/null
@@ -0,0 +1,43 @@
+// Copyright 2017 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.
+
+#![feature(catch_expr)]
+
+// This test checks that borrows made and returned inside catch blocks are properly constrained
+pub fn main() {
+    {
+        // Test that borrows returned from a catch block must be valid for the lifetime of the
+        // result variable
+        let _result: Result<(), &str> = do catch {
+            let my_string = String::from("");
+            let my_str: & str = & my_string;
+            Err(my_str) ?;
+            Err("") ?;
+            Ok(())
+        }; //~ ERROR `my_string` does not live long enough
+    }
+
+    {
+        // Test that borrows returned from catch blocks freeze their referent
+        let mut i = 5;
+        let k = &mut i;
+        let mut j: Result<(), &mut i32> = do catch {
+            Err(k) ?;
+            i = 10; //~ ERROR cannot assign to `i` because it is borrowed
+            Ok(())
+        };
+        ::std::mem::drop(k); //~ ERROR use of moved value: `k`
+        i = 40; //~ ERROR cannot assign to `i` because it is borrowed
+
+        let i_ptr = if let Err(i_ptr) = j { i_ptr } else { panic ! ("") };
+        *i_ptr = 50;
+    }
+}
+
diff --git a/src/test/compile-fail/catch-bad-type.rs b/src/test/compile-fail/catch-bad-type.rs
new file mode 100644 (file)
index 0000000..cff9f50
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright 2017 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.
+
+#![feature(catch_expr)]
+
+pub fn main() {
+    let res: Result<i32, i32> = do catch {
+        Err("")?; //~ ERROR the trait bound `i32: std::convert::From<&str>` is not satisfied
+        Ok(5)
+    };
+    let res: Result<i32, i32> = do catch {
+        Ok("") //~ mismatched types
+    };
+}
diff --git a/src/test/compile-fail/catch-maybe-bad-lifetime.rs b/src/test/compile-fail/catch-maybe-bad-lifetime.rs
new file mode 100644 (file)
index 0000000..b783a3d
--- /dev/null
@@ -0,0 +1,51 @@
+// Copyright 2017 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.
+
+#![feature(catch_expr)]
+
+// This test checks that borrows made and returned inside catch blocks are properly constrained
+pub fn main() {
+    {
+        // Test that a borrow which *might* be returned still freezes its referent
+        let mut i = 222;
+        let x: Result<&i32, ()> = do catch {
+            Err(())?;
+            Ok(&i)
+        };
+        x.ok().cloned();
+        i = 0; //~ ERROR cannot assign to `i` because it is borrowed
+        let _ = i;
+    }
+
+    {
+        let x = String::new();
+        let _y: Result<(), ()> = do catch {
+            Err(())?;
+            ::std::mem::drop(x);
+            Ok(())
+        };
+        println!("{}", x); //~ ERROR use of moved value: `x`
+    }
+
+    {
+        // Test that a borrow which *might* be assigned to an outer variable still freezes
+        // its referent
+        let mut i = 222;
+        let j;
+        let x: Result<(), ()> = do catch {
+            Err(())?;
+            j = &i;
+            Ok(())
+        };
+        i = 0; //~ ERROR cannot assign to `i` because it is borrowed
+        let _ = i;
+    }
+}
+
diff --git a/src/test/compile-fail/catch-opt-init.rs b/src/test/compile-fail/catch-opt-init.rs
new file mode 100644 (file)
index 0000000..48284b4
--- /dev/null
@@ -0,0 +1,26 @@
+// Copyright 2017 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.
+
+#![feature(catch_expr)]
+
+fn use_val<T: Sized>(_x: T) {}
+
+pub fn main() {
+    let cfg_res;
+    let _: Result<(), ()> = do catch {
+        Err(())?;
+        cfg_res = 5;
+        Ok::<(), ()>(())?;
+        use_val(cfg_res);
+        Ok(())
+    };
+    assert_eq!(cfg_res, 5); //~ ERROR use of possibly uninitialized variable
+}
+
index a9b28a534a3483edd4b69a98f00bcfd0f309f6dc..5a757161a78a4e6858e4d6bf7e4717c67d861d23 100644 (file)
@@ -29,4 +29,46 @@ pub fn main() {
     match catch {
         _ => {}
     };
+
+    let catch_err = do catch {
+        Err(22)?;
+        Ok(1)
+    };
+    assert_eq!(catch_err, Err(22));
+
+    let catch_okay: Result<i32, i32> = do catch {
+        if false { Err(25)?; }
+        Ok::<(), i32>(())?;
+        Ok(28)
+    };
+    assert_eq!(catch_okay, Ok(28));
+
+    let catch_from_loop: Result<i32, i32> = do catch {
+        for i in 0..10 {
+            if i < 5 { Ok::<i32, i32>(i)?; } else { Err(i)?; }
+        }
+        Ok(22)
+    };
+    assert_eq!(catch_from_loop, Err(5));
+
+    let cfg_init;
+    let _res: Result<(), ()> = do catch {
+        cfg_init = 5;
+        Ok(())
+    };
+    assert_eq!(cfg_init, 5);
+
+    let cfg_init_2;
+    let _res: Result<(), ()> = do catch {
+        cfg_init_2 = 6;
+        Err(())?;
+        Ok(())
+    };
+    assert_eq!(cfg_init_2, 6);
+
+    let my_string = "test".to_string();
+    let res: Result<&str, ()> = do catch {
+        Ok(&my_string)
+    };
+    assert_eq!(res, Ok("test"));
 }