Skip to content

Commit fba94ac

Browse files
committed
Specialize derive(Clone) to use Copy when applicable
1 parent 949b98c commit fba94ac

File tree

14 files changed

+114
-39
lines changed

14 files changed

+114
-39
lines changed

compiler/rustc_builtin_macros/src/deriving/bounds.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub fn expand_deriving_copy(
1717
span,
1818
attributes: Vec::new(),
1919
path: path_std!(marker::Copy),
20+
bound_current_trait: true,
2021
additional_bounds: Vec::new(),
2122
generics: Bounds::empty(),
2223
is_unsafe: false,

compiler/rustc_builtin_macros/src/deriving/clone.rs

Lines changed: 59 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,18 @@ pub fn expand_deriving_clone(
1515
item: &Annotatable,
1616
push: &mut dyn FnMut(Annotatable),
1717
) {
18-
// check if we can use a short form
19-
//
20-
// the short form is `fn clone(&self) -> Self { *self }`
21-
//
22-
// we can use the short form if:
23-
// - the item is Copy (unfortunately, all we can check is whether it's also deriving Copy)
24-
// - there are no generic parameters (after specialization this limitation can be removed)
25-
// if we used the short form with generics, we'd have to bound the generics with
26-
// Clone + Copy, and then there'd be no Clone impl at all if the user fills in something
27-
// that is Clone but not Copy. and until specialization we can't write both impls.
28-
// - the item is a union with Copy fields
29-
// Unions with generic parameters still can derive Clone because they require Copy
30-
// for deriving, Clone alone is not enough.
31-
// Wherever Clone is implemented for fields is irrelevant so we don't assert it.
3218
let bounds;
3319
let substructure;
20+
let is_union;
3421
let is_shallow;
3522
match *item {
3623
Annotatable::Item(ref annitem) => match annitem.kind {
3724
ItemKind::Struct(_, Generics { ref params, .. })
3825
| ItemKind::Enum(_, Generics { ref params, .. }) => {
26+
// FIXME: although the use of specialization already removes the need for checking whether
27+
// the type already derives `Copy`, `rustc_scalar_valid_range_*` types derive
28+
// `Clone` AND `Copy` and cannot be constructed unless unsafe blocks surround the expression,
29+
// thus this part is preserved.
3930
let container_id = cx.current_expansion.id.expn_data().parent.expect_local();
4031
let has_derive_copy = cx.resolver.has_derive_copy(container_id);
4132
if has_derive_copy
@@ -46,27 +37,27 @@ pub fn expand_deriving_clone(
4637
bounds = vec![];
4738
is_shallow = true;
4839
substructure = combine_substructure(Box::new(|c, s, sub| {
49-
cs_clone_shallow("Clone", c, s, sub, false)
40+
cs_clone_shallow(c, s, sub, false)
5041
}));
5142
} else {
5243
bounds = vec![];
5344
is_shallow = false;
54-
substructure =
55-
combine_substructure(Box::new(|c, s, sub| cs_clone("Clone", c, s, sub)));
45+
substructure = combine_substructure(Box::new(|c, s, sub| cs_clone(c, s, sub)));
5646
}
47+
is_union = false;
5748
}
5849
ItemKind::Union(..) => {
5950
bounds = vec![Literal(path_std!(marker::Copy))];
6051
is_shallow = true;
61-
substructure = combine_substructure(Box::new(|c, s, sub| {
62-
cs_clone_shallow("Clone", c, s, sub, true)
63-
}));
52+
substructure =
53+
combine_substructure(Box::new(|c, s, sub| cs_clone_shallow(c, s, sub, true)));
54+
is_union = true;
6455
}
6556
_ => {
6657
bounds = vec![];
6758
is_shallow = false;
68-
substructure =
69-
combine_substructure(Box::new(|c, s, sub| cs_clone("Clone", c, s, sub)));
59+
is_union = false;
60+
substructure = combine_substructure(Box::new(|c, s, sub| cs_clone(c, s, sub)));
7061
}
7162
},
7263

@@ -78,7 +69,8 @@ pub fn expand_deriving_clone(
7869
let trait_def = TraitDef {
7970
span,
8071
attributes: Vec::new(),
81-
path: path_std!(clone::Clone),
72+
path: if is_union { path_std!(clone::Clone) } else { path_std!(clone::DerivedClone) },
73+
bound_current_trait: false,
8274
additional_bounds: bounds,
8375
generics: Bounds::empty(),
8476
is_unsafe: false,
@@ -89,19 +81,55 @@ pub fn expand_deriving_clone(
8981
explicit_self: borrowed_explicit_self(),
9082
args: Vec::new(),
9183
ret_ty: Self_,
92-
attributes: attrs,
84+
attributes: attrs.clone(),
9385
is_unsafe: false,
9486
unify_fieldless_variants: false,
9587
combine_substructure: substructure,
9688
}],
9789
associated_types: Vec::new(),
9890
};
9991

100-
trait_def.expand_ext(cx, mitem, item, push, is_shallow)
92+
trait_def.expand_ext(cx, mitem, item, push, is_shallow);
93+
94+
if !is_union {
95+
TraitDef {
96+
span,
97+
attributes: Vec::new(),
98+
path: path_std!(clone::Clone),
99+
bound_current_trait: true,
100+
additional_bounds: vec![],
101+
generics: Bounds::empty(),
102+
is_unsafe: false,
103+
supports_unions: false,
104+
methods: vec![MethodDef {
105+
name: sym::clone,
106+
generics: Bounds::empty(),
107+
explicit_self: borrowed_explicit_self(),
108+
args: Vec::new(),
109+
ret_ty: Self_,
110+
attributes: attrs,
111+
is_unsafe: false,
112+
unify_fieldless_variants: false,
113+
combine_substructure: combine_substructure(Box::new(|c, s, _| {
114+
c.expr_call(
115+
s,
116+
c.expr_path(c.path(s, c.std_path(&[sym::clone, sym::try_copy]))),
117+
vec![
118+
c.expr_self(s),
119+
c.expr_path(
120+
c.path(s, c.std_path(&[sym::clone, sym::DerivedClone, sym::clone])),
121+
),
122+
],
123+
)
124+
})),
125+
}],
126+
associated_types: vec![],
127+
}
128+
.expand_ext(cx, mitem, item, push, true)
129+
}
101130
}
102131

103132
fn cs_clone_shallow(
104-
name: &str,
105133
cx: &mut ExtCtxt<'_>,
106134
trait_span: Span,
107135
substr: &Substructure<'_>,
@@ -149,20 +177,15 @@ fn cs_clone_shallow(
149177
}
150178
_ => cx.span_bug(
151179
trait_span,
152-
&format!("unexpected substructure in shallow `derive({})`", name),
180+
&format!("unexpected substructure in shallow `derive(Clone)`"),
153181
),
154182
}
155183
}
156184
stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span))));
157185
cx.expr_block(cx.block(trait_span, stmts))
158186
}
159187

