Skip to content

Commit 52d534e

Browse files
committed
Detect out of bounds range pattern value
Fix #68972.
1 parent b22c152 commit 52d534e

File tree

5 files changed

+115
-6
lines changed

5 files changed

+115
-6
lines changed

compiler/rustc_error_messages/locales/en-US/mir_build.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,10 @@ mir_build_lower_range_bound_must_be_less_than_or_equal_to_upper =
206206
.label = lower bound larger than upper bound
207207
.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.
208208
209+
mir_build_literal_in_range_out_of_bounds =
210+
literal out of range for `{$ty}`
211+
.label = this value doesn't fit in `{$ty}` whose maximum value is `{$max}`
212+
209213
mir_build_lower_range_bound_must_be_less_than_upper = lower range bound must be less than upper
210214
211215
mir_build_leading_irrefutable_let_patterns = leading irrefutable {$count ->

compiler/rustc_mir_build/src/errors.rs

+10
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,16 @@ pub struct LowerRangeBoundMustBeLessThanOrEqualToUpper {
493493
pub teach: Option<()>,
494494
}
495495

496+
#[derive(Diagnostic)]
497+
#[diag(mir_build_literal_in_range_out_of_bounds)]
498+
pub struct LiteralOutOfRange<'tcx> {
499+
#[primary_span]
500+
#[label]
501+
pub span: Span,
502+
pub ty: Ty<'tcx>,
503+
pub max: u128,
504+
}
505+
496506
#[derive(Diagnostic)]
497507
#[diag(mir_build_lower_range_bound_must_be_less_than_upper, code = "E0579")]
498508
pub struct LowerRangeBoundMustBeLessThanUpper {

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

+62-6
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,20 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
129129
hi: mir::ConstantKind<'tcx>,
130130
end: RangeEnd,
131131
span: Span,
132+
lo_expr: Option<&hir::Expr<'tcx>>,
133+
hi_expr: Option<&hir::Expr<'tcx>>,
132134
) -> PatKind<'tcx> {
133135
assert_eq!(lo.ty(), ty);
134136
assert_eq!(hi.ty(), ty);
135137
let cmp = compare_const_vals(self.tcx, lo, hi, self.param_env);
138+
let max = || {
139+
self.tcx
140+
.layout_of(self.param_env.with_reveal_all_normalized(self.tcx).and(ty))
141+
.ok()
142+
.unwrap()
143+
.size
144+
.unsigned_int_max()
145+
};
136146
match (end, cmp) {
137147
// `x..y` where `x < y`.
138148
// Non-empty because the range includes at least `x`.
@@ -141,7 +151,27 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
141151
}
142152
// `x..y` where `x >= y`. The range is empty => error.
143153
(RangeEnd::Excluded, _) => {
144-
self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanUpper { span });
154+
let mut lower_overflow = false;
155+
let mut higher_overflow = false;
156+
if let Some(hir::Expr { kind: hir::ExprKind::Lit(lit), .. }) = lo_expr
157+
&& let rustc_ast::ast::LitKind::Int(val, _) = lit.node
158+
{
159+
if lo.eval_bits(self.tcx, self.param_env, ty) != val {
160+
lower_overflow = true;
161+
self.tcx.sess.emit_err(LiteralOutOfRange { span: lit.span, ty, max: max() });
162+
}
163+
}
164+
if let Some(hir::Expr { kind: hir::ExprKind::Lit(lit), .. }) = hi_expr
165+
&& let rustc_ast::ast::LitKind::Int(val, _) = lit.node
166+
{
167+
if hi.eval_bits(self.tcx, self.param_env, ty) != val {
168+
higher_overflow = true;
169+
self.tcx.sess.emit_err(LiteralOutOfRange { span: lit.span, ty, max: max() });
170+
}
171+
}
172+
if !lower_overflow && !higher_overflow {
173+
self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanUpper { span });
174+
}
145175
PatKind::Wild
146176
}
147177
// `x..=y` where `x == y`.
@@ -152,10 +182,34 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
152182
}
153183
// `x..=y` where `x > y` hence the range is empty => error.
154184
(RangeEnd::Included, _) => {
155-
self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanOrEqualToUpper {
156-
span,
157-
teach: if self.tcx.sess.teach(&error_code!(E0030)) { Some(()) } else { None },
158-
});
185+
let mut lower_overflow = false;
186+
let mut higher_overflow = false;
187+
if let Some(hir::Expr { kind: hir::ExprKind::Lit(lit), .. }) = lo_expr
188+
&& let rustc_ast::ast::LitKind::Int(val, _) = lit.node
189+
{
190+
if lo.eval_bits(self.tcx, self.param_env, ty) != val {
191+
lower_overflow = true;
192+
self.tcx.sess.emit_err(LiteralOutOfRange { span: lit.span, ty, max: max() });
193+
}
194+
}
195+
if let Some(hir::Expr { kind: hir::ExprKind::Lit(lit), .. }) = hi_expr
196+
&& let rustc_ast::ast::LitKind::Int(val, _) = lit.node
197+
{
198+
if hi.eval_bits(self.tcx, self.param_env, ty) != val {
199+
higher_overflow = true;
200+
self.tcx.sess.emit_err(LiteralOutOfRange { span: lit.span, ty, max: max() });
201+
}
202+
}
203+
if !lower_overflow && !higher_overflow {
204+
self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanOrEqualToUpper {
205+
span,
206+
teach: if self.tcx.sess.teach(&error_code!(E0030)) {
207+
Some(())
208+
} else {
209+
None
210+
},
211+
});
212+
}
159213
PatKind::Wild
160214
}
161215
}
@@ -201,7 +255,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
201255

