Skip to content

Commit 8cbffc5

Browse files
committed
Auto merge of #33905 - eddyb:mir-overflow, r=nikomatsakis
[MIR] Implement overflow checking The initial set of changes is from @Aatch's #33255 PR, rebased on master, plus: Added an `Assert` terminator to MIR, to simplify working with overflow and bounds checks. With this terminator, error cases can be accounted for directly, instead of looking for lang item calls. It also keeps the MIR slimmer, with no extra explicit blocks for the actual panic calls. Warnings can be produced when the `Assert` is known to always panic at runtime, e.g.: ```rust warning: index out of bounds: the len is 1 but the index is 3 --> <anon>:1:14 1 |> fn main() { &[std::io::stdout()][3]; } |> ^^^^^^^^^^^^^^^^^^^^^^ ``` Generalized the `OperandValue::FatPtr` optimization to any aggregate pair of immediates. This allows us to generate the same IR for overflow checks as old trans, not something worse. For example, addition on `i16` calls `llvm.sadd.with.overflow.i16`, which returns `{i16, i1}`. However, the Rust type `(i16, bool)`, has to be `{i16, i8}`, only an immediate `bool` is `i1`. But if we split the pair into an `i16` and an `i1`, we can pass them around as such for free. The latest addition is a rebase of #34054, updated to work for pairs too. Closes #34054, fixes #33873. Last but not least, the `#[rustc_inherit_overflow_checks]` attribute was introduced to control the overflow checking behavior of generic or `#[inline]` functions, when translated in another crate. It is **not** intended to be used by crates other than `libcore`, which is in the unusual position of being distributed as only an optimized build with no checks, even when used from debug mode. Before MIR-based translation, this worked out fine, as the decision for overflow was made at translation time, in the crate being compiled, but MIR stored in `rlib` has to contain the checks. To avoid always generating the checks and slowing everything down, a decision was made to use an attribute in the few spots of `libcore` that need it (see #33255 for previous discussion): * `core::ops::{Add, Sub, Mul, Neg, Shl, Shr}` implementations for integers, which have `#[inline]` methods and can be used in generic abstractions from other crates * `core::ops::{Add, Sub, Mul, Neg, Shl, Shr}Assign` same as above, for augmented assignment * `pow` and `abs` methods on integers, which intentionally piggy-back on built-in multiplication and negation, respectively, to get overflow checks * `core::iter::{Iterator, Chain, Peek}::count` and `core::iter::Enumerate::{next, nth}`, also documented as panicking on overflow, from addition, counting elements of an iterator in an `usize`
2 parents 22b36c7 + cee244d commit 8cbffc5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+1363
-431
lines changed

src/libcore/iter/iterator.rs

