Skip to content

Commit 196de1a

Browse files
committed
Add a with_cond method
Factors out the common pattern across the several places that do arithmetic checks
1 parent a50a1c2 commit 196de1a

File tree

3 files changed

+54
-46
lines changed

3 files changed

+54
-46
lines changed

src/librustc_mir/build/block.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use build::{BlockAnd, BlockAndExtension, Builder};
1212
use hair::*;
1313
use rustc::mir::repr::*;
1414
use rustc::hir;
15+
use syntax::codemap::Span;
1516

1617
impl<'a,'tcx> Builder<'a,'tcx> {
1718
pub fn ast_block(&mut self,
@@ -79,4 +80,31 @@ impl<'a,'tcx> Builder<'a,'tcx> {
7980
block.unit()
8081
})
8182
}
83+
84+
// Helper method for generating MIR inside a conditional block.
85+
pub fn with_cond<F>(&mut self, block: BasicBlock, span: Span,
86+
cond: Operand<'tcx>, f: F) -> BasicBlock
87+
where F: FnOnce(&mut Builder<'a, 'tcx>, BasicBlock) -> BasicBlock {
88+
let scope_id = self.innermost_scope_id();
89+
90+
let then_block = self.cfg.start_new_block();
91+
let else_block = self.cfg.start_new_block();
92+
93+
self.cfg.terminate(block, scope_id, span,
94+
TerminatorKind::If {
95+
cond: cond,
96+
targets: (then_block, else_block)
97+
});
98+
99+
let after = f(self, then_block);
100+
101+
// If the returned block isn't terminated, add a branch to the "else"
102+
// block
103+
if !self.cfg.terminated(after) {
104+
self.cfg.terminate(after, scope_id, span,
105+
TerminatorKind::Goto { target: else_block });
106+
}
107+
108+
else_block
109+
}
82110
}

src/librustc_mir/build/cfg.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,17 @@ impl<'tcx> CFG<'tcx> {
8686
scope: ScopeId,
8787
span: Span,
8888
kind: TerminatorKind<'tcx>) {
89-
debug_assert!(self.block_data(block).terminator.is_none(),
89+
debug_assert!(!self.terminated(block),
9090
"terminate: block {:?} already has a terminator set", block);
9191
self.block_data_mut(block).terminator = Some(Terminator {
9292
span: span,
9393
scope: scope,
9494
kind: kind,
9595
});
9696
}
97+
98+
/// Returns whether or not the given block has been terminated or not
99+
pub fn terminated(&self, block: BasicBlock) -> bool {
100+
self.block_data(block).terminator.is_some()
101+
}
97102
}

src/librustc_mir/build/expr/as_rvalue.rs

Lines changed: 20 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -88,18 +88,11 @@ impl<'a,'tcx> Builder<'a,'tcx> {
8888
this.cfg.push_assign(block, scope_id, expr_span, &is_min,
8989
Rvalue::BinaryOp(BinOp::Eq, arg.clone(), minval));
9090

91-
let of_block = this.cfg.start_new_block();
92-
let ok_block = this.cfg.start_new_block();
93-
94-
this.cfg.terminate(block, scope_id, expr_span,
95-
TerminatorKind::If {
96-
cond: Operand::Consume(is_min),
97-
targets: (of_block, ok_block)
98-
});
99-
100-
this.panic(of_block, "attempted to negate with overflow", expr_span);
101-
102-
block = ok_block;
91+
block = this.with_cond(
92+
block, expr_span, Operand::Consume(is_min), |this, block| {
93+
this.panic(block, "attempted to negate with overflow", expr_span);
94+
block
95+
});
10396
}
10497
block.and(Rvalue::UnaryOp(op, arg))
10598
}
@@ -271,21 +264,18 @@ impl<'a,'tcx> Builder<'a,'tcx> {
271264
let val = result_value.clone().field(val_fld, ty);
272265
let of = result_value.field(of_fld, bool_ty);
273266

274-
let success = self.cfg.start_new_block();
275-
let failure = self.cfg.start_new_block();
276-
277-
self.cfg.terminate(block, scope_id, span,
278-
TerminatorKind::If {
279-
cond: Operand::Consume(of),
280-
targets: (failure, success)
281-
});
282267
let msg = if op == BinOp::Shl || op == BinOp::Shr {
283268
"shift operation overflowed"
284269
} else {
285270
"arithmetic operation overflowed"
286271
};
287-
self.panic(failure, msg, span);
288-
success.and(Rvalue::Use(Operand::Consume(val)))
272+
273+
block = self.with_cond(block, span, Operand::Consume(of), |this, block| {
274+
this.panic(block, msg, span);
275+
block
276+
});
277+
278+
block.and(Rvalue::Use(Operand::Consume(val)))
289279
} else {
290280
if ty.is_integral() && (op == BinOp::Div || op == BinOp::Rem) {
291281
// Checking division and remainder is more complex, since we 1. always check
@@ -305,17 +295,10 @@ impl<'a,'tcx> Builder<'a,'tcx> {
305295
self.cfg.push_assign(block, scope_id, span, &is_zero,
306296
Rvalue::BinaryOp(BinOp::Eq, rhs.clone(), zero));
307297

308-
let zero_block = self.cfg.start_new_block();
309-
let ok_block = self.cfg.start_new_block();
310-
311-
self.cfg.terminate(block, scope_id, span,
312-
TerminatorKind::If {
313-
cond: Operand::Consume(is_zero),
314-
targets: (zero_block, ok_block)
315-
});
316-
317-
self.panic(zero_block, zero_msg, span);
318-
block = ok_block;
298+
block = self.with_cond(block, span, Operand::Consume(is_zero), |this, block| {
299+
this.panic(block, zero_msg, span);
300+
block
301+
});
319302

320303
// We only need to check for the overflow in one case:
321304
// MIN / -1, and only for signed values.
@@ -339,18 +322,10 @@ impl<'a,'tcx> Builder<'a,'tcx> {
339322
self.cfg.push_assign(block, scope_id, span, &of,
340323
Rvalue::BinaryOp(BinOp::BitAnd, is_neg_1, is_min));
341324

342-
let of_block = self.cfg.start_new_block();
343-
let ok_block = self.cfg.start_new_block();
344-
345-
self.cfg.terminate(block, scope_id, span,
346-
TerminatorKind::If {
347-
cond: Operand::Consume(of),
348-
targets: (of_block, ok_block)
349-
});
350-
351-
self.panic(of_block, overflow_msg, span);
352-
353-
block = ok_block;
325+
block = self.with_cond(block, span, Operand::Consume(of), |this, block| {
326+
this.panic(block, overflow_msg, span);
327+
block
328+
});
354329
}
355330
}
356331

0 commit comments

Comments
 (0)