Skip to content

Commit 2a2c9d3

Browse files
committed
Improve shallow Clone deriving
1 parent f1f40f8 commit 2a2c9d3

File tree

10 files changed

+199
-100
lines changed

10 files changed

+199
-100
lines changed

src/libcore/clone.rs

+16-3
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,23 @@ pub trait Clone : Sized {
106106
}
107107
}
108108

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

src/libsyntax_ext/deriving/clone.rs

+109-70
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,85 @@ 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+
let local = P(ast::Local {
119+
pat: cx.pat_wild(span),
120+
ty: Some(cx.ty_path(assert_path)),
121+
init: None,
122+
id: ast::DUMMY_NODE_ID,
123+
span: span,
124+
attrs: ast::ThinVec::new(),
125+
});
126+
let stmt = ast::Stmt {
127+
id: ast::DUMMY_NODE_ID,
128+
node: ast::StmtKind::Local(local),
129+
span: span,
130+
};
131+
stmts.push(stmt);
132+
}
133+
fn process_variant(cx: &mut ExtCtxt, stmts: &mut Vec<ast::Stmt>, variant: &VariantData) {
134+
for field in variant.fields() {
135+
// let _: AssertParamIsClone<FieldTy>;
136+
assert_ty_bounds(cx, stmts, field.ty.clone(), field.span, "AssertParamIsClone");
137+
}
138+
}
139+
140+
let mut stmts = Vec::new();
141+
if is_union {
142+
// let _: AssertParamIsCopy<Self>;
143+
let self_ty = cx.ty_path(cx.path_ident(trait_span, keywords::SelfType.ident()));
144+
assert_ty_bounds(cx, &mut stmts, self_ty, trait_span, "AssertParamIsCopy");
145+
} else {
146+
match *substr.fields {
147+
StaticStruct(vdata, ..) => {
148+
process_variant(cx, &mut stmts, vdata);
149+
}
150+
StaticEnum(enum_def, ..) => {
151+
for variant in &enum_def.variants {
152+
process_variant(cx, &mut stmts, &variant.node.data);
153+
}
154+
}
155+
_ => cx.span_bug(trait_span, &format!("unexpected substructure in \
156+
shallow `derive({})`", name))
157+
}
158+
}
159+
stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span))));
160+
cx.expr_block(cx.block(trait_span, stmts))
99161
}
100162

101163
fn cs_clone(name: &str,
102164
cx: &mut ExtCtxt,
103165
trait_span: Span,
104-
substr: &Substructure,
105-
mode: Mode)
166+
substr: &Substructure)
106167
-> P<Expr> {
107168
let ctor_path;
108169
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-
};
170+
let fn_path = cx.std_path(&["clone", "Clone", "clone"]);
113171
let subcall = |cx: &mut ExtCtxt, field: &FieldInfo| {
114172
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)
173+
cx.expr_call_global(field.span, fn_path.clone(), args)
123174
};
124175

125176
let vdata;
@@ -145,43 +196,31 @@ fn cs_clone(name: &str,
145196
}
146197
}
147198

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<_>>();
199+
match *vdata {
200+
VariantData::Struct(..) => {
201+
let fields = all_fields.iter()
202+
.map(|field| {
203+
let ident = match field.name {
204+
Some(i) => i,
205+
None => {
206+
cx.span_bug(trait_span,
207+
&format!("unnamed field in normal struct in \
208+
`derive({})`",
209+
name))
210+
}
211+
};
212+
let call = subcall(cx, field);
213+
cx.field_imm(field.span, ident, call)
214+
})
215+
.collect::<Vec<_>>();
175216

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-
}
217+
cx.expr_struct(trait_span, ctor_path, fields)
218+
}
219+
VariantData::Tuple(..) => {
220+
let subcalls = all_fields.iter().map(|f| subcall(cx, f)).collect();
221+
let path = cx.expr_path(ctor_path);
222+
cx.expr_call(trait_span, path, subcalls)
185223
}
224+
VariantData::Unit(..) => cx.expr_path(ctor_path),
186225
}
187226
}

src/libsyntax_ext/deriving/generic/mod.rs

