Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Make derive(Clone) memcpy on enum variants containing only Copy types #47867

Closed
wants to merge 8 commits into from
Closed
22 changes: 21 additions & 1 deletion src/librustc/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2150,7 +2150,27 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
}
}

ty::TyAdt(..) | ty::TyProjection(..) | ty::TyParam(..) | ty::TyAnon(..) => {
ty::TyAdt(adt, substs) => {
let attrs = self.tcx().get_attrs(adt.did);
if adt.is_enum() && attrs.iter().any(|a| a.check_name("rustc_nocopy_clone_marker")) {
let trait_id = obligation.predicate.def_id();
if Some(trait_id) == self.tcx().lang_items().clone_trait() {
// for Clone
// this doesn't work for recursive types (FIXME(Manishearth))
// let mut iter = substs.types()
// .chain(adt.all_fields().map(|f| f.ty(self.tcx(), substs)));
let mut iter = substs.types();
Where(ty::Binder(iter.collect()))
} else {
None
}
} else {
// Fallback to whatever user-defined impls exist in this case.
None
}
}

ty::TyProjection(..) | ty::TyParam(..) | ty::TyAnon(..) => {
// Fallback to whatever user-defined impls exist in this case.
None
}
Expand Down
9 changes: 9 additions & 0 deletions src/librustc/ty/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,15 @@ impl<'tcx> InstanceDef<'tcx> {
}
}

#[inline]
pub fn shim_def_id(&self) -> Option<DefId> {
if let InstanceDef::CloneShim(_, ty) = *self {
ty.ty_to_def_id()
} else {
None
}
}

#[inline]
pub fn attrs<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> ty::Attributes<'tcx> {
tcx.get_attrs(self.def_id())
Expand Down
110 changes: 104 additions & 6 deletions src/librustc_mir/shim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ fn build_clone_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
let src = Place::Local(Local::new(1+0)).deref();

