@@ -4,7 +4,7 @@ use rustc_hir::lang_items::LangItem;
44use rustc_middle:: mir:: * ;
55use rustc_middle:: ty:: query:: Providers ;
66use rustc_middle:: ty:: subst:: { InternalSubsts , Subst } ;
7- use rustc_middle:: ty:: { self , EarlyBinder , Ty , TyCtxt } ;
7+ use rustc_middle:: ty:: { self , EarlyBinder , GeneratorSubsts , Ty , TyCtxt } ;
88use rustc_target:: abi:: VariantIdx ;
99
1010use rustc_index:: vec:: { Idx , IndexVec } ;
@@ -323,6 +323,9 @@ fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -
323323 builder. tuple_like_shim ( dest, src, substs. as_closure ( ) . upvar_tys ( ) )
324324 }
325325 ty:: Tuple ( ..) => builder. tuple_like_shim ( dest, src, self_ty. tuple_fields ( ) ) ,
326+ ty:: Generator ( gen_def_id, substs, hir:: Movability :: Movable ) => {
327+ builder. generator_shim ( dest, src, * gen_def_id, substs. as_generator ( ) )
328+ }
326329 _ => bug ! ( "clone shim for `{:?}` which is not `Copy` and is not an aggregate" , self_ty) ,
327330 } ;
328331
@@ -388,7 +391,7 @@ impl<'tcx> CloneShimBuilder<'tcx> {
388391 /// offset=0 will give you the index of the next BasicBlock,
389392 /// offset=1 will give the index of the next-to-next block,
390393 /// offset=-1 will give you the index of the last-created block
391- fn block_index_offset ( & mut self , offset : usize ) -> BasicBlock {
394+ fn block_index_offset ( & self , offset : usize ) -> BasicBlock {
392395 BasicBlock :: new ( self . blocks . len ( ) + offset)
393396 }
394397
@@ -461,49 +464,106 @@ impl<'tcx> CloneShimBuilder<'tcx> {
461464 ) ;
462465 }
463466
464- fn tuple_like_shim < I > ( & mut self , dest : Place < ' tcx > , src : Place < ' tcx > , tys : I )
467+ fn clone_fields < I > (
468+ & mut self ,
469+ dest : Place < ' tcx > ,
470+ src : Place < ' tcx > ,
471+ target : BasicBlock ,
472+ mut unwind : BasicBlock ,
473+ tys : I ,
474+ ) -> BasicBlock
465475 where
466476 I : IntoIterator < Item = Ty < ' tcx > > ,
467477 {
468- let mut previous_field = None ;
478+ // For an iterator of length n, create 2*n + 1 blocks.
469479 for ( i, ity) in tys. into_iter ( ) . enumerate ( ) {
480+ // Each iteration creates two blocks, referred to here as block 2*i and block 2*i + 1.
481+ //
482+ // Block 2*i attempts to clone the field. If successful it branches to 2*i + 2 (the
483+ // next clone block). If unsuccessful it branches to the previous unwind block, which
484+ // is initially the `unwind` argument passed to this function.
485+ //
486+ // Block 2*i + 1 is the unwind block for this iteration. It drops the cloned value
487+ // created by block 2*i. We store this block in `unwind` so that the next clone block
488+ // will unwind to it if cloning fails.
489+
470490 let field = Field :: new ( i) ;
471491 let src_field = self . tcx . mk_place_field ( src, field, ity) ;
472492
473493 let dest_field = self . tcx . mk_place_field ( dest, field, ity) ;
474494
475- // #(2i + 1) is the cleanup block for the previous clone operation
476- let cleanup_block = self . block_index_offset ( 1 ) ;
477- // #(2i + 2) is the next cloning block
478- // (or the Return terminator if this is the last block)
495+ let next_unwind = self . block_index_offset ( 1 ) ;
479496 let next_block = self . block_index_offset ( 2 ) ;
497+ self . make_clone_call ( dest_field, src_field, ity, next_block, unwind) ;
498+ self . block (
499+ vec ! [ ] ,
500+ TerminatorKind :: Drop { place : dest_field, target : unwind, unwind : None } ,
501+ true ,
502+ ) ;
503+ unwind = next_unwind;
504+ }
505+ // If all clones succeed then we end up here.
506+ self . block ( vec ! [ ] , TerminatorKind :: Goto { target } , false ) ;
507+ unwind
508+ }
480509
481- // BB #(2i)
482- // `dest.i = Clone::clone(&src.i);`
483- // Goto #(2i + 2) if ok, #(2i + 1) if unwinding happens.
484- self . make_clone_call ( dest_field, src_field, ity, next_block, cleanup_block) ;
485-
486- // BB #(2i + 1) (cleanup)
487- if let Some ( ( previous_field, previous_cleanup) ) = previous_field. take ( ) {
488- // Drop previous field and goto previous cleanup block.
489- self . block (
490- vec ! [ ] ,
491- TerminatorKind :: Drop {
492- place : previous_field,
493- target : previous_cleanup,
494- unwind : None ,
495- } ,
496- true ,
497- ) ;
498- } else {
499- // Nothing to drop, just resume.
500- self . block ( vec ! [ ] , TerminatorKind :: Resume , true ) ;
501- }
510+ fn tuple_like_shim < I > ( & mut self , dest : Place < ' tcx > , src : Place < ' tcx > , tys : I )
511+ where
512+ I : IntoIterator < Item = Ty < ' tcx > > ,
513+ {
514+ self . block ( vec ! [ ] , TerminatorKind :: Goto { target : self . block_index_offset ( 3 ) } , false ) ;
515+ let unwind = self . block ( vec ! [ ] , TerminatorKind :: Resume , true ) ;
516+ let target = self . block ( vec ! [ ] , TerminatorKind :: Return , false ) ;
502517
503- previous_field = Some ( ( dest_field , cleanup_block ) ) ;
504- }
518+ let _final_cleanup_block = self . clone_fields ( dest , src , target , unwind , tys ) ;
519+ }
505520
506- self . block ( vec ! [ ] , TerminatorKind :: Return , false ) ;
521+ fn generator_shim (
522+ & mut self ,
523+ dest : Place < ' tcx > ,
524+ src : Place < ' tcx > ,
525+ gen_def_id : DefId ,
526+ substs : GeneratorSubsts < ' tcx > ,
527+ ) {
528+ self . block ( vec ! [ ] , TerminatorKind :: Goto { target : self . block_index_offset ( 3 ) } , false ) ;
529+ let unwind = self . block ( vec ! [ ] , TerminatorKind :: Resume , true ) ;
530+ // This will get overwritten with a switch once we know the target blocks
531+ let switch = self . block ( vec ! [ ] , TerminatorKind :: Unreachable , false ) ;
532+ let unwind = self . clone_fields ( dest, src, switch, unwind, substs. upvar_tys ( ) ) ;
533+ let target = self . block ( vec ! [ ] , TerminatorKind :: Return , false ) ;
534+ let unreachable = self . block ( vec ! [ ] , TerminatorKind :: Unreachable , false ) ;
535+ let mut cases = Vec :: with_capacity ( substs. state_tys ( gen_def_id, self . tcx ) . count ( ) ) ;
536+ for ( index, state_tys) in substs. state_tys ( gen_def_id, self . tcx ) . enumerate ( ) {
537+ let variant_index = VariantIdx :: new ( index) ;
538+ let dest = self . tcx . mk_place_downcast_unnamed ( dest, variant_index) ;
539+ let src = self . tcx . mk_place_downcast_unnamed ( src, variant_index) ;
540+ let clone_block = self . block_index_offset ( 1 ) ;
541+ let start_block = self . block (
542+ vec ! [ self . make_statement( StatementKind :: SetDiscriminant {
543+ place: Box :: new( Place :: return_place( ) ) ,
544+ variant_index,
545+ } ) ] ,
546+ TerminatorKind :: Goto { target : clone_block } ,
547+ false ,
548+ ) ;
549+ cases. push ( ( index as u128 , start_block) ) ;
550+ let _final_cleanup_block = self . clone_fields ( dest, src, target, unwind, state_tys) ;
551+ }
552+ let discr_ty = substs. discr_ty ( self . tcx ) ;
553+ let temp = self . make_place ( Mutability :: Mut , discr_ty) ;
554+ let rvalue = Rvalue :: Discriminant ( src) ;
555+ let statement = self . make_statement ( StatementKind :: Assign ( Box :: new ( ( temp, rvalue) ) ) ) ;
556+ match & mut self . blocks [ switch] {
557+ BasicBlockData { statements, terminator : Some ( Terminator { kind, .. } ) , .. } => {
558+ statements. push ( statement) ;
559+ * kind = TerminatorKind :: SwitchInt {
560+ discr : Operand :: Move ( temp) ,
561+ switch_ty : discr_ty,
562+ targets : SwitchTargets :: new ( cases. into_iter ( ) , unreachable) ,
563+ } ;
564+ }
565+ BasicBlockData { terminator : None , .. } => unreachable ! ( ) ,
566+ }
507567 }
508568}
509569
0 commit comments