diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs index 454a4e2111a9c..23c37676f3659 100644 --- a/src/librustc/mir/interpret/value.rs +++ b/src/librustc/mir/interpret/value.rs @@ -1,5 +1,6 @@ use std::fmt; use rustc_macros::HashStable; +use rustc_apfloat::{Float, ieee::{Double, Single}}; use crate::ty::{Ty, InferConst, ParamConst, layout::{HasDataLayout, Size}, subst::SubstsRef}; use crate::ty::PlaceholderConst; @@ -131,6 +132,20 @@ impl fmt::Display for Scalar { } } +impl From for Scalar { + #[inline(always)] + fn from(f: Single) -> Self { + Scalar::from_f32(f) + } +} + +impl From for Scalar { + #[inline(always)] + fn from(f: Double) -> Self { + Scalar::from_f64(f) + } +} + impl<'tcx> Scalar<()> { #[inline(always)] fn check_data(data: u128, size: u8) { @@ -279,6 +294,26 @@ impl<'tcx, Tag> Scalar { Scalar::Raw { data: i, size: size.bytes() as u8 } } + #[inline] + pub fn from_u8(i: u8) -> Self { + Scalar::Raw { data: i as u128, size: 1 } + } + + #[inline] + pub fn from_u16(i: u16) -> Self { + Scalar::Raw { data: i as u128, size: 2 } + } + + #[inline] + pub fn from_u32(i: u32) -> Self { + Scalar::Raw { data: i as u128, size: 4 } + } + + #[inline] + pub fn from_u64(i: u64) -> Self { + Scalar::Raw { data: i as u128, size: 8 } + } + #[inline] pub fn from_int(i: impl Into, size: Size) -> Self { let i = i.into(); @@ -292,13 +327,15 @@ impl<'tcx, Tag> Scalar { } #[inline] - pub fn from_f32(f: f32) -> Self { - Scalar::Raw { data: f.to_bits() as u128, size: 4 } + pub fn from_f32(f: Single) -> Self { + // We trust apfloat to give us properly truncated data. + Scalar::Raw { data: f.to_bits(), size: 4 } } #[inline] - pub fn from_f64(f: f64) -> Self { - Scalar::Raw { data: f.to_bits() as u128, size: 8 } + pub fn from_f64(f: Double) -> Self { + // We trust apfloat to give us properly truncated data. + Scalar::Raw { data: f.to_bits(), size: 8 } } #[inline] @@ -427,13 +464,15 @@ impl<'tcx, Tag> Scalar { } #[inline] - pub fn to_f32(self) -> InterpResult<'static, f32> { - Ok(f32::from_bits(self.to_u32()?)) + pub fn to_f32(self) -> InterpResult<'static, Single> { + // Going through `u32` to check size and truncation. + Ok(Single::from_bits(self.to_u32()? as u128)) } #[inline] - pub fn to_f64(self) -> InterpResult<'static, f64> { - Ok(f64::from_bits(self.to_u64()?)) + pub fn to_f64(self) -> InterpResult<'static, Double> { + // Going through `u64` to check size and truncation. + Ok(Double::from_bits(self.to_u64()? as u128)) } } @@ -517,12 +556,12 @@ impl<'tcx, Tag> ScalarMaybeUndef { } #[inline(always)] - pub fn to_f32(self) -> InterpResult<'tcx, f32> { + pub fn to_f32(self) -> InterpResult<'tcx, Single> { self.not_undef()?.to_f32() } #[inline(always)] - pub fn to_f64(self) -> InterpResult<'tcx, f64> { + pub fn to_f64(self) -> InterpResult<'tcx, Double> { self.not_undef()?.to_f64() } diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index ddc4bd3f9f6c3..5cc46b21fca1f 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -2035,6 +2035,14 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } } + #[inline] + pub fn is_fn_ptr(&self) -> bool { + match self.sty { + FnPtr(_) => true, + _ => false, + } + } + pub fn is_impl_trait(&self) -> bool { match self.sty { Opaque(..) => true, diff --git a/src/librustc_mir/hair/constant.rs b/src/librustc_mir/hair/constant.rs index b5604f4cb0f8e..37a2e79dae91f 100644 --- a/src/librustc_mir/hair/constant.rs +++ b/src/librustc_mir/hair/constant.rs @@ -69,8 +69,7 @@ fn parse_float<'tcx>( ) -> Result, ()> { let num = num.as_str(); use rustc_apfloat::ieee::{Single, Double}; - use rustc_apfloat::Float; - let (data, size) = match fty { + let scalar = match fty { ast::FloatTy::F32 => { num.parse::().map_err(|_| ())?; let mut f = num.parse::().unwrap_or_else(|e| { @@ -79,19 +78,19 @@ fn parse_float<'tcx>( if neg { f = -f; } - (f.to_bits(), 4) + Scalar::from_f32(f) } ast::FloatTy::F64 => { num.parse::().map_err(|_| ())?; let mut f = num.parse::().unwrap_or_else(|e| { - panic!("apfloat::ieee::Single failed to parse `{}`: {:?}", num, e) + panic!("apfloat::ieee::Double failed to parse `{}`: {:?}", num, e) }); if neg { f = -f; } - (f.to_bits(), 8) + Scalar::from_f64(f) } }; - Ok(ConstValue::Scalar(Scalar::from_uint(data, Size::from_bytes(size)))) + Ok(ConstValue::Scalar(scalar)) } diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index 5bc8b931ae867..6392e0996aec2 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -5,11 +5,11 @@ use syntax::ast::{FloatTy, IntTy, UintTy}; use syntax::symbol::sym; use rustc_apfloat::ieee::{Single, Double}; +use rustc_apfloat::{Float, FloatConvert}; use rustc::mir::interpret::{ Scalar, InterpResult, Pointer, PointerArithmetic, InterpError, }; use rustc::mir::CastKind; -use rustc_apfloat::Float; use super::{InterpretCx, Machine, PlaceTy, OpTy, Immediate}; @@ -126,7 +126,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> Ok(()) } - pub(super) fn cast_scalar( + fn cast_scalar( &self, val: Scalar, src_layout: TyLayout<'tcx>, @@ -135,12 +135,24 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> use rustc::ty::TyKind::*; trace!("Casting {:?}: {:?} to {:?}", val, src_layout.ty, dest_layout.ty); - match val.to_bits_or_ptr(src_layout.size, self) { - Err(ptr) => self.cast_from_ptr(ptr, dest_layout.ty), - Ok(data) => { - match src_layout.ty.sty { - Float(fty) => self.cast_from_float(data, fty, dest_layout.ty), - _ => self.cast_from_int(data, src_layout, dest_layout), + match src_layout.ty.sty { + // Floating point + Float(FloatTy::F32) => self.cast_from_float(val.to_f32()?, dest_layout.ty), + Float(FloatTy::F64) => self.cast_from_float(val.to_f64()?, dest_layout.ty), + // Integer(-like), including fn ptr casts and casts from enums that + // are represented as integers (this excludes univariant enums, which + // are handled in `cast` directly). + _ => { + assert!( + src_layout.ty.is_bool() || src_layout.ty.is_char() || + src_layout.ty.is_enum() || src_layout.ty.is_integral() || + src_layout.ty.is_unsafe_ptr() || src_layout.ty.is_fn_ptr() || + src_layout.ty.is_region_ptr(), + "Unexpected cast from type {:?}", src_layout.ty + ); + match val.to_bits_or_ptr(src_layout.size, self) { + Err(ptr) => self.cast_from_ptr(ptr, dest_layout.ty), + Ok(data) => self.cast_from_int(data, src_layout, dest_layout), } } } @@ -148,10 +160,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> fn cast_from_int( &self, - v: u128, + v: u128, // raw bits src_layout: TyLayout<'tcx>, dest_layout: TyLayout<'tcx>, ) -> InterpResult<'tcx, Scalar> { + // Let's make sure v is sign-extended *if* it has a signed type. let signed = src_layout.abi.is_signed(); let v = if signed { self.sign_extend(v, src_layout) @@ -166,21 +179,17 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> Ok(Scalar::from_uint(v, dest_layout.size)) } - Float(FloatTy::F32) if signed => Ok(Scalar::from_uint( - Single::from_i128(v as i128).value.to_bits(), - Size::from_bits(32) + Float(FloatTy::F32) if signed => Ok(Scalar::from_f32( + Single::from_i128(v as i128).value )), - Float(FloatTy::F64) if signed => Ok(Scalar::from_uint( - Double::from_i128(v as i128).value.to_bits(), - Size::from_bits(64) + Float(FloatTy::F64) if signed => Ok(Scalar::from_f64( + Double::from_i128(v as i128).value )), - Float(FloatTy::F32) => Ok(Scalar::from_uint( - Single::from_u128(v).value.to_bits(), - Size::from_bits(32) + Float(FloatTy::F32) => Ok(Scalar::from_f32( + Single::from_u128(v).value )), - Float(FloatTy::F64) => Ok(Scalar::from_uint( - Double::from_u128(v).value.to_bits(), - Size::from_bits(64) + Float(FloatTy::F64) => Ok(Scalar::from_f64( + Double::from_u128(v).value )), Char => { @@ -194,52 +203,36 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> } } - fn cast_from_float( + fn cast_from_float( &self, - bits: u128, - fty: FloatTy, + f: F, dest_ty: Ty<'tcx> - ) -> InterpResult<'tcx, Scalar> { + ) -> InterpResult<'tcx, Scalar> + where F: Float + Into> + FloatConvert + FloatConvert + { use rustc::ty::TyKind::*; - use rustc_apfloat::FloatConvert; match dest_ty.sty { // float -> uint Uint(t) => { let width = t.bit_width().unwrap_or_else(|| self.pointer_size().bits() as usize); - let v = match fty { - FloatTy::F32 => Single::from_bits(bits).to_u128(width).value, - FloatTy::F64 => Double::from_bits(bits).to_u128(width).value, - }; + let v = f.to_u128(width).value; // This should already fit the bit width Ok(Scalar::from_uint(v, Size::from_bits(width as u64))) }, // float -> int Int(t) => { let width = t.bit_width().unwrap_or_else(|| self.pointer_size().bits() as usize); - let v = match fty { - FloatTy::F32 => Single::from_bits(bits).to_i128(width).value, - FloatTy::F64 => Double::from_bits(bits).to_i128(width).value, - }; + let v = f.to_i128(width).value; Ok(Scalar::from_int(v, Size::from_bits(width as u64))) }, - // f64 -> f32 - Float(FloatTy::F32) if fty == FloatTy::F64 => { - Ok(Scalar::from_uint( - Single::to_bits(Double::from_bits(bits).convert(&mut false).value), - Size::from_bits(32), - )) - }, - // f32 -> f64 - Float(FloatTy::F64) if fty == FloatTy::F32 => { - Ok(Scalar::from_uint( - Double::to_bits(Single::from_bits(bits).convert(&mut false).value), - Size::from_bits(64), - )) - }, - // identity cast - Float(FloatTy:: F64) => Ok(Scalar::from_uint(bits, Size::from_bits(64))), - Float(FloatTy:: F32) => Ok(Scalar::from_uint(bits, Size::from_bits(32))), - _ => err!(Unimplemented(format!("float to {:?} cast", dest_ty))), + // float -> f32 + Float(FloatTy::F32) => + Ok(Scalar::from_f32(f.convert(&mut false).value)), + // float -> f64 + Float(FloatTy::F64) => + Ok(Scalar::from_f64(f.convert(&mut false).value)), + // That's it. + _ => bug!("invalid float to {:?} cast", dest_ty), } } diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs index 4f13eeb6fa450..db7da9359de7b 100644 --- a/src/librustc_mir/interpret/operator.rs +++ b/src/librustc_mir/interpret/operator.rs @@ -1,7 +1,6 @@ use rustc::mir; -use rustc::ty::{self, layout::{Size, TyLayout}}; +use rustc::ty::{self, layout::TyLayout}; use syntax::ast::FloatTy; -use rustc_apfloat::ieee::{Double, Single}; use rustc_apfloat::Float; use rustc::mir::interpret::{InterpResult, Scalar}; @@ -43,7 +42,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> bin_op: mir::BinOp, l: char, r: char, - ) -> InterpResult<'tcx, (Scalar, bool)> { + ) -> (Scalar, bool) { use rustc::mir::BinOp::*; let res = match bin_op { @@ -55,7 +54,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> Ge => l >= r, _ => bug!("Invalid operation on char: {:?}", bin_op), }; - return Ok((Scalar::from_bool(res), false)); + return (Scalar::from_bool(res), false); } fn binary_bool_op( @@ -63,7 +62,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> bin_op: mir::BinOp, l: bool, r: bool, - ) -> InterpResult<'tcx, (Scalar, bool)> { + ) -> (Scalar, bool) { use rustc::mir::BinOp::*; let res = match bin_op { @@ -78,46 +77,32 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> BitXor => l ^ r, _ => bug!("Invalid operation on bool: {:?}", bin_op), }; - return Ok((Scalar::from_bool(res), false)); + return (Scalar::from_bool(res), false); } - fn binary_float_op( + fn binary_float_op>>( &self, bin_op: mir::BinOp, - fty: FloatTy, - // passing in raw bits - l: u128, - r: u128, - ) -> InterpResult<'tcx, (Scalar, bool)> { + l: F, + r: F, + ) -> (Scalar, bool) { use rustc::mir::BinOp::*; - macro_rules! float_math { - ($ty:path, $size:expr) => {{ - let l = <$ty>::from_bits(l); - let r = <$ty>::from_bits(r); - let bitify = |res: ::rustc_apfloat::StatusAnd<$ty>| - Scalar::from_uint(res.value.to_bits(), Size::from_bytes($size)); - let val = match bin_op { - Eq => Scalar::from_bool(l == r), - Ne => Scalar::from_bool(l != r), - Lt => Scalar::from_bool(l < r), - Le => Scalar::from_bool(l <= r), - Gt => Scalar::from_bool(l > r), - Ge => Scalar::from_bool(l >= r), - Add => bitify(l + r), - Sub => bitify(l - r), - Mul => bitify(l * r), - Div => bitify(l / r), - Rem => bitify(l % r), - _ => bug!("invalid float op: `{:?}`", bin_op), - }; - return Ok((val, false)); - }}; - } - match fty { - FloatTy::F32 => float_math!(Single, 4), - FloatTy::F64 => float_math!(Double, 8), - } + let val = match bin_op { + Eq => Scalar::from_bool(l == r), + Ne => Scalar::from_bool(l != r), + Lt => Scalar::from_bool(l < r), + Le => Scalar::from_bool(l <= r), + Gt => Scalar::from_bool(l > r), + Ge => Scalar::from_bool(l >= r), + Add => (l + r).value.into(), + Sub => (l - r).value.into(), + Mul => (l * r).value.into(), + Div => (l / r).value.into(), + Rem => (l % r).value.into(), + _ => bug!("invalid float op: `{:?}`", bin_op), + }; + return (val, false); } fn binary_int_op( @@ -286,28 +271,35 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> match left.layout.ty.sty { ty::Char => { assert_eq!(left.layout.ty, right.layout.ty); - let left = left.to_scalar()?.to_char()?; - let right = right.to_scalar()?.to_char()?; - self.binary_char_op(bin_op, left, right) + let left = left.to_scalar()?; + let right = right.to_scalar()?; + Ok(self.binary_char_op(bin_op, left.to_char()?, right.to_char()?)) } ty::Bool => { assert_eq!(left.layout.ty, right.layout.ty); - let left = left.to_scalar()?.to_bool()?; - let right = right.to_scalar()?.to_bool()?; - self.binary_bool_op(bin_op, left, right) + let left = left.to_scalar()?; + let right = right.to_scalar()?; + Ok(self.binary_bool_op(bin_op, left.to_bool()?, right.to_bool()?)) } ty::Float(fty) => { assert_eq!(left.layout.ty, right.layout.ty); - let left = left.to_bits()?; - let right = right.to_bits()?; - self.binary_float_op(bin_op, fty, left, right) + let left = left.to_scalar()?; + let right = right.to_scalar()?; + Ok(match fty { + FloatTy::F32 => self.binary_float_op(bin_op, left.to_f32()?, right.to_f32()?), + FloatTy::F64 => self.binary_float_op(bin_op, left.to_f64()?, right.to_f64()?), + }) } _ => { // Must be integer(-like) types. Don't forget about == on fn pointers. - assert!(left.layout.ty.is_integral() || left.layout.ty.is_unsafe_ptr() || - left.layout.ty.is_fn()); - assert!(right.layout.ty.is_integral() || right.layout.ty.is_unsafe_ptr() || - right.layout.ty.is_fn()); + assert!( + left.layout.ty.is_integral() || + left.layout.ty.is_unsafe_ptr() || left.layout.ty.is_fn_ptr(), + "Unexpected LHS type {:?} for BinOp {:?}", left.layout.ty, bin_op); + assert!( + right.layout.ty.is_integral() || + right.layout.ty.is_unsafe_ptr() || right.layout.ty.is_fn_ptr(), + "Unexpected RHS type {:?} for BinOp {:?}", right.layout.ty, bin_op); // Handle operations that support pointer values if left.to_scalar_ptr()?.is_ptr() || @@ -346,13 +338,12 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> Ok(Scalar::from_bool(res)) } ty::Float(fty) => { - let val = val.to_bits(layout.size)?; let res = match (un_op, fty) { - (Neg, FloatTy::F32) => Single::to_bits(-Single::from_bits(val)), - (Neg, FloatTy::F64) => Double::to_bits(-Double::from_bits(val)), + (Neg, FloatTy::F32) => Scalar::from_f32(-val.to_f32()?), + (Neg, FloatTy::F64) => Scalar::from_f64(-val.to_f64()?), _ => bug!("Invalid float op {:?}", un_op) }; - Ok(Scalar::from_uint(res, layout.size)) + Ok(res) } _ => { assert!(layout.ty.is_integral());