Skip to content

Commit c5f81d3

Browse files
committed
[WIP] Implement shim for types that transitively derive Clone
If a type is `#[derive(Clone)]`, and all of its transitive fields are also `#[derive(Clone)]`, we skip generating the actual `clone` function. Instead, we generate a 'clone shim', which just acts like the trivial `Clone` impl for a `Copy` type. We do this even if the type itself is not `Copy`.
1 parent 0aa6751 commit c5f81d3

File tree

10 files changed

+64
-13
lines changed

10 files changed

+64
-13
lines changed

src/libcore/clone.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ pub trait Clone: Sized {
136136
/// Derive macro generating an impl of the trait `Clone`.
137137
#[rustc_builtin_macro]
138138
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
139-
#[allow_internal_unstable(core_intrinsics, derive_clone_copy)]
139+
#[allow_internal_unstable(core_intrinsics, derive_clone_copy, structural_match)]
140140
pub macro Clone($item:item) {
141141
/* compiler built-in */
142142
}

src/libcore/marker.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,12 @@ pub trait StructuralEq {
205205
// Empty.
206206
}
207207

208+
/// Temp
209+
#[cfg(not(bootstrap))]
210+
#[unstable(feature = "structural_match", issue = "31434")]
211+
#[lang = "derived_clone"]
212+
pub trait DerivedClone {}
213+
208214
/// Types whose values can be duplicated simply by copying bits.
209215
///
210216
/// By default, variable bindings have 'move semantics.' In other

src/librustc_builtin_macros/deriving/clone.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,14 @@ pub fn expand_deriving_clone(
7373
_ => cx.span_bug(span, "`#[derive(Clone)]` on trait item or impl item"),
7474
}
7575

76+
super::inject_impl_of_structural_trait(
77+
cx,
78+
span,
79+
item,
80+
path_std!(cx, marker::DerivedClone),
81+
push,
82+
);
83+
7684
let inline = cx.meta_word(span, sym::inline);
7785
let attrs = vec![cx.attribute(inline)];
7886
let trait_def = TraitDef {

src/librustc_hir/lang_items.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ language_item_table! {
160160
StructuralPeqTraitLangItem, "structural_peq", structural_peq_trait, Target::Trait;
161161
// trait injected by #[derive(Eq)], (i.e. "Total EQ"; no, I will not apologize).
162162
StructuralTeqTraitLangItem, "structural_teq", structural_teq_trait, Target::Trait;
163+
DerivedClone, "derived_clone", derived_clone_trait, Target::Trait;
163164
CopyTraitLangItem, "copy", copy_trait, Target::Trait;
164165
CloneTraitLangItem, "clone", clone_trait, Target::Trait;
165166
SyncTraitLangItem, "sync", sync_trait, Target::Trait;

src/librustc_middle/query/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1296,5 +1296,9 @@ rustc_queries! {
12961296
) -> Result<Option<ty::Instance<'tcx>>, ErrorReported> {
12971297
desc { "resolving instance `{}`", ty::Instance::new(key.value.0, key.value.1) }
12981298
}
1299+
1300+
query is_transitive_derive_clone(key: Ty<'tcx>) -> bool {
1301+
desc { "determining if item {:?} transitively derives Clone", key }
1302+
}
12991303
}
13001304
}

src/librustc_middle/ty/instance.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ pub enum InstanceDef<'tcx> {
7272
/// NB: the type must currently be monomorphic to avoid double substitution
7373
/// problems with the MIR shim bodies. `Instance::resolve` enforces this.
7474
// FIXME(#69925) support polymorphic MIR shim bodies properly instead.
75-
CloneShim(DefId, Ty<'tcx>),
75+
CloneShim(DefId, Ty<'tcx>, bool),
7676
}
7777

7878
impl<'tcx> Instance<'tcx> {
@@ -151,7 +151,7 @@ impl<'tcx> InstanceDef<'tcx> {
151151
| InstanceDef::Intrinsic(def_id)
152152
| InstanceDef::ClosureOnceShim { call_once: def_id }
153153
| InstanceDef::DropGlue(def_id, _)
154-
| InstanceDef::CloneShim(def_id, _) => def_id,
154+
| InstanceDef::CloneShim(def_id, _, _) => def_id,
155155
}
156156
}
157157

@@ -240,7 +240,8 @@ impl<'tcx> fmt::Display for Instance<'tcx> {
240240
InstanceDef::FnPtrShim(_, ty) => write!(f, " - shim({:?})", ty),
241241
InstanceDef::ClosureOnceShim { .. } => write!(f, " - shim"),
242242
InstanceDef::DropGlue(_, ty) => write!(f, " - shim({:?})", ty),
243-
InstanceDef::CloneShim(_, ty) => write!(f, " - shim({:?})", ty),
243+
InstanceDef::CloneShim(_, ty, from_derive) => write!(f, " - shim({:?}, from_derive={:?})", ty,
244+
from_derive),
244245
}
245246
}
246247
}

