Skip to content

Commit 7268501

Browse files
authored
Rollup merge of #36384 - petrochenkov:derclone, r=alexcrichton
Improve shallow `Clone` deriving `Copy` unions now support `#[derive(Clone)]`. Less code is generated for `#[derive(Clone, Copy)]`. + Unions now support `#[derive(Eq)]`. Less code is generated for `#[derive(Eq)]`. --- Example of code reduction: ``` enum E { A { a: u8, b: u16 }, B { c: [u8; 100] }, } ``` Before: ``` fn clone(&self) -> E { match (&*self,) { (&E::A { a: ref __self_0, b: ref __self_1 },) => { ::std::clone::assert_receiver_is_clone(&(*__self_0)); ::std::clone::assert_receiver_is_clone(&(*__self_1)); *self } (&E::B { c: ref __self_0 },) => { ::std::clone::assert_receiver_is_clone(&(*__self_0)); *self } } } ``` After: ``` fn clone(&self) -> E { { let _: ::std::clone::AssertParamIsClone<u8>; let _: ::std::clone::AssertParamIsClone<u16>; let _: ::std::clone::AssertParamIsClone<[u8; 100]>; *self } } ``` All the matches are removed, bound assertions are more lightweight. `let _: Checker<CheckMe>;`, unlike `checker(&check_me);`, doesn't have to be translated by rustc_trans and then inlined by LLVM, it doesn't even exist in MIR, this means faster compilation. --- Union impls are generated like this: ``` union U { a: u8, b: u16, c: [u8; 100], } ``` ``` fn clone(&self) -> U { { let _: ::std::clone::AssertParamIsCopy<Self>; *self } } ``` Fixes #36043 cc @durka r? @alexcrichton
2 parents 16ff9e2 + 62cb751 commit 7268501

File tree

14 files changed

+293
-121
lines changed

14 files changed

+293
-121
lines changed

src/libcore/clone.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,23 @@ pub trait Clone : Sized {
113113
}
114114
}
115115

116-
// FIXME(aburka): this method is used solely by #[derive] to
117-
// assert that every component of a type implements Clone.
116+
// FIXME(aburka): these structs are used solely by #[derive] to
117+
// assert that every component of a type implements Clone or Copy.
118118
//
119-
// This should never be called by user code.
119+
// These structs should never appear in user code.
120+
#[doc(hidden)]
121+
#[allow(missing_debug_implementations)]
122+
#[unstable(feature = "derive_clone_copy",
123+
reason = "deriving hack, should not be public",
124+
issue = "0")]
125+
pub struct AssertParamIsClone<T: Clone + ?Sized> { _field: ::marker::PhantomData<T> }
126+
#[doc(hidden)]
127+
#[allow(missing_debug_implementations)]
128+
#[unstable(feature = "derive_clone_copy",
129+
reason = "deriving hack, should not be public",
130+
issue = "0")]
131+
pub struct AssertParamIsCopy<T: Copy + ?Sized> { _field: ::marker::PhantomData<T> }
132+
#[cfg(stage0)]
120133
#[doc(hidden)]
121134
#[inline(always)]
122135
#[unstable(feature = "derive_clone_copy",

src/libcore/cmp.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ pub trait PartialEq<Rhs: ?Sized = Self> {
129129
/// This trait can be used with `#[derive]`. When `derive`d, because `Eq` has
130130
/// no extra methods, it is only informing the compiler that this is an
131131
/// equivalence relation rather than a partial equivalence relation. Note that
132-
/// the `derive` strategy requires all fields are `PartialEq`, which isn't
132+
/// the `derive` strategy requires all fields are `Eq`, which isn't
133133
/// always desired.
134134
///
135135
/// ## How can I implement `Eq`?
@@ -165,6 +165,17 @@ pub trait Eq: PartialEq<Self> {
165165
fn assert_receiver_is_total_eq(&self) {}
166166
}
167167

168+
// FIXME: this struct is used solely by #[derive] to
169+
// assert that every component of a type implements Eq.
170+
//
171+
// This struct should never appear in user code.
172+
#[doc(hidden)]
173+
#[allow(missing_debug_implementations)]
174+
#[unstable(feature = "derive_eq",
175+
reason = "deriving hack, should not be public",
176+
issue = "0")]
177+
pub struct AssertParamIsEq<T: Eq + ?Sized> { _field: ::marker::PhantomData<T> }
178+
168179
/// An `Ordering` is the result of a comparison between two values.
169180
///
170181
/// # Examples

