@@ -20,15 +20,15 @@ use rustc_index::Idx;
2020use 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 } ;
2524use 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+ } ;
3030use rustc_span:: { ErrorGuaranteed , Span , Symbol } ;
31- use rustc_target:: abi:: FieldIdx ;
31+ use rustc_target:: abi:: { FieldIdx , Integer } ;
3232
3333use 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.
0 commit comments