Skip to content

Commit 9861df4

Browse files
committed
rustc_const_math: use apfloat::ieee::{Single,Double} in ConstFloat.
1 parent 1409d20 commit 9861df4

File tree

7 files changed

+180
-144
lines changed

7 files changed

+180
-144
lines changed

src/librustc/ich/impls_const_math.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
//! This module contains `HashStable` implementations for various data types
1212
//! from `rustc_const_math` in no particular order.
1313
14-
impl_stable_hash_for!(enum ::rustc_const_math::ConstFloat {
15-
F32(val),
16-
F64(val)
14+
impl_stable_hash_for!(struct ::rustc_const_math::ConstFloat {
15+
ty,
16+
bits
1717
});
1818

1919
impl_stable_hash_for!(enum ::rustc_const_math::ConstInt {

src/librustc_const_eval/eval.rs

+27-28
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use rustc::util::nodemap::DefIdMap;
2626

2727
use syntax::abi::Abi;
2828
use syntax::ast;
29+
use syntax::attr;
2930
use rustc::hir::{self, Expr};
3031
use syntax_pos::Span;
3132

@@ -560,8 +561,15 @@ fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
560561
ty::TyUint(ast::UintTy::Us) => {
561562
Ok(Integral(Usize(ConstUsize::new_truncating(v, tcx.sess.target.uint_type))))
562563
},
563-
ty::TyFloat(ast::FloatTy::F64) => Ok(Float(F64(val.to_f64()))),
564-
ty::TyFloat(ast::FloatTy::F32) => Ok(Float(F32(val.to_f32()))),
564+
ty::TyFloat(fty) => {
565+
if let Some(i) = val.to_u128() {
566+
Ok(Float(ConstFloat::from_u128(i, fty)))
567+
} else {
568+
// The value must be negative, go through signed integers.
569+
let i = val.to_u128_unchecked() as i128;
570+
Ok(Float(ConstFloat::from_i128(i, fty)))
571+
}
572+
}
565573
ty::TyRawPtr(_) => Err(ErrKind::UnimplementedConstVal("casting an address to a raw ptr")),
566574
ty::TyChar => match val {
567575
U8(u) => Ok(Char(u as char)),
@@ -574,30 +582,25 @@ fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
574582
fn cast_const_float<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
575583
val: ConstFloat,
576584
ty: Ty<'tcx>) -> CastResult<'tcx> {
585+
let int_width = |ty| {
586+
ty::layout::Integer::from_attr(tcx, ty).size().bits() as usize
587+
};
577588
match ty.sty {
578-
ty::TyInt(_) | ty::TyUint(_) => {
579-
let i = match val {
580-
F32(f) if f >= 0.0 => U128(f as u128),
581-
F64(f) if f >= 0.0 => U128(f as u128),
582-
583-
F32(f) => I128(f as i128),
584-
F64(f) => I128(f as i128)
585-
};
586-
587-
if let (I128(_), &ty::TyUint(_)) = (i, &ty.sty) {
588-
return Err(CannotCast);
589+
ty::TyInt(ity) => {
590+
if let Some(i) = val.to_i128(int_width(attr::SignedInt(ity))) {
591+
cast_const_int(tcx, I128(i), ty)
592+
} else {
593+
Err(CannotCast)
594+
}
595+
}
596+
ty::TyUint(uty) => {
597+
if let Some(i) = val.to_u128(int_width(attr::UnsignedInt(uty))) {
598+
cast_const_int(tcx, U128(i), ty)
599+
} else {
600+
Err(CannotCast)
589601
}
590-
591-
cast_const_int(tcx, i, ty)
592602
}
593-
ty::TyFloat(ast::FloatTy::F64) => Ok(Float(F64(match val {
594-
F32(f) => f as f64,
595-
F64(f) => f
596-
}))),
597-
ty::TyFloat(ast::FloatTy::F32) => Ok(Float(F32(match val {
598-
F64(f) => f as f32,
599-
F32(f) => f
600-
}))),
603+
ty::TyFloat(fty) => Ok(Float(val.convert(fty))),
601604
_ => Err(CannotCast),
602605
}
603606
}
@@ -691,11 +694,7 @@ fn lit_to_const<'a, 'tcx>(lit: &ast::LitKind,
691694

692695
fn parse_float<'tcx>(num: &str, fty: ast::FloatTy)
693696
-> Result<ConstFloat, ErrKind<'tcx>> {
694-
let val = match fty {
695-
ast::FloatTy::F32 => num.parse::<f32>().map(F32),
696-
ast::FloatTy::F64 => num.parse::<f64>().map(F64)
697-
};
698-
val.map_err(|_| {
697+
ConstFloat::from_str(num, fty).map_err(|_| {
699698
// FIXME(#31407) this is only necessary because float parsing is buggy
700699
UnimplementedConstVal("could not evaluate float literal (see issue #31407)")
701700
})

src/librustc_const_math/float.rs

+138-66
Original file line numberDiff line numberDiff line change
@@ -9,102 +9,164 @@
99
// except according to those terms.
1010

1111
use std::cmp::Ordering;
12-
use std::hash;
13-
use std::mem::transmute;
12+
use std::num::ParseFloatError;
13+
14+
use syntax::ast;
15+
16+
use rustc_apfloat::{Float, FloatConvert, Status};
17+
use rustc_apfloat::ieee::{Single, Double};
1418

1519
use super::err::*;
1620

17-
#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)]
18-
pub enum ConstFloat {
19-
F32(f32),
20-
F64(f64)
21+
// Note that equality for `ConstFloat` means that the it is the same
22+
// constant, not that the rust values are equal. In particular, `NaN
23+
// == NaN` (at least if it's the same NaN; distinct encodings for NaN
24+
// are considering unequal).
25+
#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
26+
pub struct ConstFloat {
27+
pub ty: ast::FloatTy,
28+
29+
// This is a bit inefficient but it makes conversions below more
30+
// ergonomic, and all of this will go away once `miri` is merged.
31+
pub bits: u128,
2132
}
22-
pub use self::ConstFloat::*;
2333

2434
impl ConstFloat {
2535
/// Description of the type, not the value
2636
pub fn description(&self) -> &'static str {
27-
match *self {
28-
F32(_) => "f32",
29-
F64(_) => "f64",
30-
}
37+
self.ty.ty_to_string()
3138
}
3239

3340
pub fn is_nan(&self) -> bool {
34-
match *self {
35-
F32(f) => f.is_nan(),
36-
F64(f) => f.is_nan(),
41+
match self.ty {
42+
ast::FloatTy::F32 => Single::from_bits(self.bits).is_nan(),
43+
ast::FloatTy::F64 => Double::from_bits(self.bits).is_nan(),
3744
}
3845
}
3946

4047
/// Compares the values if they are of the same type
4148
pub fn try_cmp(self, rhs: Self) -> Result<Ordering, ConstMathErr> {
42-
match (self, rhs) {
43-
(F64(a), F64(b)) => {
49+
match (self.ty, rhs.ty) {
50+
(ast::FloatTy::F64, ast::FloatTy::F64) => {
51+
let a = Double::from_bits(self.bits);
52+
let b = Double::from_bits(rhs.bits);
4453
// This is pretty bad but it is the existing behavior.
45-
Ok(if a == b {
46-
Ordering::Equal
47-
} else if a < b {
48-
Ordering::Less
49-
} else {
50-
Ordering::Greater
51-
})
54+
Ok(a.partial_cmp(&b).unwrap_or(Ordering::Greater))
5255
}
5356

54-
(F32(a), F32(b)) => {
55-
Ok(if a == b {
56-
Ordering::Equal
57-
} else if a < b {
58-
Ordering::Less
59-
} else {
60-
Ordering::Greater
61-
})
57+
(ast::FloatTy::F32, ast::FloatTy::F32) => {
58+
let a = Single::from_bits(self.bits);
59+
let b = Single::from_bits(rhs.bits);
60+
Ok(a.partial_cmp(&b).unwrap_or(Ordering::Greater))
6261
}
6362

6463
_ => Err(CmpBetweenUnequalTypes),
6564
}
6665
}
67-
}
6866

69-
/// Note that equality for `ConstFloat` means that the it is the same
70-
/// constant, not that the rust values are equal. In particular, `NaN
71-
/// == NaN` (at least if it's the same NaN; distinct encodings for NaN
72-
/// are considering unequal).
73-
impl PartialEq for ConstFloat {
74-
fn eq(&self, other: &Self) -> bool {
75-
match (*self, *other) {
76-
(F64(a), F64(b)) => {
77-
unsafe{transmute::<_,u64>(a) == transmute::<_,u64>(b)}
67+
pub fn from_i128(input: i128, ty: ast::FloatTy) -> Self {
68+
let bits = match ty {
69+
ast::FloatTy::F32 => Single::from_i128(input).value.to_bits(),
70+
ast::FloatTy::F64 => Double::from_i128(input).value.to_bits()
71+
};
72+
ConstFloat { bits, ty }
73+
}
74+
75+
pub fn from_u128(input: u128, ty: ast::FloatTy) -> Self {
76+
let bits = match ty {
77+
ast::FloatTy::F32 => Single::from_u128(input).value.to_bits(),
78+
ast::FloatTy::F64 => Double::from_u128(input).value.to_bits()
79+
};
80+
ConstFloat { bits, ty }
81+
}
82+
83+
pub fn from_str(num: &str, ty: ast::FloatTy) -> Result<Self, ParseFloatError> {
84+
let bits = match ty {
85+
ast::FloatTy::F32 => {
86+
let rust_bits = num.parse::<f32>()?.to_bits() as u128;
87+
let apfloat = num.parse::<Single>().unwrap_or_else(|e| {
88+
panic!("apfloat::ieee::Single failed to parse `{}`: {:?}", num, e);
89+
});
90+
let apfloat_bits = apfloat.to_bits();
91+
assert!(rust_bits == apfloat_bits,
92+
"apfloat::ieee::Single gave different result for `{}`: \
93+
{}({:#x}) vs Rust's {}({:#x})",
94+
num, apfloat, apfloat_bits,
95+
Single::from_bits(rust_bits), rust_bits);
96+
apfloat_bits
7897
}
79-
(F32(a), F32(b)) => {
80-
unsafe{transmute::<_,u32>(a) == transmute::<_,u32>(b)}
98+
ast::FloatTy::F64 => {
99+
let rust_bits = num.parse::<f64>()?.to_bits() as u128;
100+
let apfloat = num.parse::<Double>().unwrap_or_else(|e| {
101+
panic!("apfloat::ieee::Double failed to parse `{}`: {:?}", num, e);
102+
});
103+
let apfloat_bits = apfloat.to_bits();
104+
assert!(rust_bits == apfloat_bits,
105+
"apfloat::ieee::Double gave different result for `{}`: \
106+
{}({:#x}) vs Rust's {}({:#x})",
107+
num, apfloat, apfloat_bits,
108+
Double::from_bits(rust_bits), rust_bits);
109+
apfloat_bits
81110
}
82-
_ => false
111+
};
112+
Ok(ConstFloat { bits, ty })
113+
}
114+
115+
pub fn to_i128(self, width: usize) -> Option<i128> {
116+
assert!(width <= 128);
117+
let r = match self.ty {
118+
ast::FloatTy::F32 => Single::from_bits(self.bits).to_i128(width),
119+
ast::FloatTy::F64 => Double::from_bits(self.bits).to_i128(width)
120+
};
121+
if r.status.intersects(Status::INVALID_OP) {
122+
None
123+
} else {
124+
Some(r.value)
83125
}
84126
}
85-
}
86127

87-
impl Eq for ConstFloat {}
128+
pub fn to_u128(self, width: usize) -> Option<u128> {
129+
assert!(width <= 128);
130+
let r = match self.ty {
131+
ast::FloatTy::F32 => Single::from_bits(self.bits).to_u128(width),
132+
ast::FloatTy::F64 => Double::from_bits(self.bits).to_u128(width)
133+
};
134+
if r.status.intersects(Status::INVALID_OP) {
135+
None
136+
} else {
137+
Some(r.value)
138+
}
139+
}
88140

89-
impl hash::Hash for ConstFloat {
90-
fn hash<H: hash::Hasher>(&self, state: &mut H) {
91-
match *self {
92-
F64(a) => {
93-
unsafe { transmute::<_,u64>(a) }.hash(state)
141+
pub fn convert(self, to: ast::FloatTy) -> Self {
142+
let bits = match (self.ty, to) {
143+
(ast::FloatTy::F32, ast::FloatTy::F32) |
144+
(ast::FloatTy::F64, ast::FloatTy::F64) => return self,
145+
146+
(ast::FloatTy::F32, ast::FloatTy::F64) => {
147+
Double::to_bits(Single::from_bits(self.bits).convert(&mut false).value)
94148
}
95-
F32(a) => {
96-
unsafe { transmute::<_,u32>(a) }.hash(state)
149+
(ast::FloatTy::F64, ast::FloatTy::F32) => {
150+
Single::to_bits(Double::from_bits(self.bits).convert(&mut false).value)
97151
}
98-
}
152+
};
153+
ConstFloat { bits, ty: to }
99154
}
100155
}
101156

102157
impl ::std::fmt::Display for ConstFloat {
103158
fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
104-
match *self {
105-
F32(f) => write!(fmt, "{}f32", f),
106-
F64(f) => write!(fmt, "{}f64", f),
159+
match self.ty {
160+
ast::FloatTy::F32 => write!(fmt, "{:#}", Single::from_bits(self.bits))?,
161+
ast::FloatTy::F64 => write!(fmt, "{:#}", Double::from_bits(self.bits))?,
107162
}
163+
write!(fmt, "{}", self.ty)
164+
}
165+
}
166+
167+
impl ::std::fmt::Debug for ConstFloat {
168+
fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
169+
::std::fmt::Display::fmt(self, fmt)
108170
}
109171
}
110172

@@ -113,11 +175,20 @@ macro_rules! derive_binop {
113175
impl ::std::ops::$op for ConstFloat {
114176
type Output = Result<Self, ConstMathErr>;
115177
fn $func(self, rhs: Self) -> Result<Self, ConstMathErr> {
116-
match (self, rhs) {
117-
(F32(a), F32(b)) => Ok(F32(a.$func(b))),
118-
(F64(a), F64(b)) => Ok(F64(a.$func(b))),
119-
_ => Err(UnequalTypes(Op::$op)),
120-
}
178+
let bits = match (self.ty, rhs.ty) {
179+
(ast::FloatTy::F32, ast::FloatTy::F32) =>{
180+
let a = Single::from_bits(self.bits);
181+
let b = Single::from_bits(rhs.bits);
182+
a.$func(b).value.to_bits()
183+
}
184+
(ast::FloatTy::F64, ast::FloatTy::F64) => {
185+
let a = Double::from_bits(self.bits);
186+
let b = Double::from_bits(rhs.bits);
187+
a.$func(b).value.to_bits()
188+
}
189+
_ => return Err(UnequalTypes(Op::$op)),
190+
};
191+
Ok(ConstFloat { bits, ty: self.ty })
121192
}
122193
}
123194
}
@@ -132,9 +203,10 @@ derive_binop!(Rem, rem);
132203
impl ::std::ops::Neg for ConstFloat {
133204
type Output = Self;
134205
fn neg(self) -> Self {
135-
match self {
136-
F32(f) => F32(-f),
137-
F64(f) => F64(-f),
138-
}
206+
let bits = match self.ty {
207+
ast::FloatTy::F32 => (-Single::from_bits(self.bits)).to_bits(),
208+
ast::FloatTy::F64 => (-Double::from_bits(self.bits)).to_bits(),
209+
};
210+
ConstFloat { bits, ty: self.ty }
139211
}
140212
}

0 commit comments

Comments
 (0)