]> git.lizzy.rs Git - rust.git/commitdiff
rustc_const_math: use apfloat::ieee::{Single,Double} in ConstFloat.
authorEduard-Mihai Burtescu <edy.burt@gmail.com>
Sun, 30 Jul 2017 04:32:11 +0000 (07:32 +0300)
committerEduard-Mihai Burtescu <edy.burt@gmail.com>
Wed, 2 Aug 2017 14:28:11 +0000 (17:28 +0300)
src/librustc/ich/impls_const_math.rs
src/librustc_const_eval/eval.rs
src/librustc_const_math/float.rs
src/librustc_const_math/int.rs
src/librustc_const_math/lib.rs
src/librustc_trans/mir/constant.rs
src/test/mir-opt/deaggregator_test.rs

index 6d11f2a87a413485ceb97bbaf6cdb2ebc4b8caf4..6790c2ac7decec5e4887ef4a589e97b321a0d5b5 100644 (file)
@@ -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 {
index 463f256fe6c6fedd1949ce1fb1931762b2834220..eb45fd9c0e0a44246c9fd8e558bac1aac050e9cd 100644 (file)
@@ -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<ConstFloat, ErrKind<'tcx>> {
-    let val = match fty {
-        ast::FloatTy::F32 => num.parse::<f32>().map(F32),
-        ast::FloatTy::F64 => num.parse::<f64>().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)")
     })
index f557edffbda462053b3a8bfcfb737d407a291bd7..719f6b6a7b3221965447d8370d5ae016e494586b 100644 (file)
 // 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<Ordering, ConstMathErr> {
-        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<Self, ParseFloatError> {
+        let bits = match ty {
+            ast::FloatTy::F32 => {
+                let rust_bits = num.parse::<f32>()?.to_bits() as u128;
+                let apfloat = num.parse::<Single>().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::<f64>()?.to_bits() as u128;
+                let apfloat = num.parse::<Double>().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<i128> {
+        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<u128> {
+        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<H: hash::Hasher>(&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<Self, ConstMathErr>;
             fn $func(self, rhs: Self) -> Result<Self, ConstMathErr> {
-                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<Self, ConstMathErr> {
 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 }
     }
 }
index d97276da9bf34b9315d7a410f403fe34a558a076..65471416e8007465ed3ad229f2a3fb1a910440e0 100644 (file)
@@ -211,48 +211,6 @@ pub fn to_u128(&self) -> Option<u128> {
         }
     }
 
-    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,
index 0dce0e1fb026f0ab7f180d67fd92291af82242d1..3947edecb5aff20215fe82888e3e4534b5660ce4 100644 (file)
@@ -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
index 393fa9c0c8e0e8291072d856ccf90f70cbe05e6d..7ece5a42ca19e18b06470853b8b3fcd2164d09d9 100644 (file)
@@ -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()),
index f136d74fa517d6fab556b2b848371403625c2eeb..81dd1932894fb326e944e6af5c035d9616fe38e4 100644 (file)
@@ -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;
 // }