+1
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ pub trait Iterator {
172172
/// assert_eq!(a.iter().count(), 5);
173173
/// ```
174174
#[inline]
175+
#[rustc_inherit_overflow_checks]
175176
#[stable(feature = "rust1", since = "1.0.0")]
176177
fn count(self) -> usize where Self: Sized {
177178
// Might overflow.

src/libcore/iter/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,7 @@ impl<A, B> Iterator for Chain<A, B> where
510510
}
511511

512512
#[inline]
513+
#[rustc_inherit_overflow_checks]
513514
fn count(self) -> usize {
514515
match self.state {
515516
ChainState::Both => self.a.count() + self.b.count(),
@@ -932,6 +933,7 @@ impl<I> Iterator for Enumerate<I> where I: Iterator {
932933
///
933934
/// Might panic if the index of the element overflows a `usize`.
934935
#[inline]
936+
#[rustc_inherit_overflow_checks]
935937
fn next(&mut self) -> Option<(usize, <I as Iterator>::Item)> {
936938
self.iter.next().map(|a| {
937939
let ret = (self.count, a);
@@ -947,6 +949,7 @@ impl<I> Iterator for Enumerate<I> where I: Iterator {
947949
}
948950

949951
#[inline]
952+
#[rustc_inherit_overflow_checks]
950953
fn nth(&mut self, n: usize) -> Option<(usize, I::Item)> {
951954
self.iter.nth(n).map(|a| {
952955
let i = self.count + n;
@@ -1008,6 +1011,7 @@ impl<I: Iterator> Iterator for Peekable<I> {
10081011
}
10091012

10101013
#[inline]
1014+
#[rustc_inherit_overflow_checks]
10111015
fn count(self) -> usize {
10121016
(if self.peeked.is_some() { 1 } else { 0 }) + self.iter.count()
10131017
}

src/libcore/num/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1033,7 +1033,7 @@ macro_rules! int_impl {
10331033
/// ```
10341034
#[stable(feature = "rust1", since = "1.0.0")]
10351035
#[inline]
1036-
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
1036+
#[rustc_inherit_overflow_checks]
10371037
pub fn pow(self, mut exp: u32) -> Self {
10381038
let mut base = self;
10391039
let mut acc = Self::one();
@@ -1075,7 +1075,7 @@ macro_rules! int_impl {
10751075
/// ```
10761076
#[stable(feature = "rust1", since = "1.0.0")]
10771077
#[inline]
1078-
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
1078+
#[rustc_inherit_overflow_checks]
10791079
pub fn abs(self) -> Self {
10801080
if self.is_negative() {
10811081
// Note that the #[inline] above means that the overflow
@@ -2061,7 +2061,7 @@ macro_rules! uint_impl {
20612061
/// ```
20622062
#[stable(feature = "rust1", since = "1.0.0")]
20632063
#[inline]
2064-
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
2064+
#[rustc_inherit_overflow_checks]
20652065
pub fn pow(self, mut exp: u32) -> Self {
20662066
let mut base = self;
20672067
let mut acc = Self::one();

src/libcore/ops.rs

+11
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ macro_rules! add_impl {
208208
type Output = $t;
209209

210210
#[inline]
211+
#[rustc_inherit_overflow_checks]
211212
fn add(self, other: $t) -> $t { self + other }
212213
}
213214

@@ -261,6 +262,7 @@ macro_rules! sub_impl {
261262
type Output = $t;
262263

263264
#[inline]
265+
#[rustc_inherit_overflow_checks]
264266
fn sub(self, other: $t) -> $t { self - other }
265267
}
266268

@@ -314,6 +316,7 @@ macro_rules! mul_impl {
314316
type Output = $t;
315317

316318
#[inline]
319+
#[rustc_inherit_overflow_checks]
317320
fn mul(self, other: $t) -> $t { self * other }
318321
}
319322

@@ -511,6 +514,7 @@ macro_rules! neg_impl_core {
511514
type Output = $t;
512515

513516
#[inline]
517+
#[rustc_inherit_overflow_checks]
514518
fn neg(self) -> $t { let $id = self; $body }
515519
}
516520

@@ -788,6 +792,7 @@ macro_rules! shl_impl {
788792
type Output = $t;
789793

790794
#[inline]
795+
#[rustc_inherit_overflow_checks]
791796
fn shl(self, other: $f) -> $t {
792797
self << other
793798
}
@@ -859,6 +864,7 @@ macro_rules! shr_impl {
859864
type Output = $t;
860865

861866
#[inline]
867+
#[rustc_inherit_overflow_checks]
862868
fn shr(self, other: $f) -> $t {
863869
self >> other
864870
}
@@ -923,6 +929,7 @@ macro_rules! add_assign_impl {
923929
#[stable(feature = "op_assign_traits", since = "1.8.0")]
924930
impl AddAssign for $t {
925931
#[inline]
932+
#[rustc_inherit_overflow_checks]
926933
fn add_assign(&mut self, other: $t) { *self += other }
927934
}
928935
)+)
@@ -967,6 +974,7 @@ macro_rules! sub_assign_impl {
967974
#[stable(feature = "op_assign_traits", since = "1.8.0")]
968975
impl SubAssign for $t {
969976
#[inline]
977+
#[rustc_inherit_overflow_checks]
970978
fn sub_assign(&mut self, other: $t) { *self -= other }
971979
}
972980
)+)
@@ -1011,6 +1019,7 @@ macro_rules! mul_assign_impl {
10111019
#[stable(feature = "op_assign_traits", since = "1.8.0")]
10121020
impl MulAssign for $t {
10131021
#[inline]
1022+
#[rustc_inherit_overflow_checks]
10141023
fn mul_assign(&mut self, other: $t) { *self *= other }
10151024
}
10161025
)+)
@@ -1275,6 +1284,7 @@ macro_rules! shl_assign_impl {
12751284
#[stable(feature = "op_assign_traits", since = "1.8.0")]
12761285
impl ShlAssign<$f> for $t {
12771286
#[inline]
1287+
#[rustc_inherit_overflow_checks]
12781288
fn shl_assign(&mut self, other: $f) {
12791289
*self <<= other
12801290
}
@@ -1337,6 +1347,7 @@ macro_rules! shr_assign_impl {
13371347
#[stable(feature = "op_assign_traits", since = "1.8.0")]
13381348
impl ShrAssign<$f> for $t {
13391349
#[inline]
1350+
#[rustc_inherit_overflow_checks]
13401351
fn shr_assign(&mut self, other: $f) {
13411352
*self >>= other
13421353
}

src/librustc/mir/repr.rs

+60-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
use graphviz::IntoCow;
1212
use middle::const_val::ConstVal;
13-
use rustc_const_math::{ConstUsize, ConstInt};
13+
use rustc_const_math::{ConstUsize, ConstInt, ConstMathErr};
1414
use hir::def_id::DefId;
1515
use ty::subst::Substs;
1616
use ty::{self, AdtDef, ClosureSubsts, FnOutput, Region, Ty};
@@ -354,6 +354,16 @@ pub enum TerminatorKind<'tcx> {
354354
/// Cleanups to be done if the call unwinds.
355355
cleanup: Option<BasicBlock>
356356
},
357+
358+
/// Jump to the target if the condition has the expected value,
359+
/// otherwise panic with a message and a cleanup target.
360+
Assert {
361+
cond: Operand<'tcx>,
362+
expected: bool,
363+
msg: AssertMessage<'tcx>,
364+
target: BasicBlock,
365+
cleanup: Option<BasicBlock>
366+
}
357367
}
358368

359369
impl<'tcx> Terminator<'tcx> {
@@ -389,6 +399,8 @@ impl<'tcx> TerminatorKind<'tcx> {
389399
Drop { ref target, unwind: None, .. } => {
390400
slice::ref_slice(target).into_cow()
391401
}
402+
Assert { target, cleanup: Some(unwind), .. } => vec![target, unwind].into_cow(),
403+
Assert { ref target, .. } => slice::ref_slice(target).into_cow(),
392404
}
393405
}
394406

@@ -413,6 +425,8 @@ impl<'tcx> TerminatorKind<'tcx> {
413425
Drop { ref mut target, unwind: None, .. } => {
414426
vec![target]
415427
}
428+
Assert { ref mut target, cleanup: Some(ref mut unwind), .. } => vec![target, unwind],
429+
Assert { ref mut target, .. } => vec![target]
416430
}
417431
}
418432
}
@@ -495,6 +509,26 @@ impl<'tcx> TerminatorKind<'tcx> {
495509
}
496510
write!(fmt, ")")
497511
}
512+
Assert { ref cond, expected, ref msg, .. } => {
513+
write!(fmt, "assert(")?;
514+
if !expected {
515+
write!(fmt, "!")?;
516+
}
517+
write!(fmt, "{:?}, ", cond)?;
518+
519+
match *msg {
520+
AssertMessage::BoundsCheck { ref len, ref index } => {
521+
write!(fmt, "{:?}, {:?}, {:?}",
522+
"index out of bounds: the len is {} but the index is {}",
523+
len, index)?;
524+
}
525+
AssertMessage::Math(ref err) => {
526+
write!(fmt, "{:?}", err.description())?;
527+
}
528+
}
529+
530+
write!(fmt, ")")
531+
}
498532
}
499533
}
500534

@@ -532,10 +566,21 @@ impl<'tcx> TerminatorKind<'tcx> {
532566
Drop { unwind: Some(_), .. } => {
533567
vec!["return".into_cow(), "unwind".into_cow()]
534568
}
569+
Assert { cleanup: None, .. } => vec!["".into()],
570+
Assert { .. } =>
571+
vec!["success".into_cow(), "unwind".into_cow()]
535572
}
536573
}
537574
}
538575

576+
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
577+
pub enum AssertMessage<'tcx> {
578+
BoundsCheck {
579+
len: Operand<'tcx>,
580+
index: Operand<'tcx>
581+
},
582+
Math(ConstMathErr)
583+
}
539584

540585
///////////////////////////////////////////////////////////////////////////
541586
// Statements
@@ -787,6 +832,7 @@ pub enum Rvalue<'tcx> {
787832
Cast(CastKind, Operand<'tcx>, Ty<'tcx>),
788833

789834
BinaryOp(BinOp, Operand<'tcx>, Operand<'tcx>),
835+
CheckedBinaryOp(BinOp, Operand<'tcx>, Operand<'tcx>),
790836

791837
UnaryOp(UnOp, Operand<'tcx>),
792838

@@ -880,6 +926,16 @@ pub enum BinOp {
880926
Gt,
881927
}
882928

929+
impl BinOp {
930+
pub fn is_checkable(self) -> bool {
931+
use self::BinOp::*;
932+
match self {
933+
Add | Sub | Mul | Shl | Shr => true,
934+
_ => false
935+
}
936+
}
937+
}
938+
883939
#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
884940
pub enum UnOp {
885941
/// The `!` operator for logical inversion
@@ -898,6 +954,9 @@ impl<'tcx> Debug for Rvalue<'tcx> {
898954
Len(ref a) => write!(fmt, "Len({:?})", a),
899955
Cast(ref kind, ref lv, ref ty) => write!(fmt, "{:?} as {:?} ({:?})", lv, ty, kind),
900956
BinaryOp(ref op, ref a, ref b) => write!(fmt, "{:?}({:?}, {:?})", op, a, b),
957+
CheckedBinaryOp(ref op, ref a, ref b) => {
958+
write!(fmt, "Checked{:?}({:?}, {:?})", op, a, b)
959+
}
901960
UnaryOp(ref op, ref a) => write!(fmt, "{:?}({:?})", op, a),
902961
Box(ref t) => write!(fmt, "Box({:?})", t),
903962
InlineAsm { ref asm, ref outputs, ref inputs } => {

src/librustc/mir/tcx.rs

+7
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,13 @@ impl<'a, 'gcx, 'tcx> Mir<'tcx> {
183183
let rhs_ty = self.operand_ty(tcx, rhs);
184184
Some(self.binop_ty(tcx, op, lhs_ty, rhs_ty))
185185
}
186+
Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) => {
187+
let lhs_ty = self.operand_ty(tcx, lhs);
188+
let rhs_ty = self.operand_ty(tcx, rhs);
189+
let ty = self.binop_ty(tcx, op, lhs_ty, rhs_ty);
190+
let ty = tcx.mk_tup(vec![ty, tcx.types.bool]);
191+
Some(ty)
192+
}
186193
Rvalue::UnaryOp(_, ref operand) => {
187194
Some(self.operand_ty(tcx, operand))
188195
}

src/librustc/mir/visit.rs

+33
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,11 @@ macro_rules! make_mir_visitor {
127127
self.super_terminator_kind(block, kind);
128128
}
129129

130+
fn visit_assert_message(&mut self,
131+
msg: & $($mutability)* AssertMessage<'tcx>) {
132+
self.super_assert_message(msg);
133+
}
134+
130135
fn visit_rvalue(&mut self,
131136
rvalue: & $($mutability)* Rvalue<'tcx>) {
132137
self.super_rvalue(rvalue);
@@ -426,6 +431,31 @@ macro_rules! make_mir_visitor {
426431
}
427432
cleanup.map(|t| self.visit_branch(block, t));
428433
}
434+
435+
TerminatorKind::Assert { ref $($mutability)* cond,
436+
expected: _,
437+
ref $($mutability)* msg,
438+
target,
439+
cleanup } => {
440+
self.visit_operand(cond);
441+
self.visit_assert_message(msg);
442+
self.visit_branch(block, target);
443+
cleanup.map(|t| self.visit_branch(block, t));
444+
}
445+
}
446+
}
447+
448+
fn super_assert_message(&mut self,
449+
msg: & $($mutability)* AssertMessage<'tcx>) {
450+
match *msg {
451+
AssertMessage::BoundsCheck {
452+
ref $($mutability)* len,
453+
ref $($mutability)* index
454+
} => {
455+
self.visit_operand(len);
456+
self.visit_operand(index);
457+
}
458+
AssertMessage::Math(_) => {}
429459
}
430460
}
431461

@@ -461,6 +491,9 @@ macro_rules! make_mir_visitor {
461491
}
462492

463493
Rvalue::BinaryOp(_bin_op,
494+
ref $($mutability)* lhs,
495+
ref $($mutability)* rhs) |
496+
Rvalue::CheckedBinaryOp(_bin_op,
464497
ref $($mutability)* lhs,
465498
ref $($mutability)* rhs) => {
466499
self.visit_operand(lhs);

src/librustc_borrowck/borrowck/mir/dataflow/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -450,13 +450,14 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D>
450450
repr::TerminatorKind::Return |
451451
repr::TerminatorKind::Resume => {}
452452
repr::TerminatorKind::Goto { ref target } |
453+
repr::TerminatorKind::Assert { ref target, cleanup: None, .. } |
453454
repr::TerminatorKind::Drop { ref target, location: _, unwind: None } |
454-
455455
repr::TerminatorKind::DropAndReplace {
456456
ref target, value: _, location: _, unwind: None
457457
} => {
458458
self.propagate_bits_into_entry_set_for(in_out, changed, target);
459459
}
460+
repr::TerminatorKind::Assert { ref target, cleanup: Some(ref unwind), .. } |
460461
repr::TerminatorKind::Drop { ref target, location: _, unwind: Some(ref unwind) } |
461462
repr::TerminatorKind::DropAndReplace {
462463
ref target, value: _, location: _, unwind: Some(ref unwind)

src/librustc_borrowck/borrowck/mir/gather_moves.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,8 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
595595
bb_ctxt.on_operand(SK::Repeat, operand, source),
596596
Rvalue::Cast(ref _kind, ref operand, ref _ty) =>
597597
bb_ctxt.on_operand(SK::Cast, operand, source),
598-
Rvalue::BinaryOp(ref _binop, ref operand1, ref operand2) => {
598+
Rvalue::BinaryOp(ref _binop, ref operand1, ref operand2) |
599+
Rvalue::CheckedBinaryOp(ref _binop, ref operand1, ref operand2) => {
599600
bb_ctxt.on_operand(SK::BinaryOp, operand1, source);
600601
bb_ctxt.on_operand(SK::BinaryOp, operand2, source);
601602
}
@@ -662,6 +663,22 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
662663
bb_ctxt.on_operand(SK::If, cond, source);
663664
}
664665

666+
TerminatorKind::Assert {
667+
ref cond, expected: _,
668+
ref msg, target: _, cleanup: _
669+
} => {
670+
// The `cond` is always of (copyable) type `bool`,
671+
// so there will never be anything to move.
672+
let _ = cond;
673+
match *msg {
674+
AssertMessage:: BoundsCheck { ref len, ref index } => {
675+
// Same for the usize length and index in bounds-checking.
676+
let _ = (len, index);
677+
}
678+
AssertMessage::Math(_) => {}
679+
}
680+
}
681+
665682
TerminatorKind::SwitchInt { switch_ty: _, values: _, targets: _, ref discr } |
666683
TerminatorKind::Switch { adt_def: _, targets: _, ref discr } => {
667684
// The `discr` is not consumed; that is instead

0 commit comments

Comments
 (0)