rustc_const_math: use apfloat::ieee::{Single,Double} in ConstFloat.

This commit is contained in:
Eduard-Mihai Burtescu 2017-07-30 07:32:11 +03:00
parent 1409d205ad
commit 9861df4760
7 changed files with 182 additions and 146 deletions

View 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 {

View File

@ -26,6 +26,7 @@ use rustc::util::nodemap::DefIdMap;
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)
}
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::TyUint(uty) => {
if let Some(i) = val.to_u128(int_width(attr::UnsignedInt(uty))) {
cast_const_int(tcx, U128(i), ty)
} else {
Err(CannotCast)
}
}
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)")
})

View File

@ -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<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 {}
impl hash::Hash for ConstFloat {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
match *self {
F64(a) => {
unsafe { transmute::<_,u64>(a) }.hash(state)
}
F32(a) => {
unsafe { transmute::<_,u32>(a) }.hash(state)
}
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)
}
}
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)
}
(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 @@ derive_binop!(Rem, rem);
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 }
}
}

View File

@ -211,48 +211,6 @@ impl ConstInt {
}
}
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,

View 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

View 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 @@ impl<'tcx> Const<'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()),

View 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;
// }