about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2023-09-30 08:15:34 +0200
committerRalf Jung <post@ralfj.de>2024-01-26 17:23:36 +0100
commitcda358857214bb5a2b3df4f011102ffee3843a72 (patch)
tree94228c3e908821d7d4a9fe1fea70ba2d75c7c2de
parent69db514ed9238bb11f5d2c576fe26020e3b99a52 (diff)
downloadrust-cda358857214bb5a2b3df4f011102ffee3843a72.tar.gz
rust-cda358857214bb5a2b3df4f011102ffee3843a72.zip
make matching on NaN a hard error
-rw-r--r--compiler/rustc_middle/src/mir/interpret/value.rs4
-rw-r--r--compiler/rustc_middle/src/ty/consts/int.rs71
-rw-r--r--compiler/rustc_mir_build/messages.ftl4
-rw-r--r--compiler/rustc_mir_build/src/errors.rs9
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs35
-rw-r--r--tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.rs32
-rw-r--r--tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr82
-rw-r--r--tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.rs21
-rw-r--r--tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.stderr25
9 files changed, 197 insertions, 86 deletions
diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs
index 5ecff04f3ae..e937c17c8ac 100644
--- a/compiler/rustc_middle/src/mir/interpret/value.rs
+++ b/compiler/rustc_middle/src/mir/interpret/value.rs
@@ -418,8 +418,8 @@ impl<'tcx, Prov: Provenance> Scalar<Prov> {
 
     #[inline]
     pub fn to_float<F: Float>(self) -> InterpResult<'tcx, F> {
-        // Going through `to_uint` to check size and truncation.
-        Ok(F::from_bits(self.to_uint(Size::from_bits(F::BITS))?))
+        // Going through `to_bits` to check size and truncation.
+        Ok(F::from_bits(self.to_bits(Size::from_bits(F::BITS))?))
     }
 
     #[inline]
diff --git a/compiler/rustc_middle/src/ty/consts/int.rs b/compiler/rustc_middle/src/ty/consts/int.rs
index 310cf113b11..d810e358f85 100644
--- a/compiler/rustc_middle/src/ty/consts/int.rs
+++ b/compiler/rustc_middle/src/ty/consts/int.rs
@@ -249,11 +249,6 @@ impl ScalarInt {
         }
     }
 
-    #[inline]
-    pub fn try_to_target_usize(&self, tcx: TyCtxt<'_>) -> Result<u64, Size> {
-        Ok(self.to_bits(tcx.data_layout.pointer_size)? as u64)
-    }
-
     /// Tries to convert the `ScalarInt` to an unsigned integer of the given size.
     /// Fails if the size of the `ScalarInt` is not equal to `size` and returns the
     /// `ScalarInt`s size in that case.
@@ -262,24 +257,12 @@ impl ScalarInt {
         self.to_bits(size)
     }
 
-    // Tries to convert the `ScalarInt` to `bool`. Fails if the `size` of the `ScalarInt`
-    // in not equal to `Size { raw: 1 }` or if the value is not 0 or 1 and returns the `size`
-    // value of the `ScalarInt` in that case.
-    #[inline]
-    pub fn try_to_bool(self) -> Result<bool, Size> {
-        match self.try_to_u8()? {
-            0 => Ok(false),
-            1 => Ok(true),
-            _ => Err(self.size()),
-        }
-    }
-
     // Tries to convert the `ScalarInt` to `u8`. Fails if the `size` of the `ScalarInt`
     // in not equal to `Size { raw: 1 }` and returns the `size` value of the `ScalarInt` in
     // that case.
     #[inline]
     pub fn try_to_u8(self) -> Result<u8, Size> {
-        self.to_bits(Size::from_bits(8)).map(|v| u8::try_from(v).unwrap())
+        self.try_to_uint(Size::from_bits(8)).map(|v| u8::try_from(v).unwrap())
     }
 
     /// Tries to convert the `ScalarInt` to `u16`. Fails if the size of the `ScalarInt`
