@@ -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 } ;
30- use rustc_span:: { Span , Symbol } ;
31- use rustc_target:: abi:: FieldIdx ;
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+ use rustc_span:: { ErrorGuaranteed , Span , Symbol } ;
31+ use rustc_target:: abi:: { FieldIdx , Integer } ;
3232
3333use std:: cmp:: Ordering ;
3434
@@ -85,127 +85,159 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
8585 )
8686 }
8787
88- fn lower_range_expr (
88+ fn lower_pattern_range_endpoint (
8989 & mut self ,
90- expr : & ' tcx hir:: Expr < ' tcx > ,
91- ) -> ( PatKind < ' tcx > , Option < Ascription < ' tcx > > ) {
92- match self . lower_lit ( expr) {
93- PatKind :: AscribeUserType { ascription, subpattern : box Pat { kind, .. } } => {
94- ( kind, Some ( ascription) )
90+ expr : Option < & ' tcx hir:: Expr < ' tcx > > ,
91+ ) -> Result < ( Option < mir:: Const < ' tcx > > , Option < Ascription < ' tcx > > ) , ErrorGuaranteed > {
92+ match expr {
93+ None => Ok ( ( None , None ) ) ,
94+ Some ( expr) => {
95+ let ( kind, ascr) = match self . lower_lit ( expr) {
96+ PatKind :: AscribeUserType { ascription, subpattern : box Pat { kind, .. } } => {
97+ ( kind, Some ( ascription) )
98+ }
99+ kind => ( kind, None ) ,
100+ } ;
101+ let value = if let PatKind :: Constant { value } = kind {
102+ value
103+ } else {
104+ let msg = format ! (
105+ "found bad range pattern endpoint `{expr:?}` outside of error recovery"
106+ ) ;
107+ return Err ( self . tcx . sess . delay_span_bug ( expr. span , msg) ) ;
108+ } ;
109+ Ok ( ( Some ( value) , ascr) )
95110 }
96- kind => ( kind, None ) ,
97111 }
98112 }
99113
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+
100167 fn lower_pattern_range (
101168 & mut self ,
102- ty : Ty < ' tcx > ,
103- lo : mir:: Const < ' tcx > ,
104- hi : mir:: Const < ' tcx > ,
169+ lo_expr : Option < & ' tcx hir:: Expr < ' tcx > > ,
170+ hi_expr : Option < & ' tcx hir:: Expr < ' tcx > > ,
105171 end : RangeEnd ,
172+ ty : Ty < ' tcx > ,
106173 span : Span ,
107- lo_expr : Option < & hir:: Expr < ' tcx > > ,
108- hi_expr : Option < & hir:: Expr < ' tcx > > ,
109- ) -> PatKind < ' tcx > {
174+ ) -> Result < PatKind < ' tcx > , ErrorGuaranteed > {
175+ if lo_expr. is_none ( ) && hi_expr. is_none ( ) {
176+ let msg = format ! ( "found twice-open range pattern (`..`) outside of error recovery" ) ;
177+ return Err ( self . tcx . sess . delay_span_bug ( span, msg) ) ;
178+ }
179+
180+ let ( lo, lo_ascr) = self . lower_pattern_range_endpoint ( lo_expr) ?;
181+ let ( hi, hi_ascr) = self . lower_pattern_range_endpoint ( hi_expr) ?;
182+
183+ let lo = lo. unwrap_or_else ( || {
184+ // Unwrap is ok because the type is known to be numeric.
185+ let lo = ty. numeric_min_val ( self . tcx ) . unwrap ( ) ;
186+ mir:: Const :: from_ty_const ( lo, self . tcx )
187+ } ) ;
188+ let hi = hi. unwrap_or_else ( || {
189+ // Unwrap is ok because the type is known to be numeric.
190+ let hi = ty. numeric_max_val ( self . tcx ) . unwrap ( ) ;
191+ mir:: Const :: from_ty_const ( hi, self . tcx )
192+ } ) ;
110193 assert_eq ! ( lo. ty( ) , ty) ;
111194 assert_eq ! ( hi. ty( ) , ty) ;
195+
112196 let cmp = compare_const_vals ( self . tcx , lo, hi, self . param_env ) ;
113- let max = || {
114- self . tcx
115- . layout_of ( self . param_env . with_reveal_all_normalized ( self . tcx ) . and ( ty) )
116- . ok ( )
117- . unwrap ( )
118- . size
119- . unsigned_int_max ( )
120- } ;
121- match ( end, cmp) {
197+ let mut kind = match ( end, cmp) {
122198 // `x..y` where `x < y`.
123199 // Non-empty because the range includes at least `x`.
124200 ( RangeEnd :: Excluded , Some ( Ordering :: Less ) ) => {
125201 PatKind :: Range ( Box :: new ( PatRange { lo, hi, end } ) )
126202 }
127- // `x..y` where `x >= y`. The range is empty => error.
128- ( RangeEnd :: Excluded , _) => {
129- let mut lower_overflow = false ;
130- let mut higher_overflow = false ;
131- if let Some ( hir:: Expr { kind : hir:: ExprKind :: Lit ( lit) , .. } ) = lo_expr
132- && let rustc_ast:: ast:: LitKind :: Int ( val, _) = lit. node
133- {
134- if lo. eval_bits ( self . tcx , self . param_env ) != val {
135- lower_overflow = true ;
136- self . tcx . sess . emit_err ( LiteralOutOfRange { span : lit. span , ty, max : max ( ) } ) ;
137- }
138- }
139- if let Some ( hir:: Expr { kind : hir:: ExprKind :: Lit ( lit) , .. } ) = hi_expr
140- && let rustc_ast:: ast:: LitKind :: Int ( val, _) = lit. node
141- {
142- if hi. eval_bits ( self . tcx , self . param_env ) != val {
143- higher_overflow = true ;
144- self . tcx . sess . emit_err ( LiteralOutOfRange { span : lit. span , ty, max : max ( ) } ) ;
145- }
146- }
147- if !lower_overflow && !higher_overflow {
148- self . tcx . sess . emit_err ( LowerRangeBoundMustBeLessThanUpper { span } ) ;
149- }
150- PatKind :: Wild
151- }
152203 // `x..=y` where `x == y`.
153204 ( RangeEnd :: Included , Some ( Ordering :: Equal ) ) => PatKind :: Constant { value : lo } ,
154205 // `x..=y` where `x < y`.
155206 ( RangeEnd :: Included , Some ( Ordering :: Less ) ) => {
156207 PatKind :: Range ( Box :: new ( PatRange { lo, hi, end } ) )
157208 }
158- // `x..=y` where `x > y` hence the range is empty => error.
159- ( RangeEnd :: Included , _) => {
160- let mut lower_overflow = false ;
161- let mut higher_overflow = false ;
162- if let Some ( hir:: Expr { kind : hir:: ExprKind :: Lit ( lit) , .. } ) = lo_expr
163- && let rustc_ast:: ast:: LitKind :: Int ( val, _) = lit. node
164- {
165- if lo. eval_bits ( self . tcx , self . param_env ) != val {
166- lower_overflow = true ;
167- self . tcx . sess . emit_err ( LiteralOutOfRange { span : lit. span , ty, max : max ( ) } ) ;
209+ // `x..y` where `x >= y`, or `x..=y` where `x > y`. The range is empty => error.
210+ _ => {
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) ?;
214+ let e = match end {
215+ RangeEnd :: Included => {
216+ self . tcx . sess . emit_err ( LowerRangeBoundMustBeLessThanOrEqualToUpper {
217+ span,
218+ teach : self . tcx . sess . teach ( & error_code ! ( E0030 ) ) . then_some ( ( ) ) ,
219+ } )
168220 }
169- }
170- if let Some ( hir:: Expr { kind : hir:: ExprKind :: Lit ( lit) , .. } ) = hi_expr
171- && let rustc_ast:: ast:: LitKind :: Int ( val, _) = lit. node
172- {
173- if hi. eval_bits ( self . tcx , self . param_env ) != val {
174- higher_overflow = true ;
175- self . tcx . sess . emit_err ( LiteralOutOfRange { span : lit. span , ty, max : max ( ) } ) ;
221+ RangeEnd :: Excluded => {
222+ self . tcx . sess . emit_err ( LowerRangeBoundMustBeLessThanUpper { span } )
176223 }
177- }
178- if !lower_overflow && !higher_overflow {
179- self . tcx . sess . emit_err ( LowerRangeBoundMustBeLessThanOrEqualToUpper {
180- span,
181- teach : self . tcx . sess . teach ( & error_code ! ( E0030 ) ) . then_some ( ( ) ) ,
182- } ) ;
183- }
184- PatKind :: Wild
224+ } ;
225+ return Err ( e) ;
185226 }
186- }
187- }
227+ } ;
188228
189- fn normalize_range_pattern_ends (
190- & self ,
191- ty : Ty < ' tcx > ,
192- lo : Option < & PatKind < ' tcx > > ,
193- hi : Option < & PatKind < ' tcx > > ,
194- ) -> Option < ( mir:: Const < ' tcx > , mir:: Const < ' tcx > ) > {
195- match ( lo, hi) {
196- ( Some ( PatKind :: Constant { value : lo } ) , Some ( PatKind :: Constant { value : hi } ) ) => {
197- Some ( ( * lo, * hi) )
198- }
199- ( Some ( PatKind :: Constant { value : lo } ) , None ) => {
200- let hi = ty. numeric_max_val ( self . tcx ) ?;
201- Some ( ( * lo, mir:: Const :: from_ty_const ( hi, self . tcx ) ) )
202- }
203- ( None , Some ( PatKind :: Constant { value : hi } ) ) => {
204- let lo = ty. numeric_min_val ( self . tcx ) ?;
205- Some ( ( mir:: Const :: from_ty_const ( lo, self . tcx ) , * hi) )
229+ // If we are handling a range with associated constants (e.g.
230+ // `Foo::<'a>::A..=Foo::B`), we need to put the ascriptions for the associated
231+ // constants somewhere. Have them on the range pattern.
232+ for ascr in [ lo_ascr, hi_ascr] {
233+ if let Some ( ascription) = ascr {
234+ kind = PatKind :: AscribeUserType {
235+ ascription,
236+ subpattern : Box :: new ( Pat { span, ty, kind } ) ,
237+ } ;
206238 }
207- _ => None ,
208239 }
240+ Ok ( kind)
209241 }
210242
211243 #[ instrument( skip( self ) , level = "debug" ) ]
@@ -220,37 +252,10 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
220252
221253 hir:: PatKind :: Range ( ref lo_expr, ref hi_expr, end) => {
222254 let ( lo_expr, hi_expr) = ( lo_expr. as_deref ( ) , hi_expr. as_deref ( ) ) ;
223- let lo_span = lo_expr. map_or ( pat. span , |e| e. span ) ;
224- let lo = lo_expr. map ( |e| self . lower_range_expr ( e) ) ;
225- let hi = hi_expr. map ( |e| self . lower_range_expr ( e) ) ;
226-
227- let ( lp, hp) = ( lo. as_ref ( ) . map ( |( x, _) | x) , hi. as_ref ( ) . map ( |( x, _) | x) ) ;
228- let mut kind = match self . normalize_range_pattern_ends ( ty, lp, hp) {
229- Some ( ( lc, hc) ) => {
230- self . lower_pattern_range ( ty, lc, hc, end, lo_span, lo_expr, hi_expr)
231- }
232- None => {
233- let msg = format ! (
234- "found bad range pattern `{:?}` outside of error recovery" ,
235- ( & lo, & hi) ,
236- ) ;
237- self . tcx . sess . delay_span_bug ( pat. span , msg) ;
238- PatKind :: Wild
239- }
240- } ;
241-
242- // If we are handling a range with associated constants (e.g.
243- // `Foo::<'a>::A..=Foo::B`), we need to put the ascriptions for the associated
244- // constants somewhere. Have them on the range pattern.
245- for end in & [ lo, hi] {
246- if let Some ( ( _, Some ( ascription) ) ) = end {
247- let subpattern = Box :: new ( Pat { span : pat. span , ty, kind } ) ;
248- kind =
249- PatKind :: AscribeUserType { ascription : ascription. clone ( ) , subpattern } ;
250- }
251- }
252-
253- kind
255+ // FIXME?: returning `_` can cause inaccurate "unreachable" warnings. This can be
256+ // fixed by returning `PatKind::Const(ConstKind::Error(...))` if #115937 gets
257+ // merged.
258+ self . lower_pattern_range ( lo_expr, hi_expr, end, ty, span) . unwrap_or ( PatKind :: Wild )
254259 }
255260
256261 hir:: PatKind :: Path ( ref qpath) => {
0 commit comments