src/libsyntax/ext/build.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ pub trait AstBuilder {
9797
typ: P<ast::Ty>,
9898
ex: P<ast::Expr>)
9999
-> P<ast::Stmt>;
100+
fn stmt_let_type_only(&self, span: Span, ty: P<ast::Ty>) -> ast::Stmt;
100101
fn stmt_item(&self, sp: Span, item: P<ast::Item>) -> ast::Stmt;
101102

102103
// blocks
@@ -577,6 +578,23 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
577578
})
578579
}
579580

581+
// Generate `let _: Type;`, usually used for type assertions.
582+
fn stmt_let_type_only(&self, span: Span, ty: P<ast::Ty>) -> ast::Stmt {
583+
let local = P(ast::Local {
584+
pat: self.pat_wild(span),
585+
ty: Some(ty),
586+
init: None,
587+
id: ast::DUMMY_NODE_ID,
588+
span: span,
589+
attrs: ast::ThinVec::new(),
590+
});
591+
ast::Stmt {
592+
id: ast::DUMMY_NODE_ID,
593+
node: ast::StmtKind::Local(local),
594+
span: span,
595+
}
596+
}
597+
580598
fn stmt_item(&self, sp: Span, item: P<ast::Item>) -> ast::Stmt {
581599
ast::Stmt {
582600
id: ast::DUMMY_NODE_ID,

src/libsyntax_ext/deriving/clone.rs

Lines changed: 96 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,14 @@
1111
use deriving::generic::*;
1212
use deriving::generic::ty::*;
1313

14-
use syntax::ast::{Expr, Generics, ItemKind, MetaItem, VariantData};
14+
use syntax::ast::{self, Expr, Generics, ItemKind, MetaItem, VariantData};
1515
use syntax::attr;
1616
use syntax::ext::base::{Annotatable, ExtCtxt};
1717
use syntax::ext::build::AstBuilder;
18-
use syntax::parse::token::InternedString;
18+
use syntax::parse::token::{keywords, InternedString};
1919
use syntax::ptr::P;
2020
use syntax_pos::Span;
2121

22-
#[derive(PartialEq)]
23-
enum Mode {
24-
Deep,
25-
Shallow,
26-
}
27-
2822
pub fn expand_deriving_clone(cx: &mut ExtCtxt,
2923
span: Span,
3024
mitem: &MetaItem,
@@ -40,29 +34,38 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt,
4034
// if we used the short form with generics, we'd have to bound the generics with
4135
// Clone + Copy, and then there'd be no Clone impl at all if the user fills in something
4236
// that is Clone but not Copy. and until specialization we can't write both impls.
37+
// - the item is a union with Copy fields
38+
// Unions with generic parameters still can derive Clone because they require Copy
39+
// for deriving, Clone alone is not enough.
40+
// Whever Clone is implemented for fields is irrelevant so we don't assert it.
4341
let bounds;
44-
let unify_fieldless_variants;
4542
let substructure;
43+
let is_shallow;
4644
match *item {
4745
Annotatable::Item(ref annitem) => {
4846
match annitem.node {
4947
ItemKind::Struct(_, Generics { ref ty_params, .. }) |
5048
ItemKind::Enum(_, Generics { ref ty_params, .. })
51-
if ty_params.is_empty() &&
52-
attr::contains_name(&annitem.attrs, "rustc_copy_clone_marker") => {
53-
49+
if attr::contains_name(&annitem.attrs, "rustc_copy_clone_marker") &&
50+
ty_params.is_empty() => {
51+
bounds = vec![];
52+
is_shallow = true;
53+
substructure = combine_substructure(Box::new(|c, s, sub| {
54+
cs_clone_shallow("Clone", c, s, sub, false)
55+
}));
56+
}
57+
ItemKind::Union(..) => {
5458
bounds = vec![Literal(path_std!(cx, core::marker::Copy))];
55-
unify_fieldless_variants = true;
59+
is_shallow = true;
5660
substructure = combine_substructure(Box::new(|c, s, sub| {
57-
cs_clone("Clone", c, s, sub, Mode::Shallow)
61+
cs_clone_shallow("Clone", c, s, sub, true)
5862
}));
5963
}
60-
6164
_ => {
6265
bounds = vec![];
63-
unify_fieldless_variants = false;
66+
is_shallow = false;
6467
substructure = combine_substructure(Box::new(|c, s, sub| {
65-
cs_clone("Clone", c, s, sub, Mode::Deep)
68+
cs_clone("Clone", c, s, sub)
6669
}));
6770
}
6871
}
@@ -80,7 +83,7 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt,
8083
additional_bounds: bounds,
8184
generics: LifetimeBounds::empty(),
8285
is_unsafe: false,
83-
supports_unions: false,
86+
supports_unions: true,
8487
methods: vec![MethodDef {
8588
name: "clone",
8689
generics: LifetimeBounds::empty(),
@@ -89,37 +92,72 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt,
8992
ret_ty: Self_,
9093
attributes: attrs,
9194
is_unsafe: false,
92-
unify_fieldless_variants: unify_fieldless_variants,
95+
unify_fieldless_variants: false,
9396
combine_substructure: substructure,
9497
}],
9598
associated_types: Vec::new(),
9699
};
97100

98-
trait_def.expand(cx, mitem, item, push)
101+
trait_def.expand_ext(cx, mitem, item, push, is_shallow)
102+
}
103+
104+
fn cs_clone_shallow(name: &str,
105+
cx: &mut ExtCtxt,
106+
trait_span: Span,
107+
substr: &Substructure,
108+
is_union: bool)
109+
-> P<Expr> {
110+
fn assert_ty_bounds(cx: &mut ExtCtxt, stmts: &mut Vec<ast::Stmt>,
111+
ty: P<ast::Ty>, span: Span, helper_name: &str) {
112+
// Generate statement `let _: helper_name<ty>;`,
113+
// set the expn ID so we can use the unstable struct.
114+
let span = super::allow_unstable(cx, span, "derive(Clone)");
115+
let assert_path = cx.path_all(span, true,
116+
cx.std_path(&["clone", helper_name]),
117+
vec![], vec![ty], vec![]);
118+
stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path)));
119+
}
120+
fn process_variant(cx: &mut ExtCtxt, stmts: &mut Vec<ast::Stmt>, variant: &VariantData) {
121+
for field in variant.fields() {
122+
// let _: AssertParamIsClone<FieldTy>;
123+
assert_ty_bounds(cx, stmts, field.ty.clone(), field.span, "AssertParamIsClone");
124+
}
125+
}
126+
127+
let mut stmts = Vec::new();
128+
if is_union {
129+
// let _: AssertParamIsCopy<Self>;
130+
let self_ty = cx.ty_path(cx.path_ident(trait_span, keywords::SelfType.ident()));
131+
assert_ty_bounds(cx, &mut stmts, self_ty, trait_span, "AssertParamIsCopy");
132+
} else {
133+
match *substr.fields {
134+
StaticStruct(vdata, ..) => {
135+
process_variant(cx, &mut stmts, vdata);
136+
}
137+
StaticEnum(enum_def, ..) => {
138+
for variant in &enum_def.variants {
139+
process_variant(cx, &mut stmts, &variant.node.data);
140+
}
141+
}
142+
_ => cx.span_bug(trait_span, &format!("unexpected substructure in \
143+
shallow `derive({})`", name))
144+
}
145+
}
146+
stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span))));
147+
cx.expr_block(cx.block(trait_span, stmts))
99148
}
100149

