Skip to content

Commit a3e4be4

Browse files
committed
Auto merge of #116284 - RalfJung:no-nan-match, r=<try>
make matching on NaN a hard error These arms would never be hit anyway, so the pattern makes little sense. We have had a future-compat lint against float matches in general for a *long* time, so I hope we can get away with immediately making this a hard error.
2 parents 9136560 + 92ddac2 commit a3e4be4

File tree

9 files changed

+172
-85
lines changed

9 files changed

+172
-85
lines changed

compiler/rustc_middle/src/mir/interpret/value.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ impl<'tcx, Prov: Provenance> Scalar<Prov> {
413413
#[inline]
414414
pub fn to_float<F: Float>(self) -> InterpResult<'tcx, F> {
415415
// Going through `to_uint` to check size and truncation.
416-
Ok(F::from_bits(self.to_uint(Size::from_bits(F::BITS))?))
416+
Ok(F::from_bits(self.to_bits(Size::from_bits(F::BITS))?))
417417
}
418418

419419
#[inline]

compiler/rustc_middle/src/ty/consts/int.rs

+44-27
Original file line numberDiff line numberDiff line change
@@ -249,11 +249,6 @@ impl ScalarInt {
249249
}
250250
}
251251

252-
#[inline]
253-
pub fn try_to_target_usize(&self, tcx: TyCtxt<'_>) -> Result<u64, Size> {
254-
Ok(self.to_bits(tcx.data_layout.pointer_size)? as u64)
255-
}
256-
257252
/// Tries to convert the `ScalarInt` to an unsigned integer of the given size.
258253
/// Fails if the size of the `ScalarInt` is not equal to `size` and returns the
259254
/// `ScalarInt`s size in that case.
@@ -262,56 +257,61 @@ impl ScalarInt {
262257
self.to_bits(size)
263258
}
264259

265-
// Tries to convert the `ScalarInt` to `bool`. Fails if the `size` of the `ScalarInt`
266-
// in not equal to `Size { raw: 1 }` or if the value is not 0 or 1 and returns the `size`
267-
// value of the `ScalarInt` in that case.
268-
#[inline]
269-
pub fn try_to_bool(self) -> Result<bool, Size> {
270-
match self.try_to_u8()? {
271-
0 => Ok(false),
272-
1 => Ok(true),
273-
_ => Err(self.size()),
274-
}
275-
}
276-
277260
// Tries to convert the `ScalarInt` to `u8`. Fails if the `size` of the `ScalarInt`
278261
// in not equal to `Size { raw: 1 }` and returns the `size` value of the `ScalarInt` in
279262
// that case.
280263
#[inline]
281264
pub fn try_to_u8(self) -> Result<u8, Size> {
282-
self.to_bits(Size::from_bits(8)).map(|v| u8::try_from(v).unwrap())
265+
self.try_to_uint(Size::from_bits(8)).map(|v| u8::try_from(v).unwrap())
283266
}
284267

285268
/// Tries to convert the `ScalarInt` to `u16`. Fails if the size of the `ScalarInt`
286269
/// in not equal to `Size { raw: 2 }` and returns the `size` value of the `ScalarInt` in
287270
/// that case.
288271
#[inline]
289272
pub fn try_to_u16(self) -> Result<u16, Size> {
290-
self.to_bits(Size::from_bits(16)).map(|v| u16::try_from(v).unwrap())
273+
self.try_to_uint(Size::from_bits(16)).map(|v| u16::try_from(v).unwrap())
291274
}
292275

293276
/// Tries to convert the `ScalarInt` to `u32`. Fails if the `size` of the `ScalarInt`
294277
/// in not equal to `Size { raw: 4 }` and returns the `size` value of the `ScalarInt` in
295278
/// that case.
296279
#[inline]
297280
pub fn try_to_u32(self) -> Result<u32, Size> {
298-
self.to_bits(Size::from_bits(32)).map(|v| u32::try_from(v).unwrap())
281+
self.try_to_uint(Size::from_bits(32)).map(|v| u32::try_from(v).unwrap())
299282
}
300283

301284
/// Tries to convert the `ScalarInt` to `u64`. Fails if the `size` of the `ScalarInt`
302285
/// in not equal to `Size { raw: 8 }` and returns the `size` value of the `ScalarInt` in
303286
/// that case.
304287
#[inline]
305288
pub fn try_to_u64(self) -> Result<u64, Size> {
306-
self.to_bits(Size::from_bits(64)).map(|v| u64::try_from(v).unwrap())
289+
self.try_to_uint(Size::from_bits(64)).map(|v| u64::try_from(v).unwrap())
307290
}
308291