match self_ty.sty {
_ if is_copy => builder.copy_shim(),
_ if is_copy => { builder.copy_shim(dest, src); }
ty::TyArray(ty, len) => {
let len = len.val.to_const_int().unwrap().to_u64().unwrap();
builder.array_shim(dest, src, ty, len)
Expand All @@ -313,6 +313,7 @@ fn build_clone_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
)
}
ty::TyTuple(tys, _) => builder.tuple_like_shim(dest, src, tys.iter().cloned()),
ty::TyAdt(adt, substs) => builder.enum_shim(adt, substs, dest, src),
_ => {
bug!("clone shim for `{:?}` which is not `Copy` and is not an aggregate", self_ty)
}
Expand Down Expand Up @@ -340,7 +341,13 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> {
let substs = tcx.mk_substs_trait(self_ty, &[]);
let sig = tcx.fn_sig(def_id).subst(tcx, substs);
let sig = tcx.erase_late_bound_regions(&sig);
let span = tcx.def_span(def_id);
let span_def_id = if let Some(did) = self_ty.ty_to_def_id() {
did
} else {
def_id
};

let span = tcx.def_span(span_def_id);

CloneShimBuilder {
tcx,
Expand Down Expand Up @@ -401,15 +408,15 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> {
}
}

fn copy_shim(&mut self) {
let rcvr = Place::Local(Local::new(1+0)).deref();
fn copy_shim(&mut self, dest: Place<'tcx>, src: Place<'tcx>) -> BasicBlock {
let rcvr = src;
let ret_statement = self.make_statement(
StatementKind::Assign(
Place::Local(RETURN_PLACE),
dest,
Rvalue::Use(Operand::Copy(rcvr))
)
);
self.block(vec![ret_statement], TerminatorKind::Return, false);
self.block(vec![ret_statement], TerminatorKind::Return, false)
}

fn make_place(&mut self, mutability: Mutability, ty: Ty<'tcx>) -> Place<'tcx> {
Expand Down Expand Up @@ -671,6 +678,97 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> {

self.block(vec![], TerminatorKind::Return, false);
}

fn enum_shim(&mut self, adt: &'tcx ty::AdtDef, substs: &'tcx Substs<'tcx>,
dest: Place<'tcx>, src: Place<'tcx>) {
use rustc::ty::util::IntTypeExt;
if !adt.is_enum() {
bug!("We only make Clone shims for enum ADTs");
}

let param_env = self.tcx.param_env(adt.did);
let all_copy = adt.all_fields().all(|field| {
!field.ty(self.tcx, substs)
.moves_by_default(self.tcx, param_env, self.span)
});

if all_copy {
self.copy_shim(dest, src);
return;
}

// should be u128 maybe?
let discr_ty = adt.repr.discr_type().to_ty(self.tcx);
let discr = self.make_place(Mutability::Not, discr_ty);

let assign_discr = self.make_statement(
StatementKind::Assign(
discr.clone(),
Rvalue::Discriminant(src.clone())
)
);
// insert dummy first block
let entry_block = self.block(vec![], TerminatorKind::Abort, false);
let switch = self.make_enum_match(adt, substs, discr, dest, src, param_env);

let source_info = self.source_info();
self.blocks[entry_block].statements = vec![assign_discr];
self.blocks[entry_block].terminator = Some(Terminator { source_info, kind: switch });
}

fn make_enum_match(&mut self, adt: &'tcx ty::AdtDef,
substs: &'tcx Substs<'tcx>,
discr: Place<'tcx>,
dest: Place<'tcx>,
receiver: Place<'tcx>,
param_env: ty::ParamEnv<'tcx>) -> TerminatorKind<'tcx> {

let mut values = vec![];
let mut targets = vec![];
for (idx, variant) in adt.variants.iter().enumerate() {

// in case a variant is Copy, don't generate the manual Clone
// match arm, instead let the copy_shim default deal with it
let all_copy = variant.fields.iter().all(|field| {
!field.ty(self.tcx, substs)
.moves_by_default(self.tcx, param_env, self.span)
});

if all_copy {
continue;
}

values.push(adt.discriminant_for_variant(self.tcx, idx));

let src_variant = receiver.clone().downcast(adt, idx);
let dest_variant = dest.clone().downcast(adt, idx);


// the next to next block created will be the one
// from tuple_like_shim and will handle the cloning
let clone_block = self.block_index_offset(1);
let set_discr = self.make_statement(StatementKind::SetDiscriminant {
place: dest.clone(),
variant_index: idx
});

targets.push(self.block(vec![set_discr], TerminatorKind::Goto {
target: clone_block
}, false));

let tcx = self.tcx;
let iter = variant.fields.iter().map(|field| field.ty(tcx, substs));
self.tuple_like_shim(dest_variant, src_variant, iter);
}
// In the default arm, fall back to a copy
targets.push(self.copy_shim(dest, receiver));
TerminatorKind::SwitchInt {
discr: Operand::Move(discr),
switch_ty: self.tcx.types.usize,
values: From::from(values),
targets,
}
}
}

/// Build a "call" shim for `def_id`. The shim calls the
Expand Down
18 changes: 15 additions & 3 deletions src/librustc_trans_utils/symbol_names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,8 @@ fn get_symbol_hash<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
hasher.finish()
}

fn def_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId)
-> ty::SymbolName
// The boolean is whether this is a clone shim
fn def_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> ty::SymbolName
{
let mut buffer = SymbolPathBuffer::new();
item_path::with_forced_absolute_paths(|| {
Expand Down Expand Up @@ -329,7 +329,19 @@ fn compute_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance

let hash = get_symbol_hash(tcx, def_id, instance, instance_ty, substs);

SymbolPathBuffer::from_interned(tcx.def_symbol_name(def_id)).finish(hash)
let shim_id = instance.def.shim_def_id();

let lookup_def_id = if let Some(shim_id) = shim_id {
shim_id
} else {
def_id
};

let mut buf = SymbolPathBuffer::from_interned(tcx.def_symbol_name(lookup_def_id));
if shim_id.is_some() {
buf.push("{{clone-shim}}");
}
buf.finish(hash)
}

// Follow C++ namespace-mangling style, see
Expand Down
3 changes: 3 additions & 0 deletions src/libsyntax/ext/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ pub fn add_derived_markers<T>(cx: &mut ExtCtxt, span: Span, traits: &[ast::Path]
if names.contains(&Symbol::intern("Copy")) {
let meta = cx.meta_word(span, Symbol::intern("rustc_copy_clone_marker"));
attrs.push(cx.attribute(span, meta));
} else if names.contains(&Symbol::intern("Clone")) {
let meta = cx.meta_word(span, Symbol::intern("rustc_nocopy_clone_marker"));
attrs.push(cx.attribute(span, meta));
}
attrs
})
Expand Down
5 changes: 4 additions & 1 deletion src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -869,7 +869,10 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
"rustc_attrs",
"internal implementation detail",
cfg_fn!(rustc_attrs))),

("rustc_nocopy_clone_marker", Whitelisted, Gated(Stability::Unstable,
"rustc_attrs",
"internal implementation detail",
cfg_fn!(rustc_attrs))),
// FIXME: #14408 whitelist docs since rustdoc looks at them
("doc", Whitelisted, Ungated),

Expand Down
10 changes: 10 additions & 0 deletions src/libsyntax_ext/deriving/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,16 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt,
substructure = combine_substructure(Box::new(|c, s, sub| {
cs_clone_shallow("Clone", c, s, sub, false)
}));
} else if attr::contains_name(&annitem.attrs, "rustc_nocopy_clone_marker") {
if let ItemKind::Enum(..) = annitem.node {
// Do nothing, this will be handled in a shim
return
}
bounds = vec![];
is_shallow = false;
substructure = combine_substructure(Box::new(|c, s, sub| {
cs_clone("Clone", c, s, sub)
}));
} else {
bounds = vec![];
is_shallow = false;
Expand Down