Skip to content

Commit b4e4ead

Browse files
committed
Fix range overflow checking
1 parent 7a186d3 commit b4e4ead

File tree

7 files changed

+94
-62
lines changed

7 files changed

+94
-62
lines changed

compiler/rustc_mir_build/messages.ftl

+1-1
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ mir_build_leading_irrefutable_let_patterns = leading irrefutable {$count ->
173173
174174
mir_build_literal_in_range_out_of_bounds =
175175
literal out of range for `{$ty}`
176-
.label = this value doesn't fit in `{$ty}` whose maximum value is `{$max}`
176+
.label = this value does not fit into the type `{$ty}` whose range is `{$min}..={$max}`
177177
178178
mir_build_lower_range_bound_must_be_less_than_or_equal_to_upper =
179179
lower range bound must be less than or equal to upper

compiler/rustc_mir_build/src/errors.rs

+1
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,7 @@ pub struct LiteralOutOfRange<'tcx> {
551551
#[label]
552552
pub span: Span,
553553
pub ty: Ty<'tcx>,
554+
pub min: i128,
554555
pub max: u128,
555556
}
556557

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

+63-31
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,15 @@ use rustc_index::Idx;
2020
use rustc_middle::mir::interpret::{
2121
ErrorHandled, GlobalId, LitToConstError, LitToConstInput, Scalar,
2222
};
23-
use rustc_middle::mir::{self, Const, UserTypeProjection};
24-
use rustc_middle::mir::{BorrowKind, Mutability};
23+
use rustc_middle::mir::{self, BorrowKind, Const, Mutability, UserTypeProjection};
2524
use rustc_middle::thir::{Ascription, BindingMode, FieldPat, LocalVarId, Pat, PatKind, PatRange};
26-
use rustc_middle::ty::CanonicalUserTypeAnnotation;
27-
use rustc_middle::ty::TypeVisitableExt;
28-
use rustc_middle::ty::{self, AdtDef, Region, Ty, TyCtxt, UserType};
29-
use rustc_middle::ty::{GenericArg, GenericArgsRef};
25+
use rustc_middle::ty::layout::IntegerExt;
26+
use rustc_middle::ty::{
27+
self, AdtDef, CanonicalUserTypeAnnotation, GenericArg, GenericArgsRef, Region, Ty, TyCtxt,
28+
TypeVisitableExt, UserType,
29+
};
3030
use rustc_span::{ErrorGuaranteed, Span, Symbol};
31-
use rustc_target::abi::FieldIdx;
31+
use rustc_target::abi::{FieldIdx, Integer};
3232

3333
use std::cmp::Ordering;
3434

@@ -111,6 +111,59 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
111111
}
112112
}
113113

