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`
This commit is contained in:
commit
8cbffc5bcf
62 changed files with 1363 additions and 431 deletions
|
|
@ -172,6 +172,7 @@ pub trait Iterator {
|
|||
/// assert_eq!(a.iter().count(), 5);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
fn count(self) -> usize where Self: Sized {
|
||||
// Might overflow.
|
||||
|
|
|
|||
|
|
@ -510,6 +510,7 @@ impl<A, B> Iterator for Chain<A, B> where
|
|||
}
|
||||
|
||||
#[inline]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
fn count(self) -> usize {
|
||||
match self.state {
|
||||
ChainState::Both => self.a.count() + self.b.count(),
|
||||
|
|
@ -932,6 +933,7 @@ impl<I> Iterator for Enumerate<I> where I: Iterator {
|
|||
///
|
||||
/// Might panic if the index of the element overflows a `usize`.
|
||||
#[inline]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
fn next(&mut self) -> Option<(usize, <I as Iterator>::Item)> {
|
||||
self.iter.next().map(|a| {
|
||||
let ret = (self.count, a);
|
||||
|
|
@ -947,6 +949,7 @@ impl<I> Iterator for Enumerate<I> where I: Iterator {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
fn nth(&mut self, n: usize) -> Option<(usize, I::Item)> {
|
||||
self.iter.nth(n).map(|a| {
|
||||
let i = self.count + n;
|
||||
|
|
@ -1008,6 +1011,7 @@ impl<I: Iterator> Iterator for Peekable<I> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
fn count(self) -> usize {
|
||||
(if self.peeked.is_some() { 1 } else { 0 }) + self.iter.count()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1033,7 +1033,7 @@ macro_rules! int_impl {
|
|||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[inline]
|
||||
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
|
||||
#[rustc_inherit_overflow_checks]
|
||||
pub fn pow(self, mut exp: u32) -> Self {
|
||||
let mut base = self;
|
||||
let mut acc = Self::one();
|
||||
|
|
@ -1075,7 +1075,7 @@ macro_rules! int_impl {
|
|||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[inline]
|
||||
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
|
||||
#[rustc_inherit_overflow_checks]
|
||||
pub fn abs(self) -> Self {
|
||||
if self.is_negative() {
|
||||
// Note that the #[inline] above means that the overflow
|
||||
|
|
@ -2061,7 +2061,7 @@ macro_rules! uint_impl {
|
|||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[inline]
|
||||
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
|
||||
#[rustc_inherit_overflow_checks]
|
||||
pub fn pow(self, mut exp: u32) -> Self {
|
||||
let mut base = self;
|
||||
let mut acc = Self::one();
|
||||
|
|
|
|||
|
|
@ -208,6 +208,7 @@ macro_rules! add_impl {
|
|||
type Output = $t;
|
||||
|
||||
#[inline]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
fn add(self, other: $t) -> $t { self + other }
|
||||
}
|
||||
|
||||
|
|
@ -261,6 +262,7 @@ macro_rules! sub_impl {
|
|||
type Output = $t;
|
||||
|
||||
#[inline]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
fn sub(self, other: $t) -> $t { self - other }
|
||||
}
|
||||
|
||||
|
|
@ -314,6 +316,7 @@ macro_rules! mul_impl {
|
|||
type Output = $t;
|
||||
|
||||
#[inline]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
fn mul(self, other: $t) -> $t { self * other }
|
||||
}
|
||||
|
||||
|
|
@ -511,6 +514,7 @@ macro_rules! neg_impl_core {
|
|||
type Output = $t;
|
||||
|
||||
#[inline]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
fn neg(self) -> $t { let $id = self; $body }
|
||||
}
|
||||
|
||||
|
|
@ -788,6 +792,7 @@ macro_rules! shl_impl {
|
|||
type Output = $t;
|
||||
|
||||
#[inline]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
fn shl(self, other: $f) -> $t {
|
||||
self << other
|
||||
}
|
||||
|
|
@ -859,6 +864,7 @@ macro_rules! shr_impl {
|
|||
type Output = $t;
|
||||
|
||||
#[inline]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
fn shr(self, other: $f) -> $t {
|
||||
self >> other
|
||||
}
|
||||
|
|
@ -923,6 +929,7 @@ macro_rules! add_assign_impl {
|
|||
#[stable(feature = "op_assign_traits", since = "1.8.0")]
|
||||
impl AddAssign for $t {
|
||||
#[inline]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
fn add_assign(&mut self, other: $t) { *self += other }
|
||||
}
|
||||
)+)
|
||||
|
|
@ -967,6 +974,7 @@ macro_rules! sub_assign_impl {
|
|||
#[stable(feature = "op_assign_traits", since = "1.8.0")]
|
||||
impl SubAssign for $t {
|
||||
#[inline]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
fn sub_assign(&mut self, other: $t) { *self -= other }
|
||||
}
|
||||
)+)
|
||||
|
|
@ -1011,6 +1019,7 @@ macro_rules! mul_assign_impl {
|
|||
#[stable(feature = "op_assign_traits", since = "1.8.0")]
|
||||
impl MulAssign for $t {
|
||||
#[inline]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
fn mul_assign(&mut self, other: $t) { *self *= other }
|
||||
}
|
||||
)+)
|
||||
|
|
@ -1275,6 +1284,7 @@ macro_rules! shl_assign_impl {
|
|||
#[stable(feature = "op_assign_traits", since = "1.8.0")]
|
||||
impl ShlAssign<$f> for $t {
|
||||
#[inline]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
fn shl_assign(&mut self, other: $f) {
|
||||
*self <<= other
|
||||
}
|
||||
|
|
@ -1337,6 +1347,7 @@ macro_rules! shr_assign_impl {
|
|||
#[stable(feature = "op_assign_traits", since = "1.8.0")]
|
||||
impl ShrAssign<$f> for $t {
|
||||
#[inline]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
fn shr_assign(&mut self, other: $f) {
|
||||
*self >>= other
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
use graphviz::IntoCow;
|
||||
use middle::const_val::ConstVal;
|
||||
use rustc_const_math::{ConstUsize, ConstInt};
|
||||
use rustc_const_math::{ConstUsize, ConstInt, ConstMathErr};
|
||||
use hir::def_id::DefId;
|
||||
use ty::subst::Substs;
|
||||
use ty::{self, AdtDef, ClosureSubsts, FnOutput, Region, Ty};
|
||||
|
|
@ -354,6 +354,16 @@ pub enum TerminatorKind<'tcx> {
|
|||
/// Cleanups to be done if the call unwinds.
|
||||
cleanup: Option<BasicBlock>
|
||||
},
|
||||
|
||||
/// Jump to the target if the condition has the expected value,
|
||||
/// otherwise panic with a message and a cleanup target.
|
||||
Assert {
|
||||
cond: Operand<'tcx>,
|
||||
expected: bool,
|
||||
msg: AssertMessage<'tcx>,
|
||||
target: BasicBlock,
|
||||
cleanup: Option<BasicBlock>
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Terminator<'tcx> {
|
||||
|
|
@ -389,6 +399,8 @@ impl<'tcx> TerminatorKind<'tcx> {
|
|||
Drop { ref target, unwind: None, .. } => {
|
||||
slice::ref_slice(target).into_cow()
|
||||
}
|
||||
Assert { target, cleanup: Some(unwind), .. } => vec![target, unwind].into_cow(),
|
||||
Assert { ref target, .. } => slice::ref_slice(target).into_cow(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -413,6 +425,8 @@ impl<'tcx> TerminatorKind<'tcx> {
|
|||
Drop { ref mut target, unwind: None, .. } => {
|
||||
vec![target]
|
||||
}
|
||||
Assert { ref mut target, cleanup: Some(ref mut unwind), .. } => vec![target, unwind],
|
||||
Assert { ref mut target, .. } => vec![target]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -495,6 +509,26 @@ impl<'tcx> TerminatorKind<'tcx> {
|
|||
}
|
||||
write!(fmt, ")")
|
||||
}
|
||||
Assert { ref cond, expected, ref msg, .. } => {
|
||||
write!(fmt, "assert(")?;
|
||||
if !expected {
|
||||
write!(fmt, "!")?;
|
||||
}
|
||||
write!(fmt, "{:?}, ", cond)?;
|
||||
|
||||
match *msg {
|
||||
AssertMessage::BoundsCheck { ref len, ref index } => {
|
||||
write!(fmt, "{:?}, {:?}, {:?}",
|
||||
"index out of bounds: the len is {} but the index is {}",
|
||||
len, index)?;
|
||||
}
|
||||
AssertMessage::Math(ref err) => {
|
||||
write!(fmt, "{:?}", err.description())?;
|
||||
}
|
||||
}
|
||||
|
||||
write!(fmt, ")")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -532,10 +566,21 @@ impl<'tcx> TerminatorKind<'tcx> {
|
|||
Drop { unwind: Some(_), .. } => {
|
||||
vec!["return".into_cow(), "unwind".into_cow()]
|
||||
}
|
||||
Assert { cleanup: None, .. } => vec!["".into()],
|
||||
Assert { .. } =>
|
||||
vec!["success".into_cow(), "unwind".into_cow()]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
|
||||
pub enum AssertMessage<'tcx> {
|
||||
BoundsCheck {
|
||||
len: Operand<'tcx>,
|
||||
index: Operand<'tcx>
|
||||
},
|
||||
Math(ConstMathErr)
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Statements
|
||||
|
|
@ -787,6 +832,7 @@ pub enum Rvalue<'tcx> {
|
|||
Cast(CastKind, Operand<'tcx>, Ty<'tcx>),
|
||||
|
||||
BinaryOp(BinOp, Operand<'tcx>, Operand<'tcx>),
|
||||
CheckedBinaryOp(BinOp, Operand<'tcx>, Operand<'tcx>),
|
||||
|
||||
UnaryOp(UnOp, Operand<'tcx>),
|
||||
|
||||
|
|
@ -880,6 +926,16 @@ pub enum BinOp {
|
|||
Gt,
|
||||
}
|
||||
|
||||
impl BinOp {
|
||||
pub fn is_checkable(self) -> bool {
|
||||
use self::BinOp::*;
|
||||
match self {
|
||||
Add | Sub | Mul | Shl | Shr => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
|
||||
pub enum UnOp {
|
||||
/// The `!` operator for logical inversion
|
||||
|
|
@ -898,6 +954,9 @@ impl<'tcx> Debug for Rvalue<'tcx> {
|
|||
Len(ref a) => write!(fmt, "Len({:?})", a),
|
||||
Cast(ref kind, ref lv, ref ty) => write!(fmt, "{:?} as {:?} ({:?})", lv, ty, kind),
|
||||
BinaryOp(ref op, ref a, ref b) => write!(fmt, "{:?}({:?}, {:?})", op, a, b),
|
||||
CheckedBinaryOp(ref op, ref a, ref b) => {
|
||||
write!(fmt, "Checked{:?}({:?}, {:?})", op, a, b)
|
||||
}
|
||||
UnaryOp(ref op, ref a) => write!(fmt, "{:?}({:?})", op, a),
|
||||
Box(ref t) => write!(fmt, "Box({:?})", t),
|
||||
InlineAsm { ref asm, ref outputs, ref inputs } => {
|
||||
|
|
|
|||
|
|
@ -183,6 +183,13 @@ impl<'a, 'gcx, 'tcx> Mir<'tcx> {
|
|||
let rhs_ty = self.operand_ty(tcx, rhs);
|
||||
Some(self.binop_ty(tcx, op, lhs_ty, rhs_ty))
|
||||
}
|
||||
Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) => {
|
||||
let lhs_ty = self.operand_ty(tcx, lhs);
|
||||
let rhs_ty = self.operand_ty(tcx, rhs);
|
||||
let ty = self.binop_ty(tcx, op, lhs_ty, rhs_ty);
|
||||
let ty = tcx.mk_tup(vec![ty, tcx.types.bool]);
|
||||
Some(ty)
|
||||
}
|
||||
Rvalue::UnaryOp(_, ref operand) => {
|
||||
Some(self.operand_ty(tcx, operand))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,6 +127,11 @@ macro_rules! make_mir_visitor {
|
|||
self.super_terminator_kind(block, kind);
|
||||
}
|
||||
|
||||
fn visit_assert_message(&mut self,
|
||||
msg: & $($mutability)* AssertMessage<'tcx>) {
|
||||
self.super_assert_message(msg);
|
||||
}
|
||||
|
||||
fn visit_rvalue(&mut self,
|
||||
rvalue: & $($mutability)* Rvalue<'tcx>) {
|
||||
self.super_rvalue(rvalue);
|
||||
|
|
@ -426,6 +431,31 @@ macro_rules! make_mir_visitor {
|
|||
}
|
||||
cleanup.map(|t| self.visit_branch(block, t));
|
||||
}
|
||||
|
||||
TerminatorKind::Assert { ref $($mutability)* cond,
|
||||
expected: _,
|
||||
ref $($mutability)* msg,
|
||||
target,
|
||||
cleanup } => {
|
||||
self.visit_operand(cond);
|
||||
self.visit_assert_message(msg);
|
||||
self.visit_branch(block, target);
|
||||
cleanup.map(|t| self.visit_branch(block, t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn super_assert_message(&mut self,
|
||||
msg: & $($mutability)* AssertMessage<'tcx>) {
|
||||
match *msg {
|
||||
AssertMessage::BoundsCheck {
|
||||
ref $($mutability)* len,
|
||||
ref $($mutability)* index
|
||||
} => {
|
||||
self.visit_operand(len);
|
||||
self.visit_operand(index);
|
||||
}
|
||||
AssertMessage::Math(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -461,6 +491,9 @@ macro_rules! make_mir_visitor {
|
|||
}
|
||||
|
||||
Rvalue::BinaryOp(_bin_op,
|
||||
ref $($mutability)* lhs,
|
||||
ref $($mutability)* rhs) |
|
||||
Rvalue::CheckedBinaryOp(_bin_op,
|
||||
ref $($mutability)* lhs,
|
||||
ref $($mutability)* rhs) => {
|
||||
self.visit_operand(lhs);
|
||||
|
|
|
|||
|
|
@ -450,13 +450,14 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D>
|
|||
repr::TerminatorKind::Return |
|
||||
repr::TerminatorKind::Resume => {}
|
||||
repr::TerminatorKind::Goto { ref target } |
|
||||
repr::TerminatorKind::Assert { ref target, cleanup: None, .. } |
|
||||
repr::TerminatorKind::Drop { ref target, location: _, unwind: None } |
|
||||
|
||||
repr::TerminatorKind::DropAndReplace {
|
||||
ref target, value: _, location: _, unwind: None
|
||||
} => {
|
||||
self.propagate_bits_into_entry_set_for(in_out, changed, target);
|
||||
}
|
||||
repr::TerminatorKind::Assert { ref target, cleanup: Some(ref unwind), .. } |
|
||||
repr::TerminatorKind::Drop { ref target, location: _, unwind: Some(ref unwind) } |
|
||||
repr::TerminatorKind::DropAndReplace {
|
||||
ref target, value: _, location: _, unwind: Some(ref unwind)
|
||||
|
|
|
|||
|
|
@ -595,7 +595,8 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
|
|||
bb_ctxt.on_operand(SK::Repeat, operand, source),
|
||||
Rvalue::Cast(ref _kind, ref operand, ref _ty) =>
|
||||
bb_ctxt.on_operand(SK::Cast, operand, source),
|
||||
Rvalue::BinaryOp(ref _binop, ref operand1, ref operand2) => {
|
||||
Rvalue::BinaryOp(ref _binop, ref operand1, ref operand2) |
|
||||
Rvalue::CheckedBinaryOp(ref _binop, ref operand1, ref operand2) => {
|
||||
bb_ctxt.on_operand(SK::BinaryOp, operand1, source);
|
||||
bb_ctxt.on_operand(SK::BinaryOp, operand2, source);
|
||||
}
|
||||
|
|
@ -662,6 +663,22 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
|
|||
bb_ctxt.on_operand(SK::If, cond, source);
|
||||
}
|
||||
|
||||
TerminatorKind::Assert {
|
||||
ref cond, expected: _,
|
||||
ref msg, target: _, cleanup: _
|
||||
} => {
|
||||
// The `cond` is always of (copyable) type `bool`,
|
||||
// so there will never be anything to move.
|
||||
let _ = cond;
|
||||
match *msg {
|
||||
AssertMessage:: BoundsCheck { ref len, ref index } => {
|
||||
// Same for the usize length and index in bounds-checking.
|
||||
let _ = (len, index);
|
||||
}
|
||||
AssertMessage::Math(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
TerminatorKind::SwitchInt { switch_ty: _, values: _, targets: _, ref discr } |
|
||||
TerminatorKind::Switch { adt_def: _, targets: _, ref discr } => {
|
||||
// The `discr` is not consumed; that is instead
|
||||
|
|
|
|||
|
|
@ -380,12 +380,6 @@ pub enum ErrKind {
|
|||
NotOn(ConstVal),
|
||||
CallOn(ConstVal),
|
||||
|
||||
DivideByZero,
|
||||
DivideWithOverflow,
|
||||
ModuloByZero,
|
||||
ModuloWithOverflow,
|
||||
ShiftLeftWithOverflow,
|
||||
ShiftRightWithOverflow,
|
||||
MissingStructField,
|
||||
NonConstPath,
|
||||
UnimplementedConstVal(&'static str),
|
||||
|
|
@ -396,7 +390,7 @@ pub enum ErrKind {
|
|||
IndexedNonVec,
|
||||
IndexNegative,
|
||||
IndexNotInt,
|
||||
IndexOutOfBounds,
|
||||
IndexOutOfBounds { len: u64, index: u64 },
|
||||
RepeatCountNotNatural,
|
||||
RepeatCountNotInt,
|
||||
|
||||
|
|
@ -436,12 +430,6 @@ impl ConstEvalErr {
|
|||
NotOn(ref const_val) => format!("not on {}", const_val.description()).into_cow(),
|
||||
CallOn(ref const_val) => format!("call on {}", const_val.description()).into_cow(),
|
||||
|
||||
DivideByZero => "attempted to divide by zero".into_cow(),
|
||||
DivideWithOverflow => "attempted to divide with overflow".into_cow(),
|
||||
ModuloByZero => "attempted remainder with a divisor of zero".into_cow(),
|
||||
ModuloWithOverflow => "attempted remainder with overflow".into_cow(),
|
||||
ShiftLeftWithOverflow => "attempted left shift with overflow".into_cow(),
|
||||
ShiftRightWithOverflow => "attempted right shift with overflow".into_cow(),
|
||||
MissingStructField => "nonexistent struct field".into_cow(),
|
||||
NonConstPath => "non-constant path in constant expression".into_cow(),
|
||||
UnimplementedConstVal(what) =>
|
||||
|
|
@ -453,7 +441,10 @@ impl ConstEvalErr {
|
|||
IndexedNonVec => "indexing is only supported for arrays".into_cow(),
|
||||
IndexNegative => "indices must be non-negative integers".into_cow(),
|
||||
IndexNotInt => "indices must be integers".into_cow(),
|
||||
IndexOutOfBounds => "array index out of bounds".into_cow(),
|
||||
IndexOutOfBounds { len, index } => {
|
||||
format!("index out of bounds: the len is {} but the index is {}",
|
||||
len, index).into_cow()
|
||||
}
|
||||
RepeatCountNotNatural => "repeat count must be a natural number".into_cow(),
|
||||
RepeatCountNotInt => "repeat count must be integers".into_cow(),
|
||||
|
||||
|
|
@ -847,7 +838,9 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|||
};
|
||||
assert_eq!(idx as usize as u64, idx);
|
||||
match arr {
|
||||
Array(_, n) if idx >= n => signal!(e, IndexOutOfBounds),
|
||||
Array(_, n) if idx >= n => {
|
||||
signal!(e, IndexOutOfBounds { len: n, index: idx })
|
||||
}
|
||||
Array(v, n) => if let hir::ExprVec(ref v) = tcx.map.expect_expr(v).node {
|
||||
assert_eq!(n as usize as u64, n);
|
||||
eval_const_expr_partial(tcx, &v[idx as usize], ty_hint, fn_args)?
|
||||
|
|
@ -855,7 +848,9 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|||
bug!()
|
||||
},
|
||||
|
||||
Repeat(_, n) if idx >= n => signal!(e, IndexOutOfBounds),
|
||||
Repeat(_, n) if idx >= n => {
|
||||
signal!(e, IndexOutOfBounds { len: n, index: idx })
|
||||
}
|
||||
Repeat(elem, _) => eval_const_expr_partial(
|
||||
tcx,
|
||||
&tcx.map.expect_expr(elem),
|
||||
|
|
@ -863,14 +858,13 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|||
fn_args,
|
||||
)?,
|
||||
|
||||
ByteStr(ref data) if idx >= data.len() as u64 => signal!(e, IndexOutOfBounds),
|
||||
ByteStr(ref data) if idx >= data.len() as u64 => {
|
||||
signal!(e, IndexOutOfBounds { len: data.len() as u64, index: idx })
|
||||
}
|
||||
ByteStr(data) => {
|
||||
Integral(U8(data[idx as usize]))
|
||||
},
|
||||
|
||||
Str(ref s) if idx as usize >= s.len() => signal!(e, IndexOutOfBounds),
|
||||
// FIXME: return a const char
|
||||
Str(_) => signal!(e, UnimplementedConstVal("indexing into str")),
|
||||
_ => signal!(e, IndexedNonVec),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
use syntax::ast;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, RustcEncodable, RustcDecodable)]
|
||||
pub enum ConstMathErr {
|
||||
NotInRange,
|
||||
CmpBetweenUnequalTypes,
|
||||
|
|
@ -25,7 +25,7 @@ pub enum ConstMathErr {
|
|||
}
|
||||
pub use self::ConstMathErr::*;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, RustcEncodable, RustcDecodable)]
|
||||
pub enum Op {
|
||||
Add,
|
||||
Sub,
|
||||
|
|
|
|||
|
|
@ -66,15 +66,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
|||
idx.clone(),
|
||||
Operand::Consume(len.clone())));
|
||||
|
||||
let (success, failure) = (this.cfg.start_new_block(), this.cfg.start_new_block());
|
||||
this.cfg.terminate(block,
|
||||
scope_id,
|
||||
expr_span,
|
||||
TerminatorKind::If {
|
||||
cond: Operand::Consume(lt),
|
||||
targets: (success, failure),
|
||||
});
|
||||
this.panic_bounds_check(failure, idx.clone(), Operand::Consume(len), expr_span);
|
||||
let msg = AssertMessage::BoundsCheck {
|
||||
len: Operand::Consume(len),
|
||||
index: idx.clone()
|
||||
};
|
||||
let success = this.assert(block, Operand::Consume(lt), true,
|
||||
msg, expr_span);
|
||||
success.and(slice.index(idx))
|
||||
}
|
||||
ExprKind::SelfRef => {
|
||||
|
|
|
|||
|
|
@ -10,12 +10,20 @@
|
|||
|
||||
//! See docs in build/expr/mod.rs
|
||||
|
||||
use std;
|
||||
|
||||
use rustc_const_math::{ConstMathErr, Op};
|
||||
use rustc_data_structures::fnv::FnvHashMap;
|
||||
|
||||
use build::{BlockAnd, BlockAndExtension, Builder};
|
||||
use build::expr::category::{Category, RvalueFunc};
|
||||
use hair::*;
|
||||
use rustc_const_math::{ConstInt, ConstIsize};
|
||||
use rustc::middle::const_val::ConstVal;
|
||||
use rustc::ty;
|
||||
use rustc::mir::repr::*;
|
||||
use syntax::ast;
|
||||
use syntax::codemap::Span;
|
||||
|
||||
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
/// Compile `expr`, yielding an rvalue.
|
||||
|
|
@ -66,10 +74,25 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
|||
ExprKind::Binary { op, lhs, rhs } => {
|
||||
let lhs = unpack!(block = this.as_operand(block, lhs));
|
||||
let rhs = unpack!(block = this.as_operand(block, rhs));
|
||||
block.and(Rvalue::BinaryOp(op, lhs, rhs))
|
||||
this.build_binary_op(block, op, expr_span, expr.ty,
|
||||
lhs, rhs)
|
||||
}
|
||||
ExprKind::Unary { op, arg } => {
|
||||
let arg = unpack!(block = this.as_operand(block, arg));
|
||||
// Check for -MIN on signed integers
|
||||
if this.hir.check_overflow() && op == UnOp::Neg && expr.ty.is_signed() {
|
||||
let bool_ty = this.hir.bool_ty();
|
||||
|
||||
let minval = this.minval_literal(expr_span, expr.ty);
|
||||
let is_min = this.temp(bool_ty);
|
||||
|
||||
this.cfg.push_assign(block, scope_id, expr_span, &is_min,
|
||||
Rvalue::BinaryOp(BinOp::Eq, arg.clone(), minval));
|
||||
|
||||
let err = ConstMathErr::Overflow(Op::Neg);
|
||||
block = this.assert(block, Operand::Consume(is_min), false,
|
||||
AssertMessage::Math(err), expr_span);
|
||||
}
|
||||
block.and(Rvalue::UnaryOp(op, arg))
|
||||
}
|
||||
ExprKind::Box { value, value_extents } => {
|
||||
|
|
@ -218,4 +241,148 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_binary_op(&mut self, mut block: BasicBlock,
|
||||
op: BinOp, span: Span, ty: ty::Ty<'tcx>,
|
||||
lhs: Operand<'tcx>, rhs: Operand<'tcx>) -> BlockAnd<Rvalue<'tcx>> {
|
||||
let scope_id = self.innermost_scope_id();
|
||||
let bool_ty = self.hir.bool_ty();
|
||||
if self.hir.check_overflow() && op.is_checkable() && ty.is_integral() {
|
||||
let result_tup = self.hir.tcx().mk_tup(vec![ty, bool_ty]);
|
||||
let result_value = self.temp(result_tup);
|
||||
|
||||
self.cfg.push_assign(block, scope_id, span,
|
||||
&result_value, Rvalue::CheckedBinaryOp(op,
|
||||
lhs,
|
||||
rhs));
|
||||
let val_fld = Field::new(0);
|
||||
let of_fld = Field::new(1);
|
||||
|
||||
let val = result_value.clone().field(val_fld, ty);
|
||||
let of = result_value.field(of_fld, bool_ty);
|
||||
|
||||
let err = ConstMathErr::Overflow(match op {
|
||||
BinOp::Add => Op::Add,
|
||||
BinOp::Sub => Op::Sub,
|
||||
BinOp::Mul => Op::Mul,
|
||||
BinOp::Shl => Op::Shl,
|
||||
BinOp::Shr => Op::Shr,
|
||||
_ => {
|
||||
bug!("MIR build_binary_op: {:?} is not checkable", op)
|
||||
}
|
||||
});
|
||||
|
||||
block = self.assert(block, Operand::Consume(of), false,
|
||||
AssertMessage::Math(err), span);
|
||||
|
||||
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
|
||||
// and 2. there are two possible failure cases, divide-by-zero and overflow.
|
||||
|
||||
let (zero_err, overflow_err) = if op == BinOp::Div {
|
||||
(ConstMathErr::DivisionByZero,
|
||||
ConstMathErr::Overflow(Op::Div))
|
||||
} else {
|
||||
(ConstMathErr::RemainderByZero,
|
||||
ConstMathErr::Overflow(Op::Rem))
|
||||
};
|
||||
|
||||
// Check for / 0
|
||||
let is_zero = self.temp(bool_ty);
|
||||
let zero = self.zero_literal(span, ty);
|
||||
self.cfg.push_assign(block, scope_id, span, &is_zero,
|
||||
Rvalue::BinaryOp(BinOp::Eq, rhs.clone(), zero));
|
||||
|
||||
block = self.assert(block, Operand::Consume(is_zero), false,
|
||||
AssertMessage::Math(zero_err), span);
|
||||
|
||||
// We only need to check for the overflow in one case:
|
||||
// MIN / -1, and only for signed values.
|
||||
if ty.is_signed() {
|
||||
let neg_1 = self.neg_1_literal(span, ty);
|
||||
let min = self.minval_literal(span, ty);
|
||||
|
||||
let is_neg_1 = self.temp(bool_ty);
|
||||
let is_min = self.temp(bool_ty);
|
||||
let of = self.temp(bool_ty);
|
||||
|
||||
// this does (rhs == -1) & (lhs == MIN). It could short-circuit instead
|
||||
|
||||
self.cfg.push_assign(block, scope_id, span, &is_neg_1,
|
||||
Rvalue::BinaryOp(BinOp::Eq, rhs.clone(), neg_1));
|
||||
self.cfg.push_assign(block, scope_id, span, &is_min,
|
||||
Rvalue::BinaryOp(BinOp::Eq, lhs.clone(), min));
|
||||
|
||||
let is_neg_1 = Operand::Consume(is_neg_1);
|
||||
let is_min = Operand::Consume(is_min);
|
||||
self.cfg.push_assign(block, scope_id, span, &of,
|
||||
Rvalue::BinaryOp(BinOp::BitAnd, is_neg_1, is_min));
|
||||
|
||||
block = self.assert(block, Operand::Consume(of), false,
|
||||
AssertMessage::Math(overflow_err), span);
|
||||
}
|
||||
}
|
||||
|
||||
block.and(Rvalue::BinaryOp(op, lhs, rhs))
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to get a `-1` value of the appropriate type
|
||||
fn neg_1_literal(&mut self, span: Span, ty: ty::Ty<'tcx>) -> Operand<'tcx> {
|
||||
let literal = match ty.sty {
|
||||
ty::TyInt(ity) => {
|
||||
let val = match ity {
|
||||
ast::IntTy::I8 => ConstInt::I8(-1),
|
||||
ast::IntTy::I16 => ConstInt::I16(-1),
|
||||
ast::IntTy::I32 => ConstInt::I32(-1),
|
||||
ast::IntTy::I64 => ConstInt::I64(-1),
|
||||
ast::IntTy::Is => {
|
||||
let int_ty = self.hir.tcx().sess.target.int_type;
|
||||
let val = ConstIsize::new(-1, int_ty).unwrap();
|
||||
ConstInt::Isize(val)
|
||||
}
|
||||
};
|
||||
|
||||
Literal::Value { value: ConstVal::Integral(val) }
|
||||
}
|
||||
_ => {
|
||||
span_bug!(span, "Invalid type for neg_1_literal: `{:?}`", ty)
|
||||
}
|
||||
};
|
||||
|
||||
self.literal_operand(span, ty, literal)
|
||||
}
|
||||
|
||||
// Helper to get the minimum value of the appropriate type
|
||||
fn minval_literal(&mut self, span: Span, ty: ty::Ty<'tcx>) -> Operand<'tcx> {
|
||||
let literal = match ty.sty {
|
||||
ty::TyInt(ity) => {
|
||||
let val = match ity {
|
||||
ast::IntTy::I8 => ConstInt::I8(std::i8::MIN),
|
||||
ast::IntTy::I16 => ConstInt::I16(std::i16::MIN),
|
||||
ast::IntTy::I32 => ConstInt::I32(std::i32::MIN),
|
||||
ast::IntTy::I64 => ConstInt::I64(std::i64::MIN),
|
||||
ast::IntTy::Is => {
|
||||
let int_ty = self.hir.tcx().sess.target.int_type;
|
||||
let min = match int_ty {
|
||||
ast::IntTy::I32 => std::i32::MIN as i64,
|
||||
ast::IntTy::I64 => std::i64::MIN,
|
||||
_ => unreachable!()
|
||||
};
|
||||
let val = ConstIsize::new(min, int_ty).unwrap();
|
||||
ConstInt::Isize(val)
|
||||
}
|
||||
};
|
||||
|
||||
Literal::Value { value: ConstVal::Integral(val) }
|
||||
}
|
||||
_ => {
|
||||
span_bug!(span, "Invalid type for minval_literal: `{:?}`", ty)
|
||||
}
|
||||
};
|
||||
|
||||
self.literal_operand(span, ty, literal)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,6 +63,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
|||
// only affects weird things like `x += {x += 1; x}`
|
||||
// -- is that equal to `x + (x + 1)` or `2*(x+1)`?
|
||||
|
||||
let lhs = this.hir.mirror(lhs);
|
||||
let lhs_ty = lhs.ty;
|
||||
|
||||
// As above, RTL.
|
||||
let rhs = unpack!(block = this.as_operand(block, rhs));
|
||||
let lhs = unpack!(block = this.as_lvalue(block, lhs));
|
||||
|
|
@ -70,10 +73,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
|||
// we don't have to drop prior contents or anything
|
||||
// because AssignOp is only legal for Copy types
|
||||
// (overloaded ops should be desugared into a call).
|
||||
this.cfg.push_assign(block, scope_id, expr_span, &lhs,
|
||||
Rvalue::BinaryOp(op,
|
||||
Operand::Consume(lhs.clone()),
|
||||
rhs));
|
||||
let result = unpack!(block = this.build_binary_op(block, op, expr_span, lhs_ty,
|
||||
Operand::Consume(lhs.clone()), rhs));
|
||||
this.cfg.push_assign(block, scope_id, expr_span, &lhs, result);
|
||||
|
||||
block.unit()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,9 +12,14 @@
|
|||
//! kind of thing.
|
||||
|
||||
use build::Builder;
|
||||
use rustc::ty::Ty;
|
||||
|
||||
use rustc_const_math::{ConstInt, ConstUsize, ConstIsize};
|
||||
use rustc::middle::const_val::ConstVal;
|
||||
use rustc::ty::{self, Ty};
|
||||
|
||||
use rustc::mir::repr::*;
|
||||
use std::u32;
|
||||
use syntax::ast;
|
||||
use syntax::codemap::Span;
|
||||
|
||||
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
|
|
@ -50,6 +55,53 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
|||
Rvalue::Aggregate(AggregateKind::Tuple, vec![])
|
||||
}
|
||||
|
||||
// Returns a zero literal operand for the appropriate type, works for
|
||||
// bool, char, integers and floats.
|
||||
pub fn zero_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> {
|
||||
let literal = match ty.sty {
|
||||
ty::TyBool => {
|
||||
self.hir.false_literal()
|
||||
}
|
||||
ty::TyChar => Literal::Value { value: ConstVal::Char('\0') },
|
||||
ty::TyUint(ity) => {
|
||||
let val = match ity {
|
||||
ast::UintTy::U8 => ConstInt::U8(0),
|
||||
ast::UintTy::U16 => ConstInt::U16(0),
|
||||
ast::UintTy::U32 => ConstInt::U32(0),
|
||||
ast::UintTy::U64 => ConstInt::U64(0),
|
||||
ast::UintTy::Us => {
|
||||
let uint_ty = self.hir.tcx().sess.target.uint_type;
|
||||
let val = ConstUsize::new(0, uint_ty).unwrap();
|
||||
ConstInt::Usize(val)
|
||||
}
|
||||
};
|
||||
|
||||
Literal::Value { value: ConstVal::Integral(val) }
|
||||
}
|
||||
ty::TyInt(ity) => {
|
||||
let val = match ity {
|
||||
ast::IntTy::I8 => ConstInt::I8(0),
|
||||
ast::IntTy::I16 => ConstInt::I16(0),
|
||||
ast::IntTy::I32 => ConstInt::I32(0),
|
||||
ast::IntTy::I64 => ConstInt::I64(0),
|
||||
ast::IntTy::Is => {
|
||||
let int_ty = self.hir.tcx().sess.target.int_type;
|
||||
let val = ConstIsize::new(0, int_ty).unwrap();
|
||||
ConstInt::Isize(val)
|
||||
}
|
||||
};
|
||||
|
||||
Literal::Value { value: ConstVal::Integral(val) }
|
||||
}
|
||||
ty::TyFloat(_) => Literal::Value { value: ConstVal::Float(0.0) },
|
||||
_ => {
|
||||
span_bug!(span, "Invalid type for zero_literal: `{:?}`", ty)
|
||||
}
|
||||
};
|
||||
|
||||
self.literal_operand(span, ty, literal)
|
||||
}
|
||||
|
||||
pub fn push_usize(&mut self,
|
||||
block: BasicBlock,
|
||||
scope_id: ScopeId,
|
||||
|
|
|
|||
|
|
@ -517,7 +517,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
|
||||
|
||||
|
||||
pub fn build_drop_and_replace(&mut self,
|
||||
block: BasicBlock,
|
||||
span: Span,
|
||||
|
|
@ -538,48 +537,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
|||
next_target.unit()
|
||||
}
|
||||
|
||||
// Panicking
|
||||
// =========
|
||||
// FIXME: should be moved into their own module
|
||||
pub fn panic_bounds_check(&mut self,
|
||||
block: BasicBlock,
|
||||
index: Operand<'tcx>,
|
||||
len: Operand<'tcx>,
|
||||
span: Span) {
|
||||
// fn(&(filename: &'static str, line: u32), index: usize, length: usize) -> !
|
||||
let region = ty::ReStatic; // FIXME(mir-borrowck): use a better region?
|
||||
let func = self.lang_function(lang_items::PanicBoundsCheckFnLangItem);
|
||||
let args = self.hir.tcx().replace_late_bound_regions(&func.ty.fn_args(), |_| region).0;
|
||||
|
||||
let ref_ty = args[0];
|
||||
let tup_ty = if let ty::TyRef(_, tyandmut) = ref_ty.sty {
|
||||
tyandmut.ty
|
||||
} else {
|
||||
span_bug!(span, "unexpected panic_bound_check type: {:?}", func.ty);
|
||||
};
|
||||
|
||||
let (tuple, tuple_ref) = (self.temp(tup_ty), self.temp(ref_ty));
|
||||
let (file, line) = self.span_to_fileline_args(span);
|
||||
let elems = vec![Operand::Constant(file), Operand::Constant(line)];
|
||||
let scope_id = self.innermost_scope_id();
|
||||
// FIXME: We should have this as a constant, rather than a stack variable (to not pollute
|
||||
// icache with cold branch code), however to achieve that we either have to rely on rvalue
|
||||
// promotion or have some way, in MIR, to create constants.
|
||||
self.cfg.push_assign(block, scope_id, span, &tuple, // tuple = (file_arg, line_arg);
|
||||
Rvalue::Aggregate(AggregateKind::Tuple, elems));
|
||||
// FIXME: is this region really correct here?
|
||||
self.cfg.push_assign(block, scope_id, span, &tuple_ref, // tuple_ref = &tuple;
|
||||
Rvalue::Ref(region, BorrowKind::Shared, tuple));
|
||||
let cleanup = self.diverge_cleanup();
|
||||
self.cfg.terminate(block, scope_id, span, TerminatorKind::Call {
|
||||
func: Operand::Constant(func),
|
||||
args: vec![Operand::Consume(tuple_ref), index, len],
|
||||
destination: None,
|
||||
cleanup: cleanup,
|
||||
});
|
||||
}
|
||||
|
||||
/// Create diverge cleanup and branch to it from `block`.
|
||||
// FIXME: Remove this (used only for unreachable cases in match).
|
||||
pub fn panic(&mut self, block: BasicBlock, msg: &'static str, span: Span) {
|
||||
// fn(&(msg: &'static str filename: &'static str, line: u32)) -> !
|
||||
let region = ty::ReStatic; // FIXME(mir-borrowck): use a better region?
|
||||
|
|
@ -622,6 +581,32 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
|||
});
|
||||
}
|
||||
|
||||
/// Create an Assert terminator and return the success block.
|
||||
/// If the boolean condition operand is not the expected value,
|
||||
/// a runtime panic will be caused with the given message.
|
||||
pub fn assert(&mut self, block: BasicBlock,
|
||||
cond: Operand<'tcx>,
|
||||
expected: bool,
|
||||
msg: AssertMessage<'tcx>,
|
||||
span: Span)
|
||||
-> BasicBlock {
|
||||
let scope_id = self.innermost_scope_id();
|
||||
|
||||
let success_block = self.cfg.start_new_block();
|
||||
let cleanup = self.diverge_cleanup();
|
||||
|
||||
self.cfg.terminate(block, scope_id, span,
|
||||
TerminatorKind::Assert {
|
||||
cond: cond,
|
||||
expected: expected,
|
||||
msg: msg,
|
||||
target: success_block,
|
||||
cleanup: cleanup
|
||||
});
|
||||
|
||||
success_block
|
||||
}
|
||||
|
||||
fn lang_function(&mut self, lang_item: lang_items::LangItem) -> Constant<'tcx> {
|
||||
let funcdid = match self.hir.tcx().lang_items.require(lang_item) {
|
||||
Ok(d) => d,
|
||||
|
|
|
|||
|
|
@ -17,32 +17,70 @@
|
|||
|
||||
use hair::*;
|
||||
use rustc::mir::repr::*;
|
||||
use rustc::mir::transform::MirSource;
|
||||
|
||||
use rustc::middle::const_val::ConstVal;
|
||||
use rustc_const_eval as const_eval;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::hir::intravisit::FnKind;
|
||||
use rustc::hir::map::blocks::FnLikeNode;
|
||||
use rustc::infer::InferCtxt;
|
||||
use rustc::ty::subst::{Subst, Substs};
|
||||
use rustc::ty::{self, Ty, TyCtxt};
|
||||
use syntax::parse::token;
|
||||
use rustc::hir;
|
||||
use rustc_const_math::{ConstInt, ConstUsize};
|
||||
use syntax::attr::AttrMetaMethods;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Cx<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
|
||||
constness: hir::Constness
|
||||
constness: hir::Constness,
|
||||
|
||||
/// True if this constant/function needs overflow checks.
|
||||
check_overflow: bool
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
|
||||
pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
|
||||
constness: hir::Constness)
|
||||
src: MirSource)
|
||||
-> Cx<'a, 'gcx, 'tcx> {
|
||||
let constness = match src {
|
||||
MirSource::Const(_) |
|
||||
MirSource::Static(..) => hir::Constness::Const,
|
||||
MirSource::Fn(id) => {
|
||||
let fn_like = FnLikeNode::from_node(infcx.tcx.map.get(id));
|
||||
match fn_like.map(|f| f.kind()) {
|
||||
Some(FnKind::ItemFn(_, _, _, c, _, _, _)) => c,
|
||||
Some(FnKind::Method(_, m, _, _)) => m.constness,
|
||||
_ => hir::Constness::NotConst
|
||||
}
|
||||
}
|
||||
MirSource::Promoted(..) => bug!()
|
||||
};
|
||||
|
||||
let attrs = infcx.tcx.map.attrs(src.item_id());
|
||||
|
||||
// Some functions always have overflow checks enabled,
|
||||
// however, they may not get codegen'd, depending on
|
||||
// the settings for the crate they are translated in.
|
||||
let mut check_overflow = attrs.iter().any(|item| {
|
||||
item.check_name("rustc_inherit_overflow_checks")
|
||||
});
|
||||
|
||||
// Respect -Z force-overflow-checks=on and -C debug-assertions.
|
||||
check_overflow |= infcx.tcx.sess.opts.debugging_opts.force_overflow_checks
|
||||
.unwrap_or(infcx.tcx.sess.opts.debug_assertions);
|
||||
|
||||
// Constants and const fn's always need overflow checks.
|
||||
check_overflow |= constness == hir::Constness::Const;
|
||||
|
||||
Cx {
|
||||
tcx: infcx.tcx,
|
||||
infcx: infcx,
|
||||
constness: constness,
|
||||
check_overflow: check_overflow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -154,6 +192,10 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
|
|||
pub fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
pub fn check_overflow(&self) -> bool {
|
||||
self.check_overflow
|
||||
}
|
||||
}
|
||||
|
||||
mod block;
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ use rustc::ty::subst::Substs;
|
|||
use rustc::util::nodemap::NodeMap;
|
||||
use rustc::hir;
|
||||
use rustc::hir::intravisit::{self, FnKind, Visitor};
|
||||
use rustc::hir::map::blocks::FnLikeNode;
|
||||
use syntax::ast;
|
||||
use syntax::codemap::Span;
|
||||
|
||||
|
|
@ -116,20 +115,7 @@ impl<'a, 'gcx, 'tcx> CxBuilder<'a, 'gcx, 'tcx> {
|
|||
{
|
||||
let src = self.src;
|
||||
let mir = self.infcx.enter(|infcx| {
|
||||
let constness = match src {
|
||||
MirSource::Const(_) |
|
||||
MirSource::Static(..) => hir::Constness::Const,
|
||||
MirSource::Fn(id) => {
|
||||
let fn_like = FnLikeNode::from_node(infcx.tcx.map.get(id));
|
||||
match fn_like.map(|f| f.kind()) {
|
||||
Some(FnKind::ItemFn(_, _, _, c, _, _, _)) => c,
|
||||
Some(FnKind::Method(_, m, _, _)) => m.constness,
|
||||
_ => hir::Constness::NotConst
|
||||
}
|
||||
}
|
||||
MirSource::Promoted(..) => bug!()
|
||||
};
|
||||
let (mut mir, scope_auxiliary) = f(Cx::new(&infcx, constness));
|
||||
let (mut mir, scope_auxiliary) = f(Cx::new(&infcx, src));
|
||||
|
||||
// Convert the Mir to global types.
|
||||
let mut globalizer = GlobalizeMir {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads {
|
|||
/* nothing to do */
|
||||
},
|
||||
TerminatorKind::Call { cleanup: ref mut unwind, .. } |
|
||||
TerminatorKind::Assert { cleanup: ref mut unwind, .. } |
|
||||
TerminatorKind::DropAndReplace { ref mut unwind, .. } |
|
||||
TerminatorKind::Drop { ref mut unwind, .. } => {
|
||||
unwind.take();
|
||||
|
|
|
|||
|
|
@ -332,61 +332,6 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns true if the block ends in a bounds check branch, i.e.:
|
||||
/// len = Len(array);
|
||||
/// cond = Lt(idx, len);
|
||||
/// if cond {
|
||||
/// ...
|
||||
/// } else {
|
||||
/// loc = (...);
|
||||
/// loc_ref = &loc;
|
||||
/// panic_bounds_check(loc_ref, idx, len);
|
||||
/// }
|
||||
fn is_bounds_check(&self, bb: BasicBlock,
|
||||
cond_op: &Operand<'tcx>,
|
||||
if_else: BasicBlock) -> bool {
|
||||
use rustc::mir::repr::Lvalue::*;
|
||||
use rustc::mir::repr::Operand::Consume;
|
||||
use rustc::mir::repr::Rvalue::*;
|
||||
use rustc::mir::repr::StatementKind::*;
|
||||
use rustc::mir::repr::TerminatorKind::*;
|
||||
|
||||
let stmts = &self.mir[bb].statements;
|
||||
let stmts_panic = &self.mir[if_else].statements;
|
||||
if stmts.len() < 2 || stmts_panic.len() != 2 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let all = (&stmts[stmts.len() - 2].kind,
|
||||
&stmts[stmts.len() - 1].kind,
|
||||
cond_op,
|
||||
&stmts_panic[0].kind,
|
||||
&stmts_panic[1].kind,
|
||||
&self.mir[if_else].terminator().kind);
|
||||
match all {
|
||||
(&Assign(Temp(len), Len(_)),
|
||||
&Assign(Temp(cond), BinaryOp(BinOp::Lt, ref idx, Consume(Temp(len2)))),
|
||||
/* if */ &Consume(Temp(cond2)), /* {...} else */
|
||||
&Assign(Temp(loc), Aggregate(..)),
|
||||
&Assign(Temp(loc_ref), Ref(_, _, Temp(loc2))),
|
||||
&Call {
|
||||
func: Operand::Constant(Constant {
|
||||
literal: Literal::Item { def_id, .. }, ..
|
||||
}),
|
||||
ref args,
|
||||
destination: None,
|
||||
..
|
||||
}) => {
|
||||
len == len2 && cond == cond2 && loc == loc2 &&
|
||||
args[0] == Consume(Temp(loc_ref)) &&
|
||||
args[1] == *idx &&
|
||||
args[2] == Consume(Temp(len)) &&
|
||||
Some(def_id) == self.tcx.lang_items.panic_bounds_check_fn()
|
||||
}
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
/// Qualify a whole const, static initializer or const fn.
|
||||
fn qualify_const(&mut self) -> Qualif {
|
||||
let mir = self.mir;
|
||||
|
|
@ -402,6 +347,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
|
|||
TerminatorKind::Goto { target } |
|
||||
// Drops are considered noops.
|
||||
TerminatorKind::Drop { target, .. } |
|
||||
TerminatorKind::Assert { target, .. } |
|
||||
TerminatorKind::Call { destination: Some((_, target)), .. } => {
|
||||
Some(target)
|
||||
}
|
||||
|
|
@ -411,15 +357,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
|
|||
return Qualif::empty();
|
||||
}
|
||||
|
||||
// Need to allow bounds checking branches.
|
||||
TerminatorKind::If { ref cond, targets: (if_true, if_else) } => {
|
||||
if self.is_bounds_check(bb, cond, if_else) {
|
||||
Some(if_true)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
TerminatorKind::If {..} |
|
||||
TerminatorKind::Switch {..} |
|
||||
TerminatorKind::SwitchInt {..} |
|
||||
TerminatorKind::DropAndReplace { .. } |
|
||||
|
|
@ -630,6 +568,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
|
|||
Rvalue::Use(_) |
|
||||
Rvalue::Repeat(..) |
|
||||
Rvalue::UnaryOp(..) |
|
||||
Rvalue::CheckedBinaryOp(..) |
|
||||
Rvalue::Cast(CastKind::ReifyFnPointer, _, _) |
|
||||
Rvalue::Cast(CastKind::UnsafeFnPointer, _, _) |
|
||||
Rvalue::Cast(CastKind::Unsize, _, _) => {}
|
||||
|
|
|
|||
|
|
@ -181,7 +181,17 @@ fn simplify_branches(mir: &mut Mir) {
|
|||
}
|
||||
}
|
||||
|
||||
TerminatorKind::Assert { target, cond: Operand::Constant(Constant {
|
||||
literal: Literal::Value {
|
||||
value: ConstVal::Bool(cond)
|
||||
}, ..
|
||||
}), expected, .. } if cond == expected => {
|
||||
changed = true;
|
||||
TerminatorKind::Goto { target: target }
|
||||
}
|
||||
|
||||
TerminatorKind::SwitchInt { ref targets, .. } if targets.len() == 1 => {
|
||||
changed = true;
|
||||
TerminatorKind::Goto { target: targets[0] }
|
||||
}
|
||||
_ => continue
|
||||
|
|
|
|||
|
|
@ -431,6 +431,21 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
self.check_call_inputs(mir, term, &sig, args);
|
||||
}
|
||||
}
|
||||
TerminatorKind::Assert { ref cond, ref msg, .. } => {
|
||||
let cond_ty = mir.operand_ty(tcx, cond);
|
||||
if cond_ty != tcx.types.bool {
|
||||
span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty);
|
||||
}
|
||||
|
||||
if let AssertMessage::BoundsCheck { ref len, ref index } = *msg {
|
||||
if mir.operand_ty(tcx, len) != tcx.types.usize {
|
||||
span_mirbug!(self, len, "bounds-check length non-usize {:?}", len)
|
||||
}
|
||||
if mir.operand_ty(tcx, index) != tcx.types.usize {
|
||||
span_mirbug!(self, index, "bounds-check index non-usize {:?}", index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -561,7 +576,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
TerminatorKind::Drop { target, unwind, .. } |
|
||||
TerminatorKind::DropAndReplace { target, unwind, .. } => {
|
||||
TerminatorKind::DropAndReplace { target, unwind, .. } |
|
||||
TerminatorKind::Assert { target, cleanup: unwind, .. } => {
|
||||
self.assert_iscleanup(mir, block, target, is_cleanup);
|
||||
if let Some(unwind) = unwind {
|
||||
if is_cleanup {
|
||||
|
|
|
|||
|
|
@ -880,7 +880,7 @@ fn compare_values<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
|
|||
rhs_t: Ty<'tcx>,
|
||||
debug_loc: DebugLoc)
|
||||
-> Result<'blk, 'tcx> {
|
||||
let did = langcall(bcx,
|
||||
let did = langcall(bcx.tcx(),
|
||||
None,
|
||||
&format!("comparison of `{}`", rhs_t),
|
||||
StrEqFnLangItem);
|
||||
|
|
|
|||
|
|
@ -769,12 +769,12 @@ pub fn fail_if_zero_or_overflows<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
|
|||
rhs: ValueRef,
|
||||
rhs_t: Ty<'tcx>)
|
||||
-> Block<'blk, 'tcx> {
|
||||
let (zero_text, overflow_text) = if divrem.node == hir::BiDiv {
|
||||
("attempted to divide by zero",
|
||||
"attempted to divide with overflow")
|
||||
use rustc_const_math::{ConstMathErr, Op};
|
||||
|
||||
let (zero_err, overflow_err) = if divrem.node == hir::BiDiv {
|
||||
(ConstMathErr::DivisionByZero, ConstMathErr::Overflow(Op::Div))
|
||||
} else {
|
||||
("attempted remainder with a divisor of zero",
|
||||
"attempted remainder with overflow")
|
||||
(ConstMathErr::RemainderByZero, ConstMathErr::Overflow(Op::Rem))
|
||||
};
|
||||
let debug_loc = call_info.debug_loc();
|
||||
|
||||
|
|
@ -802,7 +802,7 @@ pub fn fail_if_zero_or_overflows<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
|
|||
}
|
||||
};
|
||||
let bcx = with_cond(cx, is_zero, |bcx| {
|
||||
controlflow::trans_fail(bcx, call_info, InternedString::new(zero_text))
|
||||
controlflow::trans_fail(bcx, call_info, InternedString::new(zero_err.description()))
|
||||
});
|
||||
|
||||
// To quote LLVM's documentation for the sdiv instruction:
|
||||
|
|
@ -828,7 +828,8 @@ pub fn fail_if_zero_or_overflows<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
|
|||
C_integral(llty, min, true),
|
||||
debug_loc);
|
||||
with_cond(bcx, is_min, |bcx| {
|
||||
controlflow::trans_fail(bcx, call_info, InternedString::new(overflow_text))
|
||||
controlflow::trans_fail(bcx, call_info,
|
||||
InternedString::new(overflow_err.description()))
|
||||
})
|
||||
})
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ use monomorphize;
|
|||
use type_::Type;
|
||||
use value::Value;
|
||||
use rustc::ty::{self, Ty, TyCtxt};
|
||||
use rustc::ty::layout::Layout;
|
||||
use rustc::traits::{self, SelectionContext, ProjectionMode};
|
||||
use rustc::ty::fold::TypeFoldable;
|
||||
use rustc::hir;
|
||||
|
|
@ -99,6 +100,63 @@ pub fn type_is_immediate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns Some([a, b]) if the type has a pair of fields with types a and b.
|
||||
pub fn type_pair_fields<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>)
|
||||
-> Option<[Ty<'tcx>; 2]> {
|
||||
match ty.sty {
|
||||
ty::TyEnum(adt, substs) | ty::TyStruct(adt, substs) => {
|
||||
assert_eq!(adt.variants.len(), 1);
|
||||
let fields = &adt.variants[0].fields;
|
||||
if fields.len() != 2 {
|
||||
return None;
|
||||
}
|
||||
Some([monomorphize::field_ty(ccx.tcx(), substs, &fields[0]),
|
||||
monomorphize::field_ty(ccx.tcx(), substs, &fields[1])])
|
||||
}
|
||||
ty::TyClosure(_, ty::ClosureSubsts { upvar_tys: tys, .. }) |
|
||||
ty::TyTuple(tys) => {
|
||||
if tys.len() != 2 {
|
||||
return None;
|
||||
}
|
||||
Some([tys[0], tys[1]])
|
||||
}
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the type is represented as a pair of immediates.
|
||||
pub fn type_is_imm_pair<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>)
|
||||
-> bool {
|
||||
let tcx = ccx.tcx();
|
||||
let layout = tcx.normalizing_infer_ctxt(ProjectionMode::Any).enter(|infcx| {
|
||||
match ty.layout(&infcx) {
|
||||
Ok(layout) => layout,
|
||||
Err(err) => {
|
||||
bug!("type_is_imm_pair: layout for `{:?}` failed: {}",
|
||||
ty, err);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
match *layout {
|
||||
Layout::FatPointer { .. } => true,
|
||||
Layout::Univariant { ref variant, .. } => {
|
||||
// There must be only 2 fields.
|
||||
if variant.offset_after_field.len() != 2 {
|
||||
return false;
|
||||
}
|
||||
|
||||
match type_pair_fields(ccx, ty) {
|
||||
Some([a, b]) => {
|
||||
type_is_immediate(ccx, a) && type_is_immediate(ccx, b)
|
||||
}
|
||||
None => false
|
||||
}
|
||||
}
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
/// Identify types which have size zero at runtime.
|
||||
pub fn type_is_zero_size<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
use machine::llsize_of_alloc;
|
||||
|
|
@ -1165,18 +1223,18 @@ pub fn normalize_and_test_predicates<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|||
})
|
||||
}
|
||||
|
||||
pub fn langcall(bcx: Block,
|
||||
pub fn langcall(tcx: TyCtxt,
|
||||
span: Option<Span>,
|
||||
msg: &str,
|
||||
li: LangItem)
|
||||
-> DefId {
|
||||
match bcx.tcx().lang_items.require(li) {
|
||||
match tcx.lang_items.require(li) {
|
||||
Ok(id) => id,
|
||||
Err(s) => {
|
||||
let msg = format!("{} {}", msg, s);
|
||||
match span {
|
||||
Some(span) => bcx.tcx().sess.span_fatal(span, &msg[..]),
|
||||
None => bcx.tcx().sess.fatal(&msg[..]),
|
||||
Some(span) => tcx.sess.span_fatal(span, &msg[..]),
|
||||
None => tcx.sess.fatal(&msg[..]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -716,7 +716,10 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
|||
if iv >= len {
|
||||
// FIXME #3170: report this earlier on in the const-eval
|
||||
// pass. Reporting here is a bit late.
|
||||
const_err(cx, e.span, Err(ErrKind::IndexOutOfBounds), trueconst)?;
|
||||
const_err(cx, e.span, Err(ErrKind::IndexOutOfBounds {
|
||||
len: len,
|
||||
index: iv
|
||||
}), trueconst)?;
|
||||
C_undef(val_ty(arr).element_type())
|
||||
} else {
|
||||
const_get_elt(arr, &[iv as c_uint])
|
||||
|
|
|
|||
|
|
@ -400,7 +400,7 @@ pub fn trans_fail<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|||
let align = machine::llalign_of_min(ccx, val_ty(expr_file_line_const));
|
||||
let expr_file_line = consts::addr_of(ccx, expr_file_line_const, align, "panic_loc");
|
||||
let args = vec!(expr_file_line);
|
||||
let did = langcall(bcx, Some(call_info.span), "", PanicFnLangItem);
|
||||
let did = langcall(bcx.tcx(), Some(call_info.span), "", PanicFnLangItem);
|
||||
Callee::def(ccx, did, ccx.tcx().mk_substs(Substs::empty()))
|
||||
.call(bcx, call_info.debug_loc(), ArgVals(&args), None).bcx
|
||||
}
|
||||
|
|
@ -428,7 +428,7 @@ pub fn trans_fail_bounds_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|||
let align = machine::llalign_of_min(ccx, val_ty(file_line_const));
|
||||
let file_line = consts::addr_of(ccx, file_line_const, align, "panic_bounds_check_loc");
|
||||
let args = vec!(file_line, index, len);
|
||||
let did = langcall(bcx, Some(call_info.span), "", PanicBoundsCheckFnLangItem);
|
||||
let did = langcall(bcx.tcx(), Some(call_info.span), "", PanicBoundsCheckFnLangItem);
|
||||
Callee::def(ccx, did, ccx.tcx().mk_substs(Substs::empty()))
|
||||
.call(bcx, call_info.debug_loc(), ArgVals(&args), None).bcx
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2220,6 +2220,8 @@ impl OverflowOpViaIntrinsic {
|
|||
rhs: ValueRef,
|
||||
binop_debug_loc: DebugLoc)
|
||||
-> (Block<'blk, 'tcx>, ValueRef) {
|
||||
use rustc_const_math::{ConstMathErr, Op};
|
||||
|
||||
let llfn = self.to_intrinsic(bcx, lhs_t);
|
||||
|
||||
let val = Call(bcx, llfn, &[lhs, rhs], binop_debug_loc);
|
||||
|
|
@ -2230,13 +2232,19 @@ impl OverflowOpViaIntrinsic {
|
|||
binop_debug_loc);
|
||||
|
||||
let expect = bcx.ccx().get_intrinsic(&"llvm.expect.i1");
|
||||
Call(bcx, expect, &[cond, C_integral(Type::i1(bcx.ccx()), 0, false)],
|
||||
binop_debug_loc);
|
||||
let expected = Call(bcx, expect, &[cond, C_bool(bcx.ccx(), false)],
|
||||
binop_debug_loc);
|
||||
|
||||
let op = match *self {
|
||||
OverflowOpViaIntrinsic::Add => Op::Add,
|
||||
OverflowOpViaIntrinsic::Sub => Op::Sub,
|
||||
OverflowOpViaIntrinsic::Mul => Op::Mul
|
||||
};
|
||||
|
||||
let bcx =
|
||||
base::with_cond(bcx, cond, |bcx|
|
||||
base::with_cond(bcx, expected, |bcx|
|
||||
controlflow::trans_fail(bcx, info,
|
||||
InternedString::new("arithmetic operation overflowed")));
|
||||
InternedString::new(ConstMathErr::Overflow(op).description())));
|
||||
|
||||
(bcx, result)
|
||||
}
|
||||
|
|
@ -2252,6 +2260,8 @@ impl OverflowOpViaInputCheck {
|
|||
binop_debug_loc: DebugLoc)
|
||||
-> (Block<'blk, 'tcx>, ValueRef)
|
||||
{
|
||||
use rustc_const_math::{ConstMathErr, Op};
|
||||
|
||||
let lhs_llty = val_ty(lhs);
|
||||
let rhs_llty = val_ty(rhs);
|
||||
|
||||
|
|
@ -2266,16 +2276,16 @@ impl OverflowOpViaInputCheck {
|
|||
|
||||
let outer_bits = And(bcx, rhs, invert_mask, binop_debug_loc);
|
||||
let cond = build_nonzero_check(bcx, outer_bits, binop_debug_loc);
|
||||
let result = match *self {
|
||||
let (result, op) = match *self {
|
||||
OverflowOpViaInputCheck::Shl =>
|
||||
build_unchecked_lshift(bcx, lhs, rhs, binop_debug_loc),
|
||||
(build_unchecked_lshift(bcx, lhs, rhs, binop_debug_loc), Op::Shl),
|
||||
OverflowOpViaInputCheck::Shr =>
|
||||
build_unchecked_rshift(bcx, lhs_t, lhs, rhs, binop_debug_loc),
|
||||
(build_unchecked_rshift(bcx, lhs_t, lhs, rhs, binop_debug_loc), Op::Shr)
|
||||
};
|
||||
let bcx =
|
||||
base::with_cond(bcx, cond, |bcx|
|
||||
controlflow::trans_fail(bcx, info,
|
||||
InternedString::new("shift operation overflowed")));
|
||||
InternedString::new(ConstMathErr::Overflow(op).description())));
|
||||
|
||||
(bcx, result)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ pub fn trans_exchange_free_dyn<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|||
-> Block<'blk, 'tcx> {
|
||||
let _icx = push_ctxt("trans_exchange_free");
|
||||
|
||||
let def_id = langcall(bcx, None, "", ExchangeFreeFnLangItem);
|
||||
let def_id = langcall(bcx.tcx(), None, "", ExchangeFreeFnLangItem);
|
||||
let args = [PointerCast(bcx, v, Type::i8p(bcx.ccx())), size, align];
|
||||
Callee::def(bcx.ccx(), def_id, bcx.tcx().mk_substs(Substs::empty()))
|
||||
.call(bcx, debug_loc, ArgVals(&args), None).bcx
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ pub fn lvalue_temps<'bcx,'tcx>(bcx: Block<'bcx,'tcx>,
|
|||
// in an ValueRef without an alloca.
|
||||
assert!(common::type_is_immediate(bcx.ccx(), ty) ||
|
||||
common::type_is_fat_ptr(bcx.tcx(), ty));
|
||||
} else if common::type_is_imm_pair(bcx.ccx(), ty) {
|
||||
// We allow pairs and uses of any of their 2 fields.
|
||||
} else {
|
||||
// These sorts of types require an alloca. Note that
|
||||
// type_is_immediate() may *still* be true, particularly
|
||||
|
|
@ -111,6 +113,21 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for TempAnalyzer<'mir, 'bcx, 'tcx> {
|
|||
context: LvalueContext) {
|
||||
debug!("visit_lvalue(lvalue={:?}, context={:?})", lvalue, context);
|
||||
|
||||
// Allow uses of projections of immediate pair fields.
|
||||
if let mir::Lvalue::Projection(ref proj) = *lvalue {
|
||||
if let mir::Lvalue::Temp(index) = proj.base {
|
||||
let ty = self.mir.temp_decls[index as usize].ty;
|
||||
let ty = self.bcx.monomorphize(&ty);
|
||||
if common::type_is_imm_pair(self.bcx.ccx(), ty) {
|
||||
if let mir::ProjectionElem::Field(..) = proj.elem {
|
||||
if let LvalueContext::Consume = context {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match *lvalue {
|
||||
mir::Lvalue::Temp(index) => {
|
||||
match context {
|
||||
|
|
@ -161,6 +178,7 @@ pub fn cleanup_kinds<'bcx,'tcx>(_bcx: Block<'bcx,'tcx>,
|
|||
/* nothing to do */
|
||||
}
|
||||
TerminatorKind::Call { cleanup: unwind, .. } |
|
||||
TerminatorKind::Assert { cleanup: unwind, .. } |
|
||||
TerminatorKind::DropAndReplace { unwind, .. } |
|
||||
TerminatorKind::Drop { unwind, .. } => {
|
||||
if let Some(unwind) = unwind {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
// except according to those terms.
|
||||
|
||||
use llvm::{self, ValueRef};
|
||||
use rustc_const_eval::ErrKind;
|
||||
use rustc::middle::lang_items;
|
||||
use rustc::ty;
|
||||
use rustc::mir::repr as mir;
|
||||
use abi::{Abi, FnType, ArgType};
|
||||
|
|
@ -16,22 +18,28 @@ use adt;
|
|||
use base;
|
||||
use build;
|
||||
use callee::{Callee, CalleeData, Fn, Intrinsic, NamedTupleConstructor, Virtual};
|
||||
use common::{self, type_is_fat_ptr, Block, BlockAndBuilder, LandingPad, C_undef};
|
||||
use common::{self, Block, BlockAndBuilder, LandingPad};
|
||||
use common::{C_bool, C_str_slice, C_struct, C_u32, C_uint, C_undef};
|
||||
use consts;
|
||||
use debuginfo::DebugLoc;
|
||||
use Disr;
|
||||
use machine::{llalign_of_min, llbitsize_of_real};
|
||||
use machine::{llalign_of_min, llbitsize_of_real, llsize_of_store};
|
||||
use meth;
|
||||
use type_of;
|
||||
use glue;
|
||||
use type_::Type;
|
||||
|
||||
use rustc_data_structures::fnv::FnvHashMap;
|
||||
use syntax::parse::token;
|
||||
|
||||
use super::{MirContext, TempRef};
|
||||
use super::analyze::CleanupKind;
|
||||
use super::constant::Const;
|
||||
use super::lvalue::{LvalueRef, load_fat_ptr};
|
||||
use super::operand::OperandRef;
|
||||
use super::operand::OperandValue::{self, FatPtr, Immediate, Ref};
|
||||
use super::operand::OperandValue::*;
|
||||
|
||||
use std::cmp;
|
||||
|
||||
impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
||||
pub fn trans_block(&mut self, bb: mir::BasicBlock) {
|
||||
|
|
@ -212,6 +220,110 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, cleanup } => {
|
||||
let cond = self.trans_operand(&bcx, cond).immediate();
|
||||
let const_cond = common::const_to_opt_uint(cond).map(|c| c == 1);
|
||||
|
||||
// Don't translate the panic block if success if known.
|
||||
if const_cond == Some(expected) {
|
||||
funclet_br(self, bcx, target);
|
||||
return;
|
||||
}
|
||||
|
||||
// Pass the condition through llvm.expect for branch hinting.
|
||||
let expect = bcx.ccx().get_intrinsic(&"llvm.expect.i1");
|
||||
let cond = bcx.call(expect, &[cond, C_bool(bcx.ccx(), expected)], None);
|
||||
|
||||
// Create the failure block and the conditional branch to it.
|
||||
let lltarget = llblock(self, target);
|
||||
let panic_block = self.fcx.new_block("panic", None);
|
||||
if expected {
|
||||
bcx.cond_br(cond, lltarget, panic_block.llbb);
|
||||
} else {
|
||||
bcx.cond_br(cond, panic_block.llbb, lltarget);
|
||||
}
|
||||
|
||||
// After this point, bcx is the block for the call to panic.
|
||||
bcx = panic_block.build();
|
||||
|
||||
// Get the location information.
|
||||
let loc = bcx.sess().codemap().lookup_char_pos(terminator.span.lo);
|
||||
let filename = token::intern_and_get_ident(&loc.file.name);
|
||||
let filename = C_str_slice(bcx.ccx(), filename);
|
||||
let line = C_u32(bcx.ccx(), loc.line as u32);
|
||||
|
||||
// Put together the arguments to the panic entry point.
|
||||
let (lang_item, args, const_err) = match *msg {
|
||||
mir::AssertMessage::BoundsCheck { ref len, ref index } => {
|
||||
let len = self.trans_operand(&mut bcx, len).immediate();
|
||||
let index = self.trans_operand(&mut bcx, index).immediate();
|
||||
|
||||
let const_err = common::const_to_opt_uint(len).and_then(|len| {
|
||||
common::const_to_opt_uint(index).map(|index| {
|
||||
ErrKind::IndexOutOfBounds {
|
||||
len: len,
|
||||
index: index
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
let file_line = C_struct(bcx.ccx(), &[filename, line], false);
|
||||
let align = llalign_of_min(bcx.ccx(), common::val_ty(file_line));
|
||||
let file_line = consts::addr_of(bcx.ccx(),
|
||||
file_line,
|
||||
align,
|
||||
"panic_bounds_check_loc");
|
||||
(lang_items::PanicBoundsCheckFnLangItem,
|
||||
vec![file_line, index, len],
|
||||
const_err)
|
||||
}
|
||||
mir::AssertMessage::Math(ref err) => {
|
||||
let msg_str = token::intern_and_get_ident(err.description());
|
||||
let msg_str = C_str_slice(bcx.ccx(), msg_str);
|
||||
let msg_file_line = C_struct(bcx.ccx(),
|
||||
&[msg_str, filename, line],
|
||||
false);
|
||||
let align = llalign_of_min(bcx.ccx(), common::val_ty(msg_file_line));
|
||||
let msg_file_line = consts::addr_of(bcx.ccx(),
|
||||
msg_file_line,
|
||||
align,
|
||||
"panic_loc");
|
||||
(lang_items::PanicFnLangItem,
|
||||
vec![msg_file_line],
|
||||
Some(ErrKind::Math(err.clone())))
|
||||
}
|
||||
};
|
||||
|
||||
// If we know we always panic, and the error message
|
||||
// is also constant, then we can produce a warning.
|
||||
if const_cond == Some(!expected) {
|
||||
if let Some(err) = const_err {
|
||||
let _ = consts::const_err(bcx.ccx(),
|
||||
terminator.span,
|
||||
Err::<(), _>(err),
|
||||
consts::TrueConst::No);
|
||||
}
|
||||
}
|
||||
|
||||
// Obtain the panic entry point.
|
||||
let def_id = common::langcall(bcx.tcx(), Some(terminator.span), "", lang_item);
|
||||
let callee = Callee::def(bcx.ccx(), def_id,
|
||||
bcx.ccx().empty_substs_for_def_id(def_id));
|
||||
let llfn = callee.reify(bcx.ccx()).val;
|
||||
|
||||
// Translate the actual panic invoke/call.
|
||||
if let Some(unwind) = cleanup {
|
||||
bcx.invoke(llfn,
|
||||
&args,
|
||||
self.unreachable_block().llbb,
|
||||
llblock(self, unwind),
|
||||
cleanup_bundle);
|
||||
} else {
|
||||
bcx.call(llfn, &args, cleanup_bundle);
|
||||
bcx.unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
mir::TerminatorKind::DropAndReplace { .. } => {
|
||||
bug!("undesugared DropAndReplace in trans: {:?}", data);
|
||||
}
|
||||
|
|
@ -319,8 +431,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
let val = self.trans_operand(&bcx, arg).val;
|
||||
self.trans_argument(&bcx, val, &mut llargs, &fn_ty,
|
||||
let op = self.trans_operand(&bcx, arg);
|
||||
self.trans_argument(&bcx, op, &mut llargs, &fn_ty,
|
||||
&mut idx, &mut callee.data);
|
||||
}
|
||||
if let Some(tup) = untuple {
|
||||
|
|
@ -358,7 +470,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
if let ReturnDest::IndirectOperand(dst, _) = ret_dest {
|
||||
// Make a fake operand for store_return
|
||||
let op = OperandRef {
|
||||
val: OperandValue::Ref(dst),
|
||||
val: Ref(dst),
|
||||
ty: sig.output.unwrap()
|
||||
};
|
||||
self.store_return(&bcx, ret_dest, fn_ty.ret, op);
|
||||
|
|
@ -396,7 +508,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
ret_bcx.at_start(|ret_bcx| {
|
||||
debug_loc.apply_to_bcx(ret_bcx);
|
||||
let op = OperandRef {
|
||||
val: OperandValue::Immediate(invokeret),
|
||||
val: Immediate(invokeret),
|
||||
ty: sig.output.unwrap()
|
||||
};
|
||||
self.store_return(&ret_bcx, ret_dest, fn_ty.ret, op);
|
||||
|
|
@ -407,7 +519,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
fn_ty.apply_attrs_callsite(llret);
|
||||
if let Some((_, target)) = *destination {
|
||||
let op = OperandRef {
|
||||
val: OperandValue::Immediate(llret),
|
||||
val: Immediate(llret),
|
||||
ty: sig.output.unwrap()
|
||||
};
|
||||
self.store_return(&bcx, ret_dest, fn_ty.ret, op);
|
||||
|
|
@ -422,25 +534,36 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
|
||||
fn trans_argument(&mut self,
|
||||
bcx: &BlockAndBuilder<'bcx, 'tcx>,
|
||||
val: OperandValue,
|
||||
mut op: OperandRef<'tcx>,
|
||||
llargs: &mut Vec<ValueRef>,
|
||||
fn_ty: &FnType,
|
||||
next_idx: &mut usize,
|
||||
callee: &mut CalleeData) {
|
||||
// Treat the values in a fat pointer separately.
|
||||
if let FatPtr(ptr, meta) = val {
|
||||
if *next_idx == 0 {
|
||||
if let Virtual(idx) = *callee {
|
||||
let llfn = bcx.with_block(|bcx| {
|
||||
meth::get_virtual_method(bcx, meta, idx)
|
||||
});
|
||||
let llty = fn_ty.llvm_type(bcx.ccx()).ptr_to();
|
||||
*callee = Fn(bcx.pointercast(llfn, llty));
|
||||
if let Pair(a, b) = op.val {
|
||||
// Treat the values in a fat pointer separately.
|
||||
if common::type_is_fat_ptr(bcx.tcx(), op.ty) {
|
||||
let (ptr, meta) = (a, b);
|
||||
if *next_idx == 0 {
|
||||
if let Virtual(idx) = *callee {
|
||||
let llfn = bcx.with_block(|bcx| {
|
||||
meth::get_virtual_method(bcx, meta, idx)
|
||||
});
|
||||
let llty = fn_ty.llvm_type(bcx.ccx()).ptr_to();
|
||||
*callee = Fn(bcx.pointercast(llfn, llty));
|
||||
}
|
||||
}
|
||||
|
||||
let imm_op = |x| OperandRef {
|
||||
val: Immediate(x),
|
||||
// We won't be checking the type again.
|
||||
ty: bcx.tcx().types.err
|
||||
};
|
||||
self.trans_argument(bcx, imm_op(ptr), llargs, fn_ty, next_idx, callee);
|
||||
self.trans_argument(bcx, imm_op(meta), llargs, fn_ty, next_idx, callee);
|
||||
return;
|
||||
}
|
||||
self.trans_argument(bcx, Immediate(ptr), llargs, fn_ty, next_idx, callee);
|
||||
self.trans_argument(bcx, Immediate(meta), llargs, fn_ty, next_idx, callee);
|
||||
return;
|
||||
|
||||
op = op.pack_if_pair(bcx);
|
||||
}
|
||||
|
||||
let arg = &fn_ty.args[*next_idx];
|
||||
|
|
@ -456,7 +579,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
}
|
||||
|
||||
// Force by-ref if we have to load through a cast pointer.
|
||||
let (mut llval, by_ref) = match val {
|
||||
let (mut llval, by_ref) = match op.val {
|
||||
Immediate(llval) if arg.is_indirect() || arg.cast.is_some() => {
|
||||
let llscratch = build::AllocaFcx(bcx.fcx(), arg.original_ty, "arg");
|
||||
bcx.store(llval, llscratch);
|
||||
|
|
@ -464,7 +587,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
}
|
||||
Immediate(llval) => (llval, false),
|
||||
Ref(llval) => (llval, true),
|
||||
FatPtr(_, _) => bug!("fat pointers handled above")
|
||||
Pair(..) => bug!("pairs handled above")
|
||||
};
|
||||
|
||||
if by_ref && !arg.is_indirect() {
|
||||
|
|
@ -511,12 +634,16 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
let ptr = adt::trans_field_ptr_builder(bcx, &base_repr, base, Disr(0), n);
|
||||
let val = if common::type_is_fat_ptr(bcx.tcx(), ty) {
|
||||
let (lldata, llextra) = load_fat_ptr(bcx, ptr);
|
||||
FatPtr(lldata, llextra)
|
||||
Pair(lldata, llextra)
|
||||
} else {
|
||||
// trans_argument will load this if it needs to
|
||||
Ref(ptr)
|
||||
};
|
||||
self.trans_argument(bcx, val, llargs, fn_ty, next_idx, callee);
|
||||
let op = OperandRef {
|
||||
val: val,
|
||||
ty: ty
|
||||
};
|
||||
self.trans_argument(bcx, op, llargs, fn_ty, next_idx, callee);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -528,11 +655,29 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
elem = bcx.trunc(elem, Type::i1(bcx.ccx()));
|
||||
}
|
||||
// If the tuple is immediate, the elements are as well
|
||||
let val = Immediate(elem);
|
||||
self.trans_argument(bcx, val, llargs, fn_ty, next_idx, callee);
|
||||
let op = OperandRef {
|
||||
val: Immediate(elem),
|
||||
ty: ty
|
||||
};
|
||||
self.trans_argument(bcx, op, llargs, fn_ty, next_idx, callee);
|
||||
}
|
||||
}
|
||||
Pair(a, b) => {
|
||||
let elems = [a, b];
|
||||
for (n, &ty) in arg_types.iter().enumerate() {
|
||||
let mut elem = elems[n];
|
||||
// Truncate bools to i1, if needed
|
||||
if ty.is_bool() && common::val_ty(elem) != Type::i1(bcx.ccx()) {
|
||||
elem = bcx.trunc(elem, Type::i1(bcx.ccx()));
|
||||
}
|
||||
// Pair is always made up of immediates
|
||||
let op = OperandRef {
|
||||
val: Immediate(elem),
|
||||
ty: ty
|
||||
};
|
||||
self.trans_argument(bcx, op, llargs, fn_ty, next_idx, callee);
|
||||
}
|
||||
}
|
||||
FatPtr(_, _) => bug!("tuple is a fat pointer?!")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -688,7 +833,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
let f = Callee::def(bcx.ccx(), def_id, substs);
|
||||
let datum = f.reify(bcx.ccx());
|
||||
val = OperandRef {
|
||||
val: OperandValue::Immediate(datum.val),
|
||||
val: Immediate(datum.val),
|
||||
ty: datum.ty
|
||||
};
|
||||
}
|
||||
|
|
@ -707,29 +852,74 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
op: OperandRef<'tcx>) {
|
||||
use self::ReturnDest::*;
|
||||
|
||||
match dest {
|
||||
Nothing => (),
|
||||
Store(dst) => ret_ty.store(bcx, op.immediate(), dst),
|
||||
// Handle the simple cases that don't require casts, first.
|
||||
let llcast_ty = match dest {
|
||||
Nothing => return,
|
||||
Store(dst) => {
|
||||
if let Some(llcast_ty) = ret_ty.cast {
|
||||
llcast_ty
|
||||
} else {
|
||||
ret_ty.store(bcx, op.immediate(), dst);
|
||||
return;
|
||||
}
|
||||
}
|
||||
IndirectOperand(tmp, idx) => {
|
||||
let op = self.trans_load(bcx, tmp, op.ty);
|
||||
self.temps[idx as usize] = TempRef::Operand(Some(op));
|
||||
return;
|
||||
}
|
||||
DirectOperand(idx) => {
|
||||
let op = if type_is_fat_ptr(bcx.tcx(), op.ty) {
|
||||
let llval = op.immediate();
|
||||
let ptr = bcx.extract_value(llval, 0);
|
||||
let meta = bcx.extract_value(llval, 1);
|
||||
|
||||
OperandRef {
|
||||
val: OperandValue::FatPtr(ptr, meta),
|
||||
ty: op.ty
|
||||
}
|
||||
if let Some(llcast_ty) = ret_ty.cast {
|
||||
llcast_ty
|
||||
} else {
|
||||
op
|
||||
};
|
||||
let op = op.unpack_if_pair(bcx);
|
||||
self.temps[idx as usize] = TempRef::Operand(Some(op));
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// The actual return type is a struct, but the ABI
|
||||
// adaptation code has cast it into some scalar type. The
|
||||
// code that follows is the only reliable way I have
|
||||
// found to do a transform like i64 -> {i32,i32}.
|
||||
// Basically we dump the data onto the stack then memcpy it.
|
||||
//
|
||||
// Other approaches I tried:
|
||||
// - Casting rust ret pointer to the foreign type and using Store
|
||||
// is (a) unsafe if size of foreign type > size of rust type and
|
||||
// (b) runs afoul of strict aliasing rules, yielding invalid
|
||||
// assembly under -O (specifically, the store gets removed).
|
||||
// - Truncating foreign type to correct integral type and then
|
||||
// bitcasting to the struct type yields invalid cast errors.
|
||||
|
||||
// We instead thus allocate some scratch space...
|
||||
let llscratch = bcx.alloca(llcast_ty, "fn_ret_cast");
|
||||
bcx.with_block(|bcx| base::call_lifetime_start(bcx, llscratch));
|
||||
|
||||
// ...where we first store the value...
|
||||
bcx.store(op.immediate(), llscratch);
|
||||
|
||||
let ccx = bcx.ccx();
|
||||
match dest {
|
||||
Store(dst) => {
|
||||
// ...and then memcpy it to the intended destination.
|
||||
base::call_memcpy(bcx,
|
||||
bcx.pointercast(dst, Type::i8p(ccx)),
|
||||
bcx.pointercast(llscratch, Type::i8p(ccx)),
|
||||
C_uint(ccx, llsize_of_store(ccx, ret_ty.original_ty)),
|
||||
cmp::min(llalign_of_min(ccx, ret_ty.original_ty),
|
||||
llalign_of_min(ccx, llcast_ty)) as u32);
|
||||
}
|
||||
DirectOperand(idx) => {
|
||||
let llptr = bcx.pointercast(llscratch, ret_ty.original_ty.ptr_to());
|
||||
let op = self.trans_load(bcx, llptr, op.ty);
|
||||
self.temps[idx as usize] = TempRef::Operand(Some(op));
|
||||
}
|
||||
Nothing | IndirectOperand(_, _) => bug!()
|
||||
}
|
||||
|
||||
bcx.with_block(|bcx| base::call_lifetime_end(bcx, llscratch));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,12 +12,13 @@ use llvm::{self, ValueRef};
|
|||
use rustc::middle::const_val::ConstVal;
|
||||
use rustc_const_eval::ErrKind;
|
||||
use rustc_const_math::ConstInt::*;
|
||||
use rustc_const_math::ConstMathErr;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::infer::TransNormalize;
|
||||
use rustc::mir::repr as mir;
|
||||
use rustc::mir::tcx::LvalueTy;
|
||||
use rustc::traits;
|
||||
use rustc::ty::{self, Ty, TypeFoldable};
|
||||
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
|
||||
use rustc::ty::cast::{CastTy, IntTy};
|
||||
use rustc::ty::subst::Substs;
|
||||
use {abi, adt, base, Disr};
|
||||
|
|
@ -98,9 +99,15 @@ impl<'tcx> Const<'tcx> {
|
|||
Const::new(val, ty)
|
||||
}
|
||||
|
||||
fn get_pair(&self) -> (ValueRef, ValueRef) {
|
||||
(const_get_elt(self.llval, &[0]),
|
||||
const_get_elt(self.llval, &[1]))
|
||||
}
|
||||
|
||||
fn get_fat_ptr(&self) -> (ValueRef, ValueRef) {
|
||||
(const_get_elt(self.llval, &[abi::FAT_PTR_ADDR as u32]),
|
||||
const_get_elt(self.llval, &[abi::FAT_PTR_EXTRA as u32]))
|
||||
assert_eq!(abi::FAT_PTR_ADDR, 0);
|
||||
assert_eq!(abi::FAT_PTR_EXTRA, 1);
|
||||
self.get_pair()
|
||||
}
|
||||
|
||||
fn as_lvalue(&self) -> ConstLvalue<'tcx> {
|
||||
|
|
@ -115,9 +122,9 @@ impl<'tcx> Const<'tcx> {
|
|||
let llty = type_of::immediate_type_of(ccx, self.ty);
|
||||
let llvalty = val_ty(self.llval);
|
||||
|
||||
let val = if common::type_is_fat_ptr(ccx.tcx(), self.ty) {
|
||||
let (data, extra) = self.get_fat_ptr();
|
||||
OperandValue::FatPtr(data, extra)
|
||||
let val = if common::type_is_imm_pair(ccx, self.ty) {
|
||||
let (a, b) = self.get_pair();
|
||||
OperandValue::Pair(a, b)
|
||||
} else if common::type_is_immediate(ccx, self.ty) && llty == llvalty {
|
||||
// If the types match, we can use the value directly.
|
||||
OperandValue::Immediate(self.llval)
|
||||
|
|
@ -263,6 +270,11 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
|
|||
fn trans(&mut self) -> Result<Const<'tcx>, ConstEvalFailure> {
|
||||
let tcx = self.ccx.tcx();
|
||||
let mut bb = mir::START_BLOCK;
|
||||
|
||||
// Make sure to evaluate all statemenets to
|
||||
// report as many errors as we possibly can.
|
||||
let mut failure = Ok(());
|
||||
|
||||
loop {
|
||||
let data = self.mir.basic_block_data(bb);
|
||||
for statement in &data.statements {
|
||||
|
|
@ -270,8 +282,10 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
|
|||
mir::StatementKind::Assign(ref dest, ref rvalue) => {
|
||||
let ty = self.mir.lvalue_ty(tcx, dest);
|
||||
let ty = self.monomorphize(&ty).to_ty(tcx);
|
||||
let value = self.const_rvalue(rvalue, ty, statement.span)?;
|
||||
self.store(dest, value, statement.span);
|
||||
match self.const_rvalue(rvalue, ty, statement.span) {
|
||||
Ok(value) => self.store(dest, value, statement.span),
|
||||
Err(err) => if failure.is_ok() { failure = Err(err); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -282,19 +296,35 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
|
|||
mir::TerminatorKind::Drop { target, .. } | // No dropping.
|
||||
mir::TerminatorKind::Goto { target } => target,
|
||||
mir::TerminatorKind::Return => {
|
||||
failure?;
|
||||
return Ok(self.return_value.unwrap_or_else(|| {
|
||||
span_bug!(span, "no returned value in constant");
|
||||
}))
|
||||
}
|
||||
|
||||
// This is only supported to make bounds checking work.
|
||||
mir::TerminatorKind::If { ref cond, targets: (true_bb, false_bb) } => {
|
||||
mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, .. } => {
|
||||
let cond = self.const_operand(cond, span)?;
|
||||
if common::const_to_uint(cond.llval) != 0 {
|
||||
true_bb
|
||||
} else {
|
||||
false_bb
|
||||
let cond_bool = common::const_to_uint(cond.llval) != 0;
|
||||
if cond_bool != expected {
|
||||
let err = match *msg {
|
||||
mir::AssertMessage::BoundsCheck { ref len, ref index } => {
|
||||
let len = self.const_operand(len, span)?;
|
||||
let index = self.const_operand(index, span)?;
|
||||
ErrKind::IndexOutOfBounds {
|
||||
len: common::const_to_uint(len.llval),
|
||||
index: common::const_to_uint(index.llval)
|
||||
}
|
||||
}
|
||||
mir::AssertMessage::Math(ref err) => {
|
||||
ErrKind::Math(err.clone())
|
||||
}
|
||||
};
|
||||
match consts::const_err(self.ccx, span, Err(err), TrueConst::Yes) {
|
||||
Ok(()) => {}
|
||||
Err(err) => if failure.is_ok() { failure = Err(err); }
|
||||
}
|
||||
}
|
||||
target
|
||||
}
|
||||
|
||||
mir::TerminatorKind::Call { ref func, ref args, ref destination, .. } => {
|
||||
|
|
@ -308,22 +338,21 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
|
|||
func, fn_ty)
|
||||
};
|
||||
|
||||
// Indexing OOB doesn't call a const fn, handle it.
|
||||
if Some(instance.def) == tcx.lang_items.panic_bounds_check_fn() {
|
||||
consts::const_err(self.ccx, span,
|
||||
Err(ErrKind::IndexOutOfBounds),
|
||||
TrueConst::Yes)?;
|
||||
let mut const_args = Vec::with_capacity(args.len());
|
||||
for arg in args {
|
||||
match self.const_operand(arg, span) {
|
||||
Ok(arg) => const_args.push(arg),
|
||||
Err(err) => if failure.is_ok() { failure = Err(err); }
|
||||
}
|
||||
}
|
||||
|
||||
let args = args.iter().map(|arg| {
|
||||
self.const_operand(arg, span)
|
||||
}).collect::<Result<Vec<_>, _>>()?;
|
||||
let value = MirConstContext::trans_def(self.ccx, instance, args)?;
|
||||
if let Some((ref dest, target)) = *destination {
|
||||
self.store(dest, value, span);
|
||||
match MirConstContext::trans_def(self.ccx, instance, const_args) {
|
||||
Ok(value) => self.store(dest, value, span),
|
||||
Err(err) => if failure.is_ok() { failure = Err(err); }
|
||||
}
|
||||
target
|
||||
} else {
|
||||
span_bug!(span, "diverging {:?} in constant", terminator.kind)
|
||||
span_bug!(span, "diverging {:?} in constant", terminator.kind);
|
||||
}
|
||||
}
|
||||
_ => span_bug!(span, "{:?} in constant", terminator.kind)
|
||||
|
|
@ -413,8 +442,16 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
|
|||
} else {
|
||||
span_bug!(span, "index is not an integer-constant expression")
|
||||
};
|
||||
(Base::Value(const_get_elt(base.llval, &[iv as u32])),
|
||||
ptr::null_mut())
|
||||
|
||||
// Produce an undef instead of a LLVM assertion on OOB.
|
||||
let len = common::const_to_uint(tr_base.len(self.ccx));
|
||||
let llelem = if iv < len {
|
||||
const_get_elt(base.llval, &[iv as u32])
|
||||
} else {
|
||||
C_undef(type_of::type_of(self.ccx, projected_ty))
|
||||
};
|
||||
|
||||
(Base::Value(llelem), ptr::null_mut())
|
||||
}
|
||||
_ => span_bug!(span, "{:?} in constant", projection.elem)
|
||||
};
|
||||
|
|
@ -485,9 +522,17 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
|
|||
}
|
||||
|
||||
mir::Rvalue::Aggregate(ref kind, ref operands) => {
|
||||
let fields = operands.iter().map(|operand| {
|
||||
Ok(self.const_operand(operand, span)?.llval)
|
||||
}).collect::<Result<Vec<_>, _>>()?;
|
||||
// Make sure to evaluate all operands to
|
||||
// report as many errors as we possibly can.
|
||||
let mut fields = Vec::with_capacity(operands.len());
|
||||
let mut failure = Ok(());
|
||||
for operand in operands {
|
||||
match self.const_operand(operand, span) {
|
||||
Ok(val) => fields.push(val.llval),
|
||||
Err(err) => if failure.is_ok() { failure = Err(err); }
|
||||
}
|
||||
}
|
||||
failure?;
|
||||
|
||||
// FIXME Shouldn't need to manually trigger closure instantiations.
|
||||
if let mir::AggregateKind::Closure(def_id, substs) = *kind {
|
||||
|
|
@ -656,7 +701,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
|
|||
consts::ptrcast(data_ptr, ll_cast_ty)
|
||||
}
|
||||
} else {
|
||||
bug!("Unexpected non-FatPtr operand")
|
||||
bug!("Unexpected non-fat-pointer operand")
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -702,73 +747,28 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
|
|||
let ty = lhs.ty;
|
||||
let binop_ty = self.mir.binop_ty(tcx, op, lhs.ty, rhs.ty);
|
||||
let (lhs, rhs) = (lhs.llval, rhs.llval);
|
||||
assert!(!ty.is_simd());
|
||||
let is_float = ty.is_fp();
|
||||
let signed = ty.is_signed();
|
||||
Const::new(const_scalar_binop(op, lhs, rhs, ty), binop_ty)
|
||||
}
|
||||
|
||||
if let (Some(lhs), Some(rhs)) = (to_const_int(lhs, ty, tcx),
|
||||
to_const_int(rhs, ty, tcx)) {
|
||||
let result = match op {
|
||||
mir::BinOp::Add => lhs + rhs,
|
||||
mir::BinOp::Sub => lhs - rhs,
|
||||
mir::BinOp::Mul => lhs * rhs,
|
||||
mir::BinOp::Div => lhs / rhs,
|
||||
mir::BinOp::Rem => lhs % rhs,
|
||||
mir::BinOp::Shl => lhs << rhs,
|
||||
mir::BinOp::Shr => lhs >> rhs,
|
||||
_ => Ok(lhs)
|
||||
};
|
||||
consts::const_err(self.ccx, span,
|
||||
result.map_err(ErrKind::Math),
|
||||
TrueConst::Yes)?;
|
||||
}
|
||||
mir::Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) => {
|
||||
let lhs = self.const_operand(lhs, span)?;
|
||||
let rhs = self.const_operand(rhs, span)?;
|
||||
let ty = lhs.ty;
|
||||
let val_ty = self.mir.binop_ty(tcx, op, lhs.ty, rhs.ty);
|
||||
let binop_ty = tcx.mk_tup(vec![val_ty, tcx.types.bool]);
|
||||
let (lhs, rhs) = (lhs.llval, rhs.llval);
|
||||
assert!(!ty.is_fp());
|
||||
|
||||
let llval = unsafe {
|
||||
match op {
|
||||
mir::BinOp::Add if is_float => llvm::LLVMConstFAdd(lhs, rhs),
|
||||
mir::BinOp::Add => llvm::LLVMConstAdd(lhs, rhs),
|
||||
|
||||
mir::BinOp::Sub if is_float => llvm::LLVMConstFSub(lhs, rhs),
|
||||
mir::BinOp::Sub => llvm::LLVMConstSub(lhs, rhs),
|
||||
|
||||
mir::BinOp::Mul if is_float => llvm::LLVMConstFMul(lhs, rhs),
|
||||
mir::BinOp::Mul => llvm::LLVMConstMul(lhs, rhs),
|
||||
|
||||
mir::BinOp::Div if is_float => llvm::LLVMConstFDiv(lhs, rhs),
|
||||
mir::BinOp::Div if signed => llvm::LLVMConstSDiv(lhs, rhs),
|
||||
mir::BinOp::Div => llvm::LLVMConstUDiv(lhs, rhs),
|
||||
|
||||
mir::BinOp::Rem if is_float => llvm::LLVMConstFRem(lhs, rhs),
|
||||
mir::BinOp::Rem if signed => llvm::LLVMConstSRem(lhs, rhs),
|
||||
mir::BinOp::Rem => llvm::LLVMConstURem(lhs, rhs),
|
||||
|
||||
mir::BinOp::BitXor => llvm::LLVMConstXor(lhs, rhs),
|
||||
mir::BinOp::BitAnd => llvm::LLVMConstAnd(lhs, rhs),
|
||||
mir::BinOp::BitOr => llvm::LLVMConstOr(lhs, rhs),
|
||||
mir::BinOp::Shl => {
|
||||
let rhs = base::cast_shift_const_rhs(op.to_hir_binop(), lhs, rhs);
|
||||
llvm::LLVMConstShl(lhs, rhs)
|
||||
}
|
||||
mir::BinOp::Shr => {
|
||||
let rhs = base::cast_shift_const_rhs(op.to_hir_binop(), lhs, rhs);
|
||||
if signed { llvm::LLVMConstAShr(lhs, rhs) }
|
||||
else { llvm::LLVMConstLShr(lhs, rhs) }
|
||||
}
|
||||
mir::BinOp::Eq | mir::BinOp::Ne |
|
||||
mir::BinOp::Lt | mir::BinOp::Le |
|
||||
mir::BinOp::Gt | mir::BinOp::Ge => {
|
||||
if is_float {
|
||||
let cmp = base::bin_op_to_fcmp_predicate(op.to_hir_binop());
|
||||
llvm::ConstFCmp(cmp, lhs, rhs)
|
||||
} else {
|
||||
let cmp = base::bin_op_to_icmp_predicate(op.to_hir_binop(),
|
||||
signed);
|
||||
llvm::ConstICmp(cmp, lhs, rhs)
|
||||
}
|
||||
}
|
||||
match const_scalar_checked_binop(tcx, op, lhs, rhs, ty) {
|
||||
Some((llval, of)) => {
|
||||
let llof = C_bool(self.ccx, of);
|
||||
Const::new(C_struct(self.ccx, &[llval, llof], false), binop_ty)
|
||||
}
|
||||
};
|
||||
Const::new(llval, binop_ty)
|
||||
None => {
|
||||
span_bug!(span, "{:?} got non-integer operands: {:?} and {:?}",
|
||||
rvalue, Value(lhs), Value(rhs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mir::Rvalue::UnaryOp(op, ref operand) => {
|
||||
|
|
@ -781,11 +781,6 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
mir::UnOp::Neg => {
|
||||
if let Some(cval) = to_const_int(lloperand, operand.ty, tcx) {
|
||||
consts::const_err(self.ccx, span,
|
||||
(-cval).map_err(ErrKind::Math),
|
||||
TrueConst::Yes)?;
|
||||
}
|
||||
let is_float = operand.ty.is_fp();
|
||||
unsafe {
|
||||
if is_float {
|
||||
|
|
@ -804,6 +799,97 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
|
|||
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn const_scalar_binop(op: mir::BinOp,
|
||||
lhs: ValueRef,
|
||||
rhs: ValueRef,
|
||||
input_ty: Ty) -> ValueRef {
|
||||
assert!(!input_ty.is_simd());
|
||||
let is_float = input_ty.is_fp();
|
||||
let signed = input_ty.is_signed();
|
||||
|
||||
unsafe {
|
||||
match op {
|
||||
mir::BinOp::Add if is_float => llvm::LLVMConstFAdd(lhs, rhs),
|
||||
mir::BinOp::Add => llvm::LLVMConstAdd(lhs, rhs),
|
||||
|
||||
mir::BinOp::Sub if is_float => llvm::LLVMConstFSub(lhs, rhs),
|
||||
mir::BinOp::Sub => llvm::LLVMConstSub(lhs, rhs),
|
||||
|
||||
mir::BinOp::Mul if is_float => llvm::LLVMConstFMul(lhs, rhs),
|
||||
mir::BinOp::Mul => llvm::LLVMConstMul(lhs, rhs),
|
||||
|
||||
mir::BinOp::Div if is_float => llvm::LLVMConstFDiv(lhs, rhs),
|
||||
mir::BinOp::Div if signed => llvm::LLVMConstSDiv(lhs, rhs),
|
||||
mir::BinOp::Div => llvm::LLVMConstUDiv(lhs, rhs),
|
||||
|
||||
mir::BinOp::Rem if is_float => llvm::LLVMConstFRem(lhs, rhs),
|
||||
mir::BinOp::Rem if signed => llvm::LLVMConstSRem(lhs, rhs),
|
||||
mir::BinOp::Rem => llvm::LLVMConstURem(lhs, rhs),
|
||||
|
||||
mir::BinOp::BitXor => llvm::LLVMConstXor(lhs, rhs),
|
||||
mir::BinOp::BitAnd => llvm::LLVMConstAnd(lhs, rhs),
|
||||
mir::BinOp::BitOr => llvm::LLVMConstOr(lhs, rhs),
|
||||
mir::BinOp::Shl => {
|
||||
let rhs = base::cast_shift_const_rhs(op.to_hir_binop(), lhs, rhs);
|
||||
llvm::LLVMConstShl(lhs, rhs)
|
||||
}
|
||||
mir::BinOp::Shr => {
|
||||
let rhs = base::cast_shift_const_rhs(op.to_hir_binop(), lhs, rhs);
|
||||
if signed { llvm::LLVMConstAShr(lhs, rhs) }
|
||||
else { llvm::LLVMConstLShr(lhs, rhs) }
|
||||
}
|
||||
mir::BinOp::Eq | mir::BinOp::Ne |
|
||||
mir::BinOp::Lt | mir::BinOp::Le |
|
||||
mir::BinOp::Gt | mir::BinOp::Ge => {
|
||||
if is_float {
|
||||
let cmp = base::bin_op_to_fcmp_predicate(op.to_hir_binop());
|
||||
llvm::ConstFCmp(cmp, lhs, rhs)
|
||||
} else {
|
||||
let cmp = base::bin_op_to_icmp_predicate(op.to_hir_binop(),
|
||||
signed);
|
||||
llvm::ConstICmp(cmp, lhs, rhs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn const_scalar_checked_binop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
op: mir::BinOp,
|
||||
lllhs: ValueRef,
|
||||
llrhs: ValueRef,
|
||||
input_ty: Ty<'tcx>)
|
||||
-> Option<(ValueRef, bool)> {
|
||||
if let (Some(lhs), Some(rhs)) = (to_const_int(lllhs, input_ty, tcx),
|
||||
to_const_int(llrhs, input_ty, tcx)) {
|
||||
let result = match op {
|
||||
mir::BinOp::Add => lhs + rhs,
|
||||
mir::BinOp::Sub => lhs - rhs,
|
||||
mir::BinOp::Mul => lhs * rhs,
|
||||
mir::BinOp::Shl => lhs << rhs,
|
||||
mir::BinOp::Shr => lhs >> rhs,
|
||||
_ => {
|
||||
bug!("Operator `{:?}` is not a checkable operator", op)
|
||||
}
|
||||
};
|
||||
|
||||
let of = match result {
|
||||
Ok(_) => false,
|
||||
Err(ConstMathErr::Overflow(_)) |
|
||||
Err(ConstMathErr::ShiftNegative) => true,
|
||||
Err(err) => {
|
||||
bug!("Operator `{:?}` on `{:?}` and `{:?}` errored: {}",
|
||||
op, lhs, rhs, err.description());
|
||||
}
|
||||
};
|
||||
|
||||
Some((const_scalar_binop(op, lllhs, llrhs, input_ty), of))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
||||
|
|
|
|||
|
|
@ -13,12 +13,12 @@ use rustc::ty::Ty;
|
|||
use rustc::mir::repr as mir;
|
||||
use base;
|
||||
use common::{self, Block, BlockAndBuilder};
|
||||
use datum;
|
||||
use value::Value;
|
||||
use type_of;
|
||||
use type_::Type;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use super::lvalue::load_fat_ptr;
|
||||
use super::{MirContext, TempRef};
|
||||
|
||||
/// The representation of a Rust value. The enum variant is in fact
|
||||
|
|
@ -31,9 +31,8 @@ pub enum OperandValue {
|
|||
Ref(ValueRef),
|
||||
/// A single LLVM value.
|
||||
Immediate(ValueRef),
|
||||
/// A fat pointer. The first ValueRef is the data and the second
|
||||
/// is the extra.
|
||||
FatPtr(ValueRef, ValueRef)
|
||||
/// A pair of immediate LLVM values. Used by fat pointers too.
|
||||
Pair(ValueRef, ValueRef)
|
||||
}
|
||||
|
||||
/// An `OperandRef` is an "SSA" reference to a Rust value, along with
|
||||
|
|
@ -64,15 +63,15 @@ impl<'tcx> fmt::Debug for OperandRef<'tcx> {
|
|||
write!(f, "OperandRef(Immediate({:?}) @ {:?})",
|
||||
Value(i), self.ty)
|
||||
}
|
||||
OperandValue::FatPtr(a, d) => {
|
||||
write!(f, "OperandRef(FatPtr({:?}, {:?}) @ {:?})",
|
||||
Value(a), Value(d), self.ty)
|
||||
OperandValue::Pair(a, b) => {
|
||||
write!(f, "OperandRef(Pair({:?}, {:?}) @ {:?})",
|
||||
Value(a), Value(b), self.ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> OperandRef<'tcx> {
|
||||
impl<'bcx, 'tcx> OperandRef<'tcx> {
|
||||
/// Asserts that this operand refers to a scalar and returns
|
||||
/// a reference to its value.
|
||||
pub fn immediate(self) -> ValueRef {
|
||||
|
|
@ -81,6 +80,54 @@ impl<'tcx> OperandRef<'tcx> {
|
|||
_ => bug!()
|
||||
}
|
||||
}
|
||||
|
||||
/// If this operand is a Pair, we return an
|
||||
/// Immediate aggregate with the two values.
|
||||
pub fn pack_if_pair(mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>)
|
||||
-> OperandRef<'tcx> {
|
||||
if let OperandValue::Pair(a, b) = self.val {
|
||||
// Reconstruct the immediate aggregate.
|
||||
let llty = type_of::type_of(bcx.ccx(), self.ty);
|
||||
let mut llpair = common::C_undef(llty);
|
||||
let elems = [a, b];
|
||||
for i in 0..2 {
|
||||
let mut elem = elems[i];
|
||||
// Extend boolean i1's to i8.
|
||||
if common::val_ty(elem) == Type::i1(bcx.ccx()) {
|
||||
elem = bcx.zext(elem, Type::i8(bcx.ccx()));
|
||||
}
|
||||
llpair = bcx.insert_value(llpair, elem, i);
|
||||
}
|
||||
self.val = OperandValue::Immediate(llpair);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// If this operand is a pair in an Immediate,
|
||||
/// we return a Pair with the two halves.
|
||||
pub fn unpack_if_pair(mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>)
|
||||
-> OperandRef<'tcx> {
|
||||
if let OperandValue::Immediate(llval) = self.val {
|
||||
// Deconstruct the immediate aggregate.
|
||||
if common::type_is_imm_pair(bcx.ccx(), self.ty) {
|
||||
let mut a = bcx.extract_value(llval, 0);
|
||||
let mut b = bcx.extract_value(llval, 1);
|
||||
|
||||
let pair_fields = common::type_pair_fields(bcx.ccx(), self.ty);
|
||||
if let Some([a_ty, b_ty]) = pair_fields {
|
||||
if a_ty.is_bool() {
|
||||
a = bcx.trunc(a, Type::i1(bcx.ccx()));
|
||||
}
|
||||
if b_ty.is_bool() {
|
||||
b = bcx.trunc(b, Type::i1(bcx.ccx()));
|
||||
}
|
||||
}
|
||||
|
||||
self.val = OperandValue::Pair(a, b);
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
||||
|
|
@ -92,15 +139,24 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
{
|
||||
debug!("trans_load: {:?} @ {:?}", Value(llval), ty);
|
||||
|
||||
let val = match datum::appropriate_rvalue_mode(bcx.ccx(), ty) {
|
||||
datum::ByValue => {
|
||||
OperandValue::Immediate(base::load_ty_builder(bcx, llval, ty))
|
||||
}
|
||||
datum::ByRef if common::type_is_fat_ptr(bcx.tcx(), ty) => {
|
||||
let (lldata, llextra) = load_fat_ptr(bcx, llval);
|
||||
OperandValue::FatPtr(lldata, llextra)
|
||||
}
|
||||
datum::ByRef => OperandValue::Ref(llval)
|
||||
let val = if common::type_is_imm_pair(bcx.ccx(), ty) {
|
||||
let a_ptr = bcx.struct_gep(llval, 0);
|
||||
let b_ptr = bcx.struct_gep(llval, 1);
|
||||
|
||||
// This is None only for fat pointers, which don't
|
||||
// need any special load-time behavior anyway.
|
||||
let pair_fields = common::type_pair_fields(bcx.ccx(), ty);
|
||||
let (a, b) = if let Some([a_ty, b_ty]) = pair_fields {
|
||||
(base::load_ty_builder(bcx, a_ptr, a_ty),
|
||||
base::load_ty_builder(bcx, b_ptr, b_ty))
|
||||
} else {
|
||||
(bcx.load(a_ptr), bcx.load(b_ptr))
|
||||
};
|
||||
OperandValue::Pair(a, b)
|
||||
} else if common::type_is_immediate(bcx.ccx(), ty) {
|
||||
OperandValue::Immediate(base::load_ty_builder(bcx, llval, ty))
|
||||
} else {
|
||||
OperandValue::Ref(llval)
|
||||
};
|
||||
|
||||
OperandRef { val: val, ty: ty }
|
||||
|
|
@ -131,6 +187,26 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
// Moves out of pair fields are trivial.
|
||||
if let &mir::Lvalue::Projection(ref proj) = lvalue {
|
||||
if let mir::Lvalue::Temp(index) = proj.base {
|
||||
let temp_ref = &self.temps[index as usize];
|
||||
if let &TempRef::Operand(Some(o)) = temp_ref {
|
||||
match (o.val, &proj.elem) {
|
||||
(OperandValue::Pair(a, b),
|
||||
&mir::ProjectionElem::Field(ref f, ty)) => {
|
||||
let llval = [a, b][f.index()];
|
||||
return OperandRef {
|
||||
val: OperandValue::Immediate(llval),
|
||||
ty: bcx.monomorphize(&ty)
|
||||
};
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for most lvalues, to consume them we just load them
|
||||
// out from their home
|
||||
let tr_lvalue = self.trans_lvalue(bcx, lvalue);
|
||||
|
|
@ -173,8 +249,12 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
match operand.val {
|
||||
OperandValue::Ref(r) => base::memcpy_ty(bcx, lldest, r, operand.ty),
|
||||
OperandValue::Immediate(s) => base::store_ty(bcx, s, lldest, operand.ty),
|
||||
OperandValue::FatPtr(data, extra) => {
|
||||
base::store_fat_ptr(bcx, data, extra, lldest, operand.ty);
|
||||
OperandValue::Pair(a, b) => {
|
||||
use build::*;
|
||||
let a = base::from_immediate(bcx, a);
|
||||
let b = base::from_immediate(bcx, b);
|
||||
Store(bcx, a, StructGEP(bcx, lldest, 0));
|
||||
Store(bcx, b, StructGEP(bcx, lldest, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use llvm::ValueRef;
|
||||
use llvm::{self, ValueRef};
|
||||
use rustc::ty::{self, Ty};
|
||||
use rustc::ty::cast::{CastTy, IntTy};
|
||||
use rustc::mir::repr as mir;
|
||||
|
|
@ -16,7 +16,7 @@ use rustc::mir::repr as mir;
|
|||
use asm;
|
||||
use base;
|
||||
use callee::Callee;
|
||||
use common::{self, C_uint, BlockAndBuilder, Result};
|
||||
use common::{self, val_ty, C_bool, C_null, C_uint, BlockAndBuilder, Result};
|
||||
use datum::{Datum, Lvalue};
|
||||
use debuginfo::DebugLoc;
|
||||
use adt;
|
||||
|
|
@ -27,6 +27,7 @@ use value::Value;
|
|||
use Disr;
|
||||
|
||||
use super::MirContext;
|
||||
use super::constant::const_scalar_checked_binop;
|
||||
use super::operand::{OperandRef, OperandValue};
|
||||
use super::lvalue::{LvalueRef, get_dataptr, get_meta};
|
||||
|
||||
|
|
@ -66,9 +67,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
// `CoerceUnsized` can be passed by a where-clause,
|
||||
// so the (generic) MIR may not be able to expand it.
|
||||
let operand = self.trans_operand(&bcx, source);
|
||||
let operand = operand.pack_if_pair(&bcx);
|
||||
bcx.with_block(|bcx| {
|
||||
match operand.val {
|
||||
OperandValue::FatPtr(..) => bug!(),
|
||||
OperandValue::Pair(..) => bug!(),
|
||||
OperandValue::Immediate(llval) => {
|
||||
// unsize from an immediate structure. We don't
|
||||
// really need a temporary alloca here, but
|
||||
|
|
@ -253,7 +255,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
assert!(common::type_is_fat_ptr(bcx.tcx(), cast_ty));
|
||||
|
||||
match operand.val {
|
||||
OperandValue::FatPtr(lldata, llextra) => {
|
||||
OperandValue::Pair(lldata, llextra) => {
|
||||
// unsize from a fat pointer - this is a
|
||||
// "trait-object-to-supertrait" coercion, for
|
||||
// example,
|
||||
|
|
@ -262,7 +264,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
// the types match up.
|
||||
let llcast_ty = type_of::fat_ptr_base_ty(bcx.ccx(), cast_ty);
|
||||
let lldata = bcx.pointercast(lldata, llcast_ty);
|
||||
OperandValue::FatPtr(lldata, llextra)
|
||||
OperandValue::Pair(lldata, llextra)
|
||||
}
|
||||
OperandValue::Immediate(lldata) => {
|
||||
// "standard" unsize
|
||||
|
|
@ -270,7 +272,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
base::unsize_thin_ptr(bcx, lldata,
|
||||
operand.ty, cast_ty)
|
||||
});
|
||||
OperandValue::FatPtr(lldata, llextra)
|
||||
OperandValue::Pair(lldata, llextra)
|
||||
}
|
||||
OperandValue::Ref(_) => {
|
||||
bug!("by-ref operand {:?} in trans_rvalue_operand",
|
||||
|
|
@ -341,13 +343,13 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
mir::CastKind::Misc => { // Casts from a fat-ptr.
|
||||
let ll_cast_ty = type_of::immediate_type_of(bcx.ccx(), cast_ty);
|
||||
let ll_from_ty = type_of::immediate_type_of(bcx.ccx(), operand.ty);
|
||||
if let OperandValue::FatPtr(data_ptr, meta_ptr) = operand.val {
|
||||
if let OperandValue::Pair(data_ptr, meta_ptr) = operand.val {
|
||||
if common::type_is_fat_ptr(bcx.tcx(), cast_ty) {
|
||||
let ll_cft = ll_cast_ty.field_types();
|
||||
let ll_fft = ll_from_ty.field_types();
|
||||
let data_cast = bcx.pointercast(data_ptr, ll_cft[0]);
|
||||
assert_eq!(ll_cft[1].kind(), ll_fft[1].kind());
|
||||
OperandValue::FatPtr(data_cast, meta_ptr)
|
||||
OperandValue::Pair(data_cast, meta_ptr)
|
||||
} else { // cast to thin-ptr
|
||||
// Cast of fat-ptr to thin-ptr is an extraction of data-ptr and
|
||||
// pointer-cast of that pointer to desired pointer type.
|
||||
|
|
@ -355,7 +357,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
OperandValue::Immediate(llval)
|
||||
}
|
||||
} else {
|
||||
bug!("Unexpected non-FatPtr operand")
|
||||
bug!("Unexpected non-Pair operand")
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -384,8 +386,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
}
|
||||
} else {
|
||||
OperandRef {
|
||||
val: OperandValue::FatPtr(tr_lvalue.llval,
|
||||
tr_lvalue.llextra),
|
||||
val: OperandValue::Pair(tr_lvalue.llval,
|
||||
tr_lvalue.llextra),
|
||||
ty: ref_ty,
|
||||
}
|
||||
};
|
||||
|
|
@ -406,8 +408,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
let rhs = self.trans_operand(&bcx, rhs);
|
||||
let llresult = if common::type_is_fat_ptr(bcx.tcx(), lhs.ty) {
|
||||
match (lhs.val, rhs.val) {
|
||||
(OperandValue::FatPtr(lhs_addr, lhs_extra),
|
||||
OperandValue::FatPtr(rhs_addr, rhs_extra)) => {
|
||||
(OperandValue::Pair(lhs_addr, lhs_extra),
|
||||
OperandValue::Pair(rhs_addr, rhs_extra)) => {
|
||||
bcx.with_block(|bcx| {
|
||||
base::compare_fat_ptrs(bcx,
|
||||
lhs_addr, lhs_extra,
|
||||
|
|
@ -430,6 +432,21 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
};
|
||||
(bcx, operand)
|
||||
}
|
||||
mir::Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) => {
|
||||
let lhs = self.trans_operand(&bcx, lhs);
|
||||
let rhs = self.trans_operand(&bcx, rhs);
|
||||
let result = self.trans_scalar_checked_binop(&bcx, op,
|
||||
lhs.immediate(), rhs.immediate(),
|
||||
lhs.ty);
|
||||
let val_ty = self.mir.binop_ty(bcx.tcx(), op, lhs.ty, rhs.ty);
|
||||
let operand_ty = bcx.tcx().mk_tup(vec![val_ty, bcx.tcx().types.bool]);
|
||||
let operand = OperandRef {
|
||||
val: result,
|
||||
ty: operand_ty
|
||||
};
|
||||
|
||||
(bcx, operand)
|
||||
}
|
||||
|
||||
mir::Rvalue::UnaryOp(op, ref operand) => {
|
||||
let operand = self.trans_operand(&bcx, operand);
|
||||
|
|
@ -556,6 +573,68 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trans_scalar_checked_binop(&mut self,
|
||||
bcx: &BlockAndBuilder<'bcx, 'tcx>,
|
||||
op: mir::BinOp,
|
||||
lhs: ValueRef,
|
||||
rhs: ValueRef,
|
||||
input_ty: Ty<'tcx>) -> OperandValue {
|
||||
// This case can currently arise only from functions marked
|
||||
// with #[rustc_inherit_overflow_checks] and inlined from
|
||||
// another crate (mostly core::num generic/#[inline] fns),
|
||||
// while the current crate doesn't use overflow checks.
|
||||
if !bcx.ccx().check_overflow() {
|
||||
let val = self.trans_scalar_binop(bcx, op, lhs, rhs, input_ty);
|
||||
return OperandValue::Pair(val, C_bool(bcx.ccx(), false));
|
||||
}
|
||||
|
||||
// First try performing the operation on constants, which
|
||||
// will only succeed if both operands are constant.
|
||||
// This is necessary to determine when an overflow Assert
|
||||
// will always panic at runtime, and produce a warning.
|
||||
match const_scalar_checked_binop(bcx.tcx(), op, lhs, rhs, input_ty) {
|
||||
Some((val, of)) => {
|
||||
return OperandValue::Pair(val, C_bool(bcx.ccx(), of));
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
let (val, of) = match op {
|
||||
// These are checked using intrinsics
|
||||
mir::BinOp::Add | mir::BinOp::Sub | mir::BinOp::Mul => {
|
||||
let oop = match op {
|
||||
mir::BinOp::Add => OverflowOp::Add,
|
||||
mir::BinOp::Sub => OverflowOp::Sub,
|
||||
mir::BinOp::Mul => OverflowOp::Mul,
|
||||
_ => unreachable!()
|
||||
};
|
||||
let intrinsic = get_overflow_intrinsic(oop, bcx, input_ty);
|
||||
let res = bcx.call(intrinsic, &[lhs, rhs], None);
|
||||
|
||||
(bcx.extract_value(res, 0),
|
||||
bcx.extract_value(res, 1))
|
||||
}
|
||||
mir::BinOp::Shl | mir::BinOp::Shr => {
|
||||
let lhs_llty = val_ty(lhs);
|
||||
let rhs_llty = val_ty(rhs);
|
||||
let invert_mask = bcx.with_block(|bcx| {
|
||||
common::shift_mask_val(bcx, lhs_llty, rhs_llty, true)
|
||||
});
|
||||
let outer_bits = bcx.and(rhs, invert_mask);
|
||||
|
||||
let of = bcx.icmp(llvm::IntNE, outer_bits, C_null(rhs_llty));
|
||||
let val = self.trans_scalar_binop(bcx, op, lhs, rhs, input_ty);
|
||||
|
||||
(val, of)
|
||||
}
|
||||
_ => {
|
||||
bug!("Operator `{:?}` is not a checkable operator", op)
|
||||
}
|
||||
};
|
||||
|
||||
OperandValue::Pair(val, of)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rvalue_creates_operand<'bcx, 'tcx>(_mir: &mir::Mir<'tcx>,
|
||||
|
|
@ -566,6 +645,7 @@ pub fn rvalue_creates_operand<'bcx, 'tcx>(_mir: &mir::Mir<'tcx>,
|
|||
mir::Rvalue::Len(..) |
|
||||
mir::Rvalue::Cast(..) | // (*)
|
||||
mir::Rvalue::BinaryOp(..) |
|
||||
mir::Rvalue::CheckedBinaryOp(..) |
|
||||
mir::Rvalue::UnaryOp(..) |
|
||||
mir::Rvalue::Box(..) |
|
||||
mir::Rvalue::Use(..) =>
|
||||
|
|
@ -579,3 +659,75 @@ pub fn rvalue_creates_operand<'bcx, 'tcx>(_mir: &mir::Mir<'tcx>,
|
|||
|
||||
// (*) this is only true if the type is suitable
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum OverflowOp {
|
||||
Add, Sub, Mul
|
||||
}
|
||||
|
||||
fn get_overflow_intrinsic(oop: OverflowOp, bcx: &BlockAndBuilder, ty: Ty) -> ValueRef {
|
||||
use syntax::ast::IntTy::*;
|
||||
use syntax::ast::UintTy::*;
|
||||
use rustc::ty::{TyInt, TyUint};
|
||||
|
||||
let tcx = bcx.tcx();
|
||||
|
||||
let new_sty = match ty.sty {
|
||||
TyInt(Is) => match &tcx.sess.target.target.target_pointer_width[..] {
|
||||
"32" => TyInt(I32),
|
||||
"64" => TyInt(I64),
|
||||
_ => panic!("unsupported target word size")
|
||||
},
|
||||
TyUint(Us) => match &tcx.sess.target.target.target_pointer_width[..] {
|
||||
"32" => TyUint(U32),
|
||||
"64" => TyUint(U64),
|
||||
_ => panic!("unsupported target word size")
|
||||
},
|
||||
ref t @ TyUint(_) | ref t @ TyInt(_) => t.clone(),
|
||||
_ => panic!("tried to get overflow intrinsic for op applied to non-int type")
|
||||
};
|
||||
|
||||
let name = match oop {
|
||||
OverflowOp::Add => match new_sty {
|
||||
TyInt(I8) => "llvm.sadd.with.overflow.i8",
|
||||
TyInt(I16) => "llvm.sadd.with.overflow.i16",
|
||||
TyInt(I32) => "llvm.sadd.with.overflow.i32",
|
||||
TyInt(I64) => "llvm.sadd.with.overflow.i64",
|
||||
|
||||
TyUint(U8) => "llvm.uadd.with.overflow.i8",
|
||||
TyUint(U16) => "llvm.uadd.with.overflow.i16",
|
||||
TyUint(U32) => "llvm.uadd.with.overflow.i32",
|
||||
TyUint(U64) => "llvm.uadd.with.overflow.i64",
|
||||
|
||||
_ => unreachable!(),
|
||||
},
|
||||
OverflowOp::Sub => match new_sty {
|
||||
TyInt(I8) => "llvm.ssub.with.overflow.i8",
|
||||
TyInt(I16) => "llvm.ssub.with.overflow.i16",
|
||||
TyInt(I32) => "llvm.ssub.with.overflow.i32",
|
||||
TyInt(I64) => "llvm.ssub.with.overflow.i64",
|
||||
|
||||
TyUint(U8) => "llvm.usub.with.overflow.i8",
|
||||
TyUint(U16) => "llvm.usub.with.overflow.i16",
|
||||
TyUint(U32) => "llvm.usub.with.overflow.i32",
|
||||
TyUint(U64) => "llvm.usub.with.overflow.i64",
|
||||
|
||||
_ => unreachable!(),
|
||||
},
|
||||
OverflowOp::Mul => match new_sty {
|
||||
TyInt(I8) => "llvm.smul.with.overflow.i8",
|
||||
TyInt(I16) => "llvm.smul.with.overflow.i16",
|
||||
TyInt(I32) => "llvm.smul.with.overflow.i32",
|
||||
TyInt(I64) => "llvm.smul.with.overflow.i64",
|
||||
|
||||
TyUint(U8) => "llvm.umul.with.overflow.i8",
|
||||
TyUint(U16) => "llvm.umul.with.overflow.i16",
|
||||
TyUint(U32) => "llvm.umul.with.overflow.i32",
|
||||
TyUint(U64) => "llvm.umul.with.overflow.i64",
|
||||
|
||||
_ => unreachable!(),
|
||||
},
|
||||
};
|
||||
|
||||
bcx.ccx().get_intrinsic(&name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -502,6 +502,13 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat
|
|||
is just used to make tests pass \
|
||||
and will never be stable",
|
||||
cfg_fn!(rustc_attrs))),
|
||||
("rustc_inherit_overflow_checks", Whitelisted, Gated("rustc_attrs",
|
||||
"the `#[rustc_inherit_overflow_checks]` \
|
||||
attribute is just used to control \
|
||||
overflow checking behavior of several \
|
||||
libcore functions that are inlined \
|
||||
across crates and will never be stable",
|
||||
cfg_fn!(rustc_attrs))),
|
||||
|
||||
("allow_internal_unstable", Normal, Gated("allow_internal_unstable",
|
||||
EXPLAIN_ALLOW_INTERNAL_UNSTABLE,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
const A: &'static [i32] = &[];
|
||||
const B: i32 = (&A)[1];
|
||||
//~^ ERROR: array index out of bounds
|
||||
//~^ ERROR index out of bounds: the len is 0 but the index is 1
|
||||
|
||||
fn main() {
|
||||
let _ = B;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@
|
|||
// except according to those terms.
|
||||
|
||||
const A: [i32; 0] = [];
|
||||
const B: i32 = A[1]; //~ ERROR: array index out of bounds
|
||||
const B: i32 = A[1];
|
||||
//~^ ERROR index out of bounds: the len is 0 but the index is 1
|
||||
|
||||
fn main() {
|
||||
let _ = B;
|
||||
|
|
|
|||
|
|
@ -8,13 +8,15 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// ignore-tidy-linelength
|
||||
|
||||
#![feature(const_indexing)]
|
||||
|
||||
const FOO: [u32; 3] = [1, 2, 3];
|
||||
const BAR: u32 = FOO[5]; // no error, because the error below occurs before regular const eval
|
||||
|
||||
const BLUB: [u32; FOO[4]] = [5, 6];
|
||||
//~^ ERROR array length constant evaluation error: array index out of bounds [E0250]
|
||||
//~^ ERROR array length constant evaluation error: index out of bounds: the len is 3 but the index is 4 [E0250]
|
||||
|
||||
fn main() {
|
||||
let _ = BAR;
|
||||
|
|
|
|||
|
|
@ -15,8 +15,10 @@ pub const A: i8 = -std::i8::MIN; //~ ERROR attempted to negate with overflow
|
|||
pub const B: u8 = 200u8 + 200u8; //~ ERROR attempted to add with overflow
|
||||
pub const C: u8 = 200u8 * 4; //~ ERROR attempted to multiply with overflow
|
||||
pub const D: u8 = 42u8 - (42u8 + 1); //~ ERROR attempted to subtract with overflow
|
||||
pub const E: u8 = [5u8][1]; //~ ERROR index out of bounds
|
||||
pub const E: u8 = [5u8][1];
|
||||
//~^ ERROR index out of bounds: the len is 1 but the index is 1
|
||||
|
||||
fn main() {
|
||||
let _e = [6u8][1]; //~ ERROR: array index out of bounds
|
||||
let _e = [6u8][1];
|
||||
//~^ ERROR index out of bounds: the len is 1 but the index is 1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,11 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// compile-flags: -Zforce-overflow-checks=on
|
||||
|
||||
// these errors are not actually "const_err", they occur in trans/consts
|
||||
// and are unconditional warnings that can't be denied or allowed
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#![allow(exceeding_bitshifts)]
|
||||
#![allow(const_err)]
|
||||
|
||||
|
|
@ -21,10 +22,9 @@ fn black_box<T>(_: T) {
|
|||
|
||||
// Make sure that the two uses get two errors.
|
||||
const FOO: u8 = [5u8][1];
|
||||
//~^ ERROR array index out of bounds
|
||||
//~^^ ERROR array index out of bounds
|
||||
//~^ ERROR index out of bounds: the len is 1 but the index is 1
|
||||
//~^^ ERROR index out of bounds: the len is 1 but the index is 1
|
||||
|
||||
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
|
||||
fn main() {
|
||||
let a = -std::i8::MIN;
|
||||
//~^ WARN attempted to negate with overflow
|
||||
|
|
@ -36,7 +36,7 @@ fn main() {
|
|||
let d = 42u8 - (42u8 + 1);
|
||||
//~^ WARN attempted to subtract with overflow
|
||||
let _e = [5u8][1];
|
||||
//~^ WARN array index out of bounds
|
||||
//~^ WARN index out of bounds: the len is 1 but the index is 1
|
||||
black_box(a);
|
||||
black_box(b);
|
||||
black_box(c);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#![allow(unused_imports)]
|
||||
|
||||
// Note: the relevant lint pass here runs before some of the constant
|
||||
|
|
@ -104,7 +103,6 @@ const VALS_U64: (u64, u64, u64, u64) =
|
|||
//~^ ERROR attempted to multiply with overflow
|
||||
);
|
||||
|
||||
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
|
||||
fn main() {
|
||||
foo(VALS_I8);
|
||||
foo(VALS_I16);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@
|
|||
// except according to those terms.
|
||||
|
||||
const FOO: &'static[u32] = &[1, 2, 3];
|
||||
const BAR: u32 = FOO[5]; //~ ERROR array index out of bounds
|
||||
const BAR: u32 = FOO[5];
|
||||
//~^ ERROR index out of bounds: the len is 3 but the index is 5
|
||||
|
||||
fn main() {
|
||||
let _ = BAR;
|
||||
|
|
|
|||
|
|
@ -12,8 +12,6 @@
|
|||
|
||||
// error-pattern:attempted to divide by zero
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
|
||||
fn main() {
|
||||
let y = 0;
|
||||
let _z = 1 / y;
|
||||
|
|
|
|||
|
|
@ -10,10 +10,8 @@
|
|||
|
||||
// ignore-pretty : (#23623) problems when ending with // comments
|
||||
|
||||
// error-pattern:attempted remainder with a divisor of zero
|
||||
// error-pattern:attempted to calculate the remainder with a divisor of zero
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
|
||||
fn main() {
|
||||
let y = 0;
|
||||
let _z = 1 % y;
|
||||
|
|
|
|||
|
|
@ -10,12 +10,10 @@
|
|||
|
||||
// ignore-pretty : (#23623) problems when ending with // comments
|
||||
|
||||
// error-pattern:thread 'main' panicked at 'arithmetic operation overflowed'
|
||||
// error-pattern:thread 'main' panicked at 'attempted to add with overflow'
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
|
||||
fn main() {
|
||||
let _x = 200u8 + 200u8 + 200u8;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,13 +10,11 @@
|
|||
|
||||
// ignore-pretty : (#23623) problems when ending with // comments
|
||||
|
||||
// error-pattern:thread 'main' panicked at 'shift operation overflowed'
|
||||
// error-pattern:thread 'main' panicked at 'attempted to shift left with overflow'
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
#![warn(exceeding_bitshifts)]
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
|
||||
fn main() {
|
||||
let _x = 1_i32 << 32;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,13 +10,11 @@
|
|||
|
||||
// ignore-pretty : (#23623) problems when ending with // comments
|
||||
|
||||
// error-pattern:thread 'main' panicked at 'shift operation overflowed'
|
||||
// error-pattern:thread 'main' panicked at 'attempted to shift left with overflow'
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
#![warn(exceeding_bitshifts)]
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
|
||||
fn main() {
|
||||
let _x = 1 << -1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,13 +10,11 @@
|
|||
|
||||
// ignore-pretty : (#23623) problems when ending with // comments
|
||||
|
||||
// error-pattern:thread 'main' panicked at 'shift operation overflowed'
|
||||
// error-pattern:thread 'main' panicked at 'attempted to shift left with overflow'
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
#![warn(exceeding_bitshifts)]
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
|
||||
fn main() {
|
||||
let _x = 1_u64 << 64;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
// ignore-pretty : (#23623) problems when ending with // comments
|
||||
|
||||
// error-pattern:thread 'main' panicked at 'shift operation overflowed'
|
||||
// error-pattern:thread 'main' panicked at 'attempted to shift left with overflow'
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
// This function is checking that our automatic truncation does not
|
||||
|
|
@ -18,8 +18,6 @@
|
|||
|
||||
#![warn(exceeding_bitshifts)]
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
|
||||
fn main() {
|
||||
// this signals overflow when checking is on
|
||||
let x = 1_i8 << 17;
|
||||
|
|
|
|||
|
|
@ -10,11 +10,9 @@
|
|||
|
||||
// ignore-pretty : (#23623) problems when ending with // comments
|
||||
|
||||
// error-pattern:thread 'main' panicked at 'arithmetic operation overflowed'
|
||||
// error-pattern:thread 'main' panicked at 'attempted to multiply with overflow'
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
|
||||
fn main() {
|
||||
let x = 200u8 * 4;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,8 +13,6 @@
|
|||
// error-pattern:thread 'main' panicked at 'attempted to negate with overflow'
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
|
||||
fn main() {
|
||||
let _x = -std::i8::MIN;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// error-pattern:thread 'main' panicked at 'arithmetic operation overflowed'
|
||||
// error-pattern:thread 'main' panicked at 'attempted to multiply with overflow'
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
fn main() {
|
||||
|
|
|
|||
|
|
@ -10,13 +10,11 @@
|
|||
|
||||
// ignore-pretty : (#23623) problems when ending with // comments
|
||||
|
||||
// error-pattern:thread 'main' panicked at 'shift operation overflowed'
|
||||
// error-pattern:thread 'main' panicked at 'attempted to shift right with overflow'
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
#![warn(exceeding_bitshifts)]
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
|
||||
fn main() {
|
||||
let _x = -1_i32 >> 32;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,13 +10,11 @@
|
|||
|
||||
// ignore-pretty : (#23623) problems when ending with // comments
|
||||
|
||||
// error-pattern:thread 'main' panicked at 'shift operation overflowed'
|
||||
// error-pattern:thread 'main' panicked at 'attempted to shift right with overflow'
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
#![warn(exceeding_bitshifts)]
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
|
||||
fn main() {
|
||||
let _x = -1_i32 >> -1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,13 +10,11 @@
|
|||
|
||||
// ignore-pretty : (#23623) problems when ending with // comments
|
||||
|
||||
// error-pattern:thread 'main' panicked at 'shift operation overflowed'
|
||||
// error-pattern:thread 'main' panicked at 'attempted to shift right with overflow'
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
#![warn(exceeding_bitshifts)]
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
|
||||
fn main() {
|
||||
let _x = -1_i64 >> 64;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
// ignore-pretty : (#23623) problems when ending with // comments
|
||||
|
||||
// error-pattern:thread 'main' panicked at 'shift operation overflowed'
|
||||
// error-pattern:thread 'main' panicked at 'attempted to shift right with overflow'
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
// This function is checking that our (type-based) automatic
|
||||
|
|
@ -18,8 +18,6 @@
|
|||
|
||||
#![warn(exceeding_bitshifts)]
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
|
||||
fn main() {
|
||||
// this signals overflow when checking is on
|
||||
let x = 2_i8 >> 17;
|
||||
|
|
|
|||
|
|
@ -10,13 +10,11 @@
|
|||
|
||||
// ignore-pretty : (#23623) problems when ending with // comments
|
||||
|
||||
// error-pattern:thread 'main' panicked at 'shift operation overflowed'
|
||||
// error-pattern:thread 'main' panicked at 'attempted to shift right with overflow'
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
#![warn(exceeding_bitshifts)]
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
|
||||
fn main() {
|
||||
let _n = 1i64 >> [64][0];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,14 +10,12 @@
|
|||
|
||||
// ignore-pretty : (#23623) problems when ending with // comments
|
||||
|
||||
// error-pattern:thread 'main' panicked at 'shift operation overflowed'
|
||||
// error-pattern:thread 'main' panicked at 'attempted to shift right with overflow'
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
#![warn(exceeding_bitshifts)]
|
||||
#![feature(const_indexing)]
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
|
||||
fn main() {
|
||||
let _n = 1i64 >> [64][0];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,9 @@
|
|||
|
||||
// ignore-pretty : (#23623) problems when ending with // comments
|
||||
|
||||
// error-pattern:thread 'main' panicked at 'arithmetic operation overflowed'
|
||||
// error-pattern:thread 'main' panicked at 'attempted to subtract with overflow'
|
||||
// compile-flags: -C debug-assertions
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
|
||||
fn main() {
|
||||
let _x = 42u8 - (42u8 + 1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ fn debug_assert() {
|
|||
}
|
||||
|
||||
fn overflow() {
|
||||
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
|
||||
fn add(a: u8, b: u8) -> u8 { a + b }
|
||||
|
||||
add(200u8, 200u8);
|
||||
|
|
|
|||
|
|
@ -19,13 +19,11 @@ use std::thread;
|
|||
macro_rules! check {
|
||||
($($e:expr),*) => {
|
||||
$(assert!(thread::spawn({
|
||||
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
|
||||
move|| { $e; }
|
||||
}).join().is_err());)*
|
||||
}
|
||||
}
|
||||
|
||||
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
|
||||
fn main() {
|
||||
check![
|
||||
isize::min_value() / -1,
|
||||
|
|
|
|||
34
src/test/run-pass/mir_cast_fn_ret.rs
Normal file
34
src/test/run-pass/mir_cast_fn_ret.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright 2016 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(rustc_attrs)]
|
||||
|
||||
pub extern "C" fn tuple2() -> (u16, u8) {
|
||||
(1, 2)
|
||||
}
|
||||
|
||||
pub extern "C" fn tuple3() -> (u8, u8, u8) {
|
||||
(1, 2, 3)
|
||||
}
|
||||
|
||||
#[rustc_mir]
|
||||
pub fn test2() -> u8 {
|
||||
tuple2().1
|
||||
}
|
||||
|
||||
#[rustc_mir]
|
||||
pub fn test3() -> u8 {
|
||||
tuple3().2
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(test2(), 2);
|
||||
assert_eq!(test3(), 3);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue