25
25
use rustc:: mir:: * ;
26
26
use rustc:: mir:: visit:: { PlaceContext , MutVisitor , Visitor } ;
27
27
use rustc:: mir:: traversal:: ReversePostorder ;
28
- use rustc:: ty:: TyCtxt ;
28
+ use rustc:: ty:: { self , TyCtxt } ;
29
29
use syntax_pos:: Span ;
30
30
31
31
use rustc_data_structures:: indexed_vec:: { IndexVec , Idx } ;
32
32
33
- use std:: iter;
34
- use std:: mem;
35
- use std:: usize;
33
+ use std:: { cmp, iter, mem, usize} ;
36
34
37
35
/// State of a temporary during collection and promotion.
38
36
#[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
@@ -150,9 +148,11 @@ pub fn collect_temps(mir: &Mir, rpo: &mut ReversePostorder) -> IndexVec<Local, T
150
148
}
151
149
152
150
struct Promoter < ' a , ' tcx : ' a > {
151
+ tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
153
152
source : & ' a mut Mir < ' tcx > ,
154
153
promoted : Mir < ' tcx > ,
155
154
temps : & ' a mut IndexVec < Local , TempState > ,
155
+ extra_statements : & ' a mut Vec < ( Location , Statement < ' tcx > ) > ,
156
156
157
157
/// If true, all nested temps are also kept in the
158
158
/// source MIR, not moved to the promoted MIR.
@@ -288,38 +288,90 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
288
288
}
289
289
290
290
fn promote_candidate ( mut self , candidate : Candidate ) {
291
- let span = self . promoted . span ;
292
- let new_operand = Operand :: Constant ( box Constant {
293
- span,
294
- ty : self . promoted . return_ty ( ) ,
295
- literal : Literal :: Promoted {
291
+ let mut rvalue = {
292
+ let promoted = & mut self . promoted ;
293
+ let literal = Literal :: Promoted {
296
294
index : Promoted :: new ( self . source . promoted . len ( ) )
297
- }
298
- } ) ;
299
- let mut rvalue = match candidate {
300
- Candidate :: Ref ( Location { block : bb, statement_index : stmt_idx } ) => {
301
- let ref mut statement = self . source [ bb] . statements [ stmt_idx] ;
302
- match statement. kind {
303
- StatementKind :: Assign ( _, ref mut rvalue) => {
304
- mem:: replace ( rvalue, Rvalue :: Use ( new_operand) )
295
+ } ;
296
+ let operand = |ty, span| {
297
+ promoted. span = span;
298
+ promoted. local_decls [ RETURN_PLACE ] =
299
+ LocalDecl :: new_return_place ( ty, span) ;
300
+ Operand :: Constant ( box Constant {
301
+ span,
302
+ ty,
303
+ literal
304
+ } )
305
+ } ;
306
+ let ( blocks, local_decls) = self . source . basic_blocks_and_local_decls_mut ( ) ;
307
+ match candidate {
308
+ Candidate :: Ref ( loc) => {
309
+ let ref mut statement = blocks[ loc. block ] . statements [ loc. statement_index ] ;
310
+ match statement. kind {
311
+ StatementKind :: Assign ( _, Rvalue :: Ref ( r, bk, ref mut place) ) => {
312
+ // Find the underlying local for this (necessarilly interior) borrow.
313
+ // HACK(eddyb) using a recursive function because of mutable borrows.
314
+ fn interior_base < ' a , ' tcx > ( place : & ' a mut Place < ' tcx > )
315
+ -> & ' a mut Place < ' tcx > {
316
+ if let Place :: Projection ( ref mut proj) = * place {
317
+ assert_ne ! ( proj. elem, ProjectionElem :: Deref ) ;
318
+ return interior_base ( & mut proj. base ) ;
319
+ }
320
+ place
321
+ }
322
+ let place = interior_base ( place) ;
323
+
324
+ let ty = place. ty ( local_decls, self . tcx ) . to_ty ( self . tcx ) ;
325
+ let ref_ty = self . tcx . mk_ref ( r,
326
+ ty:: TypeAndMut {
327
+ ty,
328
+ mutbl : bk. to_mutbl_lossy ( )
329
+ }
330
+ ) ;
331
+ let span = statement. source_info . span ;
332
+
333
+ // Create a temp to hold the promoted reference.
334
+ // This is because `*r` requires `r` to be a local,
335
+ // otherwise we would use the `promoted` directly.
336
+ let mut promoted_ref = LocalDecl :: new_temp ( ref_ty, span) ;
337
+ promoted_ref. source_info = statement. source_info ;
338
+ let promoted_ref = local_decls. push ( promoted_ref) ;
339
+ assert_eq ! ( self . temps. push( TempState :: Unpromotable ) , promoted_ref) ;
340
+ self . extra_statements . push ( ( loc, Statement {
341
+ source_info : statement. source_info ,
342
+ kind : StatementKind :: Assign (
343
+ Place :: Local ( promoted_ref) ,
344
+ Rvalue :: Use ( operand ( ref_ty, span) ) ,
345
+ )
346
+ } ) ) ;
347
+ let promoted_place = Place :: Local ( promoted_ref) . deref ( ) ;
348
+
349
+ Rvalue :: Ref ( r, bk, mem:: replace ( place, promoted_place) )
350
+ }
351
+ _ => bug ! ( )
305
352
}
306
- _ => bug ! ( )
307
353
}
308
- }
309
- Candidate :: Argument { bb, index } => {
310
- match self . source [ bb] . terminator_mut ( ) . kind {
311
- TerminatorKind :: Call { ref mut args, .. } => {
312
- Rvalue :: Use ( mem:: replace ( & mut args[ index] , new_operand) )
354
+ Candidate :: Argument { bb, index } => {
355
+ let terminator = blocks[ bb] . terminator_mut ( ) ;
356
+ match terminator. kind {
357
+ TerminatorKind :: Call { ref mut args, .. } => {
358
+ let ty = args[ index] . ty ( local_decls, self . tcx ) ;
359
+ let span = terminator. source_info . span ;
360
+ Rvalue :: Use ( mem:: replace ( & mut args[ index] , operand ( ty, span) ) )
361
+ }
362
+ _ => bug ! ( )
313
363
}
314
- _ => bug ! ( )
315
364
}
316
365
}
317
366
} ;
367
+
368
+ assert_eq ! ( self . new_block( ) , START_BLOCK ) ;
318
369
self . visit_rvalue ( & mut rvalue, Location {
319
370
block : BasicBlock :: new ( 0 ) ,
320
371
statement_index : usize:: MAX
321
372
} ) ;
322
373
374
+ let span = self . promoted . span ;
323
375
self . assign ( RETURN_PLACE , rvalue, span) ;
324
376
self . source . promoted . push ( self . promoted ) ;
325
377
}
@@ -343,43 +395,29 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
343
395
candidates : Vec < Candidate > ) {
344
396
// Visit candidates in reverse, in case they're nested.
345
397
debug ! ( "promote_candidates({:?})" , candidates) ;
398
+
399
+ let mut extra_statements = vec ! [ ] ;
346
400
for candidate in candidates. into_iter ( ) . rev ( ) {
347
- let ( span, ty) = match candidate {
348
- Candidate :: Ref ( Location { block : bb, statement_index : stmt_idx } ) => {
349
- let statement = & mir[ bb] . statements [ stmt_idx] ;
350
- let dest = match statement. kind {
351
- StatementKind :: Assign ( ref dest, _) => dest,
352
- _ => {
353
- span_bug ! ( statement. source_info. span,
354
- "expected assignment to promote" ) ;
355
- }
356
- } ;
357
- if let Place :: Local ( index) = * dest {
358
- if temps[ index] == TempState :: PromotedOut {
359
- // Already promoted.
360
- continue ;
401
+ match candidate {
402
+ Candidate :: Ref ( Location { block, statement_index } ) => {
403
+ match mir[ block] . statements [ statement_index] . kind {
404
+ StatementKind :: Assign ( Place :: Local ( local) , _) => {
405
+ if temps[ local] == TempState :: PromotedOut {
406
+ // Already promoted.
407
+ continue ;
408
+ }
361
409
}
410
+ _ => { }
362
411
}
363
- ( statement. source_info . span , dest. ty ( mir, tcx) . to_ty ( tcx) )
364
- }
365
- Candidate :: Argument { bb, index } => {
366
- let terminator = mir[ bb] . terminator ( ) ;
367
- let ty = match terminator. kind {
368
- TerminatorKind :: Call { ref args, .. } => {
369
- args[ index] . ty ( mir, tcx)
370
- }
371
- _ => {
372
- span_bug ! ( terminator. source_info. span,
373
- "expected call argument to promote" ) ;
374
- }
375
- } ;
376
- ( terminator. source_info . span , ty)
377
412
}
378
- } ;
413
+ Candidate :: Argument { .. } => { }
414
+ }
415
+
379
416
380
- // Declare return place local
381
- let initial_locals = iter:: once ( LocalDecl :: new_return_place ( ty, span) )
382
- . collect ( ) ;
417
+ // Declare return place local so that `Mir::new` doesn't complain.
418
+ let initial_locals = iter:: once (
419
+ LocalDecl :: new_return_place ( tcx. types . never , mir. span )
420
+ ) . collect ( ) ;
383
421
384
422
let mut promoter = Promoter {
385
423
promoted : Mir :: new (
@@ -393,16 +431,24 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
393
431
initial_locals,
394
432
0 ,
395
433
vec ! [ ] ,
396
- span
434
+ mir . span
397
435
) ,
436
+ tcx,
398
437
source : mir,
399
438
temps : & mut temps,
439
+ extra_statements : & mut extra_statements,
400
440
keep_original : false
401
441
} ;
402
- assert_eq ! ( promoter. new_block( ) , START_BLOCK ) ;
403
442
promoter. promote_candidate ( candidate) ;
404
443
}
405
444
445
+ // Insert each of `extra_statements` before its indicated location, which
446
+ // has to be done in reverse location order, to not invalidate the rest.
447
+ extra_statements. sort_by_key ( |& ( loc, _) | cmp:: Reverse ( loc) ) ;
448
+ for ( loc, statement) in extra_statements {
449
+ mir[ loc. block ] . statements . insert ( loc. statement_index , statement) ;
450
+ }
451
+
406
452
// Eliminate assignments to, and drops of promoted temps.
407
453
let promoted = |index : Local | temps[ index] == TempState :: PromotedOut ;
408
454
for block in mir. basic_blocks_mut ( ) {
0 commit comments