Skip to content

Commit 559ea1c

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

File tree

5 files changed

+154
-123
lines changed

5 files changed

+154
-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

+117-50
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,25 @@
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)]
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, Debug, Hash, RustcEncodable, RustcDecodable)]
1826
pub enum ConstFloat {
19-
F32(f32),
20-
F64(f64)
27+
F32(u32),
28+
F64(u64)
2129
}
22-
pub use self::ConstFloat::*;
30+
use self::ConstFloat::*;
2331

2432
impl ConstFloat {
2533
/// Description of the type, not the value
@@ -32,68 +40,119 @@ impl ConstFloat {
3240

3341
pub fn is_nan(&self) -> bool {
3442
match *self {
35-
F32(f) => f.is_nan(),
36-
F64(f) => f.is_nan(),
43+
F32(f) => Single::from_bits(f as u128).is_nan(),
44+
F64(f) => Double::from_bits(f as u128).is_nan(),
3745
}
3846
}
3947

4048
/// Compares the values if they are of the same type
4149
pub fn try_cmp(self, rhs: Self) -> Result<Ordering, ConstMathErr> {
4250
match (self, rhs) {
4351
(F64(a), F64(b)) => {
52+
let a = Double::from_bits(a as u128);
53+
let b = Double::from_bits(b as u128);
4454
// 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-
})
55+
Ok(a.partial_cmp(&b).unwrap_or(Ordering::Greater))
5256
}
5357

5458
(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-
})
59+
let a = Single::from_bits(a as u128);
60+
let b = Single::from_bits(b as u128);
61+
Ok(a.partial_cmp(&b).unwrap_or(Ordering::Greater))
6262
}
6363

