diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index 3621cb267d9..3482971cd19 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -12,14 +12,12 @@ use syntax::ast; use std::rc::Rc; use hir::def_id::DefId; -use std::hash; -use std::mem::transmute; use rustc_const_math::*; use self::ConstVal::*; -#[derive(Clone, Debug, RustcEncodable, RustcDecodable)] +#[derive(Clone, Debug, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq)] pub enum ConstVal { - Float(f64), + Float(ConstFloat), Integral(ConstInt), Str(InternedString), ByteStr(Rc>), @@ -36,55 +34,10 @@ pub enum ConstVal { Dummy, } -impl hash::Hash for ConstVal { - fn hash(&self, state: &mut H) { - match *self { - Float(a) => unsafe { transmute::<_,u64>(a) }.hash(state), - Integral(a) => a.hash(state), - Str(ref a) => a.hash(state), - ByteStr(ref a) => a.hash(state), - Bool(a) => a.hash(state), - Struct(a) => a.hash(state), - Tuple(a) => a.hash(state), - Function(a) => a.hash(state), - Array(a, n) => { a.hash(state); n.hash(state) }, - Repeat(a, n) => { a.hash(state); n.hash(state) }, - Char(c) => c.hash(state), - Dummy => ().hash(state), - } - } -} - -/// Note that equality for `ConstVal` 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 ConstVal { - fn eq(&self, other: &ConstVal) -> bool { - match (self, other) { - (&Float(a), &Float(b)) => unsafe{transmute::<_,u64>(a) == transmute::<_,u64>(b)}, - (&Integral(a), &Integral(b)) => a == b, - (&Str(ref a), &Str(ref b)) => a == b, - (&ByteStr(ref a), &ByteStr(ref b)) => a == b, - (&Bool(a), &Bool(b)) => a == b, - (&Struct(a), &Struct(b)) => a == b, - (&Tuple(a), &Tuple(b)) => a == b, - (&Function(a), &Function(b)) => a == b, - (&Array(a, an), &Array(b, bn)) => (a == b) && (an == bn), - (&Repeat(a, an), &Repeat(b, bn)) => (a == b) && (an == bn), - (&Char(a), &Char(b)) => a == b, - (&Dummy, &Dummy) => true, // FIXME: should this be false? - _ => false, - } - } -} - -impl Eq for ConstVal { } - impl ConstVal { pub fn description(&self) -> &'static str { match *self { - Float(_) => "float", + Float(f) => f.description(), Integral(i) => i.description(), Str(_) => "string literal", ByteStr(_) => "byte string literal", diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index 785b734b799..5637b44335e 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -621,18 +621,19 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, match (eval_const_expr_partial(tcx, &a, ty_hint, fn_args)?, eval_const_expr_partial(tcx, &b, b_ty, fn_args)?) { (Float(a), Float(b)) => { + use std::cmp::Ordering::*; match op.node { - hir::BiAdd => Float(a + b), - hir::BiSub => Float(a - b), - hir::BiMul => Float(a * b), - hir::BiDiv => Float(a / b), - hir::BiRem => Float(a % b), - hir::BiEq => Bool(a == b), - hir::BiLt => Bool(a < b), - hir::BiLe => Bool(a <= b), - hir::BiNe => Bool(a != b), - hir::BiGe => Bool(a >= b), - hir::BiGt => Bool(a > b), + hir::BiAdd => Float(math!(e, a + b)), + hir::BiSub => Float(math!(e, a - b)), + hir::BiMul => Float(math!(e, a * b)), + hir::BiDiv => Float(math!(e, a / b)), + hir::BiRem => Float(math!(e, a % b)), + hir::BiEq => Bool(math!(e, a.try_cmp(b)) == Equal), + hir::BiLt => Bool(math!(e, a.try_cmp(b)) == Less), + hir::BiLe => Bool(math!(e, a.try_cmp(b)) != Greater), + hir::BiNe => Bool(math!(e, a.try_cmp(b)) != Equal), + hir::BiGe => Bool(math!(e, a.try_cmp(b)) != Less), + hir::BiGt => Bool(math!(e, a.try_cmp(b)) == Greater), _ => signal!(e, InvalidOpForFloats(op.node)), } } @@ -1078,13 +1079,13 @@ fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, val: ConstInt, ty: ty:: } }, ty::TyFloat(ast::FloatTy::F64) => match val.erase_type() { - Infer(u) => Ok(Float(u as f64)), - InferSigned(i) => Ok(Float(i as f64)), + Infer(u) => Ok(Float(F64(u as f64))), + InferSigned(i) => Ok(Float(F64(i as f64))), _ => bug!("ConstInt::erase_type returned something other than Infer/InferSigned"), }, ty::TyFloat(ast::FloatTy::F32) => match val.erase_type() { - Infer(u) => Ok(Float(u as f32 as f64)), - InferSigned(i) => Ok(Float(i as f32 as f64)), + Infer(u) => Ok(Float(F32(u as f32))), + InferSigned(i) => Ok(Float(F32(i as f32))), _ => bug!("ConstInt::erase_type returned something other than Infer/InferSigned"), }, ty::TyRawPtr(_) => Err(ErrKind::UnimplementedConstVal("casting an address to a raw ptr")), @@ -1097,13 +1098,35 @@ fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, val: ConstInt, ty: ty:: } } -fn cast_const_float<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, f: f64, ty: ty::Ty) -> CastResult { +fn cast_const_float<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + val: ConstFloat, + ty: ty::Ty) -> CastResult { match ty.sty { - ty::TyInt(_) if f >= 0.0 => cast_const_int(tcx, Infer(f as u64), ty), - ty::TyInt(_) => cast_const_int(tcx, InferSigned(f as i64), ty), - ty::TyUint(_) if f >= 0.0 => cast_const_int(tcx, Infer(f as u64), ty), - ty::TyFloat(ast::FloatTy::F64) => Ok(Float(f)), - ty::TyFloat(ast::FloatTy::F32) => Ok(Float(f as f32 as f64)), + ty::TyInt(_) | ty::TyUint(_) => { + let i = match val { + F32(f) if f >= 0.0 => Infer(f as u64), + FInfer { f64: f, .. } | + F64(f) if f >= 0.0 => Infer(f as u64), + + F32(f) => InferSigned(f as i64), + FInfer { f64: f, .. } | + F64(f) => InferSigned(f as i64) + }; + + if let (InferSigned(_), &ty::TyUint(_)) = (i, &ty.sty) { + return Err(CannotCast); + } + + cast_const_int(tcx, i, ty) + } + ty::TyFloat(ast::FloatTy::F64) => Ok(Float(F64(match val { + F32(f) => f as f64, + FInfer { f64: f, .. } | F64(f) => f + }))), + ty::TyFloat(ast::FloatTy::F32) => Ok(Float(F32(match val { + F64(f) => f as f32, + FInfer { f32: f, .. } | F32(f) => f + }))), _ => Err(CannotCast), } } @@ -1161,33 +1184,43 @@ fn lit_to_const<'a, 'tcx>(lit: &ast::LitKind, infer(Infer(n), tcx, &ty::TyUint(ity)).map(Integral) }, - LitKind::Float(ref n, _) | + LitKind::Float(ref n, fty) => { + Ok(Float(parse_float(n, Some(fty), span))) + } LitKind::FloatUnsuffixed(ref n) => { - if let Ok(x) = n.parse::() { - Ok(Float(x)) - } else { - // FIXME(#31407) this is only necessary because float parsing is buggy - span_bug!(span, "could not evaluate float literal (see issue #31407)"); - } + let fty_hint = match ty_hint.map(|t| &t.sty) { + Some(&ty::TyFloat(fty)) => Some(fty), + _ => None + }; + Ok(Float(parse_float(n, fty_hint, span))) } LitKind::Bool(b) => Ok(Bool(b)), LitKind::Char(c) => Ok(Char(c)), } } +fn parse_float(num: &str, fty_hint: Option, span: Span) -> ConstFloat { + let val = match fty_hint { + Some(ast::FloatTy::F32) => num.parse::().map(F32), + Some(ast::FloatTy::F64) => num.parse::().map(F64), + None => { + num.parse::().and_then(|f32| { + num.parse::().map(|f64| { + FInfer { f32: f32, f64: f64 } + }) + }) + } + }; + val.unwrap_or_else(|_| { + // FIXME(#31407) this is only necessary because float parsing is buggy + span_bug!(span, "could not evaluate float literal (see issue #31407)"); + }) +} + pub fn compare_const_vals(a: &ConstVal, b: &ConstVal) -> Option { match (a, b) { (&Integral(a), &Integral(b)) => a.try_cmp(b).ok(), - (&Float(a), &Float(b)) => { - // This is pretty bad but it is the existing behavior. - Some(if a == b { - Ordering::Equal - } else if a < b { - Ordering::Less - } else { - Ordering::Greater - }) - } + (&Float(a), &Float(b)) => a.try_cmp(b).ok(), (&Str(ref a), &Str(ref b)) => Some(a.cmp(b)), (&Bool(a), &Bool(b)) => Some(a.cmp(&b)), (&ByteStr(ref a), &ByteStr(ref b)) => Some(a.cmp(b)), diff --git a/src/librustc_const_math/err.rs b/src/librustc_const_math/err.rs index 9970810d4e2..e4eb0f2c97e 100644 --- a/src/librustc_const_math/err.rs +++ b/src/librustc_const_math/err.rs @@ -45,17 +45,17 @@ pub fn description(&self) -> &'static str { use self::Op::*; match *self { NotInRange => "inferred value out of range", - CmpBetweenUnequalTypes => "compared two integrals of different types", - UnequalTypes(Add) => "tried to add two integrals of different types", - UnequalTypes(Sub) => "tried to subtract two integrals of different types", - UnequalTypes(Mul) => "tried to multiply two integrals of different types", - UnequalTypes(Div) => "tried to divide two integrals of different types", + CmpBetweenUnequalTypes => "compared two values of different types", + UnequalTypes(Add) => "tried to add two values of different types", + UnequalTypes(Sub) => "tried to subtract two values of different types", + UnequalTypes(Mul) => "tried to multiply two values of different types", + UnequalTypes(Div) => "tried to divide two values of different types", UnequalTypes(Rem) => { - "tried to calculate the remainder of two integrals of different types" + "tried to calculate the remainder of two values of different types" }, - UnequalTypes(BitAnd) => "tried to bitand two integrals of different types", - UnequalTypes(BitOr) => "tried to bitor two integrals of different types", - UnequalTypes(BitXor) => "tried to xor two integrals of different types", + UnequalTypes(BitAnd) => "tried to bitand two values of different types", + UnequalTypes(BitOr) => "tried to bitor two values of different types", + UnequalTypes(BitXor) => "tried to xor two values of different types", UnequalTypes(_) => unreachable!(), Overflow(Add) => "attempted to add with overflow", Overflow(Sub) => "attempted to subtract with overflow", diff --git a/src/librustc_const_math/float.rs b/src/librustc_const_math/float.rs new file mode 100644 index 00000000000..4610c183e1b --- /dev/null +++ b/src/librustc_const_math/float.rs @@ -0,0 +1,173 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::cmp::Ordering; +use std::hash; +use std::mem::transmute; + +use super::err::*; + +#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)] +pub enum ConstFloat { + F32(f32), + F64(f64), + + // When the type isn't known, we have to operate on both possibilities. + FInfer { + f32: f32, + f64: f64 + } +} +pub use self::ConstFloat::*; + +impl ConstFloat { + /// Description of the type, not the value + pub fn description(&self) -> &'static str { + match *self { + FInfer {..} => "float", + F32(_) => "f32", + F64(_) => "f64", + } + } + + pub fn is_nan(&self) -> bool { + match *self { + F32(f) => f.is_nan(), + F64(f) => f.is_nan(), + FInfer { f32, f64 } => f32.is_nan() || f64.is_nan() + } + } + + /// Compares the values if they are of the same type + pub fn try_cmp(self, rhs: Self) -> Result { + match (self, rhs) { + (F64(a), F64(b)) | + (F64(a), FInfer { f64: b, .. }) | + (FInfer { f64: a, .. }, F64(b)) | + (FInfer { f64: a, .. }, FInfer { f64: b, .. }) => { + // 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 + }) + } + + (F32(a), F32(b)) | + (F32(a), FInfer { f32: b, .. }) | + (FInfer { f32: a, .. }, F32(b)) => { + Ok(if a == b { + Ordering::Equal + } else if a < b { + Ordering::Less + } else { + 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)) | + (F64(a), FInfer { f64: b, .. }) | + (FInfer { f64: a, .. }, F64(b)) | + (FInfer { f64: a, .. }, FInfer { f64: b, .. }) => { + unsafe{transmute::<_,u64>(a) == transmute::<_,u64>(b)} + } + (F32(a), F32(b)) => { + unsafe{transmute::<_,u32>(a) == transmute::<_,u32>(b)} + } + _ => false + } + } +} + +impl Eq for ConstFloat {} + +impl hash::Hash for ConstFloat { + fn hash(&self, state: &mut H) { + match *self { + F64(a) | FInfer { f64: a, .. } => { + unsafe { transmute::<_,u64>(a) }.hash(state) + } + F32(a) => { + unsafe { transmute::<_,u32>(a) }.hash(state) + } + } + } +} + +impl ::std::fmt::Display for ConstFloat { + fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { + match *self { + FInfer { f64, .. } => write!(fmt, "{}", f64), + F32(f) => write!(fmt, "{}f32", f), + F64(f) => write!(fmt, "{}f64", f), + } + } +} + +macro_rules! derive_binop { + ($op:ident, $func:ident) => { + impl ::std::ops::$op for ConstFloat { + type Output = Result; + fn $func(self, rhs: Self) -> Result { + match (self, rhs) { + (F32(a), F32(b)) | + (F32(a), FInfer { f32: b, .. }) | + (FInfer { f32: a, .. }, F32(b)) => Ok(F32(a.$func(b))), + + (F64(a), F64(b)) | + (FInfer { f64: a, .. }, F64(b)) | + (F64(a), FInfer { f64: b, .. }) => Ok(F64(a.$func(b))), + + (FInfer { f32: a32, f64: a64 }, + FInfer { f32: b32, f64: b64 }) => Ok(FInfer { + f32: a32.$func(b32), + f64: a64.$func(b64) + }), + + _ => Err(UnequalTypes(Op::$op)), + } + } + } + } +} + +derive_binop!(Add, add); +derive_binop!(Sub, sub); +derive_binop!(Mul, mul); +derive_binop!(Div, div); +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), + FInfer { f32, f64 } => FInfer { + f32: -f32, + f64: -f64 + } + } + } +} diff --git a/src/librustc_const_math/lib.rs b/src/librustc_const_math/lib.rs index 59792d16e8b..741dd4107e0 100644 --- a/src/librustc_const_math/lib.rs +++ b/src/librustc_const_math/lib.rs @@ -32,11 +32,13 @@ extern crate serialize as rustc_serialize; // used by deriving +mod float; mod int; mod us; mod is; mod err; +pub use float::*; pub use int::*; pub use us::*; pub use is::*; diff --git a/src/librustc_mir/build/misc.rs b/src/librustc_mir/build/misc.rs index 00e89095276..c3501140fc0 100644 --- a/src/librustc_mir/build/misc.rs +++ b/src/librustc_mir/build/misc.rs @@ -56,7 +56,7 @@ pub fn unit_rvalue(&mut self) -> Rvalue<'tcx> { } // Returns a zero literal operand for the appropriate type, works for - // bool, char, integers and floats. + // bool, char and integers. pub fn zero_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> { let literal = match ty.sty { ty::TyBool => { @@ -93,7 +93,6 @@ pub fn zero_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> { Literal::Value { value: ConstVal::Integral(val) } } - ty::TyFloat(_) => Literal::Value { value: ConstVal::Float(0.0) }, _ => { span_bug!(span, "Invalid type for zero_literal: `{:?}`", ty) } diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index d352a8241ac..f5fdd87b167 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -12,6 +12,7 @@ use rustc::middle::const_val::ConstVal; use rustc_const_eval::ErrKind; use rustc_const_math::ConstInt::*; +use rustc_const_math::ConstFloat::*; use rustc_const_math::ConstMathErr; use rustc::hir::def_id::DefId; use rustc::infer::TransNormalize; @@ -63,7 +64,9 @@ 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(v) => C_floating_f64(v, llty), + ConstVal::Float(F32(v)) => C_floating_f64(v as f64, llty), + ConstVal::Float(F64(v)) => C_floating_f64(v, llty), + ConstVal::Float(FInfer {..}) => bug!("MIR must not use `{:?}`", cv), ConstVal::Bool(v) => C_bool(ccx, v), ConstVal::Integral(I8(v)) => C_integral(Type::i8(ccx), v as u64, true), ConstVal::Integral(I16(v)) => C_integral(Type::i16(ccx), v as u64, true), @@ -81,14 +84,14 @@ pub fn from_constval<'a>(ccx: &CrateContext<'a, 'tcx>, let u = v.as_u64(ccx.tcx().sess.target.uint_type); C_integral(Type::int(ccx), u, false) }, - ConstVal::Integral(Infer(v)) => C_integral(llty, v as u64, false), - ConstVal::Integral(InferSigned(v)) => C_integral(llty, v as u64, true), + ConstVal::Integral(Infer(_)) | + ConstVal::Integral(InferSigned(_)) => bug!("MIR must not use `{:?}`", cv), ConstVal::Str(ref v) => C_str_slice(ccx, v.clone()), ConstVal::ByteStr(ref v) => consts::addr_of(ccx, C_bytes(ccx, v), 1, "byte_str"), ConstVal::Struct(_) | ConstVal::Tuple(_) | ConstVal::Array(..) | ConstVal::Repeat(..) | ConstVal::Function(_) => { - bug!("MIR must not use {:?} (which refers to a local ID)", cv) + bug!("MIR must not use `{:?}` (which refers to a local ID)", cv) } ConstVal::Char(c) => C_integral(Type::char(ccx), c as u64, false), ConstVal::Dummy => bug!(), diff --git a/src/libstd/num/f32.rs b/src/libstd/num/f32.rs index c0031aa42e6..ff80a4e3053 100644 --- a/src/libstd/num/f32.rs +++ b/src/libstd/num/f32.rs @@ -1382,7 +1382,6 @@ fn test_classify() { } #[test] - #[rustc_no_mir] // FIXME #27840 MIR NAN ends up negative. fn test_integer_decode() { assert_eq!(3.14159265359f32.integer_decode(), (13176795, -22, 1)); assert_eq!((-8573.5918555f32).integer_decode(), (8779358, -10, -1)); @@ -1391,7 +1390,11 @@ fn test_integer_decode() { assert_eq!((-0f32).integer_decode(), (0, -150, -1)); assert_eq!(INFINITY.integer_decode(), (8388608, 105, 1)); assert_eq!(NEG_INFINITY.integer_decode(), (8388608, 105, -1)); - assert_eq!(NAN.integer_decode(), (12582912, 105, 1)); + + // Ignore the "sign" (quiet / signalling flag) of NAN. + // It can vary between runtime operations and LLVM folding. + let (nan_m, nan_e, _nan_s) = NAN.integer_decode(); + assert_eq!((nan_m, nan_e), (12582912, 105)); } #[test] diff --git a/src/libstd/num/f64.rs b/src/libstd/num/f64.rs index 1a46d9a3895..b7750317870 100644 --- a/src/libstd/num/f64.rs +++ b/src/libstd/num/f64.rs @@ -1277,7 +1277,6 @@ fn test_classify() { } #[test] - #[rustc_no_mir] // FIXME #27840 MIR NAN ends up negative. fn test_integer_decode() { assert_eq!(3.14159265359f64.integer_decode(), (7074237752028906, -51, 1)); assert_eq!((-8573.5918555f64).integer_decode(), (4713381968463931, -39, -1)); @@ -1286,7 +1285,11 @@ fn test_integer_decode() { assert_eq!((-0f64).integer_decode(), (0, -1075, -1)); assert_eq!(INFINITY.integer_decode(), (4503599627370496, 972, 1)); assert_eq!(NEG_INFINITY.integer_decode(), (4503599627370496, 972, -1)); - assert_eq!(NAN.integer_decode(), (6755399441055744, 972, 1)); + + // Ignore the "sign" (quiet / signalling flag) of NAN. + // It can vary between runtime operations and LLVM folding. + let (nan_m, nan_e, _nan_s) = NAN.integer_decode(); + assert_eq!((nan_m, nan_e), (6755399441055744, 972)); } #[test] diff --git a/src/test/run-pass/issue-32805.rs b/src/test/run-pass/issue-32805.rs new file mode 100644 index 00000000000..ea49cf3e7be --- /dev/null +++ b/src/test/run-pass/issue-32805.rs @@ -0,0 +1,26 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] + +#[rustc_mir] +fn const_mir() -> f32 { 9007199791611905.0 } + +#[rustc_no_mir] +fn const_old() -> f32 { 9007199791611905.0 } + +fn main() { + let original = "9007199791611905.0"; // (1<<53)+(1<<29)+1 + let expected = "9007200000000000"; + + assert_eq!(const_mir().to_string(), expected); + assert_eq!(const_old().to_string(), expected); + assert_eq!(original.parse::().unwrap().to_string(), expected); +}