@@ -310,6 +310,7 @@ fn build_clone_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
310
310
)
311
311
}
312
312
ty:: TyTuple ( tys, _) => builder. tuple_like_shim ( & * * tys, AggregateKind :: Tuple ) ,
313
+ ty:: TyAdt ( adt, substs) => builder. enum_shim ( adt, substs) ,
313
314
_ => {
314
315
bug ! ( "clone shim for `{:?}` which is not `Copy` and is not an aggregate" , self_ty)
315
316
}
@@ -626,6 +627,87 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> {
626
627
self . block ( vec ! [ ] , TerminatorKind :: Resume , true ) ;
627
628
}
628
629
630
+ fn enum_shim ( & mut self , adt : & ' tcx ty:: AdtDef , substs : & ' tcx Substs < ' tcx > ) {
631
+ if !adt. is_enum ( ) {
632
+ bug ! ( "We only make Clone shims for enum ADTs" ) ;
633
+ }
634
+ let receiver = Place :: Local ( Local :: new ( 1 +0 ) ) . deref ( ) ;
635
+ // should be u128 maybe?
636
+ let discr_ty = self . tcx . types . usize ;
637
+ let discr = self . make_place ( Mutability :: Not , discr_ty) ;
638
+ let assign_discr = self . make_statement (
639
+ StatementKind :: Assign (
640
+ discr. clone ( ) ,
641
+ Rvalue :: Discriminant ( receiver. clone ( ) )
642
+ )
643
+ ) ;
644
+ let switch = self . make_enum_match ( adt, substs, discr, receiver) ;
645
+ self . block ( vec ! [ assign_discr] , switch, false ) ;
646
+ }
647
+
648
+ fn make_enum_match ( & mut self , adt : & ' tcx ty:: AdtDef ,
649
+ substs : & ' tcx Substs < ' tcx > ,
650
+ discr : Place < ' tcx > ,
651
+ receiver : Place < ' tcx > ) -> TerminatorKind < ' tcx > {
652
+
653
+ let mut values = vec ! [ ] ;
654
+ let mut targets = vec ! [ ] ;
655
+ let mut blocks = 0 ;
656
+ for ( idx, variant) in adt. variants . iter ( ) . enumerate ( ) {
657
+ values. push ( adt. discriminant_for_variant ( self . tcx , idx) ) ;
658
+
659
+ let variant_place = receiver. clone ( ) . downcast ( adt, idx) ;
660
+ let mut returns = vec ! [ ] ;
661
+ // FIXME(Manishearth) this code has a lot in common
662
+ // with tuple_like_shim
663
+ for ( i, field) in variant. fields . iter ( ) . enumerate ( ) {
664
+ let field_ty = field. ty ( self . tcx , substs) ;
665
+ let receiver_field = variant_place. clone ( ) . field ( Field :: new ( i) , field_ty) ;
666
+
667
+ // BB (blocks + 2i)
668
+ returns. push (
669
+ self . make_clone_call (
670
+ & field_ty,
671
+ receiver_field,
672
+ BasicBlock :: new ( blocks + 2 * i + 2 ) ,
673
+ BasicBlock :: new ( blocks + 2 * i + 1 ) ,
674
+ )
675
+ ) ;
676
+ // BB #(2i + 1) (cleanup)
677
+ if i == 0 {
678
+ // Nothing to drop, just resume.
679
+ self . block ( vec ! [ ] , TerminatorKind :: Resume , true ) ;
680
+ } else {
681
+ // Drop previous field and goto previous cleanup block.
682
+ self . block ( vec ! [ ] , TerminatorKind :: Drop {
683
+ location : returns[ i - 1 ] . clone ( ) ,
684
+ target : BasicBlock :: new ( blocks + 2 * i - 1 ) ,
685
+ unwind : None ,
686
+ } , true ) ;
687
+ }
688
+ }
689
+ let ret_statement = self . make_statement (
690
+ StatementKind :: Assign (
691
+ Place :: Local ( RETURN_PLACE ) ,
692
+ Rvalue :: Aggregate (
693
+ box AggregateKind :: Adt ( adt, idx, substs, None ) ,
694
+ returns. into_iter ( ) . map ( Operand :: Move ) . collect ( )
695
+ )
696
+ )
697
+ ) ;
698
+ targets. push ( self . block ( vec ! [ ret_statement] , TerminatorKind :: Return , false ) ) ;
699
+ blocks += variant. fields . len ( ) * 2 + 1 ;
700
+ }
701
+ // the nonexistant extra case
702
+ targets. push ( self . block ( vec ! [ ] , TerminatorKind :: Abort , true ) ) ;
703
+ TerminatorKind :: SwitchInt {
704
+ discr : Operand :: Move ( discr) ,
705
+ switch_ty : self . tcx . types . usize ,
706
+ values : From :: from ( values) ,
707
+ targets,
708
+ }
709
+ }
710
+
629
711
fn tuple_like_shim ( & mut self , tys : & [ ty:: Ty < ' tcx > ] , kind : AggregateKind < ' tcx > ) {
630
712
match kind {
631
713
AggregateKind :: Tuple | AggregateKind :: Closure ( ..) => ( ) ,
0 commit comments