Skip to content

Commit 8b2db58

Browse files
committed
rustc_const_{math,eval}: use APFloat in ConstFloat.
1 parent 5b9b0d2 commit 8b2db58

File tree

4 files changed

+164
-123
lines changed

4 files changed

+164
-123
lines changed

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

+129-50
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,23 @@
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;
1413

14+
use syntax::ast;
15+
16+
use super::apfloat::{Float, IeeeSingle, IeeeDouble, OpStatus, Round};
1517
use super::err::*;
1618

17-
#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)]
19+
// Note that equality for `ConstFloat` means that the it is the same
20+
// constant, not that the rust values are equal. In particular, `NaN
21+
// == NaN` (at least if it's the same NaN; distinct encodings for NaN
22+
// are considering unequal).
23+
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, RustcEncodable, RustcDecodable)]
1824
pub enum ConstFloat {
19-
F32(f32),
20-
F64(f64)
25+
F32(u32),
26+
F64(u64)
2127
}
22-
pub use self::ConstFloat::*;
28+
use self::ConstFloat::*;
2329

2430
impl ConstFloat {
2531
/// Description of the type, not the value
@@ -32,68 +38,133 @@ impl ConstFloat {
3238

3339
pub fn is_nan(&self) -> bool {
3440
match *self {
35-
F32(f) => f.is_nan(),
36-
F64(f) => f.is_nan(),
41+
F32(f) => IeeeSingle::from_bits(f as u128).is_nan(),
42+
F64(f) => IeeeDouble::from_bits(f as u128).is_nan(),
3743
}
3844
}
3945

4046
/// Compares the values if they are of the same type
4147
pub fn try_cmp(self, rhs: Self) -> Result<Ordering, ConstMathErr> {
4248
match (self, rhs) {
4349
(F64(a), F64(b)) => {
50+
let a = IeeeDouble::from_bits(a as u128);
51+
let b = IeeeDouble::from_bits(b as u128);
4452
// 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-
})
53+
Ok(a.partial_cmp(&b).unwrap_or(Ordering::Greater))
5254
}
5355

5456
(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+
let a = IeeeSingle::from_bits(a as u128);
58+
let b = IeeeSingle::from_bits(b as u128);
59+
Ok(a.partial_cmp(&b).unwrap_or(Ordering::Greater))
6260
}
6361

6462
_ => Err(CmpBetweenUnequalTypes),
6563
}
6664
}
67-
}
6865

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)}
66+
pub fn from_i128(input: i128, fty: ast::FloatTy) -> Self {
67+
match fty {
68+
ast::FloatTy::F32 => {
69+
let (r, _) = IeeeSingle::from_i128(input, Round::NearestTiesToEven);
70+
F32(r.to_bits() as u32)
7871
}
79-
(F32(a), F32(b)) => {
80-
unsafe{transmute::<_,u32>(a) == transmute::<_,u32>(b)}
72+
ast::FloatTy::F64 => {
73+
let (r, _) = IeeeDouble::from_i128(input, Round::NearestTiesToEven);
74+
F64(r.to_bits() as u64)
8175
}
82-
_ => false
8376
}
8477
}
85-
}
8678

