@@ -4,7 +4,7 @@ use rustc_hir::lang_items::LangItem;
4
4
use rustc_middle:: mir:: * ;
5
5
use rustc_middle:: ty:: query:: Providers ;
6
6
use 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 } ;
8
8
use rustc_target:: abi:: VariantIdx ;
9
9
10
10
use rustc_index:: vec:: { Idx , IndexVec } ;
@@ -323,6 +323,9 @@ fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -
323
323
builder. tuple_like_shim ( dest, src, substs. as_closure ( ) . upvar_tys ( ) )
324
324
}
325
325
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
+ }
326
329
_ => bug ! ( "clone shim for `{:?}` which is not `Copy` and is not an aggregate" , self_ty) ,
327
330
} ;
328
331
@@ -388,7 +391,7 @@ impl<'tcx> CloneShimBuilder<'tcx> {
388
391
/// offset=0 will give you the index of the next BasicBlock,
389
392
/// offset=1 will give the index of the next-to-next block,
390
393
/// 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 {
392
395
BasicBlock :: new ( self . blocks . len ( ) + offset)
393
396
}
394
397
@@ -461,49 +464,106 @@ impl<'tcx> CloneShimBuilder<'tcx> {
461
464
) ;
462
465
}
463
466
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
465
475
where
466
476
I : IntoIterator < Item = Ty < ' tcx > > ,
467
477
{
468
- let mut previous_field = None ;
478
+ // For an iterator of length n, create 2*n + 1 blocks.
469
479
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
+
470
490
let field = Field :: new ( i) ;
471
491
let src_field = self . tcx . mk_place_field ( src, field, ity) ;
472
492
473
493
let dest_field = self . tcx . mk_place_field ( dest, field, ity) ;
474
494
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 ) ;
479
496
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
+ }
480
509
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 ) ;
502
517
503
- previous_field = Some ( ( dest_field , cleanup_block ) ) ;
504
- }
518
+ let _final_cleanup_block = self . clone_fields ( dest , src , target , unwind , tys ) ;
519
+ }
505
520
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
+ }
507
567
}
508
568
}
509
569
0 commit comments