@@ -287,7 +270,7 @@ impl ScalarInt {
     /// that case.
     #[inline]
     pub fn try_to_u16(self) -> Result<u16, Size> {
-        self.to_bits(Size::from_bits(16)).map(|v| u16::try_from(v).unwrap())
+        self.try_to_uint(Size::from_bits(16)).map(|v| u16::try_from(v).unwrap())
     }
 
     /// Tries to convert the `ScalarInt` to `u32`. Fails if the `size` of the `ScalarInt`
@@ -295,7 +278,7 @@ impl ScalarInt {
     /// that case.
     #[inline]
     pub fn try_to_u32(self) -> Result<u32, Size> {
-        self.to_bits(Size::from_bits(32)).map(|v| u32::try_from(v).unwrap())
+        self.try_to_uint(Size::from_bits(32)).map(|v| u32::try_from(v).unwrap())
     }
 
     /// Tries to convert the `ScalarInt` to `u64`. Fails if the `size` of the `ScalarInt`
@@ -303,7 +286,7 @@ impl ScalarInt {
     /// that case.
     #[inline]
     pub fn try_to_u64(self) -> Result<u64, Size> {
-        self.to_bits(Size::from_bits(64)).map(|v| u64::try_from(v).unwrap())
+        self.try_to_uint(Size::from_bits(64)).map(|v| u64::try_from(v).unwrap())
     }
 
     /// Tries to convert the `ScalarInt` to `u128`. Fails if the `size` of the `ScalarInt`
@@ -311,7 +294,24 @@ impl ScalarInt {
     /// that case.
     #[inline]
     pub fn try_to_u128(self) -> Result<u128, Size> {
-        self.to_bits(Size::from_bits(128))
+        self.try_to_uint(Size::from_bits(128))
+    }
+
+    #[inline]
+    pub fn try_to_target_usize(&self, tcx: TyCtxt<'_>) -> Result<u64, Size> {
+        self.try_to_uint(tcx.data_layout.pointer_size).map(|v| u64::try_from(v).unwrap())
+    }
+
+    // Tries to convert the `ScalarInt` to `bool`. Fails if the `size` of the `ScalarInt`
+    // in not equal to `Size { raw: 1 }` or if the value is not 0 or 1 and returns the `size`
+    // value of the `ScalarInt` in that case.
+    #[inline]
+    pub fn try_to_bool(self) -> Result<bool, Size> {
+        match self.try_to_u8()? {
+            0 => Ok(false),
+            1 => Ok(true),
+            _ => Err(self.size()),
+        }
     }
 
     /// Tries to convert the `ScalarInt` to a signed integer of the given size.
@@ -357,6 +357,27 @@ impl ScalarInt {
     pub fn try_to_i128(self) -> Result<i128, Size> {
         self.try_to_int(Size::from_bits(128))
     }
+
+    #[inline]
+    pub fn try_to_target_isize(&self, tcx: TyCtxt<'_>) -> Result<i64, Size> {
+        self.try_to_int(tcx.data_layout.pointer_size).map(|v| i64::try_from(v).unwrap())
+    }
+
+    #[inline]
+    pub fn try_to_float<F: Float>(self) -> Result<F, Size> {
+        // Going through `to_uint` to check size and truncation.
+        Ok(F::from_bits(self.to_bits(Size::from_bits(F::BITS))?))
+    }
+
+    #[inline]
+    pub fn try_to_f32(self) -> Result<Single, Size> {
+        self.try_to_float()
+    }
+
+    #[inline]
+    pub fn try_to_f64(self) -> Result<Double, Size> {
+        self.try_to_float()
+    }
 }
 
 macro_rules! from {
@@ -399,11 +420,7 @@ impl TryFrom<ScalarInt> for bool {
     type Error = Size;
     #[inline]
     fn try_from(int: ScalarInt) -> Result<Self, Size> {
-        int.to_bits(Size::from_bytes(1)).and_then(|u| match u {
-            0 => Ok(false),
-            1 => Ok(true),
-            _ => Err(Size::from_bytes(1)),
-        })
+        int.try_to_bool()
     }
 }
 
diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl
index 9631b72f20c..14d09535d7d 100644
--- a/compiler/rustc_mir_build/messages.ftl
+++ b/compiler/rustc_mir_build/messages.ftl
@@ -232,6 +232,10 @@ mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsa
     .note = mutating layout constrained fields cannot statically be checked for valid values
     .label = mutation of layout constrained field
 
+mir_build_nan_pattern = cannot use NaN in patterns
+    .note = NaNs compare inequal to everything, even themselves, so this pattern would never match
+    .help = try using the `is_nan` method instead
+
 mir_build_non_const_path = runtime values cannot be referenced in patterns
 
 mir_build_non_empty_never_pattern =
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index e1b998b2471..eef05051985 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -780,6 +780,15 @@ pub struct UnsizedPattern<'tcx> {
     pub non_sm_ty: Ty<'tcx>,
 }
 
+#[derive(Diagnostic)]
+#[diag(mir_build_nan_pattern)]
+#[note]
+#[help]
+pub struct NaNPattern {
+    #[primary_span]
+    pub span: Span,
+}
+
 #[derive(LintDiagnostic)]
 #[diag(mir_build_float_pattern)]
 pub struct FloatPattern;
diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
index 22305f03a76..cb67bc6b0b3 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
@@ -1,3 +1,4 @@
+use rustc_apfloat::Float;
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_index::Idx;
@@ -16,7 +17,7 @@ use std::cell::Cell;
 
 use super::PatCtxt;
 use crate::errors::{
-    FloatPattern, IndirectStructuralMatch, InvalidPattern, NonPartialEqMatch,
+    FloatPattern, IndirectStructuralMatch, InvalidPattern, NaNPattern, NonPartialEqMatch,
     NontrivialStructuralMatch, PointerPattern, TypeNotStructural, UnionPattern, UnsizedPattern,
 };
 
@@ -317,16 +318,6 @@ impl<'tcx> ConstToPat<'tcx> {
         let param_env = self.param_env;
 
         let kind = match ty.kind() {
-            ty::Float(_) => {
-                self.saw_const_match_lint.set(true);
-                tcx.emit_node_span_lint(
-                    lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
-                    id,
-                    span,
-                    FloatPattern,
-                );
-                return Err(FallbackToOpaqueConst);
-            }
             // If the type is not structurally comparable, just emit the constant directly,
             // causing the pattern match code to treat it opaquely.
             // FIXME: This code doesn't emit errors itself, the caller emits the errors.
@@ -486,6 +477,28 @@ impl<'tcx> ConstToPat<'tcx> {
                     }
                 }
             },
+            ty::Float(flt) => {
+                let v = cv.unwrap_leaf();
+                let is_nan = match flt {
+                    ty::FloatTy::F32 => v.try_to_f32().unwrap().is_nan(),
+                    ty::FloatTy::F64 => v.try_to_f64().unwrap().is_nan(),
+                };
+                if is_nan {
+                    // NaNs are not ever equal to anything so they make no sense as patterns.
+                    // Also see <https://github.com/rust-lang/rfcs/pull/3535>.
+                    let e = tcx.dcx().emit_err(NaNPattern { span });
+                    self.saw_const_match_error.set(Some(e));
+                } else {
+                    self.saw_const_match_lint.set(true);
+                    tcx.emit_node_span_lint(
+                        lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
+                        id,
+                        span,
+                        FloatPattern,
+                    );
+                }
+                return Err(FallbackToOpaqueConst);
+            }
             ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::RawPtr(..) => {
                 // The raw pointers we see here have been "vetted" by valtree construction to be
                 // just integers, so we simply allow them.
diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.rs
new file mode 100644
index 00000000000..c5bca8c970d
--- /dev/null
+++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.rs
@@ -0,0 +1,32 @@
+// Matching against NaN should result in an error
+#![feature(exclusive_range_pattern)]
+#![allow(unused)]
+#![allow(illegal_floating_point_literal_pattern)]
+
+const NAN: f64 = f64::NAN;
+
+fn main() {
+    let x = NAN;
+    match x {
+        NAN => {}, //~ ERROR cannot use NaN in patterns
+        _ => {},
+    };
+
+    match [x, 1.0] {
+        [NAN, _] => {}, //~ ERROR cannot use NaN in patterns
+        _ => {},
+    };
+
+    // Also cover range patterns
+    match x {
+        NAN..=1.0 => {}, //~ ERROR cannot use NaN in patterns
+        //~^ ERROR lower range bound must be less than or equal to upper
+        -1.0..=NAN => {}, //~ ERROR cannot use NaN in patterns
+        //~^ ERROR lower range bound must be less than or equal to upper
+        NAN.. => {}, //~ ERROR cannot use NaN in patterns
+        //~^ ERROR lower range bound must be less than or equal to upper
+        ..NAN => {}, //~ ERROR cannot use NaN in patterns
+        //~^ ERROR lower range bound must be less than upper
+        _ => {},
+    };
+}
diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr
new file mode 100644
index 00000000000..deda07150c5
--- /dev/null
+++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr
@@ -0,0 +1,82 @@
+error: cannot use NaN in patterns
+  --> $DIR/issue-6804-nan-match.rs:11:9
+   |
+LL |         NAN => {},
+   |         ^^^
+   |
+   = note: NaNs compare inequal to everything, even themselves, so this pattern would never match
+   = help: try using the `is_nan` method instead
+
+error: cannot use NaN in patterns
+  --> $DIR/issue-6804-nan-match.rs:16:10
+   |
+LL |         [NAN, _] => {},
+   |          ^^^
+   |
+   = note: NaNs compare inequal to everything, even themselves, so this pattern would never match
+   = help: try using the `is_nan` method instead
+
+error: cannot use NaN in patterns
+  --> $DIR/issue-6804-nan-match.rs:22:9
+   |
+LL |         NAN..=1.0 => {},
+   |         ^^^
+   |
+   = note: NaNs compare inequal to everything, even themselves, so this pattern would never match
+   = help: try using the `is_nan` method instead
+
+error[E0030]: lower range bound must be less than or equal to upper
+  --> $DIR/issue-6804-nan-match.rs:22:9
+   |
+LL |         NAN..=1.0 => {},
+   |         ^^^^^^^^^ lower bound larger than upper bound
+
+error: cannot use NaN in patterns
+  --> $DIR/issue-6804-nan-match.rs:24:16
+   |
+LL |         -1.0..=NAN => {},
+   |                ^^^
+   |
+   = note: NaNs compare inequal to everything, even themselves, so this pattern would never match
+   = help: try using the `is_nan` method instead
+
+error[E0030]: lower range bound must be less than or equal to upper
+  --> $DIR/issue-6804-nan-match.rs:24:9
+   |
+LL |         -1.0..=NAN => {},
+   |         ^^^^^^^^^^ lower bound larger than upper bound
+
+error: cannot use NaN in patterns
+  --> $DIR/issue-6804-nan-match.rs:26:9
+   |
+LL |         NAN.. => {},
+   |         ^^^
+   |
+   = note: NaNs compare inequal to everything, even themselves, so this pattern would never match
+   = help: try using the `is_nan` method instead
+
+error[E0030]: lower range bound must be less than or equal to upper
+  --> $DIR/issue-6804-nan-match.rs:26:9
+   |
+LL |         NAN.. => {},
+   |         ^^^^^ lower bound larger than upper bound
+
+error: cannot use NaN in patterns
+  --> $DIR/issue-6804-nan-match.rs:28:11
+   |
+LL |         ..NAN => {},
+   |           ^^^
+   |
+   = note: NaNs compare inequal to everything, even themselves, so this pattern would never match
+   = help: try using the `is_nan` method instead
+
+error[E0579]: lower range bound must be less than upper
+  --> $DIR/issue-6804-nan-match.rs:28:9
+   |
+LL |         ..NAN => {},
+   |         ^^^^^
+
+error: aborting due to 10 previous errors
+
+Some errors have detailed explanations: E0030, E0579.
+For more information about an error, try `rustc --explain E0030`.
diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.rs
deleted file mode 100644
index 0260caa82cb..00000000000
--- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-// Matching against NaN should result in a warning
-
-#![allow(unused)]
-#![deny(illegal_floating_point_literal_pattern)]
-
-const NAN: f64 = f64::NAN;
-
-fn main() {
-    let x = NAN;
-    match x {
-        NAN => {}, //~ ERROR floating-point types cannot be used
-        //~| WARN this was previously accepted by the compiler but is being phased out
-        _ => {},
-    };
-
-    match [x, 1.0] {
-        [NAN, _] => {}, //~ ERROR floating-point types cannot be used
-        //~| WARN this was previously accepted by the compiler but is being phased out
-        _ => {},
-    };
-}
diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.stderr
deleted file mode 100644
index f37255d0828..00000000000
--- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.stderr
+++ /dev/null
@@ -1,25 +0,0 @@
-error: floating-point types cannot be used in patterns
-  --> $DIR/issue-6804.rs:11:9
-   |
-LL |         NAN => {},
-   |         ^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
-note: the lint level is defined here
-  --> $DIR/issue-6804.rs:4:9
-   |
-LL | #![deny(illegal_floating_point_literal_pattern)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: floating-point types cannot be used in patterns
-  --> $DIR/issue-6804.rs:17:10
-   |
-LL |         [NAN, _] => {},
-   |          ^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #41620 <https://github.com/rust-lang/rust/issues/41620>
-
-error: aborting due to 2 previous errors
-