Skip to content

Commit 7fdc4f5

Browse files
progress
1 parent 3d829a0 commit 7fdc4f5

File tree

3 files changed

+121
-19
lines changed

3 files changed

+121
-19
lines changed

compiler/rustc_builtin_macros/src/deriving/clone.rs

Lines changed: 111 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ use crate::deriving::generic::ty::*;
22
use crate::deriving::generic::*;
33
use crate::deriving::path_std;
44

5+
use rustc_ast as ast;
56
use rustc_ast::ptr::P;
6-
use rustc_ast::{self as ast, Expr, GenericArg, Generics, ItemKind, MetaItem, VariantData};
7+
use rustc_ast::{Expr, GenericArg, Generics, ItemKind, MetaItem, VariantData};
78
use rustc_expand::base::{Annotatable, ExtCtxt};
89
use rustc_span::symbol::{kw, sym, Ident, Symbol};
910
use rustc_span::Span;
@@ -30,7 +31,8 @@ pub fn expand_deriving_clone(
3031
// for deriving, Clone alone is not enough.
3132
// Wherever Clone is implemented for fields is irrelevant so we don't assert it.
3233
let bounds;
33-
let substructure;
34+
let substructure_clone;
35+
let substructure_clone_from;
3436
let is_shallow;
3537
match *item {
3638
Annotatable::Item(ref annitem) => match annitem.kind {
@@ -45,36 +47,80 @@ pub fn expand_deriving_clone(
4547
{
4648
bounds = vec![];
4749
is_shallow = true;
48-
substructure = combine_substructure(Box::new(|c, s, sub| {
50+
substructure_clone = combine_substructure(Box::new(|c, s, sub| {
4951
cs_clone_shallow("Clone", c, s, sub, false)
5052
}));
53+
// There is no point in implementing `Clone::clone_from` for `Copy` types
54+
// because they don't own resources to preserve.
55+
// Default implementation would suffice and this would save compilation time a little.
56+
substructure_clone_from = None;
5157
} else {
5258
bounds = vec![];
5359
is_shallow = false;
54-
substructure =
60+
substructure_clone =
5561
combine_substructure(Box::new(|c, s, sub| cs_clone("Clone", c, s, sub)));
62+
if is_type_without_fields(item) {
63+
// It clones field by field
64+
// so there is no point to generate it if there aren't any.
65+
substructure_clone_from = None;
66+
} else {
67+
substructure_clone_from =
68+
Some(combine_substructure(Box::new(|c, s, sub| {
69+
cs_clone_from("Clone", c, s, sub)
70+
})))
71+
}
5672
}
5773
}
5874
ItemKind::Union(..) => {
5975
bounds = vec![Literal(path_std!(marker::Copy))];
6076
is_shallow = true;
61-
substructure = combine_substructure(Box::new(|c, s, sub| {
77+
substructure_clone = combine_substructure(Box::new(|c, s, sub| {
6278
cs_clone_shallow("Clone", c, s, sub, true)
6379
}));
80+
// Same reasoning as with `is_shallow`.
81+
substructure_clone_from = None;
6482
}
6583
_ => {
6684
bounds = vec![];
6785
is_shallow = false;
68-
substructure =
86+
substructure_clone =
6987
combine_substructure(Box::new(|c, s, sub| cs_clone("Clone", c, s, sub)));
88+
substructure_clone_from = None;
7089
}
7190
},
7291

7392
_ => cx.span_bug(span, "`#[derive(Clone)]` on trait item or impl item"),
7493
}
7594

7695
let inline = cx.meta_word(span, sym::inline);
77-
let attrs = vec![cx.attribute(inline)];
96+
let attrs = [cx.attribute(inline)];
97+
98+
let mut methods = Vec::with_capacity(2);
99+
methods.push(MethodDef {
100+
name: sym::clone,
101+
generics: Bounds::empty(),
102+
explicit_self: borrowed_explicit_self(),
103+
args: Vec::new(),
104+
ret_ty: Self_,
105+
attributes: attrs.to_vec(),
106+
is_unsafe: false,
107+
unify_fieldless_variants: false,
108+
combine_substructure: substructure_clone,
109+
});
110+
if let Some(substructure_clone_from) = substructure_clone_from {
111+
methods.push(MethodDef {
112+
name: sym::clone_from,
113+
generics: Bounds::empty(),
114+
explicit_self: mutable_explicit_self(),
115+
args: vec![(borrowed_self(), sym::other)],
116+
ret_ty: nil_ty(),
117+
attributes: attrs.to_vec(),
118+
is_unsafe: false,
119+
unify_fieldless_variants: false,
120+
combine_substructure: substructure_clone_from,
121+
})
122+
}
123+
78124
let trait_def = TraitDef {
79125
span,
80126
attributes: Vec::new(),
@@ -83,17 +129,7 @@ pub fn expand_deriving_clone(
83129
generics: Bounds::empty(),
84130
is_unsafe: false,
85131
supports_unions: true,
86-
methods: vec![MethodDef {
87-
name: sym::clone,
88-
generics: Bounds::empty(),
89-
explicit_self: borrowed_explicit_self(),
90-
args: Vec::new(),
91-
ret_ty: Self_,
92-
attributes: attrs,
93-
is_unsafe: false,
94-
unify_fieldless_variants: false,
95-
combine_substructure: substructure,
96-
}],
132+
methods,
97133
associated_types: Vec::new(),
98134
};
99135

@@ -157,6 +193,10 @@ fn cs_clone_shallow(
157193
cx.expr_block(cx.block(trait_span, stmts))
158194
}
159195

196+
fn clone_fn_full_path(cx: &ExtCtxt<'_>) -> Vec<Ident> {
197+
cx.std_path(&[sym::clone, sym::Clone, sym::clone])
198+
}
199+
160200
fn cs_clone(
161201
name: &str,
162202
cx: &mut ExtCtxt<'_>,
@@ -165,7 +205,7 @@ fn cs_clone(
165205
) -> P<Expr> {
166206
let ctor_path;
167207
let all_fields;
168-
let fn_path = cx.std_path(&[sym::clone, sym::Clone, sym::clone]);
208+
let fn_path = clone_fn_full_path(cx);
169209
let subcall = |cx: &mut ExtCtxt<'_>, field: &FieldInfo<'_>| {
170210
let args = vec![cx.expr_addr_of(field.span, field.self_.clone())];
171211
cx.expr_call_global(field.span, fn_path.clone(), args)
@@ -217,3 +257,55 @@ fn cs_clone(
217257
VariantData::Unit(..) => cx.expr_path(ctor_path),
218258
}
219259
}
260+
261+
fn cs_clone_from(
262+
name: &str,
263+
cx: &mut ExtCtxt<'_>,
264+
trait_span: Span,
265+
substr: &Substructure<'_>,
266+
) -> P<Expr> {
267+
let all_fields = match *substr.fields {
268+
Struct(.., ref af) => af,
269+
EnumMatching(.., ref af) => af,
270+
EnumNonMatchingCollapsed(ref idents, ..) => {
271+
// Cannot do something smart here.
272+
// so emit `*self = other.clone();`
273+
274+
let [self_, other] = idents[..] else{
275+
cx.span_bug(trait_span, &format!("not exactly 2 arguments in `clone_from` in `derive({})`", name))
276+
};
277+
let self_ = cx.expr_deref(trait_span, cx.expr_ident(trait_span, self_));
278+
let other = cx.expr_ident(trait_span, other);
279+
let clone_call = cx.expr_call_global(
280+
trait_span,
281+
clone_fn_full_path(cx),
282+
vec![cx.expr_addr_of(trait_span, other)],
283+
);
284+
return cx.expr(trait_span, ast::ExprKind::Assign(self_, clone_call, trait_span));
285+
}
286+
StaticEnum(..) | StaticStruct(..) => {
287+
cx.span_bug(trait_span, &format!("associated function in `derive({})`", name))
288+
}
289+
};
290+
291+
// Here we know that we have same fields in `&mut self` and in `other`
292+
// so we can call `clone_from` of each of them.
293+
let clone_from_path = cx.std_path(&[sym::clone, sym::Clone, sym::clone_from]);
294+
let fields_clones_from: Vec<_> = all_fields
295+
.iter()
296+
.map(|field| {
297+
if field.other.len() != 1 {
298+
cx.span_bug(
299+
trait_span,
300+
&format!("not exactly 2 arguments in `clone_from` in `derive({})`", name),
301+
);
302+
}
303+
cx.stmt_semi(cx.expr_call_global(
304+
field.span,
305+
clone_from_path.clone(),
306+
vec![field.self_.clone(), field.other[0].clone()],
307+
))
308+
})
309+
.collect();
310+
cx.expr_block(cx.block(trait_span, fields_clones_from))
311+
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,16 @@ pub fn borrowed(ty: Box<Ty>) -> Ty {
108108
Ptr(ty, borrowed_ptrty())
109109
}
110110

111+
/// `&self` argument
111112
pub fn borrowed_explicit_self() -> Option<Option<PtrTy>> {
112113
Some(Some(borrowed_ptrty()))
113114
}
114115

116+
/// `&mut self` argument
117+
pub fn mutable_explicit_self() -> Option<Option<PtrTy>> {
118+
Some(Some(PtrTy::Borrowed(None, ast::Mutability::Mut)))
119+
}
120+
115121
pub fn borrowed_self() -> Ty {
116122
borrowed(Box::new(Self_))
117123
}

compiler/rustc_expand/src/build.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,10 @@ impl<'a> ExtCtxt<'a> {
144144
ast::Stmt { id: ast::DUMMY_NODE_ID, span: expr.span, kind: ast::StmtKind::Expr(expr) }
145145
}
146146

147+
pub fn stmt_semi(&self, expr: P<ast::Expr>) -> ast::Stmt {
148+
ast::Stmt { id: ast::DUMMY_NODE_ID, span: expr.span, kind: ast::StmtKind::Semi(expr) }
149+
}
150+
147151
pub fn stmt_let(&self, sp: Span, mutbl: bool, ident: Ident, ex: P<ast::Expr>) -> ast::Stmt {
148152
let pat = if mutbl {
149153
let binding_mode = ast::BindingMode::ByValue(ast::Mutability::Mut);

0 commit comments

Comments
 (0)