Don't use the undefined bytes of PrimVal::Bytes

This commit is contained in:
Oliver Schneider 2018-02-21 22:02:52 +01:00 committed by Oliver Schneider
parent 7218836500
commit df283df887
No known key found for this signature in database
GPG Key ID: A69F8D225B3AD7D9
12 changed files with 260 additions and 282 deletions

View File

@ -10,7 +10,7 @@ mod value;
pub use self::error::{EvalError, EvalResult, EvalErrorKind};
pub use self::value::{PrimVal, PrimValKind, Value, Pointer, bytes_to_f32, bytes_to_f64};
pub use self::value::{PrimVal, PrimValKind, Value, Pointer};
use std::collections::BTreeMap;
use std::fmt;

View File

@ -4,22 +4,6 @@ use ty::layout::{Align, HasDataLayout};
use ty;
use super::{EvalResult, MemoryPointer, PointerArithmetic};
use syntax::ast::FloatTy;
use rustc_const_math::ConstFloat;
pub fn bytes_to_f32(bits: u128) -> ConstFloat {
ConstFloat {
bits,
ty: FloatTy::F32,
}
}
pub fn bytes_to_f64(bits: u128) -> ConstFloat {
ConstFloat {
bits,
ty: FloatTy::F64,
}
}
/// A `Value` represents a single self-contained Rust value.
///
@ -182,10 +166,6 @@ impl<'tcx> PrimVal {
PrimVal::Bytes(n as u128)
}
pub fn from_float(f: ConstFloat) -> Self {
PrimVal::Bytes(f.bits)
}
pub fn from_bool(b: bool) -> Self {
PrimVal::Bytes(b as u128)
}
@ -260,14 +240,6 @@ impl<'tcx> PrimVal {
})
}
pub fn to_f32(self) -> EvalResult<'tcx, ConstFloat> {
self.to_bytes().map(bytes_to_f32)
}
pub fn to_f64(self) -> EvalResult<'tcx, ConstFloat> {
self.to_bytes().map(bytes_to_f64)
}
pub fn to_bool(self) -> EvalResult<'tcx, bool> {
match self.to_bytes()? {
0 => Ok(false),

View File

@ -1845,10 +1845,34 @@ impl<'a, 'gcx, 'tcx> AdtDef {
..
}) => {
trace!("discriminants: {} ({:?})", b, repr_type);
discr = Discr {
val: b,
ty: repr_type.to_ty(tcx),
};
let ty = repr_type.to_ty(tcx);
if ty.is_signed() {
let (ty, param_env) = tcx
.lift_to_global(&(ty, param_env))
.unwrap_or_else(|| {
bug!("MIR: discriminants({:?}, {:?}) got \
type with inference types/regions",
ty, param_env);
});
let size = tcx.global_tcx()
.layout_of(param_env.and(ty))
.expect("int layout")
.size
.bits();
let val = b as i128;
// sign extend to i128
let amt = 128 - size;
let val = (val << amt) >> amt;
discr = Discr {
val: val as u128,
ty,
};
} else {
discr = Discr {
val: b,
ty,
};
}
}
_ => {
if !expr_did.is_local() {

View File

@ -22,7 +22,6 @@ use rustc::middle::region;
use rustc::ty::{self, Ty};
use rustc::mir::*;
use rustc::mir::interpret::{Value, PrimVal};
use syntax::ast;
use syntax_pos::Span;
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
@ -382,9 +381,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// Helper to get a `-1` value of the appropriate type
fn neg_1_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> {
let bits = self.hir.type_bit_size(ty);
let n = (!0u128) >> (128 - bits);
let literal = Literal::Value {
value: self.hir.tcx().mk_const(ty::Const {
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(-1i128 as u128))),
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(n))),
ty
})
};
@ -394,31 +395,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// Helper to get the minimum value of the appropriate type
fn minval_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> {
let literal = match ty.sty {
ty::TyInt(ity) => {
let ity = match ity {
ast::IntTy::Isize => self.hir.tcx().sess.target.isize_ty,
other => other,
};
let val = match ity {
ast::IntTy::I8 => i8::min_value() as i128,
ast::IntTy::I16 => i16::min_value() as i128,
ast::IntTy::I32 => i32::min_value() as i128,
ast::IntTy::I64 => i64::min_value() as i128,
ast::IntTy::I128 => i128::min_value() as i128,
ast::IntTy::Isize => unreachable!(),
};
Literal::Value {
value: self.hir.tcx().mk_const(ty::Const {
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(val as u128))),
ty
})
}
}
_ => {
span_bug!(span, "Invalid type for minval_literal: `{:?}`", ty)
}
assert!(ty.is_signed());
let bits = self.hir.type_bit_size(ty);
let n = 1 << (bits - 1);
let literal = Literal::Value {
value: self.hir.tcx().mk_const(ty::Const {
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(n))),
ty
})
};
self.literal_operand(span, ty, literal)

