@@ -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 } ;
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 } ;
32
32
33
33
use std:: cmp:: Ordering ;
34
34
@@ -85,127 +85,159 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
85
85
)
86
86
}
87
87
88
- fn lower_range_expr (
88
+ fn lower_pattern_range_endpoint (
89
89
& 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) )
95
110
}
96
- kind => ( kind, None ) ,
97
111
}
98
112
}
99
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
+
100
167
fn lower_pattern_range (
101
168
& 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 > > ,
105
171
end : RangeEnd ,
172
+ ty : Ty < ' tcx > ,
106
173
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
+ } ) ;
110
193
assert_eq ! ( lo. ty( ) , ty) ;
111
194
assert_eq ! ( hi. ty( ) , ty) ;
195
+
112
196
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) {
122
198
// `x..y` where `x < y`.
123
199
// Non-empty because the range includes at least `x`.
124
200
( RangeEnd :: Excluded , Some ( Ordering :: Less ) ) => {
125
201
PatKind :: Range ( Box :: new ( PatRange { lo, hi, end } ) )
126
202
}
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
- }
152
203
// `x..=y` where `x == y`.
153
204
( RangeEnd :: Included , Some ( Ordering :: Equal ) ) => PatKind :: Constant { value : lo } ,
154
205
// `x..=y` where `x < y`.
155
206
( RangeEnd :: Included , Some ( Ordering :: Less ) ) => {
156
207
PatKind :: Range ( Box :: new ( PatRange { lo, hi, end } ) )
157
208
}
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
+ } )
168
220
}
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 } )
176
223
}
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) ;
185
226
}
186
- }
187
- }
227
+ } ;
188
228
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
+ } ;
206
238
}
207
- _ => None ,
208
239
}
240
+ Ok ( kind)
209
241
}
210
242
211
243
#[ instrument( skip( self ) , level = "debug" ) ]
@@ -220,37 +252,10 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
220
252
221
253
hir:: PatKind :: Range ( ref lo_expr, ref hi_expr, end) => {
222
254
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 )
254
259
}
255
260
256
261
hir:: PatKind :: Path ( ref qpath) => {
0 commit comments