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

Specialize derive(Clone) to use Copy when applicable #95668

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/rustc_builtin_macros/src/deriving/bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub fn expand_deriving_copy(
span,
attributes: Vec::new(),
path: path_std!(marker::Copy),
bound_current_trait: true,
additional_bounds: Vec::new(),
generics: Bounds::empty(),
is_unsafe: false,
Expand Down
95 changes: 57 additions & 38 deletions compiler/rustc_builtin_macros/src/deriving/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,17 @@ pub fn expand_deriving_clone(
item: &Annotatable,
push: &mut dyn FnMut(Annotatable),
) {
// check if we can use a short form
//
// the short form is `fn clone(&self) -> Self { *self }`
//
// we can use the short form if:
// - the item is Copy (unfortunately, all we can check is whether it's also deriving Copy)
// - there are no generic parameters (after specialization this limitation can be removed)
// if we used the short form with generics, we'd have to bound the generics with
// Clone + Copy, and then there'd be no Clone impl at all if the user fills in something
// that is Clone but not Copy. and until specialization we can't write both impls.
// - the item is a union with Copy fields
// Unions with generic parameters still can derive Clone because they require Copy
// for deriving, Clone alone is not enough.
// Wherever Clone is implemented for fields is irrelevant so we don't assert it.
let bounds;
let substructure;
let is_shallow;
match *item {
Annotatable::Item(ref annitem) => match annitem.kind {
ItemKind::Struct(_, Generics { ref params, .. })
| ItemKind::Enum(_, Generics { ref params, .. }) => {
// FIXME: although the use of specialization already removes the need for checking whether
// the type already derives `Copy`, `rustc_scalar_valid_range_*` types derive
// `Clone` AND `Copy` and cannot be constructed unless unsafe blocks surround the expression,
// thus this part is preserved.
let container_id = cx.current_expansion.id.expn_data().parent.expect_local();
let has_derive_copy = cx.resolver.has_derive_copy(container_id);
if has_derive_copy
Expand All @@ -46,27 +36,24 @@ pub fn expand_deriving_clone(
bounds = vec![];
is_shallow = true;
substructure = combine_substructure(Box::new(|c, s, sub| {
cs_clone_shallow("Clone", c, s, sub, false)
cs_clone_shallow(c, s, sub, false)
}));
} else {
bounds = vec![];
bounds = vec![Literal(path_std!(clone::Clone))];
is_shallow = false;
substructure =
combine_substructure(Box::new(|c, s, sub| cs_clone("Clone", c, s, sub)));
substructure = combine_substructure(Box::new(|c, s, sub| cs_clone(c, s, sub)));
}
}
ItemKind::Union(..) => {
bounds = vec![Literal(path_std!(marker::Copy))];
is_shallow = true;
substructure = combine_substructure(Box::new(|c, s, sub| {
cs_clone_shallow("Clone", c, s, sub, true)
}));
substructure =
combine_substructure(Box::new(|c, s, sub| cs_clone_shallow(c, s, sub, true)));
}
_ => {
bounds = vec![];
bounds = vec![Literal(path_std!(clone::Clone))];
is_shallow = false;
substructure =
combine_substructure(Box::new(|c, s, sub| cs_clone("Clone", c, s, sub)));
substructure = combine_substructure(Box::new(|c, s, sub| cs_clone(c, s, sub)));
}
},

Expand All @@ -78,7 +65,8 @@ pub fn expand_deriving_clone(
let trait_def = TraitDef {
span,
attributes: Vec::new(),
path: path_std!(clone::Clone),
path: if is_shallow { path_std!(clone::Clone) } else { path_std!(clone::DerivedClone) },
bound_current_trait: false,
additional_bounds: bounds,
generics: Bounds::empty(),
is_unsafe: false,
Expand All @@ -89,19 +77,55 @@ pub fn expand_deriving_clone(
explicit_self: borrowed_explicit_self(),
args: Vec::new(),
ret_ty: Self_,
attributes: attrs,
attributes: attrs.clone(),
is_unsafe: false,
unify_fieldless_variants: false,
combine_substructure: substructure,
}],
associated_types: Vec::new(),
};

trait_def.expand_ext(cx, mitem, item, push, is_shallow)
trait_def.expand_ext(cx, mitem, item, push, is_shallow);

if !is_shallow {
TraitDef {
span,
attributes: Vec::new(),
path: path_std!(clone::Clone),
bound_current_trait: true,
additional_bounds: vec![],
generics: Bounds::empty(),
is_unsafe: false,
supports_unions: false,
methods: vec![MethodDef {
name: sym::clone,
generics: Bounds::empty(),
explicit_self: borrowed_explicit_self(),
args: Vec::new(),
ret_ty: Self_,
attributes: attrs,
is_unsafe: false,
unify_fieldless_variants: false,
combine_substructure: combine_substructure(Box::new(|c, s, _| {
c.expr_call(
s,
c.expr_path(c.path(s, c.std_path(&[sym::clone, sym::try_copy]))),
vec![
c.expr_self(s),
c.expr_path(
c.path(s, c.std_path(&[sym::clone, sym::DerivedClone, sym::clone])),
),
],
)
})),
}],
associated_types: vec![],
}
.expand_ext(cx, mitem, item, push, true)
}
}

fn cs_clone_shallow(
name: &str,
cx: &mut ExtCtxt<'_>,
trait_span: Span,
substr: &Substructure<'_>,
Expand Down Expand Up @@ -149,20 +173,15 @@ fn cs_clone_shallow(
}
_ => cx.span_bug(
trait_span,
&format!("unexpected substructure in shallow `derive({})`", name),
&format!("unexpected substructure in shallow `derive(Clone)`"),
),
}
}
stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span))));
cx.expr_block(cx.block(trait_span, stmts))
}

