2525use rustc:: mir:: * ;
2626use rustc:: mir:: visit:: { PlaceContext , MutVisitor , Visitor } ;
2727use rustc:: mir:: traversal:: ReversePostorder ;
28- use rustc:: ty:: TyCtxt ;
28+ use rustc:: ty:: { self , TyCtxt } ;
2929use syntax_pos:: Span ;
3030
3131use rustc_data_structures:: indexed_vec:: { IndexVec , Idx } ;
3232
33- use std:: iter;
34- use std:: mem;
35- use std:: usize;
33+ use std:: { cmp, iter, mem, usize} ;
3634
3735/// State of a temporary during collection and promotion.
3836#[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
@@ -150,9 +148,11 @@ pub fn collect_temps(mir: &Mir, rpo: &mut ReversePostorder) -> IndexVec<Local, T
150148}
151149
152150struct Promoter < ' a , ' tcx : ' a > {
151+ tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
153152 source : & ' a mut Mir < ' tcx > ,
154153 promoted : Mir < ' tcx > ,
155154 temps : & ' a mut IndexVec < Local , TempState > ,
155+ extra_statements : & ' a mut Vec < ( Location , Statement < ' tcx > ) > ,
156156
157157 /// If true, all nested temps are also kept in the
158158 /// source MIR, not moved to the promoted MIR.
@@ -288,38 +288,90 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
288288 }
289289
290290 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 {
296294 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 ! ( )
305352 }
306- _ => bug ! ( )
307353 }
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 ! ( )
313363 }
314- _ => bug ! ( )
315364 }
316365 }
317366 } ;
367+
368+ assert_eq ! ( self . new_block( ) , START_BLOCK ) ;
318369 self . visit_rvalue ( & mut rvalue, Location {
319370 block : BasicBlock :: new ( 0 ) ,
320371 statement_index : usize:: MAX
321372 } ) ;
322373
374+ let span = self . promoted . span ;
323375 self . assign ( RETURN_PLACE , rvalue, span) ;
324376 self . source . promoted . push ( self . promoted ) ;
325377 }
@@ -343,43 +395,29 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
343395 candidates : Vec < Candidate > ) {
344396 // Visit candidates in reverse, in case they're nested.
345397 debug ! ( "promote_candidates({:?})" , candidates) ;
398+
399+ let mut extra_statements = vec ! [ ] ;
346400 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+ }
361409 }
410+ _ => { }
362411 }
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)
377412 }
378- } ;
413+ Candidate :: Argument { .. } => { }
414+ }
415+
379416
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 ( ) ;
383421
384422 let mut promoter = Promoter {
385423 promoted : Mir :: new (
@@ -393,16 +431,24 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
393431 initial_locals,
394432 0 ,
395433 vec ! [ ] ,
396- span
434+ mir . span
397435 ) ,
436+ tcx,
398437 source : mir,
399438 temps : & mut temps,
439+ extra_statements : & mut extra_statements,
400440 keep_original : false
401441 } ;
402- assert_eq ! ( promoter. new_block( ) , START_BLOCK ) ;
403442 promoter. promote_candidate ( candidate) ;
404443 }
405444
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+
406452 // Eliminate assignments to, and drops of promoted temps.
407453 let promoted = |index : Local | temps[ index] == TempState :: PromotedOut ;
408454 for block in mir. basic_blocks_mut ( ) {
0 commit comments