@@ -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