View File

@ -149,6 +149,26 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
}
}
pub fn type_bit_size(
&self,
ty: Ty<'tcx>,
) -> u64 {
let tcx = self.tcx.global_tcx();
let (ty, param_env) = self
.tcx
.lift_to_global(&(ty, self.param_env))
.unwrap_or_else(|| {
bug!("MIR: Cx::const_eval_literal({:?}, {:?}) got \
type with inference types/regions",
ty, self.param_env);
});
tcx
.layout_of(param_env.and(ty))
.expect("int layout")
.size
.bits()
}
pub fn const_eval_literal(
&mut self,
lit: &'tcx ast::LitKind,
@ -156,6 +176,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
sp: Span,
neg: bool,
) -> Literal<'tcx> {
trace!("const_eval_literal: {:#?}, {:?}, {:?}, {:?}", lit, ty, sp, neg);
let tcx = self.tcx.global_tcx();
let parse_float = |num: &str, fty| -> ConstFloat {
@ -165,6 +186,15 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
})
};
let clamp = |n| {
let size = self.type_bit_size(ty);
trace!("clamp {} with size {} and amt {}", n, size, 128 - size);
let amt = 128 - size;
let result = (n << amt) >> amt;
trace!("clamp result: {}", result);
result
};
use rustc::mir::interpret::*;
let lit = match *lit {
LitKind::Str(ref s, _) => {
@ -185,9 +215,10 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
LitKind::Int(n, _) if neg => {
let n = n as i128;
let n = n.overflowing_neg().0;
Value::ByVal(PrimVal::Bytes(n as u128))
let n = clamp(n as u128);
Value::ByVal(PrimVal::Bytes(n))
},
LitKind::Int(n, _) => Value::ByVal(PrimVal::Bytes(n)),
LitKind::Int(n, _) => Value::ByVal(PrimVal::Bytes(clamp(n))),
LitKind::Float(n, fty) => {
let n = n.as_str();
let mut f = parse_float(&n, fty);

View File

@ -14,75 +14,37 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
src_ty: Ty<'tcx>,
dest_ty: Ty<'tcx>,
) -> EvalResult<'tcx, PrimVal> {
use rustc::ty::TypeVariants::*;
trace!("Casting {:?}: {:?} to {:?}", val, src_ty, dest_ty);
let src_kind = self.ty_to_primval_kind(src_ty)?;
match val {
PrimVal::Undef => Ok(PrimVal::Undef),
PrimVal::Ptr(ptr) => self.cast_from_ptr(ptr, dest_ty),
val @ PrimVal::Bytes(_) => {
use rustc::mir::interpret::PrimValKind::*;
match src_kind {
F32 => self.cast_from_float(val.to_f32()?, dest_ty),
F64 => self.cast_from_float(val.to_f64()?, dest_ty),
I8 | I16 | I32 | I64 | I128 => {
self.cast_from_signed_int(val.to_i128()?, dest_ty)
}
Bool | Char | U8 | U16 | U32 | U64 | U128 | FnPtr | Ptr => {
self.cast_from_int(val.to_u128()?, dest_ty, false)
}
PrimVal::Bytes(b) => {
match src_ty.sty {
TyFloat(fty) => self.cast_from_float(b, fty, dest_ty),
_ => self.cast_from_int(b, src_ty, dest_ty),
}
}
}
}
fn cast_from_signed_int(&self, val: i128, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
self.cast_from_int(val as u128, ty, val < 0)
}
fn int_to_int(&self, v: i128, ty: IntTy) -> u128 {
match ty {
IntTy::I8 => v as i8 as u128,
IntTy::I16 => v as i16 as u128,
IntTy::I32 => v as i32 as u128,
IntTy::I64 => v as i64 as u128,
IntTy::I128 => v as u128,
IntTy::Isize => {
let ty = self.tcx.sess.target.isize_ty;
self.int_to_int(v, ty)
}
}
}
fn int_to_uint(&self, v: u128, ty: UintTy) -> u128 {
match ty {
UintTy::U8 => v as u8 as u128,
UintTy::U16 => v as u16 as u128,
UintTy::U32 => v as u32 as u128,
UintTy::U64 => v as u64 as u128,
UintTy::U128 => v,
UintTy::Usize => {
let ty = self.tcx.sess.target.usize_ty;
self.int_to_uint(v, ty)
}
}
}
fn cast_from_int(
&self,
v: u128,
ty: Ty<'tcx>,
negative: bool,
src_ty: Ty<'tcx>,
dest_ty: Ty<'tcx>,
) -> EvalResult<'tcx, PrimVal> {
trace!("cast_from_int: {}, {}, {}", v, ty, negative);
trace!("cast_from_int: {}, {}, {}", v, src_ty, dest_ty);
use rustc::ty::TypeVariants::*;
match ty.sty {
// Casts to bool are not permitted by rustc, no need to handle them here.
TyInt(ty) => Ok(PrimVal::Bytes(self.int_to_int(v as i128, ty))),
TyUint(ty) => Ok(PrimVal::Bytes(self.int_to_uint(v, ty))),
match dest_ty.sty {
TyInt(_) | TyUint(_) => {
let v = self.sign_extend(v, src_ty)?;
let v = self.truncate(v, dest_ty)?;
Ok(PrimVal::Bytes(v))
}
TyFloat(fty) if negative => Ok(PrimVal::Bytes(ConstFloat::from_i128(v as i128, fty).bits)),
TyFloat(fty) if src_ty.is_signed() => Ok(PrimVal::Bytes(ConstFloat::from_i128(v as i128, fty).bits)),
TyFloat(fty) => Ok(PrimVal::Bytes(ConstFloat::from_u128(v, fty).bits)),
TyChar if v as u8 as u128 == v => Ok(PrimVal::Bytes(v)),
@ -91,31 +53,42 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
// No alignment check needed for raw pointers. But we have to truncate to target ptr size.
TyRawPtr(_) => Ok(PrimVal::Bytes(self.memory.truncate_to_ptr(v).0 as u128)),
_ => err!(Unimplemented(format!("int to {:?} cast", ty))),
// Casts to bool are not permitted by rustc, no need to handle them here.
_ => err!(Unimplemented(format!("int to {:?} cast", dest_ty))),
}
}
fn cast_from_float(&self, val: ConstFloat, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
fn cast_from_float(&self, bits: u128, fty: FloatTy, dest_ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
use rustc::ty::TypeVariants::*;
match ty.sty {
use rustc_apfloat::FloatConvert;
match dest_ty.sty {
// float -> uint
TyUint(t) => {
let width = t.bit_width().unwrap_or(self.memory.pointer_size() as usize * 8);
match val.ty {
FloatTy::F32 => Ok(PrimVal::Bytes(Single::from_bits(val.bits).to_u128(width).value)),
FloatTy::F64 => Ok(PrimVal::Bytes(Double::from_bits(val.bits).to_u128(width).value)),
match fty {
FloatTy::F32 => Ok(PrimVal::Bytes(Single::from_bits(bits).to_u128(width).value)),
FloatTy::F64 => Ok(PrimVal::Bytes(Double::from_bits(bits).to_u128(width).value)),
}
},
// float -> int
TyInt(t) => {
let width = t.bit_width().unwrap_or(self.memory.pointer_size() as usize * 8);
match val.ty {
FloatTy::F32 => Ok(PrimVal::from_i128(Single::from_bits(val.bits).to_i128(width).value)),
FloatTy::F64 => Ok(PrimVal::from_i128(Double::from_bits(val.bits).to_i128(width).value)),
match fty {
FloatTy::F32 => Ok(PrimVal::from_i128(Single::from_bits(bits).to_i128(width).value)),
FloatTy::F64 => Ok(PrimVal::from_i128(Double::from_bits(bits).to_i128(width).value)),
}
},
TyFloat(fty) => Ok(PrimVal::from_float(val.convert(fty))),
_ => err!(Unimplemented(format!("float to {:?} cast", ty))),
// f64 -> f32
TyFloat(FloatTy::F32) if fty == FloatTy::F64 => {
Ok(PrimVal::Bytes(Single::to_bits(Double::from_bits(bits).convert(&mut false).value)))
},
// f32 -> f64
TyFloat(FloatTy::F64) if fty == FloatTy::F32 => {
Ok(PrimVal::Bytes(Double::to_bits(Single::from_bits(bits).convert(&mut false).value)))
},
// identity cast
TyFloat(_) => Ok(PrimVal::Bytes(bits)),
_ => err!(Unimplemented(format!("float to {:?} cast", dest_ty))),
}
}

View File

@ -19,7 +19,7 @@ use rustc::mir::interpret::{
};
use super::{Place, PlaceExtra, Memory,
HasMemory, MemoryKind, operator,
HasMemory, MemoryKind,
Machine};
pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
@ -536,10 +536,10 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
UnaryOp(un_op, ref operand) => {
let val = self.eval_operand_to_primval(operand)?;
let kind = self.ty_to_primval_kind(dest_ty)?;
let val = self.unary_op(un_op, val, dest_ty)?;
self.write_primval(
dest,
operator::unary_op(un_op, val, kind)?,
val,
dest_ty,
)?;
}
@ -1677,6 +1677,22 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
self.tcx.sess.err(&e.to_string());
}
}
pub fn sign_extend(&self, value: u128, ty: Ty<'tcx>) -> EvalResult<'tcx, u128> {
let size = self.layout_of(ty)?.size.bits();
// sign extend
let amt = 128 - size;
// shift the unsigned value to the left
// and back to the right as signed (essentially fills with FF on the left)
Ok((((value << amt) as i128) >> amt) as u128)
}
pub fn truncate(&self, value: u128, ty: Ty<'tcx>) -> EvalResult<'tcx, u128> {
let size = self.layout_of(ty)?.size.bits();
let amt = 128 - size;
// truncate (shift left to drop out leftover values, shift right to fill with zeroes)
Ok((value << amt) >> amt)
}
}
impl<'mir, 'tcx> Frame<'mir, 'tcx> {

View File

@ -666,7 +666,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
}
// Now we do the actual reading
let bytes = if signed {
read_target_int(endianness, bytes).unwrap() as u128
let bytes = read_target_int(endianness, bytes).unwrap() as u128;
let amt = 128 - (size * 8);
// truncate (shift left to drop out leftover values, shift right to fill with zeroes)
(bytes << amt) >> amt
} else {
read_target_uint(endianness, bytes).unwrap()
};

View File

@ -30,5 +30,4 @@ pub use self::const_eval::{
pub use self::machine::Machine;
pub use self::operator::unary_op;
pub use self::memory::{write_target_uint, write_target_int, read_target_uint, read_target_int};

View File

@ -1,12 +1,13 @@
use rustc::mir;
use rustc::ty::Ty;
use rustc::ty::{self, Ty};
use rustc_const_math::ConstFloat;
use syntax::ast::FloatTy;
use std::cmp::Ordering;
use rustc::ty::layout::LayoutOf;
use super::{EvalContext, Place, Machine, ValTy};
use rustc::mir::interpret::{EvalResult, PrimVal, PrimValKind, Value, bytes_to_f32, bytes_to_f64};
use rustc::mir::interpret::{EvalResult, PrimVal, Value};
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
fn binop_with_overflow(
@ -55,74 +56,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
}
}
macro_rules! overflow {
(overflowing_div, $l:expr, $r:expr) => ({
let (val, overflowed) = if $r == 0 {
($l, true)
} else {
$l.overflowing_div($r)
};
let primval = PrimVal::Bytes(val as u128);
Ok((primval, overflowed))
});
(overflowing_rem, $l:expr, $r:expr) => ({
let (val, overflowed) = if $r == 0 {
($l, true)
} else {
$l.overflowing_rem($r)
};
let primval = PrimVal::Bytes(val as u128);
Ok((primval, overflowed))
});
($op:ident, $l:expr, $r:expr) => ({
let (val, overflowed) = $l.$op($r);
let primval = PrimVal::Bytes(val as u128);
Ok((primval, overflowed))
})
}
macro_rules! int_arithmetic {
($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({
let l = $l;
let r = $r;
use rustc::mir::interpret::PrimValKind::*;
match $kind {
I8 => overflow!($int_op, l as i8, r as i8),
I16 => overflow!($int_op, l as i16, r as i16),
I32 => overflow!($int_op, l as i32, r as i32),
I64 => overflow!($int_op, l as i64, r as i64),
I128 => overflow!($int_op, l as i128, r as i128),
U8 => overflow!($int_op, l as u8, r as u8),
U16 => overflow!($int_op, l as u16, r as u16),
U32 => overflow!($int_op, l as u32, r as u32),
U64 => overflow!($int_op, l as u64, r as u64),
U128 => overflow!($int_op, l as u128, r as u128),
_ => bug!("int_arithmetic should only be called on int primvals"),
}
})
}
macro_rules! int_shift {
($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({
let l = $l;
let r = $r;
let r_wrapped = r as u32;
match $kind {
I8 => overflow!($int_op, l as i8, r_wrapped),
I16 => overflow!($int_op, l as i16, r_wrapped),
I32 => overflow!($int_op, l as i32, r_wrapped),
I64 => overflow!($int_op, l as i64, r_wrapped),
I128 => overflow!($int_op, l as i128, r_wrapped),
U8 => overflow!($int_op, l as u8, r_wrapped),
U16 => overflow!($int_op, l as u16, r_wrapped),
U32 => overflow!($int_op, l as u32, r_wrapped),
U64 => overflow!($int_op, l as u64, r_wrapped),
U128 => overflow!($int_op, l as u128, r_wrapped),
_ => bug!("int_shift should only be called on int primvals"),
}.map(|(val, over)| (val, over || r != r_wrapped as u128))
})
}
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
/// Returns the result of the specified operation and whether it overflowed.
pub fn binary_op(
@ -134,11 +67,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
right_ty: Ty<'tcx>,
) -> EvalResult<'tcx, (PrimVal, bool)> {
use rustc::mir::BinOp::*;
use rustc::mir::interpret::PrimValKind::*;
let left_kind = self.ty_to_primval_kind(left_ty)?;
let right_kind = self.ty_to_primval_kind(right_ty)?;
//trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind);
trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind);
// I: Handle operations that support pointers
if !left_kind.is_float() && !right_kind.is_float() {
@ -153,11 +85,19 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
// These ops can have an RHS with a different numeric type.
if right_kind.is_int() && (bin_op == Shl || bin_op == Shr) {
return match bin_op {
Shl => int_shift!(left_kind, overflowing_shl, l, r),
Shr => int_shift!(left_kind, overflowing_shr, l, r),
let op: fn(u128, u32) -> (u128, bool) = match bin_op {
Shl => u128::overflowing_shl,
Shr => u128::overflowing_shr,
_ => bug!("it has already been checked that this is a shift op"),
};
let l = if left_ty.is_signed() {
self.sign_extend(l, left_ty)?
} else {
l
};
let (result, oflo) = op(l, r as u32);
let truncated = self.truncate(result, left_ty)?;
return Ok((PrimVal::Bytes(truncated), oflo || truncated != result));
}
if left_kind != right_kind {
@ -197,41 +137,95 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
}
};
let val = match (bin_op, left_kind) {
(_, F32) => float_op(bin_op, l, r, FloatTy::F32),
(_, F64) => float_op(bin_op, l, r, FloatTy::F64),
if left_ty.is_signed() {
let op: Option<fn(&i128, &i128) -> bool> = match bin_op {
Lt => Some(i128::lt),
Le => Some(i128::le),
Gt => Some(i128::gt),
Ge => Some(i128::ge),
_ => None,
};
if let Some(op) = op {
let l = self.sign_extend(l, left_ty)? as i128;
let r = self.sign_extend(r, right_ty)? as i128;
return Ok((PrimVal::from_bool(op(&l, &r)), false));
}
let op: Option<fn(i128, i128) -> (i128, bool)> = match bin_op {
Rem | Div if r == 0 => return Ok((PrimVal::Bytes(l), true)),
Div => Some(i128::overflowing_div),
Rem => Some(i128::overflowing_rem),
Add => Some(i128::overflowing_add),
Sub => Some(i128::overflowing_sub),
Mul => Some(i128::overflowing_mul),
_ => None,
};
if let Some(op) = op {
let l128 = self.sign_extend(l, left_ty)? as i128;
let r = self.sign_extend(r, right_ty)? as i128;
let size = self.layout_of(left_ty)?.size.bits();
match bin_op {
Rem | Div => {
// int_min / -1
if r == -1 && l == (1 << (size - 1)) {
return Ok((PrimVal::Bytes(l), true));
}
},
_ => {},
}
trace!("{}, {}, {}", l, l128, r);
let (result, mut oflo) = op(l128, r);
trace!("{}, {}", result, oflo);
if !oflo && size != 128 {
let max = 1 << (size - 1);
oflo = result >= max || result < -max;
}
let result = result as u128;
let truncated = self.truncate(result, left_ty)?;
return Ok((PrimVal::Bytes(truncated), oflo));
}
}
if let ty::TyFloat(fty) = left_ty.sty {
return Ok((float_op(bin_op, l, r, fty), false));
}
(Eq, _) => PrimVal::from_bool(l == r),
(Ne, _) => PrimVal::from_bool(l != r),
// only ints left
let val = match bin_op {
Eq => PrimVal::from_bool(l == r),
Ne => PrimVal::from_bool(l != r),
(Lt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) < (r as i128)),
(Lt, _) => PrimVal::from_bool(l < r),
(Le, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) <= (r as i128)),
(Le, _) => PrimVal::from_bool(l <= r),
(Gt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) > (r as i128)),
(Gt, _) => PrimVal::from_bool(l > r),
(Ge, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) >= (r as i128)),
(Ge, _) => PrimVal::from_bool(l >= r),
Lt => PrimVal::from_bool(l < r),
Le => PrimVal::from_bool(l <= r),
Gt => PrimVal::from_bool(l > r),
Ge => PrimVal::from_bool(l >= r),
(BitOr, _) => PrimVal::Bytes(l | r),
(BitAnd, _) => PrimVal::Bytes(l & r),
(BitXor, _) => PrimVal::Bytes(l ^ r),
BitOr => PrimVal::Bytes(l | r),
BitAnd => PrimVal::Bytes(l & r),
BitXor => PrimVal::Bytes(l ^ r),
(Add, k) if k.is_int() => return int_arithmetic!(k, overflowing_add, l, r),
(Sub, k) if k.is_int() => return int_arithmetic!(k, overflowing_sub, l, r),
(Mul, k) if k.is_int() => return int_arithmetic!(k, overflowing_mul, l, r),
(Div, k) if k.is_int() => return int_arithmetic!(k, overflowing_div, l, r),
(Rem, k) if k.is_int() => return int_arithmetic!(k, overflowing_rem, l, r),
Add | Sub | Mul | Rem | Div => {
let op: fn(u128, u128) -> (u128, bool) = match bin_op {
Add => u128::overflowing_add,
Sub => u128::overflowing_sub,
Mul => u128::overflowing_mul,
Rem | Div if r == 0 => return Ok((PrimVal::Bytes(l), true)),
Div => u128::overflowing_div,
Rem => u128::overflowing_rem,
_ => bug!(),
};
let (result, oflo) = op(l, r);
let truncated = self.truncate(result, left_ty)?;
return Ok((PrimVal::Bytes(truncated), oflo || truncated != result));
}
_ => {
let msg = format!(
"unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})",
bin_op,
left,
left_kind,
left_ty,
right,
right_kind
right_ty,
);
return err!(Unimplemented(msg));
}
@ -239,52 +233,33 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
Ok((val, false))
}
}
pub fn unary_op<'tcx>(
un_op: mir::UnOp,
val: PrimVal,
val_kind: PrimValKind,
) -> EvalResult<'tcx, PrimVal> {
use rustc::mir::UnOp::*;
use rustc::mir::interpret::PrimValKind::*;
let bytes = val.to_bytes()?;
let result_bytes = match (un_op, val_kind) {
(Not, Bool) => !val.to_bool()? as u128,
(Not, U8) => !(bytes as u8) as u128,
(Not, U16) => !(bytes as u16) as u128,
(Not, U32) => !(bytes as u32) as u128,
(Not, U64) => !(bytes as u64) as u128,
(Not, U128) => !bytes,
(Not, I8) => !(bytes as i8) as u128,
(Not, I16) => !(bytes as i16) as u128,
(Not, I32) => !(bytes as i32) as u128,
(Not, I64) => !(bytes as i64) as u128,
(Not, I128) => !(bytes as i128) as u128,
(Neg, I8) if bytes == i8::min_value() as u128 => return err!(OverflowingMath),
(Neg, I8) => -(bytes as i8) as u128,
(Neg, I16) if bytes == i16::min_value() as u128 => return err!(OverflowingMath),
(Neg, I16) => -(bytes as i16) as u128,
(Neg, I32) if bytes == i32::min_value() as u128 => return err!(OverflowingMath),
(Neg, I32) => -(bytes as i32) as u128,
(Neg, I64) if bytes == i64::min_value() as u128 => return err!(OverflowingMath),
(Neg, I64) => -(bytes as i64) as u128,
(Neg, I128) if bytes == i128::min_value() as u128 => return err!(OverflowingMath),
(Neg, I128) => -(bytes as i128) as u128,
(Neg, F32) => (-bytes_to_f32(bytes)).bits,
(Neg, F64) => (-bytes_to_f64(bytes)).bits,
_ => {
let msg = format!("unimplemented unary op: {:?}, {:?}", un_op, val);
return err!(Unimplemented(msg));
}
};
Ok(PrimVal::Bytes(result_bytes))
pub fn unary_op(
&self,
un_op: mir::UnOp,
val: PrimVal,
ty: Ty<'tcx>,
) -> EvalResult<'tcx, PrimVal> {
use rustc::mir::UnOp::*;
use rustc_apfloat::ieee::{Single, Double};
use rustc_apfloat::Float;
let bytes = val.to_bytes()?;
let size = self.layout_of(ty)?.size.bits();
let result_bytes = match (un_op, &ty.sty) {
(Not, ty::TyBool) => !val.to_bool()? as u128,
(Not, _) => !bytes,
(Neg, ty::TyFloat(FloatTy::F32)) => Single::to_bits(-Single::from_bits(bytes)),
(Neg, ty::TyFloat(FloatTy::F64)) => Double::to_bits(-Double::from_bits(bytes)),
(Neg, _) if bytes == (1 << (size - 1)) => return err!(OverflowingMath),
(Neg, _) => (-(bytes as i128)) as u128,
};
Ok(PrimVal::Bytes(self.truncate(result_bytes, ty)?))
}
}

View File

@ -20,7 +20,7 @@ use rustc::mir::visit::{Visitor, PlaceContext};
use rustc::middle::const_val::ConstVal;
use rustc::ty::{TyCtxt, self, Instance};
use rustc::mir::interpret::{Value, PrimVal, GlobalId};
use interpret::{eval_body_with_mir, mk_borrowck_eval_cx, unary_op, ValTy};
use interpret::{eval_body_with_mir, mk_borrowck_eval_cx, ValTy};
use transform::{MirPass, MirSource};
use syntax::codemap::Span;
use rustc::ty::subst::Substs;
@ -205,8 +205,7 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
let val = self.eval_operand(arg)?;
let prim = ecx.value_to_primval(ValTy { value: val.0, ty: val.1 }).ok()?;
let kind = ecx.ty_to_primval_kind(val.1).ok()?;
match unary_op(op, prim, kind) {
match ecx.unary_op(op, prim, val.1) {
Ok(val) => Some((Value::ByVal(val), place_ty, span)),
Err(mut err) => {
ecx.report(&mut err, false, Some(span));

View File

@ -17,11 +17,13 @@ fn main() {
const I: isize = -9223372036854775808isize;
assert_eq!(::std::i32::MIN as u64, 0xffffffff80000000);
assert_eq!(-2147483648isize as u64, 0xffffffff80000000);
assert_eq!(-2147483648i32 as u64, 0xffffffff80000000);
assert_eq!(::std::i64::MIN as u64, 0x8000000000000000);
#[cfg(target_pointer_width = "64")]
assert_eq!(-9223372036854775808isize as u64, 0x8000000000000000);
#[cfg(target_pointer_width = "32")]
assert_eq!(-9223372036854775808isize as u64, 0);
assert_eq!(-9223372036854775808i32 as u64, 0);
const J: usize = ::std::i32::MAX as usize;
const K: usize = -1i32 as u32 as usize;
const L: usize = ::std::i32::MIN as usize;