From 92ddac279b8c9b733117830788b7278b75cda301 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 30 Sep 2023 08:15:34 +0200 Subject: [PATCH] make matching on NaN a hard error --- .../rustc_middle/src/mir/interpret/value.rs | 2 +- compiler/rustc_middle/src/ty/consts/int.rs | 71 ++++++++++++------- compiler/rustc_mir_build/messages.ftl | 2 + compiler/rustc_mir_build/src/errors.rs | 7 ++ .../src/thir/pattern/const_to_pat.rs | 33 ++++++--- .../issue-6804-nan-match.rs | 32 +++++++++ .../issue-6804-nan-match.stderr | 64 +++++++++++++++++ .../issue-6804.rs | 21 ------ .../issue-6804.stderr | 25 ------- 9 files changed, 172 insertions(+), 85 deletions(-) create mode 100644 tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.rs create mode 100644 tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr delete mode 100644 tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.rs delete mode 100644 tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.stderr diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 0d548f8863610..0dd2edb7441c3 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -413,7 +413,7 @@ impl<'tcx, Prov: Provenance> Scalar { #[inline] pub fn to_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))?)) + 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 9d99344d5bdfb..7addf7d7a5636 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { + 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 { + 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 { self.try_to_int(Size::from_bits(128)) } + + #[inline] + pub fn try_to_target_isize(&self, tcx: TyCtxt<'_>) -> Result { + self.try_to_int(tcx.data_layout.pointer_size).map(|v| i64::try_from(v).unwrap()) + } + + #[inline] + pub fn try_to_float(self) -> Result { + // 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 { + self.try_to_float() + } + + #[inline] + pub fn try_to_f64(self) -> Result { + self.try_to_float() + } } macro_rules! from { @@ -399,11 +420,7 @@ impl TryFrom for bool { type Error = Size; #[inline] fn try_from(int: ScalarInt) -> Result { - 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 ce021923f647f..09218874c5367 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -212,6 +212,8 @@ 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 + mir_build_non_const_path = runtime values cannot be referenced in patterns mir_build_non_exhaustive_match_all_arms_guarded = diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index bee5ac550ddce..b9c3cb4c0a8a3 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -724,6 +724,13 @@ pub struct UnsizedPattern<'tcx> { pub non_sm_ty: Ty<'tcx>, } +#[derive(Diagnostic)] +#[diag(mir_build_nan_pattern)] +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 ae44246602996..71429a70143a0 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, }; @@ -310,16 +311,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_spanned_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. @@ -469,6 +460,26 @@ 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 { + self.saw_const_match_error.set(true); + tcx.sess.emit_err(NaNPattern { span }); + } else { + self.saw_const_match_lint.set(true); + tcx.emit_spanned_lint( + lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, + id, + span, + FloatPattern, + ); + } + return Err(FallbackToOpaqueConst); + } ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) => { PatKind::Constant { value: mir::Const::Ty(ty::Const::new_value(tcx, cv, ty)) } } 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 0000000000000..c5bca8c970dae --- /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 0000000000000..f7d54de4d095b --- /dev/null +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr @@ -0,0 +1,64 @@ +error: cannot use NaN in patterns + --> $DIR/issue-6804-nan-match.rs:11:9 + | +LL | NAN => {}, + | ^^^ + +error: cannot use NaN in patterns + --> $DIR/issue-6804-nan-match.rs:16:10 + | +LL | [NAN, _] => {}, + | ^^^ + +error: cannot use NaN in patterns + --> $DIR/issue-6804-nan-match.rs:22:9 + | +LL | NAN..=1.0 => {}, + | ^^^ + +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 => {}, + | ^^^ + +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.. => {}, + | ^^^ + +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 => {}, + | ^^^ + +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 0260caa82cb1d..0000000000000 --- 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 f37255d0828cd..0000000000000 --- 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 -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 - -error: aborting due to 2 previous errors -