From: Eduard-Mihai Burtescu Date: Sun, 30 Jul 2017 04:32:11 +0000 (+0300) Subject: rustc_const_math: use apfloat::ieee::{Single,Double} in ConstFloat. X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=9861df47601cf6cb105d390db6c3a753dea7622e;p=rust.git rustc_const_math: use apfloat::ieee::{Single,Double} in ConstFloat. --- diff --git a/src/librustc/ich/impls_const_math.rs b/src/librustc/ich/impls_const_math.rs index 6d11f2a87a4..6790c2ac7de 100644 --- a/src/librustc/ich/impls_const_math.rs +++ b/src/librustc/ich/impls_const_math.rs @@ -11,9 +11,9 @@ //! This module contains `HashStable` implementations for various data types //! from `rustc_const_math` in no particular order. -impl_stable_hash_for!(enum ::rustc_const_math::ConstFloat { - F32(val), - F64(val) +impl_stable_hash_for!(struct ::rustc_const_math::ConstFloat { + ty, + bits }); impl_stable_hash_for!(enum ::rustc_const_math::ConstInt { diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index 463f256fe6c..eb45fd9c0e0 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -26,6 +26,7 @@ use syntax::abi::Abi; use syntax::ast; +use syntax::attr; use rustc::hir::{self, Expr}; use syntax_pos::Span; @@ -560,8 +561,15 @@ fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty::TyUint(ast::UintTy::Us) => { Ok(Integral(Usize(ConstUsize::new_truncating(v, tcx.sess.target.uint_type)))) }, - ty::TyFloat(ast::FloatTy::F64) => Ok(Float(F64(val.to_f64()))), - ty::TyFloat(ast::FloatTy::F32) => Ok(Float(F32(val.to_f32()))), + ty::TyFloat(fty) => { + if let Some(i) = val.to_u128() { + Ok(Float(ConstFloat::from_u128(i, fty))) + } else { + // The value must be negative, go through signed integers. + let i = val.to_u128_unchecked() as i128; + Ok(Float(ConstFloat::from_i128(i, fty))) + } + } ty::TyRawPtr(_) => Err(ErrKind::UnimplementedConstVal("casting an address to a raw ptr")), ty::TyChar => match val { U8(u) => Ok(Char(u as char)), @@ -574,30 +582,25 @@ fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, fn cast_const_float<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, val: ConstFloat, ty: Ty<'tcx>) -> CastResult<'tcx> { + let int_width = |ty| { + ty::layout::Integer::from_attr(tcx, ty).size().bits() as usize + }; match ty.sty { - ty::TyInt(_) | ty::TyUint(_) => { - let i = match val { - F32(f) if f >= 0.0 => U128(f as u128), - F64(f) if f >= 0.0 => U128(f as u128), - - F32(f) => I128(f as i128), - F64(f) => I128(f as i128) - }; - - if let (I128(_), &ty::TyUint(_)) = (i, &ty.sty) { - return Err(CannotCast); + ty::TyInt(ity) => { + if let Some(i) = val.to_i128(int_width(attr::SignedInt(ity))) { + cast_const_int(tcx, I128(i), ty) + } else { + Err(CannotCast) + } + } + ty::TyUint(uty) => { + if let Some(i) = val.to_u128(int_width(attr::UnsignedInt(uty))) { + cast_const_int(tcx, U128(i), ty) + } else { + Err(CannotCast) } - - cast_const_int(tcx, i, ty) } - ty::TyFloat(ast::FloatTy::F64) => Ok(Float(F64(match val { - F32(f) => f as f64, - F64(f) => f - }))), - ty::TyFloat(ast::FloatTy::F32) => Ok(Float(F32(match val { - F64(f) => f as f32, - F32(f) => f - }))), + ty::TyFloat(fty) => Ok(Float(val.convert(fty))), _ => Err(CannotCast), } } @@ -691,11 +694,7 @@ fn lit_to_const<'a, 'tcx>(lit: &ast::LitKind, fn parse_float<'tcx>(num: &str, fty: ast::FloatTy) -> Result> { - let val = match fty { - ast::FloatTy::F32 => num.parse::().map(F32), - ast::FloatTy::F64 => num.parse::().map(F64) - }; - val.map_err(|_| { + ConstFloat::from_str(num, fty).map_err(|_| { // FIXME(#31407) this is only necessary because float parsing is buggy UnimplementedConstVal("could not evaluate float literal (see issue #31407)") }) diff --git a/src/librustc_const_math/float.rs b/src/librustc_const_math/float.rs index f557edffbda..719f6b6a7b3 100644 --- a/src/librustc_const_math/float.rs +++ b/src/librustc_const_math/float.rs @@ -9,102 +9,164 @@ // except according to those terms. use std::cmp::Ordering; -use std::hash; -use std::mem::transmute; +use std::num::ParseFloatError; + +use syntax::ast; + +use rustc_apfloat::{Float, FloatConvert, Status}; +use rustc_apfloat::ieee::{Single, Double}; use super::err::*; -#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)] -pub enum ConstFloat { - F32(f32), - F64(f64) +// Note that equality for `ConstFloat` means that the it is the same +// constant, not that the rust values are equal. In particular, `NaN +// == NaN` (at least if it's the same NaN; distinct encodings for NaN +// are considering unequal). +#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] +pub struct ConstFloat { + pub ty: ast::FloatTy, + + // This is a bit inefficient but it makes conversions below more + // ergonomic, and all of this will go away once `miri` is merged. + pub bits: u128, } -pub use self::ConstFloat::*; impl ConstFloat { /// Description of the type, not the value pub fn description(&self) -> &'static str { - match *self { - F32(_) => "f32", - F64(_) => "f64", - } + self.ty.ty_to_string() } pub fn is_nan(&self) -> bool { - match *self { - F32(f) => f.is_nan(), - F64(f) => f.is_nan(), + match self.ty { + ast::FloatTy::F32 => Single::from_bits(self.bits).is_nan(), + ast::FloatTy::F64 => Double::from_bits(self.bits).is_nan(), } } /// Compares the values if they are of the same type pub fn try_cmp(self, rhs: Self) -> Result { - match (self, rhs) { - (F64(a), F64(b)) => { + match (self.ty, rhs.ty) { + (ast::FloatTy::F64, ast::FloatTy::F64) => { + let a = Double::from_bits(self.bits); + let b = Double::from_bits(rhs.bits); // This is pretty bad but it is the existing behavior. - Ok(if a == b { - Ordering::Equal - } else if a < b { - Ordering::Less - } else { - Ordering::Greater - }) + Ok(a.partial_cmp(&b).unwrap_or(Ordering::Greater)) } - (F32(a), F32(b)) => { - Ok(if a == b { - Ordering::Equal - } else if a < b { - Ordering::Less - } else { - Ordering::Greater - }) + (ast::FloatTy::F32, ast::FloatTy::F32) => { + let a = Single::from_bits(self.bits); + let b = Single::from_bits(rhs.bits); + Ok(a.partial_cmp(&b).unwrap_or(Ordering::Greater)) } _ => Err(CmpBetweenUnequalTypes), } } -} -/// Note that equality for `ConstFloat` means that the it is the same -/// constant, not that the rust values are equal. In particular, `NaN -/// == NaN` (at least if it's the same NaN; distinct encodings for NaN -/// are considering unequal). -impl PartialEq for ConstFloat { - fn eq(&self, other: &Self) -> bool { - match (*self, *other) { - (F64(a), F64(b)) => { - unsafe{transmute::<_,u64>(a) == transmute::<_,u64>(b)} + pub fn from_i128(input: i128, ty: ast::FloatTy) -> Self { + let bits = match ty { + ast::FloatTy::F32 => Single::from_i128(input).value.to_bits(), + ast::FloatTy::F64 => Double::from_i128(input).value.to_bits() + }; + ConstFloat { bits, ty } + } + + pub fn from_u128(input: u128, ty: ast::FloatTy) -> Self { + let bits = match ty { + ast::FloatTy::F32 => Single::from_u128(input).value.to_bits(), + ast::FloatTy::F64 => Double::from_u128(input).value.to_bits() + }; + ConstFloat { bits, ty } + } + + pub fn from_str(num: &str, ty: ast::FloatTy) -> Result { + let bits = match ty { + ast::FloatTy::F32 => { + let rust_bits = num.parse::()?.to_bits() as u128; + let apfloat = num.parse::().unwrap_or_else(|e| { + panic!("apfloat::ieee::Single failed to parse `{}`: {:?}", num, e); + }); + let apfloat_bits = apfloat.to_bits(); + assert!(rust_bits == apfloat_bits, + "apfloat::ieee::Single gave different result for `{}`: \ + {}({:#x}) vs Rust's {}({:#x})", + num, apfloat, apfloat_bits, + Single::from_bits(rust_bits), rust_bits); + apfloat_bits } - (F32(a), F32(b)) => { - unsafe{transmute::<_,u32>(a) == transmute::<_,u32>(b)} + ast::FloatTy::F64 => { + let rust_bits = num.parse::()?.to_bits() as u128; + let apfloat = num.parse::().unwrap_or_else(|e| { + panic!("apfloat::ieee::Double failed to parse `{}`: {:?}", num, e); + }); + let apfloat_bits = apfloat.to_bits(); + assert!(rust_bits == apfloat_bits, + "apfloat::ieee::Double gave different result for `{}`: \ + {}({:#x}) vs Rust's {}({:#x})", + num, apfloat, apfloat_bits, + Double::from_bits(rust_bits), rust_bits); + apfloat_bits } - _ => false + }; + Ok(ConstFloat { bits, ty }) + } + + pub fn to_i128(self, width: usize) -> Option { + assert!(width <= 128); + let r = match self.ty { + ast::FloatTy::F32 => Single::from_bits(self.bits).to_i128(width), + ast::FloatTy::F64 => Double::from_bits(self.bits).to_i128(width) + }; + if r.status.intersects(Status::INVALID_OP) { + None + } else { + Some(r.value) } } -} -impl Eq for ConstFloat {} + pub fn to_u128(self, width: usize) -> Option { + assert!(width <= 128); + let r = match self.ty { + ast::FloatTy::F32 => Single::from_bits(self.bits).to_u128(width), + ast::FloatTy::F64 => Double::from_bits(self.bits).to_u128(width) + }; + if r.status.intersects(Status::INVALID_OP) { + None + } else { + Some(r.value) + } + } -impl hash::Hash for ConstFloat { - fn hash(&self, state: &mut H) { - match *self { - F64(a) => { - unsafe { transmute::<_,u64>(a) }.hash(state) + pub fn convert(self, to: ast::FloatTy) -> Self { + let bits = match (self.ty, to) { + (ast::FloatTy::F32, ast::FloatTy::F32) | + (ast::FloatTy::F64, ast::FloatTy::F64) => return self, + + (ast::FloatTy::F32, ast::FloatTy::F64) => { + Double::to_bits(Single::from_bits(self.bits).convert(&mut false).value) } - F32(a) => { - unsafe { transmute::<_,u32>(a) }.hash(state) + (ast::FloatTy::F64, ast::FloatTy::F32) => { + Single::to_bits(Double::from_bits(self.bits).convert(&mut false).value) } - } + }; + ConstFloat { bits, ty: to } } } impl ::std::fmt::Display for ConstFloat { fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { - match *self { - F32(f) => write!(fmt, "{}f32", f), - F64(f) => write!(fmt, "{}f64", f), + match self.ty { + ast::FloatTy::F32 => write!(fmt, "{:#}", Single::from_bits(self.bits))?, + ast::FloatTy::F64 => write!(fmt, "{:#}", Double::from_bits(self.bits))?, } + write!(fmt, "{}", self.ty) + } +} + +impl ::std::fmt::Debug for ConstFloat { + fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { + ::std::fmt::Display::fmt(self, fmt) } } @@ -113,11 +175,20 @@ macro_rules! derive_binop { impl ::std::ops::$op for ConstFloat { type Output = Result; fn $func(self, rhs: Self) -> Result { - match (self, rhs) { - (F32(a), F32(b)) => Ok(F32(a.$func(b))), - (F64(a), F64(b)) => Ok(F64(a.$func(b))), - _ => Err(UnequalTypes(Op::$op)), - } + let bits = match (self.ty, rhs.ty) { + (ast::FloatTy::F32, ast::FloatTy::F32) =>{ + let a = Single::from_bits(self.bits); + let b = Single::from_bits(rhs.bits); + a.$func(b).value.to_bits() + } + (ast::FloatTy::F64, ast::FloatTy::F64) => { + let a = Double::from_bits(self.bits); + let b = Double::from_bits(rhs.bits); + a.$func(b).value.to_bits() + } + _ => return Err(UnequalTypes(Op::$op)), + }; + Ok(ConstFloat { bits, ty: self.ty }) } } } @@ -132,9 +203,10 @@ fn $func(self, rhs: Self) -> Result { impl ::std::ops::Neg for ConstFloat { type Output = Self; fn neg(self) -> Self { - match self { - F32(f) => F32(-f), - F64(f) => F64(-f), - } + let bits = match self.ty { + ast::FloatTy::F32 => (-Single::from_bits(self.bits)).to_bits(), + ast::FloatTy::F64 => (-Double::from_bits(self.bits)).to_bits(), + }; + ConstFloat { bits, ty: self.ty } } } diff --git a/src/librustc_const_math/int.rs b/src/librustc_const_math/int.rs index d97276da9bf..65471416e80 100644 --- a/src/librustc_const_math/int.rs +++ b/src/librustc_const_math/int.rs @@ -211,48 +211,6 @@ pub fn to_u128(&self) -> Option { } } - pub fn to_f32(self) -> f32 { - match self { - I8(i) => i as f32, - I16(i) => i as f32, - I32(i) => i as f32, - I64(i) => i as f32, - I128(i) => i as f32, - Isize(Is16(i)) => i as f32, - Isize(Is32(i)) => i as f32, - Isize(Is64(i)) => i as f32, - U8(i) => i as f32, - U16(i) => i as f32, - U32(i) => i as f32, - U64(i) => i as f32, - U128(i) => i as f32, - Usize(Us16(i)) => i as f32, - Usize(Us32(i)) => i as f32, - Usize(Us64(i)) => i as f32, - } - } - - pub fn to_f64(self) -> f64 { - match self { - I8(i) => i as f64, - I16(i) => i as f64, - I32(i) => i as f64, - I64(i) => i as f64, - I128(i) => i as f64, - Isize(Is16(i)) => i as f64, - Isize(Is32(i)) => i as f64, - Isize(Is64(i)) => i as f64, - U8(i) => i as f64, - U16(i) => i as f64, - U32(i) => i as f64, - U64(i) => i as f64, - U128(i) => i as f64, - Usize(Us16(i)) => i as f64, - Usize(Us32(i)) => i as f64, - Usize(Us64(i)) => i as f64, - } - } - pub fn is_negative(&self) -> bool { match *self { I8(v) => v < 0, diff --git a/src/librustc_const_math/lib.rs b/src/librustc_const_math/lib.rs index 0dce0e1fb02..3947edecb5a 100644 --- a/src/librustc_const_math/lib.rs +++ b/src/librustc_const_math/lib.rs @@ -26,6 +26,8 @@ #![feature(i128)] #![feature(i128_type)] +extern crate rustc_apfloat; + extern crate syntax; extern crate serialize as rustc_serialize; // used by deriving diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 393fa9c0c8e..7ece5a42ca1 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -11,7 +11,7 @@ use llvm::{self, ValueRef}; use rustc::middle::const_val::{ConstEvalErr, ConstVal, ErrKind}; use rustc_const_math::ConstInt::*; -use rustc_const_math::ConstFloat::*; +use rustc_const_math::ConstFloat; use rustc_const_math::{ConstInt, ConstMathErr}; use rustc::hir::def_id::DefId; use rustc::infer::TransNormalize; @@ -95,8 +95,13 @@ pub fn from_constval<'a>(ccx: &CrateContext<'a, 'tcx>, -> Const<'tcx> { let llty = type_of::type_of(ccx, ty); let val = match cv { - ConstVal::Float(F32(v)) => C_floating_f64(v as f64, llty), - ConstVal::Float(F64(v)) => C_floating_f64(v, llty), + ConstVal::Float(v) => { + let v_f64 = match v { + ConstFloat::F32(v) => f32::from_bits(v) as f64, + ConstFloat::F64(v) => f64::from_bits(v) + }; + C_floating_f64(v_f64, llty) + } ConstVal::Bool(v) => C_bool(ccx, v), ConstVal::Integral(ref i) => return Const::from_constint(ccx, i), ConstVal::Str(ref v) => C_str_slice(ccx, v.clone()), diff --git a/src/test/mir-opt/deaggregator_test.rs b/src/test/mir-opt/deaggregator_test.rs index f136d74fa51..81dd1932894 100644 --- a/src/test/mir-opt/deaggregator_test.rs +++ b/src/test/mir-opt/deaggregator_test.rs @@ -25,7 +25,7 @@ fn main() {} // bb0: { // _2 = _1; // _3 = _2; -// _0 = Baz { x: _3, y: const F32(0), z: const false }; +// _0 = Baz { x: _3, y: const 0f32, z: const false }; // return; // } // END rustc.node13.Deaggregator.before.mir @@ -34,7 +34,7 @@ fn main() {} // _2 = _1; // _3 = _2; // (_0.0: usize) = _3; -// (_0.1: f32) = const F32(0); +// (_0.1: f32) = const 0f32; // (_0.2: bool) = const false; // return; // }