@@ -20,15 +20,15 @@ use rustc_index::Idx;
20
20
use rustc_middle:: mir:: interpret:: {
21
21
ErrorHandled , GlobalId , LitToConstError , LitToConstInput , Scalar ,
22
22
} ;
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 } ;
25
24
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
+ } ;
30
30
use rustc_span:: { ErrorGuaranteed , Span , Symbol } ;
31
- use rustc_target:: abi:: FieldIdx ;
31
+ use rustc_target:: abi:: { FieldIdx , Integer } ;
32
32
33
33
use std:: cmp:: Ordering ;
34
34
@@ -111,6 +111,59 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
111
111
}
112
112
}
113
113
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
+
114
167
fn lower_pattern_range (
115
168
& mut self ,
116
169
lo_expr : Option < & ' tcx hir:: Expr < ' tcx > > ,
@@ -155,29 +208,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
155
208
}
156
209
// `x..y` where `x >= y`, or `x..=y` where `x > y`. The range is empty => error.
157
210
_ => {
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) ?;
181
214
let e = match end {
182
215
RangeEnd :: Included => {
183
216
self . tcx . sess . emit_err ( LowerRangeBoundMustBeLessThanOrEqualToUpper {
@@ -219,7 +252,6 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
219
252
220
253
hir:: PatKind :: Range ( ref lo_expr, ref hi_expr, end) => {
221
254
let ( lo_expr, hi_expr) = ( lo_expr. as_deref ( ) , hi_expr. as_deref ( ) ) ;
222
- let span = lo_expr. map_or ( span, |e| e. span ) ;
223
255
// FIXME?: returning `_` can cause inaccurate "unreachable" warnings. This can be
224
256
// fixed by returning `PatKind::Const(ConstKind::Error(...))` if #115937 gets
225
257
// merged.
0 commit comments