+20-7
Original file line numberDiff line numberDiff line change
@@ -401,18 +401,29 @@ impl<'a> TraitDef<'a> {
401401
mitem: &ast::MetaItem,
402402
item: &'a Annotatable,
403403
push: &mut FnMut(Annotatable)) {
404+
self.expand_ext(cx, mitem, item, push, false);
405+
}
406+
407+
pub fn expand_ext(&self,
408+
cx: &mut ExtCtxt,
409+
mitem: &ast::MetaItem,
410+
item: &'a Annotatable,
411+
push: &mut FnMut(Annotatable),
412+
from_scratch: bool) {
404413
match *item {
405414
Annotatable::Item(ref item) => {
406415
let newitem = match item.node {
407416
ast::ItemKind::Struct(ref struct_def, ref generics) => {
408-
self.expand_struct_def(cx, &struct_def, item.ident, generics)
417+
self.expand_struct_def(cx, &struct_def, item.ident, generics, from_scratch)
409418
}
410419
ast::ItemKind::Enum(ref enum_def, ref generics) => {
411-
self.expand_enum_def(cx, enum_def, &item.attrs, item.ident, generics)
420+
self.expand_enum_def(cx, enum_def, &item.attrs,
421+
item.ident, generics, from_scratch)
412422
}
413423
ast::ItemKind::Union(ref struct_def, ref generics) => {
414424
if self.supports_unions {
415-
self.expand_struct_def(cx, &struct_def, item.ident, generics)
425+
self.expand_struct_def(cx, &struct_def, item.ident,
426+
generics, from_scratch)
416427
} else {
417428
cx.span_err(mitem.span,
418429
"this trait cannot be derived for unions");
@@ -661,7 +672,8 @@ impl<'a> TraitDef<'a> {
661672
cx: &mut ExtCtxt,
662673
struct_def: &'a VariantData,
663674
type_ident: Ident,
664-
generics: &Generics)
675+
generics: &Generics,
676+
from_scratch: bool)
665677
-> P<ast::Item> {
666678
let field_tys: Vec<P<ast::Ty>> = struct_def.fields()
667679
.iter()
@@ -674,7 +686,7 @@ impl<'a> TraitDef<'a> {
674686
let (explicit_self, self_args, nonself_args, tys) =
675687
method_def.split_self_nonself_args(cx, self, type_ident, generics);
676688

677-
let body = if method_def.is_static() {
689+
let body = if from_scratch || method_def.is_static() {
678690
method_def.expand_static_struct_method_body(cx,
679691
self,
680692
struct_def,
@@ -709,7 +721,8 @@ impl<'a> TraitDef<'a> {
709721
enum_def: &'a EnumDef,
710722
type_attrs: &[ast::Attribute],
711723
type_ident: Ident,
712-
generics: &Generics)
724+
generics: &Generics,
725+
from_scratch: bool)
713726
-> P<ast::Item> {
714727
let mut field_tys = Vec::new();
715728

@@ -727,7 +740,7 @@ impl<'a> TraitDef<'a> {
727740
let (explicit_self, self_args, nonself_args, tys) =
728741
method_def.split_self_nonself_args(cx, self, type_ident, generics);
729742

730-
let body = if method_def.is_static() {
743+
let body = if from_scratch || method_def.is_static() {
731744
method_def.expand_static_enum_method_body(cx,
732745
self,
733746
enum_def,

src/test/compile-fail/borrowck/borrowck-union-borrow-nested.rs

+1-5
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,12 @@ struct S {
1818
b: u16,
1919
}
2020

21+
#[derive(Clone, Copy)]
2122
union U {
2223
s: S,
2324
c: u32,
2425
}
2526

26-
impl Clone for U {
27-
fn clone(&self) -> Self { *self }
28-
}
29-
impl Copy for U {}
30-
3127
fn main() {
3228
unsafe {
3329
{

src/test/compile-fail/borrowck/borrowck-union-borrow.rs

+1-5
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,12 @@
1212

1313
#![feature(untagged_unions)]
1414

15+
#[derive(Clone, Copy)]
1516
union U {
1617
a: u8,
1718
b: u64,
1819
}
1920

20-
impl Clone for U {
21-
fn clone(&self) -> Self { *self }
22-
}
23-
impl Copy for U {}
24-
2521
fn main() {
2622
unsafe {
2723
let mut u = U { b: 0 };

0 commit comments

Comments
 (0)