87-
impl Eq for ConstFloat {}
79+
pub fn from_u128(input: u128, fty: ast::FloatTy) -> Self {
80+
match fty {
81+
ast::FloatTy::F32 => {
82+
let (r, _) = IeeeSingle::from_u128(input, Round::NearestTiesToEven);
83+
F32(r.to_bits() as u32)
84+
}
85+
ast::FloatTy::F64 => {
86+
let (r, _) = IeeeDouble::from_u128(input, Round::NearestTiesToEven);
87+
F64(r.to_bits() as u64)
88+
}
89+
}
90+
}
8891

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)
92+
pub fn from_str(num: &str, fty: ast::FloatTy) -> Result<Self, ParseFloatError> {
93+
match fty {
94+
ast::FloatTy::F32 => {
95+
let rust_bits = num.parse::<f32>()?.to_bits();
96+
let apfloat = num.parse::<IeeeSingle>().unwrap_or_else(|e| {
97+
panic!("apfloat::IeeeSingle failed to parse `{}`: {:?}", num, e);
98+
});
99+
let apfloat_bits = apfloat.to_bits() as u32;
100+
assert!(rust_bits == apfloat_bits,
101+
"apfloat::IeeeSingle gave different result for `{}`: \
102+
{}({:#x}) vs Rust's {}({:#x})", num,
103+
F32(apfloat_bits), apfloat_bits, F32(rust_bits), rust_bits);
104+
Ok(F32(apfloat_bits))
105+
}
106+
ast::FloatTy::F64 => {
107+
let rust_bits = num.parse::<f64>()?.to_bits();
108+
let apfloat = num.parse::<IeeeDouble>().unwrap_or_else(|e| {
109+
panic!("apfloat::IeeeDouble failed to parse `{}`: {:?}", num, e);
110+
});
111+
let apfloat_bits = apfloat.to_bits() as u64;
112+
assert!(rust_bits == apfloat_bits,
113+
"apfloat::IeeeDouble gave different result for `{}`: \
114+
{}({:#x}) vs Rust's {}({:#x})", num,
115+
F64(apfloat_bits), apfloat_bits, F64(rust_bits), rust_bits);
116+
Ok(F64(apfloat_bits))
117+
}
118+
}
119+
}
120+
121+
pub fn to_i128(self, width: usize) -> Option<i128> {
122+
assert!(width <= 128);
123+
let (r, fs) = match self {
124+
F32(f) => {
125+
IeeeSingle::from_bits(f as u128).to_i128(width, Round::TowardZero, &mut true)
126+
}
127+
F64(f) => {
128+
IeeeDouble::from_bits(f as u128).to_i128(width, Round::TowardZero, &mut true)
129+
}
130+
};
131+
if fs.intersects(OpStatus::INVALID_OP) {
132+
None
133+
} else {
134+
Some(r)
135+
}
136+
}
137+
138+
pub fn to_u128(self, width: usize) -> Option<u128> {
139+
assert!(width <= 128);
140+
let (r, fs) = match self {
141+
F32(f) => {
142+
IeeeSingle::from_bits(f as u128).to_u128(width, Round::TowardZero, &mut true)
143+
}
144+
F64(f) => {
145+
IeeeDouble::from_bits(f as u128).to_u128(width, Round::TowardZero, &mut true)
146+
}
147+
};
148+
if fs.intersects(OpStatus::INVALID_OP) {
149+
None
150+
} else {
151+
Some(r)
152+
}
153+
}
154+
155+
pub fn convert(self, fty: ast::FloatTy) -> Self {
156+
match (self, fty) {
157+
(F32(f), ast::FloatTy::F32) => F32(f),
158+
(F64(f), ast::FloatTy::F64) => F64(f),
159+
(F32(f), ast::FloatTy::F64) => {
160+
let from = IeeeSingle::from_bits(f as u128);
161+
let (to, _) = from.convert(Round::NearestTiesToEven, &mut false);
162+
F64(IeeeDouble::to_bits(to) as u64)
94163
}
95-
F32(a) => {
96-
unsafe { transmute::<_,u32>(a) }.hash(state)
164+
(F64(f), ast::FloatTy::F32) => {
165+
let from = IeeeDouble::from_bits(f as u128);
166+
let (to, _) = from.convert(Round::NearestTiesToEven, &mut false);
167+
F32(IeeeSingle::to_bits(to) as u32)
97168
}
98169
}
99170
}
@@ -102,8 +173,8 @@ impl hash::Hash for ConstFloat {
102173
impl ::std::fmt::Display for ConstFloat {
103174
fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
104175
match *self {
105-
F32(f) => write!(fmt, "{}f32", f),
106-
F64(f) => write!(fmt, "{}f64", f),
176+
F32(f) => write!(fmt, "{}f32", IeeeSingle::from_bits(f as u128)),
177+
F64(f) => write!(fmt, "{}f64", IeeeDouble::from_bits(f as u128)),
107178
}
108179
}
109180
}
@@ -114,8 +185,16 @@ macro_rules! derive_binop {
114185
type Output = Result<Self, ConstMathErr>;
115186
fn $func(self, rhs: Self) -> Result<Self, ConstMathErr> {
116187
match (self, rhs) {
117-
(F32(a), F32(b)) => Ok(F32(a.$func(b))),
118-
(F64(a), F64(b)) => Ok(F64(a.$func(b))),
188+
(F32(a), F32(b)) =>{
189+
let a = IeeeSingle::from_bits(a as u128);
190+
let b = IeeeSingle::from_bits(b as u128);
191+
Ok(F32(a.$func(b).to_bits() as u32))
192+
}
193+
(F64(a), F64(b)) => {
194+
let a = IeeeDouble::from_bits(a as u128);
195+
let b = IeeeDouble::from_bits(b as u128);
196+
Ok(F64(a.$func(b).to_bits() as u64))
197+
}
119198
_ => Err(UnequalTypes(Op::$op)),
120199
}
121200
}
@@ -133,8 +212,8 @@ impl ::std::ops::Neg for ConstFloat {
133212
type Output = Self;
134213
fn neg(self) -> Self {
135214
match self {
136-
F32(f) => F32(-f),
137-
F64(f) => F64(-f),
215+
F32(f) => F32((-IeeeSingle::from_bits(f as u128)).to_bits() as u32),
216+
F64(f) => F64((-IeeeDouble::from_bits(f as u128)).to_bits() as u64),
138217
}
139218
}
140219
}

src/librustc_const_math/int.rs

-42
Original file line numberDiff line numberDiff line change
@@ -211,48 +211,6 @@ impl ConstInt {
211211
}
212212
}
213213

214-
pub fn to_f32(self) -> f32 {
215-
match self {
216-
I8(i) => i as f32,
217-
I16(i) => i as f32,
218-
I32(i) => i as f32,
219-
I64(i) => i as f32,
220-
I128(i) => i as f32,
221-
Isize(Is16(i)) => i as f32,
222-
Isize(Is32(i)) => i as f32,
223-
Isize(Is64(i)) => i as f32,
224-
U8(i) => i as f32,
225-
U16(i) => i as f32,
226-
U32(i) => i as f32,
227-
U64(i) => i as f32,
228-
U128(i) => i as f32,
229-
Usize(Us16(i)) => i as f32,
230-
Usize(Us32(i)) => i as f32,
231-
Usize(Us64(i)) => i as f32,
232-
}
233-
}
234-
235-
pub fn to_f64(self) -> f64 {
236-
match self {
237-
I8(i) => i as f64,
238-
I16(i) => i as f64,
239-
I32(i) => i as f64,
240-
I64(i) => i as f64,
241-
I128(i) => i as f64,
242-
Isize(Is16(i)) => i as f64,
243-
Isize(Is32(i)) => i as f64,
244-
Isize(Is64(i)) => i as f64,
245-
U8(i) => i as f64,
246-
U16(i) => i as f64,
247-
U32(i) => i as f64,
248-
U64(i) => i as f64,
249-
U128(i) => i as f64,
250-
Usize(Us16(i)) => i as f64,
251-
Usize(Us32(i)) => i as f64,
252-
Usize(Us64(i)) => i as f64,
253-
}
254-
}
255-
256214
pub fn is_negative(&self) -> bool {
257215
match *self {
258216
I8(v) => v < 0,

0 commit comments

Comments
 (0)