diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 4ed25646d436d..5f2fc0064b342 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -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 } diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs index 63bf52a9bdf78..f208a6f286b60 100644 --- a/src/librustc/ty/instance.rs +++ b/src/librustc/ty/instance.rs @@ -69,6 +69,15 @@ impl<'tcx> InstanceDef<'tcx> { } } + #[inline] + pub fn shim_def_id(&self) -> Option { + 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()) diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 42ffcc194ca8c..68c2ef29dc472 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -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) @@ -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) } @@ -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, @@ -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> { @@ -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 diff --git a/src/librustc_trans_utils/symbol_names.rs b/src/librustc_trans_utils/symbol_names.rs index fb299bf7eea0c..dd3dc5abf5b5d 100644 --- a/src/librustc_trans_utils/symbol_names.rs +++ b/src/librustc_trans_utils/symbol_names.rs @@ -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(|| { @@ -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 diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs index c7fa0331c1bd5..ab5dff07d47f0 100644 --- a/src/libsyntax/ext/derive.rs +++ b/src/libsyntax/ext/derive.rs @@ -77,6 +77,9 @@ pub fn add_derived_markers(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 }) diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 3e858c3b923a1..b31d079d031e1 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -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), diff --git a/src/libsyntax_ext/deriving/clone.rs b/src/libsyntax_ext/deriving/clone.rs index f23d22b0c365f..445b345e38041 100644 --- a/src/libsyntax_ext/deriving/clone.rs +++ b/src/libsyntax_ext/deriving/clone.rs @@ -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;