101150
fn cs_clone(name: &str,
102151
cx: &mut ExtCtxt,
103152
trait_span: Span,
104-
substr: &Substructure,
105-
mode: Mode)
153+
substr: &Substructure)
106154
-> P<Expr> {
107155
let ctor_path;
108156
let all_fields;
109-
let fn_path = match mode {
110-
Mode::Shallow => cx.std_path(&["clone", "assert_receiver_is_clone"]),
111-
Mode::Deep => cx.std_path(&["clone", "Clone", "clone"]),
112-
};
157+
let fn_path = cx.std_path(&["clone", "Clone", "clone"]);
113158
let subcall = |cx: &mut ExtCtxt, field: &FieldInfo| {
114159
let args = vec![cx.expr_addr_of(field.span, field.self_.clone())];
115-
116-
let span = if mode == Mode::Shallow {
117-
// set the expn ID so we can call the unstable method
118-
super::allow_unstable(cx, field.span, "derive(Clone)")
119-
} else {
120-
field.span
121-
};
122-
cx.expr_call_global(span, fn_path.clone(), args)
160+
cx.expr_call_global(field.span, fn_path.clone(), args)
123161
};
124162

125163
let vdata;
@@ -145,43 +183,31 @@ fn cs_clone(name: &str,
145183
}
146184
}
147185

