about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_mir/build/block.rs28
-rw-r--r--src/librustc_mir/build/cfg.rs7
-rw-r--r--src/librustc_mir/build/expr/as_rvalue.rs65
3 files changed, 54 insertions, 46 deletions
diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs
index c1626b93f0c..01930d0ddc0 100644
--- a/src/librustc_mir/build/block.rs
+++ b/src/librustc_mir/build/block.rs
@@ -12,6 +12,7 @@ use build::{BlockAnd, BlockAndExtension, Builder};
 use hair::*;
 use rustc::mir::repr::*;
 use rustc::hir;
+use syntax::codemap::Span;
 
 impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     pub fn ast_block(&mut self,
@@ -81,4 +82,31 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             block.unit()
         })
     }
+
+    // Helper method for generating MIR inside a conditional block.
+    pub fn with_cond<F>(&mut self, block: BasicBlock, span: Span,
+                        cond: Operand<'tcx>, f: F) -> BasicBlock
+    where F: FnOnce(&mut Builder<'a, 'gcx, 'tcx>, BasicBlock) -> BasicBlock {
+        let scope_id = self.innermost_scope_id();
+
+        let then_block = self.cfg.start_new_block();
+        let else_block = self.cfg.start_new_block();
+
+        self.cfg.terminate(block, scope_id, span,
+                           TerminatorKind::If {
+                               cond: cond,
+                               targets: (then_block, else_block)
+                           });
+
+        let after = f(self, then_block);
+
+        // If the returned block isn't terminated, add a branch to the "else"
+        // block
+        if !self.cfg.terminated(after) {
+            self.cfg.terminate(after, scope_id, span,
+                               TerminatorKind::Goto { target: else_block });
+        }
+
+        else_block
+    }
 }
diff --git a/src/librustc_mir/build/cfg.rs b/src/librustc_mir/build/cfg.rs
index 4859257f291..7ffef989e2f 100644
--- a/src/librustc_mir/build/cfg.rs
+++ b/src/librustc_mir/build/cfg.rs
@@ -86,7 +86,7 @@ impl<'tcx> CFG<'tcx> {
                      scope: ScopeId,
                      span: Span,
                      kind: TerminatorKind<'tcx>) {
-        debug_assert!(self.block_data(block).terminator.is_none(),
+        debug_assert!(!self.terminated(block),
                       "terminate: block {:?} already has a terminator set", block);
         self.block_data_mut(block).terminator = Some(Terminator {
             span: span,
@@ -94,4 +94,9 @@ impl<'tcx> CFG<'tcx> {
             kind: kind,
         });
     }
+
+    /// Returns whether or not the given block has been terminated or not
+    pub fn terminated(&self, block: BasicBlock) -> bool {
+        self.block_data(block).terminator.is_some()
+    }
 }
diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs
index b5b4a01dcc6..04609e7f8dd 100644
--- a/src/librustc_mir/build/expr/as_rvalue.rs
+++ b/src/librustc_mir/build/expr/as_rvalue.rs
@@ -88,18 +88,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     this.cfg.push_assign(block, scope_id, expr_span, &is_min,
                                          Rvalue::BinaryOp(BinOp::Eq, arg.clone(), minval));
 
-                    let of_block = this.cfg.start_new_block();
-                    let ok_block = this.cfg.start_new_block();
-
-                    this.cfg.terminate(block, scope_id, expr_span,
-                                       TerminatorKind::If {
-                                           cond: Operand::Consume(is_min),
-                                           targets: (of_block, ok_block)
-                                       });
-
-                    this.panic(of_block, "attempted to negate with overflow", expr_span);
-
-                    block = ok_block;
+                    block = this.with_cond(
+                        block, expr_span, Operand::Consume(is_min), |this, block| {
+                            this.panic(block, "attempted to negate with overflow", expr_span);
+                            block
+                        });
                 }
                 block.and(Rvalue::UnaryOp(op, arg))
             }
@@ -268,21 +261,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             let val = result_value.clone().field(val_fld, ty);
             let of = result_value.field(of_fld, bool_ty);
 
-            let success = self.cfg.start_new_block();
-            let failure = self.cfg.start_new_block();
-
-            self.cfg.terminate(block, scope_id, span,
-                               TerminatorKind::If {
-                                   cond: Operand::Consume(of),
-                                   targets: (failure, success)
-                               });
             let msg = if op == BinOp::Shl || op == BinOp::Shr {
                 "shift operation overflowed"
             } else {
                 "arithmetic operation overflowed"
             };
-            self.panic(failure, msg, span);
-            success.and(Rvalue::Use(Operand::Consume(val)))
+
+            block = self.with_cond(block, span, Operand::Consume(of), |this, block| {
+                this.panic(block, msg, span);
+                block
+            });
+
+            block.and(Rvalue::Use(Operand::Consume(val)))
         } else {
             if ty.is_integral() && (op == BinOp::Div || op == BinOp::Rem) {
                 // Checking division and remainder is more complex, since we 1. always check
@@ -302,17 +292,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 self.cfg.push_assign(block, scope_id, span, &is_zero,
                                      Rvalue::BinaryOp(BinOp::Eq, rhs.clone(), zero));
 
-                let zero_block = self.cfg.start_new_block();
-                let ok_block   = self.cfg.start_new_block();
-
-                self.cfg.terminate(block, scope_id, span,
-                                   TerminatorKind::If {
-                                       cond: Operand::Consume(is_zero),
-                                       targets: (zero_block, ok_block)
-                                   });
-
-                self.panic(zero_block, zero_msg, span);
-                block = ok_block;
+                block = self.with_cond(block, span, Operand::Consume(is_zero), |this, block| {
+                    this.panic(block, zero_msg, span);
+                    block
+                });
 
                 // We only need to check for the overflow in one case:
                 // MIN / -1, and only for signed values.
@@ -336,18 +319,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     self.cfg.push_assign(block, scope_id, span, &of,
                                          Rvalue::BinaryOp(BinOp::BitAnd, is_neg_1, is_min));
 
-                    let of_block = self.cfg.start_new_block();
-                    let ok_block = self.cfg.start_new_block();
-
-                    self.cfg.terminate(block, scope_id, span,
-                                       TerminatorKind::If {
-                                           cond: Operand::Consume(of),
-                                           targets: (of_block, ok_block)
-                                       });
-
-                    self.panic(of_block, overflow_msg, span);
-
-                    block = ok_block;
+                    block = self.with_cond(block, span, Operand::Consume(of), |this, block| {
+                        this.panic(block, overflow_msg, span);
+                        block
+                    });
                 }
             }