309292
/// Tries to convert the `ScalarInt` to `u128`. Fails if the `size` of the `ScalarInt`
310293
/// in not equal to `Size { raw: 16 }` and returns the `size` value of the `ScalarInt` in
311294
/// that case.
312295
#[inline]
313296
pub fn try_to_u128(self) -> Result<u128, Size> {
314-
self.to_bits(Size::from_bits(128))
297+
self.try_to_uint(Size::from_bits(128))
298+
}
299+
300+
#[inline]
301+
pub fn try_to_target_usize(&self, tcx: TyCtxt<'_>) -> Result<u64, Size> {
302+
self.try_to_uint(tcx.data_layout.pointer_size).map(|v| u64::try_from(v).unwrap())
303+
}
304+
305+
// Tries to convert the `ScalarInt` to `bool`. Fails if the `size` of the `ScalarInt`
306+
// in not equal to `Size { raw: 1 }` or if the value is not 0 or 1 and returns the `size`
307+
// value of the `ScalarInt` in that case.
308+
#[inline]
309+
pub fn try_to_bool(self) -> Result<bool, Size> {
310+
match self.try_to_u8()? {
311+
0 => Ok(false),
312+
1 => Ok(true),
313+
_ => Err(self.size()),
314+
}
315315
}
316316

317317
/// Tries to convert the `ScalarInt` to a signed integer of the given size.
@@ -357,6 +357,27 @@ impl ScalarInt {
357357
pub fn try_to_i128(self) -> Result<i128, Size> {
358358
self.try_to_int(Size::from_bits(128))
359359
}
360+
361+
#[inline]
362+
pub fn try_to_target_isize(&self, tcx: TyCtxt<'_>) -> Result<i64, Size> {
363+
self.try_to_int(tcx.data_layout.pointer_size).map(|v| i64::try_from(v).unwrap())
364+
}
365+
366+
#[inline]
367+
pub fn try_to_float<F: Float>(self) -> Result<F, Size> {
368+
// Going through `to_uint` to check size and truncation.
369+
Ok(F::from_bits(self.to_bits(Size::from_bits(F::BITS))?))
370+
}
371+
372+
#[inline]
373+
pub fn try_to_f32(self) -> Result<Single, Size> {
374+
self.try_to_float()
375+
}
376+
377+
#[inline]
378+
pub fn try_to_f64(self) -> Result<Double, Size> {
379+
self.try_to_float()
380+
}
360381
}
361382

362383
macro_rules! from {
@@ -399,11 +420,7 @@ impl TryFrom<ScalarInt> for bool {
399420
type Error = Size;
400421
#[inline]
401422
fn try_from(int: ScalarInt) -> Result<Self, Size> {
402-
int.to_bits(Size::from_bytes(1)).and_then(|u| match u {
403-
0 => Ok(false),
404-
1 => Ok(true),
405-
_ => Err(Size::from_bytes(1)),
406-
})
423+
int.try_to_bool()
407424
}
408425
}
409426

compiler/rustc_mir_build/messages.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,8 @@ mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsa
212212
.note = mutating layout constrained fields cannot statically be checked for valid values
213213
.label = mutation of layout constrained field
214214
215+
mir_build_nan_pattern = cannot use NaN in patterns
216+
215217
mir_build_non_const_path = runtime values cannot be referenced in patterns
216218
217219
mir_build_non_exhaustive_match_all_arms_guarded =

compiler/rustc_mir_build/src/errors.rs

+7
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,13 @@ pub struct UnsizedPattern<'tcx> {
724724
pub non_sm_ty: Ty<'tcx>,
725725
}
726726

727+
#[derive(Diagnostic)]
728+
#[diag(mir_build_nan_pattern)]
729+
pub struct NaNPattern {
730+
#[primary_span]
731+
pub span: Span,
732+
}
733+
727734
#[derive(LintDiagnostic)]
728735
#[diag(mir_build_float_pattern)]
729736
pub struct FloatPattern;

compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs

+22-11
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use rustc_apfloat::Float;
12
use rustc_hir as hir;
23
use rustc_hir::def_id::DefId;
34
use rustc_index::Idx;
@@ -16,7 +17,7 @@ use std::cell::Cell;
1617

1718
use super::PatCtxt;
1819
use crate::errors::{
19-
FloatPattern, IndirectStructuralMatch, InvalidPattern, NonPartialEqMatch,
20+
FloatPattern, IndirectStructuralMatch, InvalidPattern, NaNPattern, NonPartialEqMatch,
2021
NontrivialStructuralMatch, PointerPattern, TypeNotStructural, UnionPattern, UnsizedPattern,
2122
};
2223

