mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
make matching on NaN a hard error
This commit is contained in:
parent
69db514ed9
commit
cda3588572
@ -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]
|
||||
|
@ -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,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<u8, Size> {
|
||||
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<u16, Size> {
|
||||
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<u32, Size> {
|
||||
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<u64, Size> {
|
||||
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<u128, Size> {
|
||||
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.
|
||||
@ -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<u8, Size> {
|
||||
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<u16, Size> {
|
||||
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<u32, Size> {
|
||||
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<u64, Size> {
|
||||
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<u128, Size> {
|
||||
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<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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 =
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
_ => {},
|
||||
};
|
||||
}
|
@ -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`.
|
@ -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
|
||||
_ => {},
|
||||
};
|
||||
}
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user