mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-14 21:16:50 +00:00
Don't use the undefined bytes of PrimVal::Bytes
This commit is contained in:
parent
7218836500
commit
df283df887
@ -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;
|
||||
|
@ -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),
|
||||
|
@ -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() {
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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))),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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> {
|
||||
|
@ -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()
|
||||
};
|
||||
|
@ -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};
|
||||
|
@ -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)?))
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user