Skip to content

Commit 6a1d146

Browse files
committed
Add shim for simple cloning
1 parent 4aecbdb commit 6a1d146

File tree

1 file changed

+82
-0
lines changed

1 file changed

+82
-0
lines changed

src/librustc_mir/shim.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ fn build_clone_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
310310
)
311311
}
312312
ty::TyTuple(tys, _) => builder.tuple_like_shim(&**tys, AggregateKind::Tuple),
313+
ty::TyAdt(adt, substs) => builder.enum_shim(adt, substs),
313314
_ => {
314315
bug!("clone shim for `{:?}` which is not `Copy` and is not an aggregate", self_ty)
315316
}
@@ -626,6 +627,87 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> {
626627
self.block(vec![], TerminatorKind::Resume, true);
627628
}
628629

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+
629711
fn tuple_like_shim(&mut self, tys: &[ty::Ty<'tcx>], kind: AggregateKind<'tcx>) {
630712
match kind {
631713
AggregateKind::Tuple | AggregateKind::Closure(..) => (),

0 commit comments

Comments
 (0)