fn cs_clone(
name: &str,
cx: &mut ExtCtxt<'_>,
trait_span: Span,
substr: &Substructure<'_>,
) -> P<Expr> {
fn cs_clone(cx: &mut ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>) -> P<Expr> {
let ctor_path;
let all_fields;
let fn_path = cx.std_path(&[sym::clone, sym::Clone, sym::clone]);
Expand All @@ -184,10 +203,10 @@ fn cs_clone(
vdata = &variant.data;
}
EnumNonMatchingCollapsed(..) => {
cx.span_bug(trait_span, &format!("non-matching enum variants in `derive({})`", name,))
cx.span_bug(trait_span, "non-matching enum variants in `derive(Clone)`")
}
StaticEnum(..) | StaticStruct(..) => {
cx.span_bug(trait_span, &format!("associated function in `derive({})`", name))
cx.span_bug(trait_span, "associated function in `derive(Clone)`")
}
}

Expand All @@ -199,7 +218,7 @@ fn cs_clone(
let Some(ident) = field.name else {
cx.span_bug(
trait_span,
&format!("unnamed field in normal struct in `derive({})`", name,),
"unnamed field in normal struct in `derive(Clone)`",
);
};
let call = subcall(cx, field);
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub fn expand_deriving_eq(
span,
attributes: Vec::new(),
path: path_std!(cmp::Eq),
bound_current_trait: true,
additional_bounds: Vec::new(),
generics: Bounds::empty(),
is_unsafe: false,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub fn expand_deriving_ord(
span,
attributes: Vec::new(),
path: path_std!(cmp::Ord),
bound_current_trait: true,
additional_bounds: Vec::new(),
generics: Bounds::empty(),
is_unsafe: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ pub fn expand_deriving_partial_eq(
span,
attributes: Vec::new(),
path: path_std!(cmp::PartialEq),
bound_current_trait: true,
additional_bounds: Vec::new(),
generics: Bounds::empty(),
is_unsafe: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ pub fn expand_deriving_partial_ord(
span,
attributes: vec![],
path: path_std!(cmp::PartialOrd),
bound_current_trait: true,
additional_bounds: vec![],
generics: Bounds::empty(),
is_unsafe: false,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_builtin_macros/src/deriving/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub fn expand_deriving_debug(
span,
attributes: Vec::new(),
path: path_std!(fmt::Debug),
bound_current_trait: true,
additional_bounds: Vec::new(),
generics: Bounds::empty(),
is_unsafe: false,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_builtin_macros/src/deriving/decodable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub fn expand_deriving_rustc_decodable(
span,
attributes: Vec::new(),
path: Path::new_(vec![krate, sym::Decodable], None, vec![], PathKind::Global),
bound_current_trait: true,
additional_bounds: Vec::new(),
generics: Bounds::empty(),
is_unsafe: false,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_builtin_macros/src/deriving/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub fn expand_deriving_default(
span,
attributes: Vec::new(),
path: Path::new(vec![kw::Default, sym::Default]),
bound_current_trait: true,
additional_bounds: Vec::new(),
generics: Bounds::empty(),
is_unsafe: false,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_builtin_macros/src/deriving/encodable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ pub fn expand_deriving_rustc_encodable(
span,
attributes: Vec::new(),
path: Path::new_(vec![krate, sym::Encodable], None, vec![], PathKind::Global),
bound_current_trait: true,
additional_bounds: Vec::new(),
generics: Bounds::empty(),
is_unsafe: false,
Expand Down
10 changes: 7 additions & 3 deletions compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ pub struct TraitDef<'a> {
/// Path of the trait, including any type parameters
pub path: Path,

pub bound_current_trait: bool,

/// Additional bounds required of any type parameters of the type,
/// other than the current trait
pub additional_bounds: Vec<Ty>,
Expand Down Expand Up @@ -591,7 +593,7 @@ impl<'a> TraitDef<'a> {
cx.trait_bound(p.to_path(cx, self.span, type_ident, generics))
}).chain(
// require the current trait
iter::once(cx.trait_bound(trait_path.clone()))
self.bound_current_trait.then(|| cx.trait_bound(trait_path.clone()))
).chain(
// also add in any bounds from the declaration
param.bounds.iter().cloned()
Expand Down Expand Up @@ -671,8 +673,10 @@ impl<'a> TraitDef<'a> {
.map(|p| cx.trait_bound(p.to_path(cx, self.span, type_ident, generics)))
.collect();

// require the current trait
bounds.push(cx.trait_bound(trait_path.clone()));
if self.bound_current_trait {
// require the current trait
bounds.push(cx.trait_bound(trait_path.clone()));
}

let predicate = ast::WhereBoundPredicate {
span: self.span,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_builtin_macros/src/deriving/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub fn expand_deriving_hash(
span,
attributes: Vec::new(),
path,
bound_current_trait: true,
additional_bounds: Vec::new(),
generics: Bounds::empty(),
is_unsafe: false,
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_passes/src/dead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,11 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
}
return true;
}

// ignore generated `DerivedClone` but do not expose this to users.
if Some(trait_of) == self.tcx.get_diagnostic_item(sym::DerivedClone) {
return true;
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ symbols! {
Decoder,
Default,
Deref,
DerivedClone,
DirBuilder,
Display,
DoubleEndedIterator,
Expand Down Expand Up @@ -1412,6 +1413,7 @@ symbols! {
truncf32,
truncf64,
try_blocks,
try_copy,
try_from,
try_into,
try_trait_v2,
Expand Down
37 changes: 37 additions & 0 deletions library/core/src/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,43 @@ pub struct AssertParamIsCopy<T: Copy + ?Sized> {
_field: crate::marker::PhantomData<T>,
}

#[doc(hidden)]
#[unstable(
feature = "derive_clone_copy",
reason = "deriving hack, should not be public",
issue = "none"
)]
#[rustc_diagnostic_item = "DerivedClone"]
pub trait DerivedClone: Sized {
fn clone(&self) -> Self;
}

#[doc(hidden)]
#[unstable(
feature = "derive_clone_copy",
reason = "deriving hack, should not be public",
issue = "none"
)]
pub fn try_copy<T: Clone>(x: &T, clone: fn(&T) -> T) -> T {
trait TryCopy {
fn try_copy(&self, clone: fn(&Self) -> Self) -> Self;
}

impl<X: Clone> TryCopy for X {
default fn try_copy(&self, clone: fn(&Self) -> Self) -> Self {
clone(self)
}
}

impl<X: Copy> TryCopy for X {
fn try_copy(&self, _clone: fn(&Self) -> Self) -> Self {
*self
}
}

TryCopy::try_copy(x, clone)
}

/// Implementations of `Clone` for primitive types.
///
/// Implementations that cannot be described in Rust
Expand Down