src/librustc_middle/ty/structural_impls.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -676,8 +676,8 @@ impl<'a, 'tcx> Lift<'tcx> for ty::InstanceDef<'a> {
676676
ty::InstanceDef::DropGlue(def_id, ref ty) => {
677677
Some(ty::InstanceDef::DropGlue(def_id, tcx.lift(ty)?))
678678
}
679-
ty::InstanceDef::CloneShim(def_id, ref ty) => {
680-
Some(ty::InstanceDef::CloneShim(def_id, tcx.lift(ty)?))
679+
ty::InstanceDef::CloneShim(def_id, ref ty, from_derive) => {
680+
Some(ty::InstanceDef::CloneShim(def_id, tcx.lift(ty)?, from_derive))
681681
}
682682
}
683683
}
@@ -844,7 +844,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> {
844844
ClosureOnceShim { call_once: call_once.fold_with(folder) }
845845
}
846846
DropGlue(did, ty) => DropGlue(did.fold_with(folder), ty.fold_with(folder)),
847-
CloneShim(did, ty) => CloneShim(did.fold_with(folder), ty.fold_with(folder)),
847+
CloneShim(did, ty, from_derive) => CloneShim(did.fold_with(folder), ty.fold_with(folder), from_derive),
848848
},
849849
}
850850
}
@@ -856,7 +856,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> {
856856
Item(did) | VtableShim(did) | ReifyShim(did) | Intrinsic(did) | Virtual(did, _) => {
857857
did.visit_with(visitor)
858858
}
859-
FnPtrShim(did, ty) | CloneShim(did, ty) => {
859+
FnPtrShim(did, ty) | CloneShim(did, ty, _) => {
860860
did.visit_with(visitor) || ty.visit_with(visitor)
861861
}
862862
DropGlue(did, ty) => did.visit_with(visitor) || ty.visit_with(visitor),

src/librustc_mir/shim.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,13 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
9595

9696
build_drop_shim(tcx, def_id, ty)
9797
}
98-
ty::InstanceDef::CloneShim(def_id, ty) => {
98+
ty::InstanceDef::CloneShim(def_id, ty, from_derive) => {
9999
// FIXME(eddyb) support generating shims for a "shallow type",
100100
// e.g. `Foo<_>` or `[_]` instead of requiring a fully monomorphic
101101
// `Foo<Bar>` or `[String]` etc.
102102
assert!(!ty.needs_subst());
103103

104-
build_clone_shim(tcx, def_id, ty)
104+
build_clone_shim(tcx, def_id, ty, from_derive)
105105
}
106106
ty::InstanceDef::Virtual(..) => {
107107
bug!("InstanceDef::Virtual ({:?}) is for direct calls only", instance)
@@ -312,7 +312,7 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for DropShimElaborator<'a, 'tcx> {
312312
}
313313

314314
/// Builds a `Clone::clone` shim for `self_ty`. Here, `def_id` is `Clone::clone`.
315-
fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -> Body<'tcx> {
315+
fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>, from_derive: bool) -> Body<'tcx> {
316316
debug!("build_clone_shim(def_id={:?})", def_id);
317317

318318
let param_env = tcx.param_env(def_id);
@@ -324,7 +324,7 @@ fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -
324324
let src = tcx.mk_place_deref(Place::from(Local::new(1 + 0)));
325325

326326
match self_ty.kind {
327-
_ if is_copy => builder.copy_shim(),
327+
_ if is_copy || from_derive => builder.copy_shim(),
328328
ty::Array(ty, len) => {
329329
let len = len.eval_usize(tcx, param_env);
330330
builder.array_shim(dest, src, ty, len)

src/librustc_trait_selection/traits/mod.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
2828
use rustc_errors::ErrorReported;
2929
use rustc_hir as hir;
3030
use rustc_hir::def_id::DefId;
31+
use rustc_hir::lang_items::DerivedClone;
3132
use rustc_middle::middle::region;
3233
use rustc_middle::ty::fold::TypeFoldable;
3334
use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
@@ -562,6 +563,19 @@ fn type_implements_trait<'tcx>(
562563
tcx.infer_ctxt().enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
563564
}
564565

566+
fn is_transitive_derive_clone(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
567+
match ty.kind {
568+
ty::Adt(adt_def, substs) => {
569+
let derived_clone = tcx.require_lang_item(DerivedClone, None);
570+
let is_derived_clone = tcx.type_implements_trait((derived_clone, ty, InternalSubsts::empty(), ParamEnv::empty()));
571+
is_derived_clone && adt_def.all_fields().all(|field| {
572+
tcx.is_transitive_derive_clone(field.ty(tcx, substs))
573+
})
574+
}
575+
_ => true
576+
}
577+
}
578+
565579
pub fn provide(providers: &mut ty::query::Providers<'_>) {
566580
object_safety::provide(providers);
567581
*providers = ty::query::Providers {
@@ -571,6 +585,7 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) {
571585
vtable_methods,
572586
substitute_normalize_and_test_predicates,
573587
type_implements_trait,
588+
is_transitive_derive_clone,
574589
..*providers
575590
};
576591
}

src/librustc_ty/instance.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,22 @@ fn resolve_associated_item<'tcx>(
9292
assert!(!rcvr_substs.needs_infer());
9393
assert!(!trait_ref.needs_infer());
9494

95+
if Some(trait_ref.def_id) == tcx.lang_items().clone_trait() {
96+
let name = tcx.item_name(def_id);
97+
let self_ty = trait_ref.self_ty();
98+
if name == sym::clone {
99+
if tcx.is_transitive_derive_clone(self_ty) {
100+
debug!("Using CloneShim for transitive Clone derive on {:?}",
101+
self_ty);
102+
return Ok(Some(Instance {
103+
def: ty::InstanceDef::CloneShim(def_id, self_ty, true),
104+
substs: rcvr_substs,
105+
}))
106+
}
107+
}
108+
}
109+
110+
95111
let trait_def_id = tcx.trait_id_of_impl(impl_data.impl_def_id).unwrap();
96112
let trait_def = tcx.trait_def(trait_def_id);
97113
let leaf_def = trait_def
@@ -222,7 +238,7 @@ fn resolve_associated_item<'tcx>(
222238
}
223239

224240
Some(Instance {
225-
def: ty::InstanceDef::CloneShim(def_id, self_ty),
241+
def: ty::InstanceDef::CloneShim(def_id, self_ty, false),
226242
substs: rcvr_substs,
227243
})
228244
} else {

0 commit comments

Comments
 (0)