114+
/// Overflowing literals are linted against in a late pass. This is mostly fine, except when we
115+
/// encounter a range pattern like `-130i8..2`: if we believe `eval_bits`, this looks like a
116+
/// range where the endpoints are in the wrong order. To avoid a confusing error message, we
117+
/// check for overflow then.
118+
/// This is only called when the range is already known to be malformed.
119+
fn error_on_literal_overflow(
120+
&self,
121+
expr: Option<&'tcx hir::Expr<'tcx>>,
122+
ty: Ty<'tcx>,
123+
) -> Result<(), ErrorGuaranteed> {
124+
use hir::{ExprKind, UnOp};
125+
use rustc_ast::ast::LitKind;
126+
127+
let Some(mut expr) = expr else {
128+
return Ok(());
129+
};
130+
let span = expr.span;
131+
132+
// We need to inspect the original expression, because if we only inspect the output of
133+
// `eval_bits`, an overflowed value has already been wrapped around.
134+
// We mostly copy the logic from the `rustc_lint::OVERFLOWING_LITERALS` lint.
135+
let mut negated = false;
136+
if let ExprKind::Unary(UnOp::Neg, sub_expr) = expr.kind {
137+
negated = true;
138+
expr = sub_expr;
139+
}
140+
let ExprKind::Lit(lit) = expr.kind else {
141+
return Ok(());
142+
};
143+
let LitKind::Int(lit_val, _) = lit.node else {
144+
return Ok(());
145+
};
146+
let (min, max): (i128, u128) = match ty.kind() {
147+
ty::Int(ity) => {
148+
let size = Integer::from_int_ty(&self.tcx, *ity).size();
149+
(size.signed_int_min(), size.signed_int_max() as u128)
150+
}
151+
ty::Uint(uty) => {
152+
let size = Integer::from_uint_ty(&self.tcx, *uty).size();
153+
(0, size.unsigned_int_max())
154+
}
155+
_ => {
156+
return Ok(());
157+
}
158+
};
159+
// Detect literal value out of range `[min, max]` inclusive, avoiding use of `-min` to
160+
// prevent overflow/panic.
161+
if (negated && lit_val > max + 1) || (!negated && lit_val > max) {
162+
return Err(self.tcx.sess.emit_err(LiteralOutOfRange { span, ty, min, max }));
163+
}
164+
Ok(())
165+
}
166+
114167
fn lower_pattern_range(
115168
&mut self,
116169
lo_expr: Option<&'tcx hir::Expr<'tcx>>,
@@ -155,29 +208,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
155208
}
156209
// `x..y` where `x >= y`, or `x..=y` where `x > y`. The range is empty => error.
157210
_ => {
158-
let max = || {
159-
self.tcx
160-
.layout_of(self.param_env.with_reveal_all_normalized(self.tcx).and(ty))
161-
.ok()
162-
.unwrap()
163-
.size
164-
.unsigned_int_max()
165-
};
166-
// Emit a different message if there was overflow.
167-
if let Some(hir::Expr { kind: hir::ExprKind::Lit(lit), .. }) = lo_expr
168-
&& let rustc_ast::ast::LitKind::Int(val, _) = lit.node
169-
{
170-
if lo.eval_bits(self.tcx, self.param_env) != val {
171-
return Err(self.tcx.sess.emit_err(LiteralOutOfRange { span: lit.span, ty, max: max() }));
172-
}
173-
}
174-
if let Some(hir::Expr { kind: hir::ExprKind::Lit(lit), .. }) = hi_expr
175-
&& let rustc_ast::ast::LitKind::Int(val, _) = lit.node
176-
{
177-
if hi.eval_bits(self.tcx, self.param_env) != val {
178-
return Err(self.tcx.sess.emit_err(LiteralOutOfRange { span: lit.span, ty, max: max() }));
179-
}
180-
}
211+
// Emit a more appropriate message if there was overflow.
212+
self.error_on_literal_overflow(lo_expr, ty)?;
213+
self.error_on_literal_overflow(hi_expr, ty)?;
181214
let e = match end {
182215
RangeEnd::Included => {
183216
self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanOrEqualToUpper {
@@ -219,7 +252,6 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
219252

220253
hir::PatKind::Range(ref lo_expr, ref hi_expr, end) => {
221254
let (lo_expr, hi_expr) = (lo_expr.as_deref(), hi_expr.as_deref());
222-
let span = lo_expr.map_or(span, |e| e.span);
223255
// FIXME?: returning `_` can cause inaccurate "unreachable" warnings. This can be
224256
// fixed by returning `PatKind::Const(ConstKind::Error(...))` if #115937 gets
225257
// merged.

tests/ui/match/match-range-fail-2.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@ error[E0030]: lower range bound must be less than or equal to upper
22
--> $DIR/match-range-fail-2.rs:5:9
33
|
44
LL | 6 ..= 1 => { }
5-
| ^ lower bound larger than upper bound
5+
| ^^^^^^^ lower bound larger than upper bound
66

77
error[E0579]: lower range bound must be less than upper
88
--> $DIR/match-range-fail-2.rs:11:9
99
|
1010
LL | 0 .. 0 => { }
11-
| ^
11+
| ^^^^^^
1212

1313
error[E0030]: lower range bound must be less than or equal to upper
1414
--> $DIR/match-range-fail-2.rs:17:9
1515
|
1616
LL | 0xFFFF_FFFF_FFFF_FFFF ..= 1 => { }
17-
| ^^^^^^^^^^^^^^^^^^^^^ lower bound larger than upper bound
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ lower bound larger than upper bound
1818

1919
error: aborting due to 3 previous errors
2020

tests/ui/match/validate-range-endpoints.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,15 @@ fn main() {
3030
_ => {}
3131
}
3232

33-
// FIXME: error message is confusing
3433
match 0i8 {
3534
0..129 => {}
36-
//~^ ERROR lower range bound must be less than upper
35+
//~^ ERROR literal out of range
3736
0..=128 => {}
38-
//~^ ERROR lower range bound must be less than or equal to upper
37+
//~^ ERROR literal out of range
3938
-129..0 => {}
40-
//~^ ERROR lower range bound must be less than upper
39+
//~^ ERROR literal out of range
4140
-10000..=-20 => {}
42-
//~^ ERROR lower range bound must be less than or equal to upper
41+
//~^ ERROR literal out of range
4342

4443
// overflow is detected in a later pass for these
4544
128..=0 => {}

tests/ui/match/validate-range-endpoints.stderr

+20-20
Original file line numberDiff line numberDiff line change
@@ -2,58 +2,58 @@ error: literal out of range for `u8`
22
--> $DIR/validate-range-endpoints.rs:9:12
33
|
44
LL | 1..257 => {}
5-
| ^^^ this value doesn't fit in `u8` whose maximum value is `255`
5+
| ^^^ this value does not fit into the type `u8` whose range is `0..=255`
66

77
error: literal out of range for `u8`
88
--> $DIR/validate-range-endpoints.rs:11:13
99
|
1010
LL | 1..=256 => {}
11-
| ^^^ this value doesn't fit in `u8` whose maximum value is `255`
11+
| ^^^ this value does not fit into the type `u8` whose range is `0..=255`
1212

1313
error[E0030]: lower range bound must be less than or equal to upper
1414
--> $DIR/validate-range-endpoints.rs:20:9
1515
|
1616
LL | 1..=TOO_BIG => {}
17-
| ^ lower bound larger than upper bound
17+
| ^^^^^^^^^^^ lower bound larger than upper bound
1818

1919
error[E0030]: lower range bound must be less than or equal to upper
2020
--> $DIR/validate-range-endpoints.rs:22:9
2121
|
2222
LL | 1..=const { 256 } => {}
23-
| ^ lower bound larger than upper bound
23+
| ^^^^^^^^^^^^^^^^^ lower bound larger than upper bound
2424

2525
error: literal out of range for `usize`
2626
--> $DIR/validate-range-endpoints.rs:28:32
2727
|
2828
LL | 10000000000000000000..=99999999999999999999 => {}
29-
| ^^^^^^^^^^^^^^^^^^^^ this value doesn't fit in `usize` whose maximum value is `18446744073709551615`
29+
| ^^^^^^^^^^^^^^^^^^^^ this value does not fit into the type `usize` whose range is `0..=18446744073709551615`
3030

31-
error[E0579]: lower range bound must be less than upper
32-
--> $DIR/validate-range-endpoints.rs:35:9
31+
error: literal out of range for `i8`
32+
--> $DIR/validate-range-endpoints.rs:34:12
3333
|
3434
LL | 0..129 => {}
35-
| ^
35+
| ^^^ this value does not fit into the type `i8` whose range is `-128..=127`
3636

37-
error[E0030]: lower range bound must be less than or equal to upper
38-
--> $DIR/validate-range-endpoints.rs:37:9
37+
error: literal out of range for `i8`
38+
--> $DIR/validate-range-endpoints.rs:36:13
3939
|
4040
LL | 0..=128 => {}
41-
| ^ lower bound larger than upper bound
41+
| ^^^ this value does not fit into the type `i8` whose range is `-128..=127`
4242

43-
error[E0579]: lower range bound must be less than upper
44-
--> $DIR/validate-range-endpoints.rs:39:9
43+
error: literal out of range for `i8`
44+
--> $DIR/validate-range-endpoints.rs:38:9
4545
|
4646
LL | -129..0 => {}
47-
| ^^^^
47+
| ^^^^ this value does not fit into the type `i8` whose range is `-128..=127`
4848

49-
error[E0030]: lower range bound must be less than or equal to upper
50-
--> $DIR/validate-range-endpoints.rs:41:9
49+
error: literal out of range for `i8`
50+
--> $DIR/validate-range-endpoints.rs:40:9
5151
|
5252
LL | -10000..=-20 => {}
53-
| ^^^^^^ lower bound larger than upper bound
53+
| ^^^^^^ this value does not fit into the type `i8` whose range is `-128..=127`
5454

5555
error[E0004]: non-exhaustive patterns: `i8::MIN..=-17_i8` and `1_i8..=i8::MAX` not covered
56-
--> $DIR/validate-range-endpoints.rs:52:11
56+
--> $DIR/validate-range-endpoints.rs:51:11
5757
|
5858
LL | match 0i8 {
5959
| ^^^ patterns `i8::MIN..=-17_i8` and `1_i8..=i8::MAX` not covered
@@ -66,7 +66,7 @@ LL + i8::MIN..=-17_i8 | 1_i8..=i8::MAX => todo!()
6666
|
6767

6868
error[E0004]: non-exhaustive patterns: `i8::MIN..=-17_i8` not covered
69-
--> $DIR/validate-range-endpoints.rs:56:11
69+
--> $DIR/validate-range-endpoints.rs:55:11
7070
|
7171
LL | match 0i8 {
7272
| ^^^ pattern `i8::MIN..=-17_i8` not covered
@@ -80,5 +80,5 @@ LL + i8::MIN..=-17_i8 => todo!()
8080

8181
error: aborting due to 11 previous errors
8282

83-
Some errors have detailed explanations: E0004, E0030, E0579.
83+
Some errors have detailed explanations: E0004, E0030.
8484
For more information about an error, try `rustc --explain E0004`.

tests/ui/range/range-pattern-out-of-bounds-issue-68972.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ error: literal out of range for `u8`
22
--> $DIR/range-pattern-out-of-bounds-issue-68972.rs:5:14
33
|
44
LL | 251..257 => {}
5-
| ^^^ this value doesn't fit in `u8` whose maximum value is `255`
5+
| ^^^ this value does not fit into the type `u8` whose range is `0..=255`
66

77
error: literal out of range for `u8`
88
--> $DIR/range-pattern-out-of-bounds-issue-68972.rs:7:15
99
|
1010
LL | 251..=256 => {}
11-
| ^^^ this value doesn't fit in `u8` whose maximum value is `255`
11+
| ^^^ this value does not fit into the type `u8` whose range is `0..=255`
1212

1313
error: aborting due to 2 previous errors
1414

0 commit comments

Comments
 (0)