From 52d534ef63294d93d3b3f711297e962396d5e0e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 9 Jan 2023 07:10:17 +0000 Subject: [PATCH] Detect out of bounds range pattern value Fix #68972. --- .../locales/en-US/mir_build.ftl | 4 ++ compiler/rustc_mir_build/src/errors.rs | 10 +++ .../rustc_mir_build/src/thir/pattern/mod.rs | 68 +++++++++++++++++-- ...range-pattern-out-of-bounds-issue-68972.rs | 13 ++++ ...e-pattern-out-of-bounds-issue-68972.stderr | 26 +++++++ 5 files changed, 115 insertions(+), 6 deletions(-) create mode 100644 tests/ui/range/range-pattern-out-of-bounds-issue-68972.rs create mode 100644 tests/ui/range/range-pattern-out-of-bounds-issue-68972.stderr diff --git a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl index 60d3d3e69ab..aacaafeede6 100644 --- a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl +++ b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl @@ -206,6 +206,10 @@ mir_build_lower_range_bound_must_be_less_than_or_equal_to_upper = .label = lower bound larger than upper bound .teach_note = When matching against a range, the compiler verifies that the range is non-empty. Range patterns include both end-points, so this is equivalent to requiring the start of the range to be less than or equal to the end of the range. +mir_build_literal_in_range_out_of_bounds = + literal out of range for `{$ty}` + .label = this value doesn't fit in `{$ty}` whose maximum value is `{$max}` + mir_build_lower_range_bound_must_be_less_than_upper = lower range bound must be less than upper mir_build_leading_irrefutable_let_patterns = leading irrefutable {$count -> diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 68179001b91..233eecbd5b4 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -493,6 +493,16 @@ pub struct LowerRangeBoundMustBeLessThanOrEqualToUpper { pub teach: Option<()>, } +#[derive(Diagnostic)] +#[diag(mir_build_literal_in_range_out_of_bounds)] +pub struct LiteralOutOfRange<'tcx> { + #[primary_span] + #[label] + pub span: Span, + pub ty: Ty<'tcx>, + pub max: u128, +} + #[derive(Diagnostic)] #[diag(mir_build_lower_range_bound_must_be_less_than_upper, code = "E0579")] pub struct LowerRangeBoundMustBeLessThanUpper { diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 2c775b39718..7d4353c5292 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -129,10 +129,20 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { hi: mir::ConstantKind<'tcx>, end: RangeEnd, span: Span, + lo_expr: Option<&hir::Expr<'tcx>>, + hi_expr: Option<&hir::Expr<'tcx>>, ) -> PatKind<'tcx> { assert_eq!(lo.ty(), ty); assert_eq!(hi.ty(), ty); let cmp = compare_const_vals(self.tcx, lo, hi, self.param_env); + let max = || { + self.tcx + .layout_of(self.param_env.with_reveal_all_normalized(self.tcx).and(ty)) + .ok() + .unwrap() + .size + .unsigned_int_max() + }; match (end, cmp) { // `x..y` where `x < y`. // Non-empty because the range includes at least `x`. @@ -141,7 +151,27 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } // `x..y` where `x >= y`. The range is empty => error. (RangeEnd::Excluded, _) => { - self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanUpper { span }); + let mut lower_overflow = false; + let mut higher_overflow = false; + if let Some(hir::Expr { kind: hir::ExprKind::Lit(lit), .. }) = lo_expr + && let rustc_ast::ast::LitKind::Int(val, _) = lit.node + { + if lo.eval_bits(self.tcx, self.param_env, ty) != val { + lower_overflow = true; + self.tcx.sess.emit_err(LiteralOutOfRange { span: lit.span, ty, max: max() }); + } + } + if let Some(hir::Expr { kind: hir::ExprKind::Lit(lit), .. }) = hi_expr + && let rustc_ast::ast::LitKind::Int(val, _) = lit.node + { + if hi.eval_bits(self.tcx, self.param_env, ty) != val { + higher_overflow = true; + self.tcx.sess.emit_err(LiteralOutOfRange { span: lit.span, ty, max: max() }); + } + } + if !lower_overflow && !higher_overflow { + self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanUpper { span }); + } PatKind::Wild } // `x..=y` where `x == y`. @@ -152,10 +182,34 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } // `x..=y` where `x > y` hence the range is empty => error. (RangeEnd::Included, _) => { - self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanOrEqualToUpper { - span, - teach: if self.tcx.sess.teach(&error_code!(E0030)) { Some(()) } else { None }, - }); + let mut lower_overflow = false; + let mut higher_overflow = false; + if let Some(hir::Expr { kind: hir::ExprKind::Lit(lit), .. }) = lo_expr + && let rustc_ast::ast::LitKind::Int(val, _) = lit.node + { + if lo.eval_bits(self.tcx, self.param_env, ty) != val { + lower_overflow = true; + self.tcx.sess.emit_err(LiteralOutOfRange { span: lit.span, ty, max: max() }); + } + } + if let Some(hir::Expr { kind: hir::ExprKind::Lit(lit), .. }) = hi_expr + && let rustc_ast::ast::LitKind::Int(val, _) = lit.node + { + if hi.eval_bits(self.tcx, self.param_env, ty) != val { + higher_overflow = true; + self.tcx.sess.emit_err(LiteralOutOfRange { span: lit.span, ty, max: max() }); + } + } + if !lower_overflow && !higher_overflow { + self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanOrEqualToUpper { + span, + teach: if self.tcx.sess.teach(&error_code!(E0030)) { + Some(()) + } else { + None + }, + }); + } PatKind::Wild } } @@ -201,7 +255,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let (lp, hp) = (lo.as_ref().map(|(x, _)| x), hi.as_ref().map(|(x, _)| x)); let mut kind = match self.normalize_range_pattern_ends(ty, lp, hp) { - Some((lc, hc)) => self.lower_pattern_range(ty, lc, hc, end, lo_span), + Some((lc, hc)) => { + self.lower_pattern_range(ty, lc, hc, end, lo_span, lo_expr, hi_expr) + } None => { let msg = &format!( "found bad range pattern `{:?}` outside of error recovery", diff --git a/tests/ui/range/range-pattern-out-of-bounds-issue-68972.rs b/tests/ui/range/range-pattern-out-of-bounds-issue-68972.rs new file mode 100644 index 00000000000..d02caff1feb --- /dev/null +++ b/tests/ui/range/range-pattern-out-of-bounds-issue-68972.rs @@ -0,0 +1,13 @@ +#![feature(exclusive_range_pattern)] +#![allow(unreachable_patterns)] +fn main() { + match 0u8 { + 251..257 => {} + //~^ ERROR literal out of range + //~| ERROR literal out of range + 251..=256 => {} + //~^ ERROR literal out of range + //~| ERROR literal out of range + _ => {} + } +} diff --git a/tests/ui/range/range-pattern-out-of-bounds-issue-68972.stderr b/tests/ui/range/range-pattern-out-of-bounds-issue-68972.stderr new file mode 100644 index 00000000000..7b8309b9bc2 --- /dev/null +++ b/tests/ui/range/range-pattern-out-of-bounds-issue-68972.stderr @@ -0,0 +1,26 @@ +error: literal out of range for `u8` + --> $DIR/range-pattern-out-of-bounds-issue-68972.rs:5:14 + | +LL | 251..257 => {} + | ^^^ this value doesn't fit in `u8` whose maximum value is `255` + +error: literal out of range for `u8` + --> $DIR/range-pattern-out-of-bounds-issue-68972.rs:8:15 + | +LL | 251..=256 => {} + | ^^^ this value doesn't fit in `u8` whose maximum value is `255` + +error: literal out of range for `u8` + --> $DIR/range-pattern-out-of-bounds-issue-68972.rs:5:14 + | +LL | 251..257 => {} + | ^^^ this value doesn't fit in `u8` whose maximum value is `255` + +error: literal out of range for `u8` + --> $DIR/range-pattern-out-of-bounds-issue-68972.rs:8:15 + | +LL | 251..=256 => {} + | ^^^ this value doesn't fit in `u8` whose maximum value is `255` + +error: aborting due to 4 previous errors +