@@ -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 , Ty , TyCtxt , GeneratorSubsts } ;
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,111 @@ 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 ;
469478 for ( i, ity) in tys. into_iter ( ) . enumerate ( ) {
470479 let field = Field :: new ( i) ;
471480 let src_field = self . tcx . mk_place_field ( src, field, ity) ;
472481
473482 let dest_field = self . tcx . mk_place_field ( dest, field, ity) ;
474483
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)
484+ let next_unwind = self . block_index_offset ( 1 ) ;
479485 let next_block = self . block_index_offset ( 2 ) ;
486+ self . make_clone_call ( dest_field, src_field, ity, next_block, unwind) ;
487+ self . block (
488+ vec ! [ ] ,
489+ TerminatorKind :: Drop {
490+ place : dest_field,
491+ target : unwind,
492+ unwind : None ,
493+ } ,
494+ true ,
495+ ) ;
496+ unwind = next_unwind;
497+ }
498+ self . block (
499+ vec ! [ ] ,
500+ TerminatorKind :: Goto { target } ,
501+ false ,
502+ ) ;
503+ unwind
504+ }
480505
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- }
506+ fn tuple_like_shim < I > ( & mut self , dest : Place < ' tcx > , src : Place < ' tcx > , tys : I )
507+ where
508+ I : IntoIterator < Item = Ty < ' tcx > > ,
509+ {
510+ self . block (
511+ vec ! [ ] ,
512+ TerminatorKind :: Goto { target : self . block_index_offset ( 3 ) } ,
513+ false ,
514+ ) ;
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 (
529+ vec ! [ ] ,
530+ TerminatorKind :: Goto { target : self . block_index_offset ( 3 ) } ,
531+ false ,
532+ ) ;
533+ let unwind = self . block ( vec ! [ ] , TerminatorKind :: Resume , true ) ;
534+ // This will get overwritten with a switch once we know the target blocks
535+ let switch = self . block ( vec ! [ ] , TerminatorKind :: Unreachable , false ) ;
536+ let unwind = self . clone_fields ( dest, src, switch, unwind, substs. upvar_tys ( ) ) ;
537+ let target = self . block ( vec ! [ ] , TerminatorKind :: Return , false ) ;
538+ let unreachable = self . block ( vec ! [ ] , TerminatorKind :: Unreachable , false ) ;
539+ let mut cases = Vec :: with_capacity ( substs. state_tys ( gen_def_id, self . tcx ) . count ( ) ) ;
540+ for ( index, state_tys) in substs. state_tys ( gen_def_id, self . tcx ) . enumerate ( ) {
541+ let variant_index = VariantIdx :: new ( index) ;
542+ let dest = self . tcx . mk_place_downcast_unnamed ( dest, variant_index) ;
543+ let src = self . tcx . mk_place_downcast_unnamed ( src, variant_index) ;
544+ let start_block = self . block_index_offset ( 0 ) ;
545+ let clone_block = self . block_index_offset ( 1 ) ;
546+ cases. push ( ( index as u128 , start_block) ) ;
547+ self . block (
548+ vec ! [ self . make_statement( StatementKind :: SetDiscriminant {
549+ place: Box :: new( Place :: return_place( ) ) ,
550+ variant_index,
551+ } ) ] ,
552+ TerminatorKind :: Goto { target : clone_block } ,
553+ false ,
554+ ) ;
555+ let _final_cleanup_block = self . clone_fields ( dest, src, target, unwind, state_tys) ;
556+ }
557+ let discr_ty = substs. discr_ty ( self . tcx ) ;
558+ let temp = self . make_place ( Mutability :: Mut , discr_ty) ;
559+ let rvalue = Rvalue :: Discriminant ( src) ;
560+ let statement = self . make_statement ( StatementKind :: Assign ( Box :: new ( ( temp, rvalue) ) ) ) ;
561+ match & mut self . blocks [ switch] {
562+ BasicBlockData { statements, terminator : Some ( Terminator { kind, .. } ) , .. } => {
563+ statements. push ( statement) ;
564+ * kind = TerminatorKind :: SwitchInt {
565+ discr : Operand :: Move ( temp) ,
566+ switch_ty : discr_ty,
567+ targets : SwitchTargets :: new ( cases. into_iter ( ) , unreachable) ,
568+ } ;
569+ } ,
570+ BasicBlockData { terminator : None , .. } => unreachable ! ( ) ,
571+ }
507572 }
508573}
509574
0 commit comments