12
12
//! initialization and can otherwise silence errors, if
13
13
//! move analysis runs after promotion on broken MIR.
14
14
15
+ use either:: { Left , Right } ;
15
16
use rustc_hir as hir;
16
17
use rustc_middle:: mir;
17
18
use rustc_middle:: mir:: visit:: { MutVisitor , MutatingUseContext , PlaceContext , Visitor } ;
@@ -22,10 +23,11 @@ use rustc_span::Span;
22
23
23
24
use rustc_index:: { Idx , IndexSlice , IndexVec } ;
24
25
26
+ use std:: assert_matches:: assert_matches;
25
27
use std:: cell:: Cell ;
26
28
use std:: { cmp, iter, mem} ;
27
29
28
- use crate :: transform:: check_consts:: { qualifs, ConstCx } ;
30
+ use rustc_const_eval :: transform:: check_consts:: { qualifs, ConstCx } ;
29
31
30
32
/// A `MirPass` for promotion.
31
33
///
@@ -64,7 +66,7 @@ impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> {
64
66
65
67
/// State of a temporary during collection and promotion.
66
68
#[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
67
- pub enum TempState {
69
+ enum TempState {
68
70
/// No references to this temp.
69
71
Undefined ,
70
72
/// One direct assignment and any number of direct uses.
@@ -78,18 +80,11 @@ pub enum TempState {
78
80
PromotedOut ,
79
81
}
80
82
81
- impl TempState {
82
- pub fn is_promotable ( & self ) -> bool {
83
- debug ! ( "is_promotable: self={:?}" , self ) ;
84
- matches ! ( self , TempState :: Defined { .. } )
85
- }
86
- }
87
-
88
83
/// A "root candidate" for promotion, which will become the
89
84
/// returned value in a promoted MIR, unless it's a subset
90
85
/// of a larger candidate.
91
86
#[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
92
- pub struct Candidate {
87
+ struct Candidate {
93
88
location : Location ,
94
89
}
95
90
@@ -123,46 +118,43 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> {
123
118
124
119
let temp = & mut self . temps [ index] ;
125
120
debug ! ( "visit_local: temp={:?}" , temp) ;
126
- if * temp == TempState :: Undefined {
127
- match context {
121
+ * temp = match * temp {
122
+ TempState :: Undefined => match context {
128
123
PlaceContext :: MutatingUse ( MutatingUseContext :: Store )
129
124
| PlaceContext :: MutatingUse ( MutatingUseContext :: Call ) => {
130
- * temp = TempState :: Defined { location, uses : 0 , valid : Err ( ( ) ) } ;
125
+ TempState :: Defined { location, uses : 0 , valid : Err ( ( ) ) }
126
+ }
127
+ _ => TempState :: Unpromotable ,
128
+ } ,
129
+ TempState :: Defined { ref mut uses, .. } => {
130
+ // We always allow borrows, even mutable ones, as we need
131
+ // to promote mutable borrows of some ZSTs e.g., `&mut []`.
132
+ let allowed_use = match context {
133
+ PlaceContext :: MutatingUse ( MutatingUseContext :: Borrow )
134
+ | PlaceContext :: NonMutatingUse ( _) => true ,
135
+ PlaceContext :: MutatingUse ( _) | PlaceContext :: NonUse ( _) => false ,
136
+ } ;
137
+ debug ! ( "visit_local: allowed_use={:?}" , allowed_use) ;
138
+ if allowed_use {
139
+ * uses += 1 ;
131
140
return ;
132
141
}
133
- _ => { /* mark as unpromotable below */ }
142
+ TempState :: Unpromotable
134
143
}
135
- } else if let TempState :: Defined { uses, .. } = temp {
136
- // We always allow borrows, even mutable ones, as we need
137
- // to promote mutable borrows of some ZSTs e.g., `&mut []`.
138
- let allowed_use = match context {
139
- PlaceContext :: MutatingUse ( MutatingUseContext :: Borrow )
140
- | PlaceContext :: NonMutatingUse ( _) => true ,
141
- PlaceContext :: MutatingUse ( _) | PlaceContext :: NonUse ( _) => false ,
142
- } ;
143
- debug ! ( "visit_local: allowed_use={:?}" , allowed_use) ;
144
- if allowed_use {
145
- * uses += 1 ;
146
- return ;
147
- }
148
- /* mark as unpromotable below */
149
- }
150
- * temp = TempState :: Unpromotable ;
144
+ TempState :: Unpromotable | TempState :: PromotedOut => TempState :: Unpromotable ,
145
+ } ;
151
146
}
152
147
153
148
fn visit_rvalue ( & mut self , rvalue : & Rvalue < ' tcx > , location : Location ) {
154
149
self . super_rvalue ( rvalue, location) ;
155
150
156
- match * rvalue {
157
- Rvalue :: Ref ( ..) => {
158
- self . candidates . push ( Candidate { location } ) ;
159
- }
160
- _ => { }
151
+ if let Rvalue :: Ref ( ..) = * rvalue {
152
+ self . candidates . push ( Candidate { location } ) ;
161
153
}
162
154
}
163
155
}
164
156
165
- pub fn collect_temps_and_candidates < ' tcx > (
157
+ fn collect_temps_and_candidates < ' tcx > (
166
158
ccx : & ConstCx < ' _ , ' tcx > ,
167
159
) -> ( IndexVec < Local , TempState > , Vec < Candidate > ) {
168
160
let mut collector = Collector {
@@ -196,230 +188,165 @@ struct Unpromotable;
196
188
197
189
impl < ' tcx > Validator < ' _ , ' tcx > {
198
190
fn validate_candidate ( & mut self , candidate : Candidate ) -> Result < ( ) , Unpromotable > {
199
- let loc = candidate. location ;
200
- let statement = & self . body [ loc. block ] . statements [ loc. statement_index ] ;
201
- match & statement. kind {
202
- StatementKind :: Assign ( box ( _, Rvalue :: Ref ( _, kind, place) ) ) => {
203
- // We can only promote interior borrows of promotable temps (non-temps
204
- // don't get promoted anyway).
205
- self . validate_local ( place. local ) ?;
206
-
207
- // The reference operation itself must be promotable.
208
- // (Needs to come after `validate_local` to avoid ICEs.)
209
- self . validate_ref ( * kind, place) ?;
191
+ let Left ( statement) = self . body . stmt_at ( candidate. location ) else { bug ! ( ) } ;
192
+ let Some ( ( _, Rvalue :: Ref ( _, kind, place) ) ) = statement. kind . as_assign ( ) else { bug ! ( ) } ;
210
193
211
- // We do not check all the projections (they do not get promoted anyway),
212
- // but we do stay away from promoting anything involving a dereference.
213
- if place. projection . contains ( & ProjectionElem :: Deref ) {
214
- return Err ( Unpromotable ) ;
215
- }
194
+ // We can only promote interior borrows of promotable temps (non-temps
195
+ // don't get promoted anyway).
196
+ self . validate_local ( place. local ) ?;
216
197
217
- Ok ( ( ) )
218
- }
219
- _ => bug ! ( ) ,
198
+ // The reference operation itself must be promotable.
199
+ // (Needs to come after `validate_local` to avoid ICEs.)
200
+ self . validate_ref ( * kind, place) ?;
201
+
202
+ // We do not check all the projections (they do not get promoted anyway),
203
+ // but we do stay away from promoting anything involving a dereference.
204
+ if place. projection . contains ( & ProjectionElem :: Deref ) {
205
+ return Err ( Unpromotable ) ;
220
206
}
207
+
208
+ Ok ( ( ) )
221
209
}
222
210
223
211
// FIXME(eddyb) maybe cache this?
224
212
fn qualif_local < Q : qualifs:: Qualif > ( & mut self , local : Local ) -> bool {
225
- if let TempState :: Defined { location : loc, .. } = self . temps [ local] {
226
- let num_stmts = self . body [ loc. block ] . statements . len ( ) ;
227
-
228
- if loc. statement_index < num_stmts {
229
- let statement = & self . body [ loc. block ] . statements [ loc. statement_index ] ;
230
- match & statement. kind {
231
- StatementKind :: Assign ( box ( _, rhs) ) => qualifs:: in_rvalue :: < Q , _ > (
232
- self . ccx ,
233
- & mut |l| self . qualif_local :: < Q > ( l) ,
234
- rhs,
235
- ) ,
236
- _ => {
213
+ let TempState :: Defined { location : loc, .. } = self . temps [ local] else {
214
+ return false ;
215
+ } ;
216
+
217
+ let stmt_or_term = self . body . stmt_at ( loc) ;
218
+ match stmt_or_term {
219
+ Left ( statement) => {
220
+ let Some ( ( _, rhs) ) = statement. kind . as_assign ( ) else {
221
+ span_bug ! ( statement. source_info. span, "{:?} is not an assignment" , statement)
222
+ } ;
223
+ qualifs:: in_rvalue :: < Q , _ > ( self . ccx , & mut |l| self . qualif_local :: < Q > ( l) , rhs)
224
+ }
225
+ Right ( terminator) => {
226
+ assert_matches ! ( terminator. kind, TerminatorKind :: Call { .. } ) ;
227
+ let return_ty = self . body . local_decls [ local] . ty ;
228
+ Q :: in_any_value_of_ty ( self . ccx , return_ty)
229
+ }
230
+ }
231
+ }
232
+
233
+ fn validate_local ( & mut self , local : Local ) -> Result < ( ) , Unpromotable > {
234
+ let TempState :: Defined { location : loc, uses, valid } = self . temps [ local] else {
235
+ return Err ( Unpromotable ) ;
236
+ } ;
237
+
238
+ // We cannot promote things that need dropping, since the promoted value would not get
239
+ // dropped.
240
+ if self . qualif_local :: < qualifs:: NeedsDrop > ( local) {
241
+ return Err ( Unpromotable ) ;
242
+ }
243
+
244
+ if valid. is_ok ( ) {
245
+ return Ok ( ( ) ) ;
246
+ }
247
+
248
+ let ok = {
249
+ let stmt_or_term = self . body . stmt_at ( loc) ;
250
+ match stmt_or_term {
251
+ Left ( statement) => {
252
+ let Some ( ( _, rhs) ) = statement. kind . as_assign ( ) else {
237
253
span_bug ! (
238
254
statement. source_info. span,
239
255
"{:?} is not an assignment" ,
240
256
statement
241
- ) ;
242
- }
257
+ )
258
+ } ;
259
+ self . validate_rvalue ( rhs)
243
260
}
244
- } else {
245
- let terminator = self . body [ loc. block ] . terminator ( ) ;
246
- match & terminator. kind {
247
- TerminatorKind :: Call { .. } => {
248
- let return_ty = self . body . local_decls [ local] . ty ;
249
- Q :: in_any_value_of_ty ( self . ccx , return_ty)
250
- }
261
+ Right ( terminator) => match & terminator. kind {
262
+ TerminatorKind :: Call { func, args, .. } => self . validate_call ( func, args) ,
263
+ TerminatorKind :: Yield { .. } => Err ( Unpromotable ) ,
251
264
kind => {
252
265
span_bug ! ( terminator. source_info. span, "{:?} not promotable" , kind) ;
253
266
}
254
- }
267
+ } ,
255
268
}
256
- } else {
257
- false
258
- }
259
- }
269
+ } ;
260
270
261
- fn validate_local ( & mut self , local : Local ) -> Result < ( ) , Unpromotable > {
262
- if let TempState :: Defined { location : loc, uses, valid } = self . temps [ local] {
263
- // We cannot promote things that need dropping, since the promoted value
264
- // would not get dropped.
265
- if self . qualif_local :: < qualifs:: NeedsDrop > ( local) {
266
- return Err ( Unpromotable ) ;
267
- }
268
- valid. or_else ( |_| {
269
- let ok = {
270
- let block = & self . body [ loc. block ] ;
271
- let num_stmts = block. statements . len ( ) ;
272
-
273
- if loc. statement_index < num_stmts {
274
- let statement = & block. statements [ loc. statement_index ] ;
275
- match & statement. kind {
276
- StatementKind :: Assign ( box ( _, rhs) ) => self . validate_rvalue ( rhs) ,
277
- _ => {
278
- span_bug ! (
279
- statement. source_info. span,
280
- "{:?} is not an assignment" ,
281
- statement
282
- ) ;
283
- }
284
- }
285
- } else {
286
- let terminator = block. terminator ( ) ;
287
- match & terminator. kind {
288
- TerminatorKind :: Call { func, args, .. } => {
289
- self . validate_call ( func, args)
290
- }
291
- TerminatorKind :: Yield { .. } => Err ( Unpromotable ) ,
292
- kind => {
293
- span_bug ! ( terminator. source_info. span, "{:?} not promotable" , kind) ;
294
- }
295
- }
296
- }
297
- } ;
298
- self . temps [ local] = match ok {
299
- Ok ( ( ) ) => TempState :: Defined { location : loc, uses, valid : Ok ( ( ) ) } ,
300
- Err ( _) => TempState :: Unpromotable ,
301
- } ;
302
- ok
303
- } )
304
- } else {
305
- Err ( Unpromotable )
306
- }
271
+ self . temps [ local] = match ok {
272
+ Ok ( ( ) ) => TempState :: Defined { location : loc, uses, valid : Ok ( ( ) ) } ,
273
+ Err ( _) => TempState :: Unpromotable ,
274
+ } ;
275
+
276
+ ok
307
277
}
308
278
309
279
fn validate_place ( & mut self , place : PlaceRef < ' tcx > ) -> Result < ( ) , Unpromotable > {
310
- match place. last_projection ( ) {
311
- None => self . validate_local ( place. local ) ,
312
- Some ( ( place_base, elem) ) => {
313
- // Validate topmost projection, then recurse.
314
- match elem {
315
- ProjectionElem :: Deref => {
316
- let mut promotable = false ;
317
- // When a static is used by-value, that gets desugared to `*STATIC_ADDR`,
318
- // and we need to be able to promote this. So check if this deref matches
319
- // that specific pattern.
320
-
321
- // We need to make sure this is a `Deref` of a local with no further projections.
322
- // Discussion can be found at
323
- // https://github.com/rust-lang/rust/pull/74945#discussion_r463063247
324
- if let Some ( local) = place_base. as_local ( ) {
325
- if let TempState :: Defined { location, .. } = self . temps [ local] {
326
- let def_stmt = self . body [ location. block ]
327
- . statements
328
- . get ( location. statement_index ) ;
329
- if let Some ( Statement {
330
- kind :
331
- StatementKind :: Assign ( box (
332
- _,
333
- Rvalue :: Use ( Operand :: Constant ( c) ) ,
334
- ) ) ,
335
- ..
336
- } ) = def_stmt
337
- {
338
- if let Some ( did) = c. check_static_ptr ( self . tcx ) {
339
- // Evaluating a promoted may not read statics except if it got
340
- // promoted from a static (this is a CTFE check). So we
341
- // can only promote static accesses inside statics.
342
- if let Some ( hir:: ConstContext :: Static ( ..) ) = self . const_kind
343
- {
344
- if !self . tcx . is_thread_local_static ( did) {
345
- promotable = true ;
346
- }
347
- }
348
- }
349
- }
350
- }
351
- }
352
- if !promotable {
353
- return Err ( Unpromotable ) ;
354
- }
355
- }
356
- ProjectionElem :: OpaqueCast ( ..) | ProjectionElem :: Downcast ( ..) => {
357
- return Err ( Unpromotable ) ;
358
- }
280
+ let Some ( ( place_base, elem) ) = place. last_projection ( ) else {
281
+ return self . validate_local ( place. local ) ;
282
+ } ;
359
283
360
- ProjectionElem :: ConstantIndex { .. }
361
- | ProjectionElem :: Subtype ( _)
362
- | ProjectionElem :: Subslice { .. } => { }
363
-
364
- ProjectionElem :: Index ( local) => {
365
- let mut promotable = false ;
366
- // Only accept if we can predict the index and are indexing an array.
367
- let val = if let TempState :: Defined { location : loc, .. } =
368
- self . temps [ local]
369
- {
370
- let block = & self . body [ loc. block ] ;
371
- if loc. statement_index < block. statements . len ( ) {
372
- let statement = & block. statements [ loc. statement_index ] ;
373
- match & statement. kind {
374
- StatementKind :: Assign ( box (
375
- _,
376
- Rvalue :: Use ( Operand :: Constant ( c) ) ,
377
- ) ) => c. const_ . try_eval_target_usize ( self . tcx , self . param_env ) ,
378
- _ => None ,
379
- }
380
- } else {
381
- None
382
- }
383
- } else {
384
- None
385
- } ;
386
- if let Some ( idx) = val {
387
- // Determine the type of the thing we are indexing.
388
- let ty = place_base. ty ( self . body , self . tcx ) . ty ;
389
- match ty. kind ( ) {
390
- ty:: Array ( _, len) => {
391
- // It's an array; determine its length.
392
- if let Some ( len) =
393
- len. try_eval_target_usize ( self . tcx , self . param_env )
394
- {
395
- // If the index is in-bounds, go ahead.
396
- if idx < len {
397
- promotable = true ;
398
- }
399
- }
400
- }
401
- _ => { }
402
- }
403
- }
404
- if !promotable {
405
- return Err ( Unpromotable ) ;
406
- }
284
+ // Validate topmost projection, then recurse.
285
+ match elem {
286
+ // Recurse directly.
287
+ ProjectionElem :: ConstantIndex { .. }
288
+ | ProjectionElem :: Subtype ( _)
289
+ | ProjectionElem :: Subslice { .. } => { }
407
290
408
- self . validate_local ( local) ?;
409
- }
291
+ // Never recurse.
292
+ ProjectionElem :: OpaqueCast ( ..) | ProjectionElem :: Downcast ( ..) => {
293
+ return Err ( Unpromotable ) ;
294
+ }
410
295
411
- ProjectionElem :: Field ( ..) => {
412
- let base_ty = place_base. ty ( self . body , self . tcx ) . ty ;
413
- if base_ty. is_union ( ) {
414
- // No promotion of union field accesses.
415
- return Err ( Unpromotable ) ;
416
- }
417
- }
296
+ ProjectionElem :: Deref => {
297
+ // When a static is used by-value, that gets desugared to `*STATIC_ADDR`,
298
+ // and we need to be able to promote this. So check if this deref matches
299
+ // that specific pattern.
300
+
301
+ // We need to make sure this is a `Deref` of a local with no further projections.
302
+ // Discussion can be found at
303
+ // https://github.com/rust-lang/rust/pull/74945#discussion_r463063247
304
+ if let Some ( local) = place_base. as_local ( )
305
+ && let TempState :: Defined { location, .. } = self . temps [ local]
306
+ && let Left ( def_stmt) = self . body . stmt_at ( location)
307
+ && let Some ( ( _, Rvalue :: Use ( Operand :: Constant ( c) ) ) ) = def_stmt. kind . as_assign ( )
308
+ && let Some ( did) = c. check_static_ptr ( self . tcx )
309
+ // Evaluating a promoted may not read statics except if it got
310
+ // promoted from a static (this is a CTFE check). So we
311
+ // can only promote static accesses inside statics.
312
+ && let Some ( hir:: ConstContext :: Static ( ..) ) = self . const_kind
313
+ && !self . tcx . is_thread_local_static ( did)
314
+ {
315
+ // Recurse.
316
+ } else {
317
+ return Err ( Unpromotable ) ;
418
318
}
319
+ }
320
+ ProjectionElem :: Index ( local) => {
321
+ // Only accept if we can predict the index and are indexing an array.
322
+ if let TempState :: Defined { location : loc, .. } = self . temps [ local]
323
+ && let Left ( statement) = self . body . stmt_at ( loc)
324
+ && let Some ( ( _, Rvalue :: Use ( Operand :: Constant ( c) ) ) ) = statement. kind . as_assign ( )
325
+ && let Some ( idx) = c. const_ . try_eval_target_usize ( self . tcx , self . param_env )
326
+ // Determine the type of the thing we are indexing.
327
+ && let ty:: Array ( _, len) = place_base. ty ( self . body , self . tcx ) . ty . kind ( )
328
+ // It's an array; determine its length.
329
+ && let Some ( len) = len. try_eval_target_usize ( self . tcx , self . param_env )
330
+ // If the index is in-bounds, go ahead.
331
+ && idx < len
332
+ {
333
+ self . validate_local ( local) ?;
334
+ // Recurse.
335
+ } else {
336
+ return Err ( Unpromotable ) ;
337
+ }
338
+ }
419
339
420
- self . validate_place ( place_base)
340
+ ProjectionElem :: Field ( ..) => {
341
+ let base_ty = place_base. ty ( self . body , self . tcx ) . ty ;
342
+ if base_ty. is_union ( ) {
343
+ // No promotion of union field accesses.
344
+ return Err ( Unpromotable ) ;
345
+ }
421
346
}
422
347
}
348
+
349
+ self . validate_place ( place_base)
423
350
}
424
351
425
352
fn validate_operand ( & mut self , operand : & Operand < ' tcx > ) -> Result < ( ) , Unpromotable > {
@@ -676,7 +603,7 @@ impl<'tcx> Validator<'_, 'tcx> {
676
603
}
677
604
678
605
// FIXME(eddyb) remove the differences for promotability in `static`, `const`, `const fn`.
679
- pub fn validate_candidates (
606
+ fn validate_candidates (
680
607
ccx : & ConstCx < ' _ , ' _ > ,
681
608
temps : & mut IndexSlice < Local , TempState > ,
682
609
candidates : & [ Candidate ] ,
@@ -930,7 +857,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> {
930
857
}
931
858
}
932
859
933
- pub fn promote_candidates < ' tcx > (
860
+ fn promote_candidates < ' tcx > (
934
861
body : & mut Body < ' tcx > ,
935
862
tcx : TyCtxt < ' tcx > ,
936
863
mut temps : IndexVec < Local , TempState > ,
0 commit comments