202256
let (lp, hp) = (lo.as_ref().map(|(x, _)| x), hi.as_ref().map(|(x, _)| x));
203257
let mut kind = match self.normalize_range_pattern_ends(ty, lp, hp) {
204-
Some((lc, hc)) => self.lower_pattern_range(ty, lc, hc, end, lo_span),
258+
Some((lc, hc)) => {
259+
self.lower_pattern_range(ty, lc, hc, end, lo_span, lo_expr, hi_expr)
260+
}
205261
None => {
206262
let msg = &format!(
207263
"found bad range pattern `{:?}` outside of error recovery",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#![feature(exclusive_range_pattern)]
2+
#![allow(unreachable_patterns)]
3+
fn main() {
4+
match 0u8 {
5+
251..257 => {}
6+
//~^ ERROR literal out of range
7+
//~| ERROR literal out of range
8+
251..=256 => {}
9+
//~^ ERROR literal out of range
10+
//~| ERROR literal out of range
11+
_ => {}
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
error: literal out of range for `u8`
2+
--> $DIR/range-pattern-out-of-bounds-issue-68972.rs:5:14
3+
|
4+
LL | 251..257 => {}
5+
| ^^^ this value doesn't fit in `u8` whose maximum value is `255`
6+
7+
error: literal out of range for `u8`
8+
--> $DIR/range-pattern-out-of-bounds-issue-68972.rs:8:15
9+
|
10+
LL | 251..=256 => {}
11+
| ^^^ this value doesn't fit in `u8` whose maximum value is `255`
12+
13+
error: literal out of range for `u8`
14+
--> $DIR/range-pattern-out-of-bounds-issue-68972.rs:5:14
15+
|
16+
LL | 251..257 => {}
17+
| ^^^ this value doesn't fit in `u8` whose maximum value is `255`
18+
19+
error: literal out of range for `u8`
20+
--> $DIR/range-pattern-out-of-bounds-issue-68972.rs:8:15
21+
|
22+
LL | 251..=256 => {}
23+
| ^^^ this value doesn't fit in `u8` whose maximum value is `255`
24+
25+
error: aborting due to 4 previous errors
26+

0 commit comments

Comments
 (0)