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 { #[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))?)) + // 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 { - 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,6 +257,51 @@ impl ScalarInt { self.to_bits(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.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` + /// in not equal to `Size { raw: 2 }` and returns the `size` value of the `ScalarInt` in + /// that case. + #[inline] + pub fn try_to_u16(self) -> Result { + 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` + /// in not equal to `Size { raw: 4 }` and returns the `size` value of the `ScalarInt` in + /// that case. + #[inline] + pub fn try_to_u32(self) -> Result { + 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` + /// in not equal to `Size { raw: 8 }` and returns the `size` value of the `ScalarInt` in + /// that case. + #[inline] + pub fn try_to_u64(self) -> Result { + 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` + /// in not equal to `Size { raw: 16 }` and returns the `size` value of the `ScalarInt` in + /// that case. + #[inline] + pub fn try_to_u128(self) -> Result { + 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. @@ -274,46 +314,6 @@ impl ScalarInt { } } - // 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()) - } - - /// Tries to convert the `ScalarInt` to `u16`. Fails if the size of the `ScalarInt` - /// in not equal to `Size { raw: 2 }` and returns the `size` value of the `ScalarInt` in - /// that case. - #[inline] - pub fn try_to_u16(self) -> Result { - self.to_bits(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` - /// in not equal to `Size { raw: 4 }` and returns the `size` value of the `ScalarInt` in - /// that case. - #[inline] - pub fn try_to_u32(self) -> Result { - self.to_bits(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` - /// in not equal to `Size { raw: 8 }` and returns the `size` value of the `ScalarInt` in - /// that case. - #[inline] - pub fn try_to_u64(self) -> Result { - self.to_bits(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` - /// in not equal to `Size { raw: 16 }` and returns the `size` value of the `ScalarInt` in - /// that case. - #[inline] - pub fn try_to_u128(self) -> Result { - self.to_bits(Size::from_bits(128)) - } - /// Tries to convert the `ScalarInt` to a signed 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. @@ -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 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 . + 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 -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 -