@@ -310,16 +311,6 @@ impl<'tcx> ConstToPat<'tcx> {
310311
let param_env = self.param_env;
311312

312313
let kind = match ty.kind() {
313-
ty::Float(_) => {
314-
self.saw_const_match_lint.set(true);
315-
tcx.emit_spanned_lint(
316-
lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
317-
id,
318-
span,
319-
FloatPattern,
320-
);
321-
return Err(FallbackToOpaqueConst);
322-
}
323314
// If the type is not structurally comparable, just emit the constant directly,
324315
// causing the pattern match code to treat it opaquely.
325316
// FIXME: This code doesn't emit errors itself, the caller emits the errors.
@@ -469,6 +460,26 @@ impl<'tcx> ConstToPat<'tcx> {
469460
}
470461
}
471462
},
463+
ty::Float(flt) => {
464+
let v = cv.unwrap_leaf();
465+
let is_nan = match flt {
466+
ty::FloatTy::F32 => v.try_to_f32().unwrap().is_nan(),
467+
ty::FloatTy::F64 => v.try_to_f64().unwrap().is_nan(),
468+
};
469+
if is_nan {
470+
self.saw_const_match_error.set(true);
471+
tcx.sess.emit_err(NaNPattern { span });
472+
} else {
473+
self.saw_const_match_lint.set(true);
474+
tcx.emit_spanned_lint(
475+
lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
476+
id,
477+
span,
478+
FloatPattern,
479+
);
480+
}
481+
return Err(FallbackToOpaqueConst);
482+
}
472483
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) => {
473484
PatKind::Constant { value: mir::Const::Ty(ty::Const::new_value(tcx, cv, ty)) }
474485
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Matching against NaN should result in an error
2+
#![feature(exclusive_range_pattern)]
3+
#![allow(unused)]
4+
#![allow(illegal_floating_point_literal_pattern)]
5+
6+
const NAN: f64 = f64::NAN;
7+
8+
fn main() {
9+
let x = NAN;
10+
match x {
11+
NAN => {}, //~ ERROR cannot use NaN in patterns
12+
_ => {},
13+
};
14+
15+
match [x, 1.0] {
16+
[NAN, _] => {}, //~ ERROR cannot use NaN in patterns
17+
_ => {},
18+
};
19+
20+
// Also cover range patterns
21+
match x {
22+
NAN..=1.0 => {}, //~ ERROR cannot use NaN in patterns
23+
//~^ ERROR lower range bound must be less than or equal to upper
24+
-1.0..=NAN => {}, //~ ERROR cannot use NaN in patterns
25+
//~^ ERROR lower range bound must be less than or equal to upper
26+
NAN.. => {}, //~ ERROR cannot use NaN in patterns
27+
//~^ ERROR lower range bound must be less than or equal to upper
28+
..NAN => {}, //~ ERROR cannot use NaN in patterns
29+
//~^ ERROR lower range bound must be less than upper
30+
_ => {},
31+
};
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
error: cannot use NaN in patterns
2+
--> $DIR/issue-6804-nan-match.rs:11:9
3+
|
4+
LL | NAN => {},
5+
| ^^^
6+
7+
error: cannot use NaN in patterns
8+
--> $DIR/issue-6804-nan-match.rs:16:10
9+
|
10+
LL | [NAN, _] => {},
11+
| ^^^
12+
13+
error: cannot use NaN in patterns
14+
--> $DIR/issue-6804-nan-match.rs:22:9
15+
|
16+
LL | NAN..=1.0 => {},
17+
| ^^^
18+
19+
error[E0030]: lower range bound must be less than or equal to upper
20+
--> $DIR/issue-6804-nan-match.rs:22:9
21+
|
22+
LL | NAN..=1.0 => {},
23+
| ^^^ lower bound larger than upper bound
24+
25+
error: cannot use NaN in patterns
26+
--> $DIR/issue-6804-nan-match.rs:24:16
27+
|
28+
LL | -1.0..=NAN => {},
29+
| ^^^
30+
31+
error[E0030]: lower range bound must be less than or equal to upper
32+
--> $DIR/issue-6804-nan-match.rs:24:9
33+
|
34+
LL | -1.0..=NAN => {},
35+
| ^^^^ lower bound larger than upper bound
36+
37+
error: cannot use NaN in patterns
38+
--> $DIR/issue-6804-nan-match.rs:26:9
39+
|
40+
LL | NAN.. => {},
41+
| ^^^
42+
43+
error[E0030]: lower range bound must be less than or equal to upper
44+
--> $DIR/issue-6804-nan-match.rs:26:9
45+
|
46+
LL | NAN.. => {},
47+
| ^^^ lower bound larger than upper bound
48+
49+
error: cannot use NaN in patterns
50+
--> $DIR/issue-6804-nan-match.rs:28:11
51+
|
52+
LL | ..NAN => {},
53+
| ^^^
54+
55+
error[E0579]: lower range bound must be less than upper
56+
--> $DIR/issue-6804-nan-match.rs:28:9
57+
|
58+
LL | ..NAN => {},
59+
| ^^^^^
60+
61+
error: aborting due to 10 previous errors
62+
63+
Some errors have detailed explanations: E0030, E0579.
64+
For more information about an error, try `rustc --explain E0030`.

tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.rs

-21
This file was deleted.

tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.stderr

-25
This file was deleted.

0 commit comments

Comments
 (0)