160-
fn cs_clone(
161-
name: &str,
162-
cx: &mut ExtCtxt<'_>,
163-
trait_span: Span,
164-
substr: &Substructure<'_>,
165-
) -> P<Expr> {
188+
fn cs_clone(cx: &mut ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>) -> P<Expr> {
166189
let ctor_path;
167190
let all_fields;
168191
let fn_path = cx.std_path(&[sym::clone, sym::Clone, sym::clone]);
@@ -184,10 +207,10 @@ fn cs_clone(
184207
vdata = &variant.data;
185208
}
186209
EnumNonMatchingCollapsed(..) => {
187-
cx.span_bug(trait_span, &format!("non-matching enum variants in `derive({})`", name,))
210+
cx.span_bug(trait_span, "non-matching enum variants in `derive(Clone)`")
188211
}
189212
StaticEnum(..) | StaticStruct(..) => {
190-
cx.span_bug(trait_span, &format!("associated function in `derive({})`", name))
213+
cx.span_bug(trait_span, "associated function in `derive(Clone)`")
191214
}
192215
}
193216

@@ -199,7 +222,7 @@ fn cs_clone(
199222
let Some(ident) = field.name else {
200223
cx.span_bug(
201224
trait_span,
202-
&format!("unnamed field in normal struct in `derive({})`", name,),
225+
"unnamed field in normal struct in `derive(Clone)`",
203226
);
204227
};
205228
let call = subcall(cx, field);

compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub fn expand_deriving_eq(
2525
span,
2626
attributes: Vec::new(),
2727
path: path_std!(cmp::Eq),
28+
bound_current_trait: true,
2829
additional_bounds: Vec::new(),
2930
generics: Bounds::empty(),
3031
is_unsafe: false,

compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub fn expand_deriving_ord(
2121
span,
2222
attributes: Vec::new(),
2323
path: path_std!(cmp::Ord),
24+
bound_current_trait: true,
2425
additional_bounds: Vec::new(),
2526
generics: Bounds::empty(),
2627
is_unsafe: false,

compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ pub fn expand_deriving_partial_eq(
100100
span,
101101
attributes: Vec::new(),
102102
path: path_std!(cmp::PartialEq),
103+
bound_current_trait: true,
103104
additional_bounds: Vec::new(),
104105
generics: Bounds::empty(),
105106
is_unsafe: false,

compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ pub fn expand_deriving_partial_ord(
4444
span,
4545
attributes: vec![],
4646
path: path_std!(cmp::PartialOrd),
47+
bound_current_trait: true,
4748
additional_bounds: vec![],
4849
generics: Bounds::empty(),
4950
is_unsafe: false,

compiler/rustc_builtin_macros/src/deriving/debug.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pub fn expand_deriving_debug(
2727
span,
2828
attributes: Vec::new(),
2929
path: path_std!(fmt::Debug),
30+
bound_current_trait: true,
3031
additional_bounds: Vec::new(),
3132
generics: Bounds::empty(),
3233
is_unsafe: false,

compiler/rustc_builtin_macros/src/deriving/decodable.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ pub fn expand_deriving_rustc_decodable(
2424
span,
2525
attributes: Vec::new(),
2626
path: Path::new_(vec![krate, sym::Decodable], None, vec![], PathKind::Global),
27+
bound_current_trait: true,
2728
additional_bounds: Vec::new(),
2829
generics: Bounds::empty(),
2930
is_unsafe: false,

compiler/rustc_builtin_macros/src/deriving/default.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub fn expand_deriving_default(
2828
span,
2929
attributes: Vec::new(),
3030
path: Path::new(vec![kw::Default, sym::Default]),
31+
bound_current_trait: true,
3132
additional_bounds: Vec::new(),
3233
generics: Bounds::empty(),
3334
is_unsafe: false,

compiler/rustc_builtin_macros/src/deriving/encodable.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ pub fn expand_deriving_rustc_encodable(
109109
span,
110110
attributes: Vec::new(),
111111
path: Path::new_(vec![krate, sym::Encodable], None, vec![], PathKind::Global),
112+
bound_current_trait: true,
112113
additional_bounds: Vec::new(),
113114
generics: Bounds::empty(),
114115
is_unsafe: false,

compiler/rustc_builtin_macros/src/deriving/generic/mod.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ pub struct TraitDef<'a> {
205205
/// Path of the trait, including any type parameters
206206
pub path: Path,
207207

208+
pub bound_current_trait: bool,
209+
208210
/// Additional bounds required of any type parameters of the type,
209211
/// other than the current trait
210212
pub additional_bounds: Vec<Ty>,
@@ -591,7 +593,7 @@ impl<'a> TraitDef<'a> {
591593
cx.trait_bound(p.to_path(cx, self.span, type_ident, generics))
592594
}).chain(
593595
// require the current trait
594-
iter::once(cx.trait_bound(trait_path.clone()))
596+
self.bound_current_trait.then(|| cx.trait_bound(trait_path.clone()))
595597
).chain(
596598
// also add in any bounds from the declaration
597599
param.bounds.iter().cloned()
@@ -671,8 +673,10 @@ impl<'a> TraitDef<'a> {
671673
.map(|p| cx.trait_bound(p.to_path(cx, self.span, type_ident, generics)))
672674
.collect();
673675

674-
// require the current trait
675-
bounds.push(cx.trait_bound(trait_path.clone()));
676+
if self.bound_current_trait {
677+
// require the current trait
678+
bounds.push(cx.trait_bound(trait_path.clone()));
679+
}
676680

677681
let predicate = ast::WhereBoundPredicate {
678682
span: self.span,

compiler/rustc_builtin_macros/src/deriving/hash.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ pub fn expand_deriving_hash(
2424
span,
2525
attributes: Vec::new(),
2626
path,
27+
bound_current_trait: true,
2728
additional_bounds: Vec::new(),
2829
generics: Bounds::empty(),
2930
is_unsafe: false,

compiler/rustc_span/src/symbol.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ symbols! {
168168
Decoder,
169169
Default,
170170
Deref,
171+
DerivedClone,
171172
DirBuilder,
172173
Display,
173174
DoubleEndedIterator,
@@ -1412,6 +1413,7 @@ symbols! {
14121413
truncf32,
14131414
truncf64,
14141415
try_blocks,
1416+
try_copy,
14151417
try_from,
14161418
try_into,
14171419
try_trait_v2,

library/core/src/clone.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,42 @@ pub struct AssertParamIsCopy<T: Copy + ?Sized> {
172172
_field: crate::marker::PhantomData<T>,
173173
}
174174

175+
#[doc(hidden)]
176+
#[unstable(
177+
feature = "derive_clone_copy",
178+
reason = "deriving hack, should not be public",
179+
issue = "none"
180+
)]
181+
pub trait DerivedClone: Sized {
182+
fn clone(&self) -> Self;
183+
}
184+
185+
#[doc(hidden)]
186+
#[unstable(
187+
feature = "derive_clone_copy",
188+
reason = "deriving hack, should not be public",
189+
issue = "none"
190+
)]
191+
pub fn try_copy<T: Clone>(x: &T, clone: fn(&T) -> T) -> T {
192+
trait TryCopy {
193+
fn try_copy(&self, clone: fn(&Self) -> Self) -> Self;
194+
}
195+
196+
impl<X: Clone> TryCopy for X {
197+
default fn try_copy(&self, clone: fn(&Self) -> Self) -> Self {
198+
clone(self)
199+
}
200+
}
201+
202+
impl<X: Copy> TryCopy for X {
203+
fn try_copy(&self, _clone: fn(&Self) -> Self) -> Self {
204+
*self
205+
}
206+
}
207+
208+
TryCopy::try_copy(x, clone)
209+
}
210+
175211
/// Implementations of `Clone` for primitive types.
176212
///
177213
/// Implementations that cannot be described in Rust

0 commit comments

Comments
 (0)