]> git.lizzy.rs Git - rust.git/commitdiff
Detect out of bounds range pattern value
authorEsteban Küber <esteban@kuber.com.ar>
Mon, 9 Jan 2023 07:10:17 +0000 (07:10 +0000)
committerEsteban Küber <esteban@kuber.com.ar>
Wed, 11 Jan 2023 16:50:55 +0000 (16:50 +0000)
Fix #68972.

compiler/rustc_error_messages/locales/en-US/mir_build.ftl
compiler/rustc_mir_build/src/errors.rs
compiler/rustc_mir_build/src/thir/pattern/mod.rs
tests/ui/range/range-pattern-out-of-bounds-issue-68972.rs [new file with mode: 0644]
tests/ui/range/range-pattern-out-of-bounds-issue-68972.stderr [new file with mode: 0644]

index 60d3d3e69abbe13385988c7a2478e4c33dea03e6..aacaafeede6956e02b58b4f2ea0331ee5bcf10ea 100644 (file)
@@ -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 ->
index 68179001b916dbc3ac5128f91d7ed1cdcd7fe6e6..233eecbd5b4ecb4a950d132a5196bf42f9bc60bf 100644 (file)
@@ -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 {
index 2c775b397182b352eea62549c4b7179129e20178..7d4353c52926d7be6c5cd25c9b4360dbffd3fa02 100644 (file)
@@ -129,10 +129,20 @@ fn lower_pattern_range(
         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 @@ fn lower_pattern_range(
             }
             // `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 @@ fn lower_pattern_range(
             }
             // `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 @@ fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tc
 
                 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 (file)
index 0000000..d02caff
--- /dev/null
@@ -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 (file)
index 0000000..7b8309b
--- /dev/null
@@ -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
+