6464
_ => Err(CmpBetweenUnequalTypes),
6565
}
6666
}
67-
}
6867

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)}
68+
pub fn from_i128(input: i128, fty: ast::FloatTy) -> Self {
69+
match fty {
70+
ast::FloatTy::F32 => {
71+
F32(Single::from_i128(input).value.to_bits() as u32)
7872
}
79-
(F32(a), F32(b)) => {
80-
unsafe{transmute::<_,u32>(a) == transmute::<_,u32>(b)}
73+
ast::FloatTy::F64 => {
74+
F64(Double::from_i128(input).value.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+
F32(Single::from_u128(input).value.to_bits() as u32)
83+
}
84+
ast::FloatTy::F64 => {
85+
F64(Double::from_u128(input).value.to_bits() as u64)
86+
}
87+
}
88+
}
8889

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)
90+
pub fn from_str(num: &str, fty: ast::FloatTy) -> Result<Self, ParseFloatError> {
91+
match fty {
92+
ast::FloatTy::F32 => {
93+
let rust_bits = num.parse::<f32>()?.to_bits();
94+
let apfloat = num.parse::<Single>().unwrap_or_else(|e| {
95+
panic!("apfloat::ieee::Single failed to parse `{}`: {:?}", num, e);
96+
});
97+
let apfloat_bits = apfloat.to_bits() as u32;
98+
assert!(rust_bits == apfloat_bits,
99+
"apfloat::Single gave different result for `{}`: \
100+
{}({:#x}) vs Rust's {}({:#x})", num,
101+
F32(apfloat_bits), apfloat_bits, F32(rust_bits), rust_bits);
102+
Ok(F32(apfloat_bits))
103+
}
104+
ast::FloatTy::F64 => {
105+
let rust_bits = num.parse::<f64>()?.to_bits();
106+
let apfloat = num.parse::<Double>().unwrap_or_else(|e| {
107+
panic!("apfloat::ieee::Double failed to parse `{}`: {:?}", num, e);
108+
});
109+
let apfloat_bits = apfloat.to_bits() as u64;
110+
assert!(rust_bits == apfloat_bits,
111+
"apfloat::Double gave different result for `{}`: \
112+
{}({:#x}) vs Rust's {}({:#x})", num,
113+
F64(apfloat_bits), apfloat_bits, F64(rust_bits), rust_bits);
114+
Ok(F64(apfloat_bits))
115+
}
116+
}
117+
}
118+
119+
pub fn to_i128(self, width: usize) -> Option<i128> {
120+
assert!(width <= 128);
121+
let r = match self {
122+
F32(f) => Single::from_bits(f as u128).to_i128(width),
123+
F64(f) => Double::from_bits(f as u128).to_i128(width)
124+
};
125+
if r.status.intersects(Status::INVALID_OP) {
126+
None
127+
} else {
128+
Some(r.value)
129+
}
130+
}
131+
132+
pub fn to_u128(self, width: usize) -> Option<u128> {
133+
assert!(width <= 128);
134+
let r = match self {
135+
F32(f) => Single::from_bits(f as u128).to_u128(width),
136+
F64(f) => Double::from_bits(f as u128).to_u128(width)
137+
};
138+
if r.status.intersects(Status::INVALID_OP) {
139+
None
140+
} else {
141+
Some(r.value)
142+
}
143+
}
144+
145+
pub fn convert(self, fty: ast::FloatTy) -> Self {
146+
match (self, fty) {
147+
(F32(f), ast::FloatTy::F32) => F32(f),
148+
(F64(f), ast::FloatTy::F64) => F64(f),
149+
(F32(f), ast::FloatTy::F64) => {
150+
let r: Double = Single::from_bits(f as u128).convert(&mut false).value;
151+
F64(r.to_bits() as u64)
94152
}
95-
F32(a) => {
96-
unsafe { transmute::<_,u32>(a) }.hash(state)
153+
(F64(f), ast::FloatTy::F32) => {
154+
let r: Single = Double::from_bits(f as u128).convert(&mut false).value;
155+
F32(r.to_bits() as u32)
97156
}
98157
}
99158
}
@@ -102,8 +161,8 @@ impl hash::Hash for ConstFloat {
102161
impl ::std::fmt::Display for ConstFloat {
103162
fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
104163
match *self {
105-
F32(f) => write!(fmt, "{}f32", f),
106-
F64(f) => write!(fmt, "{}f64", f),
164+
F32(f) => write!(fmt, "{}f32", Single::from_bits(f as u128)),
165+
F64(f) => write!(fmt, "{}f64", Double::from_bits(f as u128)),
107166
}
108167
}
109168
}
@@ -114,8 +173,16 @@ macro_rules! derive_binop {
114173
type Output = Result<Self, ConstMathErr>;
115174
fn $func(self, rhs: Self) -> Result<Self, ConstMathErr> {
116175
match (self, rhs) {
117-
(F32(a), F32(b)) => Ok(F32(a.$func(b))),
118-
(F64(a), F64(b)) => Ok(F64(a.$func(b))),
176+
(F32(a), F32(b)) =>{
177+
let a = Single::from_bits(a as u128);
178+
let b = Single::from_bits(b as u128);
179+
Ok(F32(a.$func(b).value.to_bits() as u32))
180+
}
181+
(F64(a), F64(b)) => {
182+
let a = Double::from_bits(a as u128);
183+
let b = Double::from_bits(b as u128);
184+
Ok(F64(a.$func(b).value.to_bits() as u64))
185+
}
119186
_ => Err(UnequalTypes(Op::$op)),
120187
}
121188
}
@@ -133,8 +200,8 @@ impl ::std::ops::Neg for ConstFloat {
133200
type Output = Self;
134201
fn neg(self) -> Self {
135202
match self {
136-
F32(f) => F32(-f),
137-
F64(f) => F64(-f),
203+
F32(f) => F32((-Single::from_bits(f as u128)).to_bits() as u32),
204+
F64(f) => F64((-Double::from_bits(f as u128)).to_bits() as u64),
138205
}
139206
}
140207
}

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,

src/librustc_const_math/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
#![feature(i128)]
2727
#![feature(i128_type)]
2828

29+
extern crate rustc_apfloat;
30+
2931
extern crate syntax;
3032

3133
extern crate serialize as rustc_serialize; // used by deriving

src/librustc_trans/mir/constant.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use llvm::{self, ValueRef};
1212
use rustc::middle::const_val::{ConstEvalErr, ConstVal, ErrKind};
1313
use rustc_const_math::ConstInt::*;
14-
use rustc_const_math::ConstFloat::*;
14+
use rustc_const_math::ConstFloat;
1515
use rustc_const_math::{ConstInt, ConstMathErr};
1616
use rustc::hir::def_id::DefId;
1717
use rustc::infer::TransNormalize;
@@ -95,8 +95,13 @@ impl<'tcx> Const<'tcx> {
9595
-> Const<'tcx> {
9696
let llty = type_of::type_of(ccx, ty);
9797
let val = match cv {
98-
ConstVal::Float(F32(v)) => C_floating_f64(v as f64, llty),
99-
ConstVal::Float(F64(v)) => C_floating_f64(v, llty),
98+
ConstVal::Float(v) => {
99+
let v_f64 = match v {
100+
ConstFloat::F32(v) => f32::from_bits(v) as f64,
101+
ConstFloat::F64(v) => f64::from_bits(v)
102+
};
103+
C_floating_f64(v_f64, llty)
104+
}
100105
ConstVal::Bool(v) => C_bool(ccx, v),
101106
ConstVal::Integral(ref i) => return Const::from_constint(ccx, i),
102107
ConstVal::Str(ref v) => C_str_slice(ccx, v.clone()),

0 commit comments

Comments
 (0)