about summary refs log tree commit diff
diff options
context:
space:
mode:
authorest31 <MTest31@outlook.com>2020-09-30 17:46:18 +0200
committerest31 <MTest31@outlook.com>2020-09-30 17:48:26 +0200
commit7f5008c8293a4d1eb3e4557a36a6bfdef34de284 (patch)
tree728d353ca824250abd9606cec36a4f0183b2979c
parentc6e4db620a7d2f569f11dcab627430921ea8aacf (diff)
downloadrust-7f5008c8293a4d1eb3e4557a36a6bfdef34de284.tar.gz
rust-7f5008c8293a4d1eb3e4557a36a6bfdef34de284.zip
Backport LLVM apfloat commit to rustc_apfloat
Backports LLVM commit: https://github.com/llvm/llvm-project/commit/e34bd1e0b03d20a506ada156d87e1b3a96d82fa2

Fixes #69532
-rw-r--r--compiler/rustc_apfloat/src/ieee.rs12
-rw-r--r--compiler/rustc_apfloat/tests/ieee.rs9
-rw-r--r--src/test/ui/issues/issue-69532.rs24
3 files changed, 45 insertions, 0 deletions
diff --git a/compiler/rustc_apfloat/src/ieee.rs b/compiler/rustc_apfloat/src/ieee.rs
index e3d941cad7a..aafd6dfb89a 100644
--- a/compiler/rustc_apfloat/src/ieee.rs
+++ b/compiler/rustc_apfloat/src/ieee.rs
@@ -1511,6 +1511,18 @@ impl<S: Semantics, T: Semantics> FloatConvert<IeeeFloat<T>> for IeeeFloat<S> {
                 sig::set_bit(&mut r.sig, T::PRECISION - 1);
             }
 
+            // If we are truncating NaN, it is possible that we shifted out all of the
+            // set bits in a signalling NaN payload. But NaN must remain NaN, so some
+            // bit in the significand must be set (otherwise it is Inf).
+            // This can only happen with sNaN. Set the 1st bit after the quiet bit,
+            // so that we still have an sNaN.
+            if r.sig[0] == 0 {
+                assert!(shift < 0, "Should not lose NaN payload on extend");
+                assert!(T::PRECISION >= 3, "Unexpectedly narrow significand");
+                assert!(*loses_info, "Missing payload should have set lost info");
+                sig::set_bit(&mut r.sig, T::PRECISION - 3);
+            }
+
             // gcc forces the Quiet bit on, which means (float)(double)(float_sNan)
             // does not give you back the same bits. This is dubious, and we
             // don't currently do it. You're really supposed to get
diff --git a/compiler/rustc_apfloat/tests/ieee.rs b/compiler/rustc_apfloat/tests/ieee.rs
index 2d8bb7d1e8e..0f3c99fba9e 100644
--- a/compiler/rustc_apfloat/tests/ieee.rs
+++ b/compiler/rustc_apfloat/tests/ieee.rs
@@ -567,6 +567,15 @@ fn fma() {
 }
 
 #[test]
+fn issue_69532() {
+    let f = Double::from_bits(0x7FF0_0000_0000_0001u64 as u128);
+    let mut loses_info = false;
+    let r: Single = f.convert(&mut loses_info).value;
+    assert!(loses_info);
+    assert!(r.is_nan());
+}
+
+#[test]
 fn min_num() {
     let f1 = Double::from_f64(1.0);
     let f2 = Double::from_f64(2.0);
diff --git a/src/test/ui/issues/issue-69532.rs b/src/test/ui/issues/issue-69532.rs
new file mode 100644
index 00000000000..81007b15074
--- /dev/null
+++ b/src/test/ui/issues/issue-69532.rs
@@ -0,0 +1,24 @@
+// run-pass
+#![feature(const_fn_transmute)]
+
+const fn make_nans() -> (f64, f64, f32, f32) {
+    let nan1: f64 = unsafe { std::mem::transmute(0x7FF0_0001_0000_0001u64) };
+    let nan2: f64 = unsafe { std::mem::transmute(0x7FF0_0000_0000_0001u64) };
+
+    let nan1_32 = nan1 as f32;
+    let nan2_32 = nan2 as f32;
+
+    (nan1, nan2, nan1_32, nan2_32)
+}
+
+static NANS: (f64, f64, f32, f32) = make_nans();
+
+fn main() {
+    let (nan1, nan2, nan1_32, nan2_32) = NANS;
+
+    assert!(nan1.is_nan());
+    assert!(nan2.is_nan());
+
+    assert!(nan1_32.is_nan());
+    assert!(nan2_32.is_nan());
+}