From 0d6b52c2f3d442d3edfea31bcc439127a4757e5e Mon Sep 17 00:00:00 2001 From: Robin Kruppe Date: Mon, 9 Oct 2017 02:14:00 +0200 Subject: Saturating casts between integers and floats (both directions). This affects regular code generation as well as constant evaluation in trans, but not the HIR constant evaluator because that one returns an error for overflowing casts and NaN-to-int casts. That error is conservatively correct and we should be careful to not accept more code in constant expressions. The changes to code generation are guarded by a new -Z flag, to be able to evaluate the performance impact. The trans constant evaluation changes are unconditional because they have no run time impact and don't affect type checking either. --- src/test/codegen/unchecked-float-casts.rs | 65 +++++++++++++ src/test/run-pass/saturating-float-casts.rs | 141 ++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+) create mode 100644 src/test/codegen/unchecked-float-casts.rs create mode 100644 src/test/run-pass/saturating-float-casts.rs (limited to 'src/test') diff --git a/src/test/codegen/unchecked-float-casts.rs b/src/test/codegen/unchecked-float-casts.rs new file mode 100644 index 00000000000..64ab19cceee --- /dev/null +++ b/src/test/codegen/unchecked-float-casts.rs @@ -0,0 +1,65 @@ +// Copyright 2017 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. + +// compile-flags: -C no-prepopulate-passes + +// This file tests that we don't generate any code for saturation if +// -Z saturating-float-casts is not enabled. + +#![crate_type = "lib"] +#![feature(i128_type)] + +// CHECK-LABEL: @f32_to_u32 +#[no_mangle] +pub fn f32_to_u32(x: f32) -> u32 { + // CHECK: fptoui + // CHECK-NOT: fcmp + // CHECK-NOT: icmp + // CHECK-NOT: select + x as u32 +} + +// CHECK-LABEL: @f32_to_i32 +#[no_mangle] +pub fn f32_to_i32(x: f32) -> i32 { + // CHECK: fptosi + // CHECK-NOT: fcmp + // CHECK-NOT: icmp + // CHECK-NOT: select + x as i32 +} + +#[no_mangle] +pub fn f64_to_u8(x: f32) -> u16 { + // CHECK-NOT: fcmp + // CHECK-NOT: icmp + // CHECK-NOT: select + x as u16 +} + +// CHECK-LABEL: @i32_to_f64 +#[no_mangle] +pub fn i32_to_f64(x: i32) -> f64 { + // CHECK: sitofp + // CHECK-NOT: fcmp + // CHECK-NOT: icmp + // CHECK-NOT: select + x as f64 +} + +// CHECK-LABEL: @u128_to_f32 +#[no_mangle] +pub fn u128_to_f32(x: u128) -> f32 { + // CHECK: uitofp + // CHECK-NOT: fcmp + // CHECK-NOT: icmp + // CHECK-NOT: select + x as f32 +} diff --git a/src/test/run-pass/saturating-float-casts.rs b/src/test/run-pass/saturating-float-casts.rs new file mode 100644 index 00000000000..53e0cea64e9 --- /dev/null +++ b/src/test/run-pass/saturating-float-casts.rs @@ -0,0 +1,141 @@ +// Copyright 2012 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. + +// compile-flags: -Z saturating-float-casts + +#![feature(test, i128, i128_type, stmt_expr_attributes)] +#![deny(overflowing_literals)] +extern crate test; + +use std::{f32, f64}; +use std::{u8, i8, u16, i16, u32, i32, u64, i64, u128, i128}; +use test::black_box; + +macro_rules! test { + ($val:expr, $src_ty:ident -> $dest_ty:ident, $expected:expr) => ( + // black_box disables constant evaluation to test run-time conversions: + assert_eq!(black_box::<$src_ty>($val) as $dest_ty, $expected, + "run time {} -> {}", stringify!($src_ty), stringify!($dest_ty)); + // ... whereas this variant triggers constant evaluation: + { + const X: $src_ty = $val; + const Y: $dest_ty = X as $dest_ty; + assert_eq!(Y, $expected, + "const eval {} -> {}", stringify!($src_ty), stringify!($dest_ty)); + } + ); + + ($fval:expr, f* -> $ity:ident, $ival:expr) => ( + test!($fval, f32 -> $ity, $ival); + test!($fval, f64 -> $ity, $ival); + ) +} + +macro_rules! common_fptoi_tests { + ($fty:ident -> $($ity:ident)+) => ({ $( + test!($fty::NAN, $fty -> $ity, 0); + test!($fty::INFINITY, $fty -> $ity, $ity::MAX); + test!($fty::NEG_INFINITY, $fty -> $ity, $ity::MIN); + // These two tests are not solely float->int tests, in particular the latter relies on + // `u128::MAX as f32` not being UB. But that's okay, since this file tests int->float + // as well, the test is just slightly misplaced. + test!($ity::MIN as $fty, $fty -> $ity, $ity::MIN); + test!($ity::MAX as $fty, $fty -> $ity, $ity::MAX); + test!(0., $fty -> $ity, 0); + test!($fty::MIN_POSITIVE, $fty -> $ity, 0); + test!(-0.9, $fty -> $ity, 0); + test!(1., $fty -> $ity, 1); + test!(42., $fty -> $ity, 42); + )+ }); + + (f* -> $($ity:ident)+) => ({ + common_fptoi_tests!(f32 -> $($ity)+); + common_fptoi_tests!(f64 -> $($ity)+); + }) +} + +macro_rules! fptoui_tests { + ($fty: ident -> $($ity: ident)+) => ({ $( + test!(-0., $fty -> $ity, 0); + test!(-$fty::MIN_POSITIVE, $fty -> $ity, 0); + test!(-0.99999994, $fty -> $ity, 0); + test!(-1., $fty -> $ity, 0); + test!(-100., $fty -> $ity, 0); + test!(#[allow(overflowing_literals)] -1e50, $fty -> $ity, 0); + test!(#[allow(overflowing_literals)] -1e130, $fty -> $ity, 0); + )+ }); + + (f* -> $($ity:ident)+) => ({ + fptoui_tests!(f32 -> $($ity)+); + fptoui_tests!(f64 -> $($ity)+); + }) +} + +pub fn main() { + common_fptoi_tests!(f* -> i8 i16 i32 i64 i128 u8 u16 u32 u64 u128); + fptoui_tests!(f* -> u8 u16 u32 u64 u128); + + // The following tests cover edge cases for some integer types. + + // u8 + test!(254., f* -> u8, 254); + test!(256., f* -> u8, 255); + + // i8 + test!(-127., f* -> i8, -127); + test!(-129., f* -> i8, -128); + test!(126., f* -> i8, 126); + test!(128., f* -> i8, 127); + + // i32 + // -2147483648. is i32::MIN (exactly) + test!(-2147483648., f* -> i32, i32::MIN); + // 2147483648. is i32::MAX rounded up + test!(2147483648., f32 -> i32, 2147483647); + // With 24 significand bits, floats with magnitude in [2^30 + 1, 2^31] are rounded to + // multiples of 2^7. Therefore, nextDown(round(i32::MAX)) is 2^31 - 128: + test!(2147483520., f32 -> i32, 2147483520); + // Similarly, nextUp(i32::MIN) is i32::MIN + 2^8 and nextDown(i32::MIN) is i32::MIN - 2^7 + test!(-2147483904., f* -> i32, i32::MIN); + test!(-2147483520., f* -> i32, -2147483520); + + // u32 -- round(MAX) and nextUp(round(MAX)) + test!(4294967040., f* -> u32, 4294967040); + test!(4294967296., f* -> u32, 4294967295); + + // u128 + // # float->int + test!(f32::MAX, f32 -> u128, 0xffffff00000000000000000000000000); + // nextDown(f32::MAX) = 2^128 - 2 * 2^104 + const SECOND_LARGEST_F32: f32 = 340282326356119256160033759537265639424.; + test!(SECOND_LARGEST_F32, f32 -> u128, 0xfffffe00000000000000000000000000); + // # int->float + // f32::MAX - 0.5 ULP and smaller should be rounded down + test!(0xfffffe00000000000000000000000000, u128 -> f32, SECOND_LARGEST_F32); + test!(0xfffffe7fffffffffffffffffffffffff, u128 -> f32, SECOND_LARGEST_F32); + test!(0xfffffe80000000000000000000000000, u128 -> f32, SECOND_LARGEST_F32); + // numbers within < 0.5 ULP of f32::MAX it should be rounded to f32::MAX + test!(0xfffffe80000000000000000000000001, u128 -> f32, f32::MAX); + test!(0xfffffeffffffffffffffffffffffffff, u128 -> f32, f32::MAX); + test!(0xffffff00000000000000000000000000, u128 -> f32, f32::MAX); + test!(0xffffff00000000000000000000000001, u128 -> f32, f32::MAX); + test!(0xffffff7fffffffffffffffffffffffff, u128 -> f32, f32::MAX); + // f32::MAX + 0.5 ULP and greater should be rounded to infinity + test!(0xffffff80000000000000000000000000, u128 -> f32, f32::INFINITY); + test!(0xffffff80000000f00000000000000000, u128 -> f32, f32::INFINITY); + test!(0xffffff87ffffffffffffffff00000001, u128 -> f32, f32::INFINITY); + + test!(!0, u128 -> f32, f32::INFINITY); + + // u128->f64 should not be affected by the u128->f32 checks + test!(0xffffff80000000000000000000000000, u128 -> f64, + 340282356779733661637539395458142568448.0); + test!(u128::MAX, u128 -> f64, 340282366920938463463374607431768211455.0); +} -- cgit 1.4.1-3-g733a5 From 354a5cb250fe386075d33539a6c2e9a75d9d7fc5 Mon Sep 17 00:00:00 2001 From: Robin Kruppe Date: Sun, 15 Oct 2017 22:28:49 +0200 Subject: Make trans const eval error on overflow and NaN, matching HIR const eval. --- src/librustc_apfloat/lib.rs | 2 +- src/librustc_llvm/ffi.rs | 2 - src/librustc_trans/mir/constant.rs | 65 ++++++++++------ src/rustllvm/RustWrapper.cpp | 13 ---- .../compile-fail/float-int-invalid-const-cast.rs | 61 +++++++++++++++ src/test/run-pass/saturating-float-casts.rs | 91 ++++++++++++---------- 6 files changed, 154 insertions(+), 80 deletions(-) create mode 100644 src/test/compile-fail/float-int-invalid-const-cast.rs (limited to 'src/test') diff --git a/src/librustc_apfloat/lib.rs b/src/librustc_apfloat/lib.rs index 2048127222b..09c9cecdcee 100644 --- a/src/librustc_apfloat/lib.rs +++ b/src/librustc_apfloat/lib.rs @@ -96,7 +96,7 @@ impl Status { } impl StatusAnd { - fn map U, U>(self, f: F) -> StatusAnd { + pub fn map U, U>(self, f: F) -> StatusAnd { StatusAnd { status: self.status, value: f(self.value), diff --git a/src/librustc_llvm/ffi.rs b/src/librustc_llvm/ffi.rs index 6f640e580c9..ac0e4dde0c1 100644 --- a/src/librustc_llvm/ffi.rs +++ b/src/librustc_llvm/ffi.rs @@ -628,8 +628,6 @@ extern "C" { pub fn LLVMConstIntGetSExtValue(ConstantVal: ValueRef) -> c_longlong; pub fn LLVMRustConstInt128Get(ConstantVal: ValueRef, SExt: bool, high: *mut u64, low: *mut u64) -> bool; - pub fn LLVMRustIsConstantFP(ConstantVal: ValueRef) -> bool; - pub fn LLVMRustConstFloatGetBits(ConstantVal: ValueRef) -> u64; // Operations on composite constants diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 6b2342e4933..6573e507bd3 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -21,7 +21,7 @@ use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc::ty::layout::{self, LayoutTyper}; use rustc::ty::cast::{CastTy, IntTy}; use rustc::ty::subst::{Kind, Substs, Subst}; -use rustc_apfloat::{ieee, Float}; +use rustc_apfloat::{ieee, Float, Status}; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use {adt, base, machine}; use abi::{self, Abi}; @@ -690,16 +690,18 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { llvm::LLVMConstIntCast(llval, ll_t_out.to_ref(), s) } (CastTy::Int(_), CastTy::Float) => { - const_cast_int_to_float(self.ccx, llval, signed, ll_t_out) + cast_const_int_to_float(self.ccx, llval, signed, ll_t_out) } (CastTy::Float, CastTy::Float) => { llvm::LLVMConstFPCast(llval, ll_t_out.to_ref()) } (CastTy::Float, CastTy::Int(IntTy::I)) => { - const_cast_from_float(&operand, true, ll_t_out) + cast_const_float_to_int(self.ccx, &operand, + true, ll_t_out, span) } (CastTy::Float, CastTy::Int(_)) => { - const_cast_from_float(&operand, false, ll_t_out) + cast_const_float_to_int(self.ccx, &operand, + false, ll_t_out, span) } (CastTy::Ptr(_), CastTy::Ptr(_)) | (CastTy::FnPtr, CastTy::Ptr(_)) | @@ -952,36 +954,49 @@ pub fn const_scalar_checked_binop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } -unsafe fn const_cast_from_float(operand: &Const, signed: bool, int_ty: Type) -> ValueRef { +unsafe fn cast_const_float_to_int(ccx: &CrateContext, + operand: &Const, + signed: bool, + int_ty: Type, + span: Span) -> ValueRef { let llval = operand.llval; - // Note: this breaks if addresses can be turned into integers (is that possible?) - // But at least an ICE is better than producing undef. - assert!(llvm::LLVMRustIsConstantFP(llval), - "const_cast_from_float: invalid llval {:?}", Value(llval)); - let bits = llvm::LLVMRustConstFloatGetBits(llval) as u128; - let int_width = int_ty.int_width() as usize; let float_bits = match operand.ty.sty { ty::TyFloat(fty) => fty.bit_width(), - _ => bug!("const_cast_from_float: operand not a float"), + _ => bug!("cast_const_float_to_int: operand not a float"), }; - // Ignore the Status, to_i128 does the Right Thing(tm) on overflow and NaN even though it - // sets INVALID_OP. + // Note: this breaks if llval is a complex constant expression rather than a simple constant. + // One way that might happen would be if addresses could be turned into integers in constant + // expressions, but that doesn't appear to be possible? + // In any case, an ICE is better than producing undef. + let llval_bits = consts::bitcast(llval, Type::ix(ccx, float_bits as u64)); + let bits = const_to_opt_u128(llval_bits, false).unwrap_or_else(|| { + panic!("could not get bits of constant float {:?}", + Value(llval)); + }); + let int_width = int_ty.int_width() as usize; + // Try to convert, but report an error for overflow and NaN. This matches HIR const eval. let cast_result = match float_bits { - 32 if signed => ieee::Single::from_bits(bits).to_i128(int_width).value as u128, - 64 if signed => ieee::Double::from_bits(bits).to_i128(int_width).value as u128, - 32 => ieee::Single::from_bits(bits).to_u128(int_width).value, - 64 => ieee::Double::from_bits(bits).to_u128(int_width).value, + 32 if signed => ieee::Single::from_bits(bits).to_i128(int_width).map(|v| v as u128), + 64 if signed => ieee::Double::from_bits(bits).to_i128(int_width).map(|v| v as u128), + 32 => ieee::Single::from_bits(bits).to_u128(int_width), + 64 => ieee::Double::from_bits(bits).to_u128(int_width), n => bug!("unsupported float width {}", n), }; - C_big_integral(int_ty, cast_result) + if cast_result.status.contains(Status::INVALID_OP) { + let err = ConstEvalErr { span: span, kind: ErrKind::CannotCast }; + err.report(ccx.tcx(), span, "expression"); + } + C_big_integral(int_ty, cast_result.value) } -unsafe fn const_cast_int_to_float(ccx: &CrateContext, - llval: ValueRef, - signed: bool, - float_ty: Type) -> ValueRef { - // Note: this breaks if addresses can be turned into integers (is that possible?) - // But at least an ICE is better than producing undef. +unsafe fn cast_const_int_to_float(ccx: &CrateContext, + llval: ValueRef, + signed: bool, + float_ty: Type) -> ValueRef { + // Note: this breaks if llval is a complex constant expression rather than a simple constant. + // One way that might happen would be if addresses could be turned into integers in constant + // expressions, but that doesn't appear to be possible? + // In any case, an ICE is better than producing undef. let value = const_to_opt_u128(llval, signed).unwrap_or_else(|| { panic!("could not get z128 value of constant integer {:?}", Value(llval)); diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index db6802320b3..20ea8d70302 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -1373,19 +1373,6 @@ extern "C" bool LLVMRustConstInt128Get(LLVMValueRef CV, bool sext, uint64_t *hig return true; } -extern "C" uint64_t LLVMRustConstFloatGetBits(LLVMValueRef CV) { - auto C = unwrap(CV); - APInt Bits = C->getValueAPF().bitcastToAPInt(); - if (!Bits.isIntN(64)) { - report_fatal_error("Float bit pattern >64 bits"); - } - return Bits.getLimitedValue(); -} - -extern "C" bool LLVMRustIsConstantFP(LLVMValueRef CV) { - return isa(unwrap(CV)); -} - extern "C" LLVMContextRef LLVMRustGetValueContext(LLVMValueRef V) { return wrap(&unwrap(V)->getContext()); } diff --git a/src/test/compile-fail/float-int-invalid-const-cast.rs b/src/test/compile-fail/float-int-invalid-const-cast.rs new file mode 100644 index 00000000000..2efefd92691 --- /dev/null +++ b/src/test/compile-fail/float-int-invalid-const-cast.rs @@ -0,0 +1,61 @@ +// Copyright 2017 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(i128_type)] +#![allow(const_err)] // this test is only about hard errors + +use std::{f32, f64}; + +// Forces evaluation of constants, triggering hard error +fn force(_: T) {} + +fn main() { + { const X: u16 = -1. as u16; force(X); } //~ ERROR constant evaluation error + { const X: u128 = -100. as u128; force(X); } //~ ERROR constant evaluation error + + { const X: i8 = f32::NAN as i8; force(X); } //~ ERROR constant evaluation error + { const X: i32 = f32::NAN as i32; force(X); } //~ ERROR constant evaluation error + { const X: u64 = f32::NAN as u64; force(X); } //~ ERROR constant evaluation error + { const X: u128 = f32::NAN as u128; force(X); } //~ ERROR constant evaluation error + + { const X: i8 = f32::INFINITY as i8; force(X); } //~ ERROR constant evaluation error + { const X: u32 = f32::INFINITY as u32; force(X); } //~ ERROR constant evaluation error + { const X: i128 = f32::INFINITY as i128; force(X); } //~ ERROR constant evaluation error + { const X: u128 = f32::INFINITY as u128; force(X); } //~ ERROR constant evaluation error + + { const X: u8 = f32::NEG_INFINITY as u8; force(X); } //~ ERROR constant evaluation error + { const X: u16 = f32::NEG_INFINITY as u16; force(X); } //~ ERROR constant evaluation error + { const X: i64 = f32::NEG_INFINITY as i64; force(X); } //~ ERROR constant evaluation error + { const X: i128 = f32::NEG_INFINITY as i128; force(X); } //~ ERROR constant evaluation error + + { const X: i8 = f64::NAN as i8; force(X); } //~ ERROR constant evaluation error + { const X: i32 = f64::NAN as i32; force(X); } //~ ERROR constant evaluation error + { const X: u64 = f64::NAN as u64; force(X); } //~ ERROR constant evaluation error + { const X: u128 = f64::NAN as u128; force(X); } //~ ERROR constant evaluation error + + { const X: i8 = f64::INFINITY as i8; force(X); } //~ ERROR constant evaluation error + { const X: u32 = f64::INFINITY as u32; force(X); } //~ ERROR constant evaluation error + { const X: i128 = f64::INFINITY as i128; force(X); } //~ ERROR constant evaluation error + { const X: u128 = f64::INFINITY as u128; force(X); } //~ ERROR constant evaluation error + + { const X: u8 = f64::NEG_INFINITY as u8; force(X); } //~ ERROR constant evaluation error + { const X: u16 = f64::NEG_INFINITY as u16; force(X); } //~ ERROR constant evaluation error + { const X: i64 = f64::NEG_INFINITY as i64; force(X); } //~ ERROR constant evaluation error + { const X: i128 = f64::NEG_INFINITY as i128; force(X); } //~ ERROR constant evaluation error + + { const X: u8 = 256. as u8; force(X); } //~ ERROR constant evaluation error + { const X: i8 = -129. as i8; force(X); } //~ ERROR constant evaluation error + { const X: i8 = 128. as i8; force(X); } //~ ERROR constant evaluation error + { const X: i32 = 2147483648. as i32; force(X); } //~ ERROR constant evaluation error + { const X: i32 = -2147483904. as i32; force(X); } //~ ERROR constant evaluation error + { const X: u32 = 4294967296. as u32; force(X); } //~ ERROR constant evaluation error + { const X: u128 = 1e40 as u128; force(X); } //~ ERROR constant evaluation error + { const X: i128 = 1e40 as i128; force(X); } //~ ERROR constant evaluation error +} \ No newline at end of file diff --git a/src/test/run-pass/saturating-float-casts.rs b/src/test/run-pass/saturating-float-casts.rs index 53e0cea64e9..1a30013c05d 100644 --- a/src/test/run-pass/saturating-float-casts.rs +++ b/src/test/run-pass/saturating-float-casts.rs @@ -1,4 +1,4 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -22,15 +22,28 @@ macro_rules! test { ($val:expr, $src_ty:ident -> $dest_ty:ident, $expected:expr) => ( // black_box disables constant evaluation to test run-time conversions: assert_eq!(black_box::<$src_ty>($val) as $dest_ty, $expected, - "run time {} -> {}", stringify!($src_ty), stringify!($dest_ty)); - // ... whereas this variant triggers constant evaluation: + "run-time {} -> {}", stringify!($src_ty), stringify!($dest_ty)); + ); + + ($fval:expr, f* -> $ity:ident, $ival:expr) => ( + test!($fval, f32 -> $ity, $ival); + test!($fval, f64 -> $ity, $ival); + ) +} + +// This macro tests const eval in addition to run-time evaluation. +// If and when saturating casts are adopted, this macro should be merged with test!() to ensure +// that run-time and const eval agree on inputs that currently trigger a const eval error. +macro_rules! test_c { + ($val:expr, $src_ty:ident -> $dest_ty:ident, $expected:expr) => ({ + test!($val, $src_ty -> $dest_ty, $expected); { const X: $src_ty = $val; const Y: $dest_ty = X as $dest_ty; assert_eq!(Y, $expected, "const eval {} -> {}", stringify!($src_ty), stringify!($dest_ty)); } - ); + }); ($fval:expr, f* -> $ity:ident, $ival:expr) => ( test!($fval, f32 -> $ity, $ival); @@ -48,11 +61,11 @@ macro_rules! common_fptoi_tests { // as well, the test is just slightly misplaced. test!($ity::MIN as $fty, $fty -> $ity, $ity::MIN); test!($ity::MAX as $fty, $fty -> $ity, $ity::MAX); - test!(0., $fty -> $ity, 0); - test!($fty::MIN_POSITIVE, $fty -> $ity, 0); + test_c!(0., $fty -> $ity, 0); + test_c!($fty::MIN_POSITIVE, $fty -> $ity, 0); test!(-0.9, $fty -> $ity, 0); - test!(1., $fty -> $ity, 1); - test!(42., $fty -> $ity, 42); + test_c!(1., $fty -> $ity, 1); + test_c!(42., $fty -> $ity, 42); )+ }); (f* -> $($ity:ident)+) => ({ @@ -84,58 +97,58 @@ pub fn main() { // The following tests cover edge cases for some integer types. - // u8 - test!(254., f* -> u8, 254); + // # u8 + test_c!(254., f* -> u8, 254); test!(256., f* -> u8, 255); - // i8 - test!(-127., f* -> i8, -127); + // # i8 + test_c!(-127., f* -> i8, -127); test!(-129., f* -> i8, -128); - test!(126., f* -> i8, 126); + test_c!(126., f* -> i8, 126); test!(128., f* -> i8, 127); - // i32 + // # i32 // -2147483648. is i32::MIN (exactly) - test!(-2147483648., f* -> i32, i32::MIN); + test_c!(-2147483648., f* -> i32, i32::MIN); // 2147483648. is i32::MAX rounded up test!(2147483648., f32 -> i32, 2147483647); // With 24 significand bits, floats with magnitude in [2^30 + 1, 2^31] are rounded to // multiples of 2^7. Therefore, nextDown(round(i32::MAX)) is 2^31 - 128: - test!(2147483520., f32 -> i32, 2147483520); + test_c!(2147483520., f32 -> i32, 2147483520); // Similarly, nextUp(i32::MIN) is i32::MIN + 2^8 and nextDown(i32::MIN) is i32::MIN - 2^7 test!(-2147483904., f* -> i32, i32::MIN); - test!(-2147483520., f* -> i32, -2147483520); + test_c!(-2147483520., f* -> i32, -2147483520); - // u32 -- round(MAX) and nextUp(round(MAX)) - test!(4294967040., f* -> u32, 4294967040); + // # u32 + // round(MAX) and nextUp(round(MAX)) + test_c!(4294967040., f* -> u32, 4294967040); test!(4294967296., f* -> u32, 4294967295); - // u128 - // # float->int - test!(f32::MAX, f32 -> u128, 0xffffff00000000000000000000000000); + // # u128 + // float->int: + test_c!(f32::MAX, f32 -> u128, 0xffffff00000000000000000000000000); // nextDown(f32::MAX) = 2^128 - 2 * 2^104 const SECOND_LARGEST_F32: f32 = 340282326356119256160033759537265639424.; - test!(SECOND_LARGEST_F32, f32 -> u128, 0xfffffe00000000000000000000000000); - // # int->float + test_c!(SECOND_LARGEST_F32, f32 -> u128, 0xfffffe00000000000000000000000000); + + // int->float: // f32::MAX - 0.5 ULP and smaller should be rounded down - test!(0xfffffe00000000000000000000000000, u128 -> f32, SECOND_LARGEST_F32); - test!(0xfffffe7fffffffffffffffffffffffff, u128 -> f32, SECOND_LARGEST_F32); - test!(0xfffffe80000000000000000000000000, u128 -> f32, SECOND_LARGEST_F32); + test_c!(0xfffffe00000000000000000000000000, u128 -> f32, SECOND_LARGEST_F32); + test_c!(0xfffffe7fffffffffffffffffffffffff, u128 -> f32, SECOND_LARGEST_F32); + test_c!(0xfffffe80000000000000000000000000, u128 -> f32, SECOND_LARGEST_F32); // numbers within < 0.5 ULP of f32::MAX it should be rounded to f32::MAX - test!(0xfffffe80000000000000000000000001, u128 -> f32, f32::MAX); - test!(0xfffffeffffffffffffffffffffffffff, u128 -> f32, f32::MAX); - test!(0xffffff00000000000000000000000000, u128 -> f32, f32::MAX); - test!(0xffffff00000000000000000000000001, u128 -> f32, f32::MAX); - test!(0xffffff7fffffffffffffffffffffffff, u128 -> f32, f32::MAX); + test_c!(0xfffffe80000000000000000000000001, u128 -> f32, f32::MAX); + test_c!(0xfffffeffffffffffffffffffffffffff, u128 -> f32, f32::MAX); + test_c!(0xffffff00000000000000000000000000, u128 -> f32, f32::MAX); + test_c!(0xffffff00000000000000000000000001, u128 -> f32, f32::MAX); + test_c!(0xffffff7fffffffffffffffffffffffff, u128 -> f32, f32::MAX); // f32::MAX + 0.5 ULP and greater should be rounded to infinity - test!(0xffffff80000000000000000000000000, u128 -> f32, f32::INFINITY); - test!(0xffffff80000000f00000000000000000, u128 -> f32, f32::INFINITY); - test!(0xffffff87ffffffffffffffff00000001, u128 -> f32, f32::INFINITY); - - test!(!0, u128 -> f32, f32::INFINITY); + test_c!(0xffffff80000000000000000000000000, u128 -> f32, f32::INFINITY); + test_c!(0xffffff80000000f00000000000000000, u128 -> f32, f32::INFINITY); + test_c!(0xffffff87ffffffffffffffff00000001, u128 -> f32, f32::INFINITY); // u128->f64 should not be affected by the u128->f32 checks - test!(0xffffff80000000000000000000000000, u128 -> f64, + test_c!(0xffffff80000000000000000000000000, u128 -> f64, 340282356779733661637539395458142568448.0); - test!(u128::MAX, u128 -> f64, 340282366920938463463374607431768211455.0); + test_c!(u128::MAX, u128 -> f64, 340282366920938463463374607431768211455.0); } -- cgit 1.4.1-3-g733a5 From ef0b99930e52ee90f9452542dd14f148bbbe13af Mon Sep 17 00:00:00 2001 From: Robin Kruppe Date: Wed, 8 Nov 2017 14:54:03 +0100 Subject: Disable u128 <-> float tests on emscripten --- src/test/run-pass/saturating-float-casts.rs | 68 +++++++++++++++++------------ 1 file changed, 39 insertions(+), 29 deletions(-) (limited to 'src/test') diff --git a/src/test/run-pass/saturating-float-casts.rs b/src/test/run-pass/saturating-float-casts.rs index 1a30013c05d..6db4d7635f0 100644 --- a/src/test/run-pass/saturating-float-casts.rs +++ b/src/test/run-pass/saturating-float-casts.rs @@ -15,7 +15,9 @@ extern crate test; use std::{f32, f64}; -use std::{u8, i8, u16, i16, u32, i32, u64, i64, u128, i128}; +use std::{u8, i8, u16, i16, u32, i32, u64, i64}; +#[cfg(not(target_os="emscripten"))] +use std::{u128, i128}; use test::black_box; macro_rules! test { @@ -92,8 +94,13 @@ macro_rules! fptoui_tests { } pub fn main() { - common_fptoi_tests!(f* -> i8 i16 i32 i64 i128 u8 u16 u32 u64 u128); - fptoui_tests!(f* -> u8 u16 u32 u64 u128); + common_fptoi_tests!(f* -> i8 i16 i32 i64 u8 u16 u32 u64); + fptoui_tests!(f* -> u8 u16 u32 u64); + // FIXME emscripten does not support i128 + #[cfg(not(target_os="emscripten"))] { + common_fptoi_tests!(f* -> i128 u128); + fptoui_tests!(f* -> u128); + } // The following tests cover edge cases for some integer types. @@ -125,30 +132,33 @@ pub fn main() { test!(4294967296., f* -> u32, 4294967295); // # u128 - // float->int: - test_c!(f32::MAX, f32 -> u128, 0xffffff00000000000000000000000000); - // nextDown(f32::MAX) = 2^128 - 2 * 2^104 - const SECOND_LARGEST_F32: f32 = 340282326356119256160033759537265639424.; - test_c!(SECOND_LARGEST_F32, f32 -> u128, 0xfffffe00000000000000000000000000); - - // int->float: - // f32::MAX - 0.5 ULP and smaller should be rounded down - test_c!(0xfffffe00000000000000000000000000, u128 -> f32, SECOND_LARGEST_F32); - test_c!(0xfffffe7fffffffffffffffffffffffff, u128 -> f32, SECOND_LARGEST_F32); - test_c!(0xfffffe80000000000000000000000000, u128 -> f32, SECOND_LARGEST_F32); - // numbers within < 0.5 ULP of f32::MAX it should be rounded to f32::MAX - test_c!(0xfffffe80000000000000000000000001, u128 -> f32, f32::MAX); - test_c!(0xfffffeffffffffffffffffffffffffff, u128 -> f32, f32::MAX); - test_c!(0xffffff00000000000000000000000000, u128 -> f32, f32::MAX); - test_c!(0xffffff00000000000000000000000001, u128 -> f32, f32::MAX); - test_c!(0xffffff7fffffffffffffffffffffffff, u128 -> f32, f32::MAX); - // f32::MAX + 0.5 ULP and greater should be rounded to infinity - test_c!(0xffffff80000000000000000000000000, u128 -> f32, f32::INFINITY); - test_c!(0xffffff80000000f00000000000000000, u128 -> f32, f32::INFINITY); - test_c!(0xffffff87ffffffffffffffff00000001, u128 -> f32, f32::INFINITY); - - // u128->f64 should not be affected by the u128->f32 checks - test_c!(0xffffff80000000000000000000000000, u128 -> f64, - 340282356779733661637539395458142568448.0); - test_c!(u128::MAX, u128 -> f64, 340282366920938463463374607431768211455.0); + #[cfg(not(target_os="emscripten"))] + { + // float->int: + test_c!(f32::MAX, f32 -> u128, 0xffffff00000000000000000000000000); + // nextDown(f32::MAX) = 2^128 - 2 * 2^104 + const SECOND_LARGEST_F32: f32 = 340282326356119256160033759537265639424.; + test_c!(SECOND_LARGEST_F32, f32 -> u128, 0xfffffe00000000000000000000000000); + + // int->float: + // f32::MAX - 0.5 ULP and smaller should be rounded down + test_c!(0xfffffe00000000000000000000000000, u128 -> f32, SECOND_LARGEST_F32); + test_c!(0xfffffe7fffffffffffffffffffffffff, u128 -> f32, SECOND_LARGEST_F32); + test_c!(0xfffffe80000000000000000000000000, u128 -> f32, SECOND_LARGEST_F32); + // numbers within < 0.5 ULP of f32::MAX it should be rounded to f32::MAX + test_c!(0xfffffe80000000000000000000000001, u128 -> f32, f32::MAX); + test_c!(0xfffffeffffffffffffffffffffffffff, u128 -> f32, f32::MAX); + test_c!(0xffffff00000000000000000000000000, u128 -> f32, f32::MAX); + test_c!(0xffffff00000000000000000000000001, u128 -> f32, f32::MAX); + test_c!(0xffffff7fffffffffffffffffffffffff, u128 -> f32, f32::MAX); + // f32::MAX + 0.5 ULP and greater should be rounded to infinity + test_c!(0xffffff80000000000000000000000000, u128 -> f32, f32::INFINITY); + test_c!(0xffffff80000000f00000000000000000, u128 -> f32, f32::INFINITY); + test_c!(0xffffff87ffffffffffffffff00000001, u128 -> f32, f32::INFINITY); + + // u128->f64 should not be affected by the u128->f32 checks + test_c!(0xffffff80000000000000000000000000, u128 -> f64, + 340282356779733661637539395458142568448.0); + test_c!(u128::MAX, u128 -> f64, 340282366920938463463374607431768211455.0); + } } -- cgit 1.4.1-3-g733a5