148-
match mode {
149-
Mode::Shallow => {
150-
let mut stmts = all_fields.iter().map(|f| {
151-
let call = subcall(cx, f);
152-
cx.stmt_expr(call)
153-
}).collect::<Vec<_>>();
154-
stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span))));
155-
cx.expr_block(cx.block(trait_span, stmts))
156-
}
157-
Mode::Deep => {
158-
match *vdata {
159-
VariantData::Struct(..) => {
160-
let fields = all_fields.iter()
161-
.map(|field| {
162-
let ident = match field.name {
163-
Some(i) => i,
164-
None => {
165-
cx.span_bug(trait_span,
166-
&format!("unnamed field in normal struct in \
167-
`derive({})`",
168-
name))
169-
}
170-
};
171-
let call = subcall(cx, field);
172-
cx.field_imm(field.span, ident, call)
173-
})
174-
.collect::<Vec<_>>();
186+
match *vdata {
187+
VariantData::Struct(..) => {
188+
let fields = all_fields.iter()
189+
.map(|field| {
190+
let ident = match field.name {
191+
Some(i) => i,
192+
None => {
193+
cx.span_bug(trait_span,
194+
&format!("unnamed field in normal struct in \
195+
`derive({})`",
196+
name))
197+
}
198+
};
199+
let call = subcall(cx, field);
200+
cx.field_imm(field.span, ident, call)
201+
})
202+
.collect::<Vec<_>>();
175203

176-
cx.expr_struct(trait_span, ctor_path, fields)
177-
}
178-
VariantData::Tuple(..) => {
179-
let subcalls = all_fields.iter().map(|f| subcall(cx, f)).collect();
180-
let path = cx.expr_path(ctor_path);
181-
cx.expr_call(trait_span, path, subcalls)
182-
}
183-
VariantData::Unit(..) => cx.expr_path(ctor_path),
184-
}
204+
cx.expr_struct(trait_span, ctor_path, fields)
205+
}
206+
VariantData::Tuple(..) => {
207+
let subcalls = all_fields.iter().map(|f| subcall(cx, f)).collect();
208+
let path = cx.expr_path(ctor_path);
209+
cx.expr_call(trait_span, path, subcalls)
185210
}
211+
VariantData::Unit(..) => cx.expr_path(ctor_path),
186212
}
187213
}

0 commit comments

Comments
 (0)