Skip to content

Commit 6712c02

Browse files
committed
assert_clone in shallow #[derive(Copy,Clone)]
1 parent 0cf4bc5 commit 6712c02

File tree

16 files changed

+114
-69
lines changed

16 files changed

+114
-69
lines changed

src/libcore/clone.rs

+9
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,15 @@ pub trait Clone : Sized {
7575
}
7676
}
7777

78+
// FIXME(aburka): this method is used solely by #[deriving] to
79+
// assert that every component of a type implements Clone.
80+
//
81+
// This should never be called by user code.
82+
#[doc(hidden)]
83+
#[inline(always)]
84+
#[stable(feature = "rust1", since = "1.0.0")]
85+
pub fn assert_receiver_is_clone<T: Clone + ?Sized>(_: &T) {}
86+
7887
#[stable(feature = "rust1", since = "1.0.0")]
7988
impl<'a, T: ?Sized> Clone for &'a T {
8089
/// Returns a shallow copy of the reference.

src/libsyntax_ext/deriving/clone.rs

+55-32
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ use syntax::ext::build::AstBuilder;
1919
use syntax::parse::token::InternedString;
2020
use syntax::ptr::P;
2121

22+
enum Mode { Assert, Clone }
23+
2224
pub fn expand_deriving_clone(cx: &mut ExtCtxt,
2325
span: Span,
2426
mitem: &MetaItem,
@@ -39,25 +41,30 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt,
3941
let substructure;
4042
let nested_match;
4143
match *item {
42-
Annotatable::Item(ref item) => {
43-
match item.node {
44+
Annotatable::Item(ref annitem) => {
45+
match annitem.node {
4446
ItemKind::Struct(_, Generics { ref ty_params, .. }) |
4547
ItemKind::Enum(_, Generics { ref ty_params, .. })
46-
if ty_params.is_empty() && attr::contains_name(&item.attrs, "derive_Copy") => {
48+
if ty_params.is_empty()
49+
&& attr::contains_name(&annitem.attrs, "derive_Copy") => {
4750

4851
bounds = vec![Literal(path_std!(cx, core::marker::Copy))];
49-
substructure = combine_substructure(Box::new(|c, s, _| {
50-
cs_shallow_clone(c, s)
52+
substructure = combine_substructure(Box::new(|c, s, sub| {
53+
cs_deep_clone("Clone", c, s, sub, Mode::Assert)
5154
}));
52-
nested_match = false;
55+
nested_match = enclose(|c, s, sub| {
56+
let inner = cs_shallow_clone(c, s);
57+
c.expr_block(c.block_all(s, vec![c.stmt_expr(sub)], Some(inner)))
58+
//^ FIXME(aburka): this generates an extra set of {} braces
59+
});
5360
}
5461

5562
_ => {
5663
bounds = vec![];
5764
substructure = combine_substructure(Box::new(|c, s, sub| {
58-
cs_deep_clone("Clone", c, s, sub)
65+
cs_deep_clone("Clone", c, s, sub, Mode::Clone)
5966
}));
60-
nested_match = true;
67+
nested_match = None;
6168
}
6269
}
6370
}
@@ -101,10 +108,14 @@ fn cs_shallow_clone(cx: &mut ExtCtxt, trait_span: Span) -> P<Expr> {
101108
fn cs_deep_clone(
102109
name: &str,
103110
cx: &mut ExtCtxt, trait_span: Span,
104-
substr: &Substructure) -> P<Expr> {
111+
substr: &Substructure,
112+
mode: Mode) -> P<Expr> {
105113
let ctor_path;
106114
let all_fields;
107-
let fn_path = cx.std_path(&["clone", "Clone", "clone"]);
115+
let fn_path = match mode {
116+
Mode::Assert => cx.std_path(&["clone", "assert_receiver_is_clone"]),
117+
Mode::Clone => cx.std_path(&["clone", "Clone", "clone"]),
118+
};
108119
let subcall = |field: &FieldInfo| {
109120
let args = vec![cx.expr_addr_of(field.span, field.self_.clone())];
110121

@@ -134,29 +145,41 @@ fn cs_deep_clone(
134145
}
135146
}
136147

137-
match *vdata {
138-
VariantData::Struct(..) => {
139-
let fields = all_fields.iter().map(|field| {
140-
let ident = match field.name {
141-
Some(i) => i,
142-
None => {
143-
cx.span_bug(trait_span,
144-
&format!("unnamed field in normal struct in \
145-
`derive({})`", name))
146-
}
147-
};
148-
cx.field_imm(field.span, ident, subcall(field))
149-
}).collect::<Vec<_>>();
150-
151-
cx.expr_struct(trait_span, ctor_path, fields)
148+
match mode {
149+
Mode::Assert => {
150+
cx.expr_block(cx.block(trait_span,
151+
all_fields.iter()
152+
.map(subcall)
153+
.map(|e| cx.stmt_expr(e))
154+
.collect(),
155+
None))
152156
}
153-
VariantData::Tuple(..) => {
154-
let subcalls = all_fields.iter().map(subcall).collect();
155-
let path = cx.expr_path(ctor_path);
156-
cx.expr_call(trait_span, path, subcalls)
157-
}
158-
VariantData::Unit(..) => {
159-
cx.expr_path(ctor_path)
157+
Mode::Clone => {
158+
match *vdata {
159+
VariantData::Struct(..) => {
160+
let fields = all_fields.iter().map(|field| {
161+
let ident = match field.name {
162+
Some(i) => i,
163+
None => {
164+
cx.span_bug(trait_span,
165+
&format!("unnamed field in normal struct in \
166+
`derive({})`", name))
167+
}
168+
};
169+
cx.field_imm(field.span, ident, subcall(field))
170+
}).collect::<Vec<_>>();
171+
172+
cx.expr_struct(trait_span, ctor_path, fields)
173+
}
174+
VariantData::Tuple(..) => {
175+
let subcalls = all_fields.iter().map(subcall).collect();
176+
let path = cx.expr_path(ctor_path);
177+
cx.expr_call(trait_span, path, subcalls)
178+
}
179+
VariantData::Unit(..) => {
180+
cx.expr_path(ctor_path)
181+
}
182+
}
160183
}
161184
}
162185
}

src/libsyntax_ext/deriving/cmp/eq.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt,
5858
name: "assert_receiver_is_total_eq",
5959
generics: LifetimeBounds::empty(),
6060
explicit_self: borrowed_explicit_self(),
61-
nested_match: true,
61+
nested_match: None,
6262
args: vec!(),
6363
ret_ty: nil_ty(),
6464
attributes: attrs,

src/libsyntax_ext/deriving/cmp/ord.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ pub fn expand_deriving_ord(cx: &mut ExtCtxt,
3838
name: "cmp",
3939
generics: LifetimeBounds::empty(),
4040
explicit_self: borrowed_explicit_self(),
41-
nested_match: true,
41+
nested_match: None,
4242
args: vec!(borrowed_self()),
4343
ret_ty: Literal(path_std!(cx, core::cmp::Ordering)),
4444
attributes: attrs,

src/libsyntax_ext/deriving/cmp/partial_eq.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ pub fn expand_deriving_partial_eq(cx: &mut ExtCtxt,
6969
name: $name,
7070
generics: LifetimeBounds::empty(),
7171
explicit_self: borrowed_explicit_self(),
72-
nested_match: true,
72+
nested_match: None,
7373
args: vec!(borrowed_self()),
7474
ret_ty: Literal(path_local!(bool)),
7575
attributes: attrs,

src/libsyntax_ext/deriving/cmp/partial_ord.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ pub fn expand_deriving_partial_ord(cx: &mut ExtCtxt,
3434
name: $name,
3535
generics: LifetimeBounds::empty(),
3636
explicit_self: borrowed_explicit_self(),
37-
nested_match: true,
37+
nested_match: None,
3838
args: vec!(borrowed_self()),
3939
ret_ty: Literal(path_local!(bool)),
4040
attributes: attrs,
@@ -59,7 +59,7 @@ pub fn expand_deriving_partial_ord(cx: &mut ExtCtxt,
5959
name: "partial_cmp",
6060
generics: LifetimeBounds::empty(),
6161
explicit_self: borrowed_explicit_self(),
62-
nested_match: true,
62+
nested_match: None,
6363
args: vec![borrowed_self()],
6464
ret_ty: ret_ty,
6565
attributes: attrs,

src/libsyntax_ext/deriving/debug.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ pub fn expand_deriving_debug(cx: &mut ExtCtxt,
4141
name: "fmt",
4242
generics: LifetimeBounds::empty(),
4343
explicit_self: borrowed_explicit_self(),
44-
nested_match: true,
44+
nested_match: None,
4545
args: vec!(fmtr),
4646
ret_ty: Literal(path_std!(cx, core::fmt::Result)),
4747
attributes: Vec::new(),

src/libsyntax_ext/deriving/decodable.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ fn expand_deriving_decodable_imp(cx: &mut ExtCtxt,
7373
vec![Path::new_(vec!(krate, "Decoder"), None, vec!(), true)])]
7474
},
7575
explicit_self: None,
76-
nested_match: true,
76+
nested_match: None,
7777
args: vec!(Ptr(Box::new(Literal(Path::new_local(typaram))),
7878
Borrowed(None, Mutability::Mutable))),
7979
ret_ty: Literal(Path::new_(

src/libsyntax_ext/deriving/default.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ pub fn expand_deriving_default(cx: &mut ExtCtxt,
3838
name: "default",
3939
generics: LifetimeBounds::empty(),
4040
explicit_self: None,
41-
nested_match: true,
41+
nested_match: None,
4242
args: Vec::new(),
4343
ret_ty: Self_,
4444
attributes: attrs,

src/libsyntax_ext/deriving/encodable.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ fn expand_deriving_encodable_imp(cx: &mut ExtCtxt,
149149
vec![Path::new_(vec![krate, "Encoder"], None, vec!(), true)])]
150150
},
151151
explicit_self: borrowed_explicit_self(),
152-
nested_match: true,
152+
nested_match: None,
153153
args: vec!(Ptr(Box::new(Literal(Path::new_local(typaram))),
154154
Borrowed(None, Mutability::Mutable))),
155155
ret_ty: Literal(Path::new_(

src/libsyntax_ext/deriving/generic/mod.rs

+25-10
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ pub struct MethodDef<'a> {
249249

250250
/// Whether the function structure is a nested `match` statement with the result of
251251
/// combine_substructe() inside, or just the result of combine_substructure()
252-
pub nested_match: bool,
252+
pub nested_match: Option<RefCell<EncloseFunc<'a>>>,
253253

254254
/// Arguments other than the self argument
255255
pub args: Vec<Ty<'a>>,
@@ -331,6 +331,9 @@ pub enum SubstructureFields<'a> {
331331
pub type CombineSubstructureFunc<'a> =
332332
Box<FnMut(&mut ExtCtxt, Span, &Substructure) -> P<Expr> + 'a>;
333333

334+
pub type EncloseFunc<'a> =
335+
Box<FnMut(&mut ExtCtxt, Span, P<Expr>) -> P<Expr> + 'a>;
336+
334337
/// Deal with non-matching enum variants. The tuple is a list of
335338
/// identifiers (one for each `Self` argument, which could be any of the
336339
/// variants since they have been collapsed together) and the identifiers
@@ -344,6 +347,13 @@ pub fn combine_substructure<'a>(f: CombineSubstructureFunc<'a>)
344347
RefCell::new(f)
345348
}
346349

350+
pub fn enclose<'a, F>(f: F)
351+
-> Option<RefCell<EncloseFunc<'a>>>
352+
where F: FnMut(&mut ExtCtxt, Span, P<Expr>) -> P<Expr> + 'a
353+
{
354+
Some(RefCell::new(Box::new(f)))
355+
}
356+
347357
/// This method helps to extract all the type parameters referenced from a
348358
/// type. For a type parameter `<T>`, it looks for either a `TyPath` that
349359
/// is not global and starts with `T`, or a `TyQPath`.
@@ -861,7 +871,14 @@ impl<'a> MethodDef<'a> {
861871
abi: Abi,
862872
explicit_self: ast::ExplicitSelf,
863873
arg_types: Vec<(Ident, P<ast::Ty>)> ,
864-
body: P<Expr>) -> ast::ImplItem {
874+
mut body: P<Expr>) -> ast::ImplItem {
875+
876+
if self.nested_match.is_some() {
877+
let mut f = self.nested_match.as_ref().unwrap().borrow_mut();
878+
let f: &mut _ = &mut *f;
879+
body = f(cx, trait_.span, body);
880+
}
881+
865882
// create the generics that aren't for Self
866883
let fn_generics = self.generics.to_generics(cx, trait_.span, type_ident, generics);
867884

@@ -988,14 +1005,12 @@ impl<'a> MethodDef<'a> {
9881005
nonself_args,
9891006
&Struct(struct_def, fields));
9901007

991-
if self.nested_match {
992-
// make a series of nested matches, to destructure the
993-
// structs. This is actually right-to-left, but it shouldn't
994-
// matter.
995-
for (arg_expr, pat) in self_args.iter().zip(patterns) {
996-
body = cx.expr_match(trait_.span, arg_expr.clone(),
997-
vec!( cx.arm(trait_.span, vec!(pat.clone()), body) ))
998-
}
1008+
// make a series of nested matches, to destructure the
1009+
// structs. This is actually right-to-left, but it shouldn't
1010+
// matter.
1011+
for (arg_expr, pat) in self_args.iter().zip(patterns) {
1012+
body = cx.expr_match(trait_.span, arg_expr.clone(),
1013+
vec!( cx.arm(trait_.span, vec!(pat.clone()), body) ))
9991014
}
10001015

10011016
body

src/libsyntax_ext/deriving/hash.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub fn expand_deriving_hash(cx: &mut ExtCtxt,
4747
vec![path_std!(cx, core::hash::Hasher)])],
4848
},
4949
explicit_self: borrowed_explicit_self(),
50-
nested_match: true,
50+
nested_match: None,
5151
args: vec!(Ptr(Box::new(Literal(arg)), Borrowed(None, Mutability::Mutable))),
5252
ret_ty: nil_ty(),
5353
attributes: vec![],

src/test/auxiliary/custom_derive_plugin.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ fn expand(cx: &mut ExtCtxt,
5555
generics: LifetimeBounds::empty(),
5656
explicit_self: borrowed_explicit_self(),
5757
args: vec![],
58-
nested_match: true,
58+
nested_match: None,
5959
ret_ty: Literal(Path::new_local("isize")),
6060
attributes: vec![],
6161
is_unsafe: false,

src/test/auxiliary/custom_derive_plugin_attr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ fn expand(cx: &mut ExtCtxt,
5757
generics: LifetimeBounds::empty(),
5858
explicit_self: borrowed_explicit_self(),
5959
args: vec![],
60-
nested_match: true,
60+
nested_match: None,
6161
ret_ty: Literal(Path::new_local("isize")),
6262
attributes: vec![],
6363
is_unsafe: false,
+3-14
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,10 @@
11
-include ../tools.mk
22

3-
REPLACEMENT := s/[0-9][0-9]*\#[0-9][0-9]*//g
4-
53
all:
64
# pretty-expand both input.rs and input_pp.rs
75
$(RUSTC) -o $(TMPDIR)/input.pretty -Z unstable-options \
8-
--pretty expanded,hygiene input.rs
6+
--pretty expanded input.rs
97
$(RUSTC) -o $(TMPDIR)/input_pp.pretty -Z unstable-options \
10-
--pretty expanded,hygiene input_pp.rs
11-
12-
# the name/ctxt numbers are very internals-dependent and thus
13-
# change relatively frequently, and testing for their exact values
14-
# them will fail annoyingly, so we just remove them
15-
#
16-
# (These need to be out-of-place because OSX/BSD & GNU sed
17-
# differ.)
18-
sed "$(REPLACEMENT)" $(TMPDIR)/input.pretty > $(TMPDIR)/input.replaced
19-
sed "$(REPLACEMENT)" $(TMPDIR)/input_pp.pretty > $(TMPDIR)/input_pp.replaced
8+
--pretty expanded input_pp.rs
209

21-
diff -u $(TMPDIR)/input.replaced $(TMPDIR)/input_pp.replaced
10+
diff -u $(TMPDIR)/input.pretty $(TMPDIR)/input_pp.pretty

src/test/run-make/expand-derive/input_pp.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,16 @@ struct A {
1919
#[allow(unused_qualifications)]
2020
impl ::clone::Clone for A {
2121
#[inline]
22-
fn clone(&self) -> A { *self }
22+
fn clone(&self) -> A {
23+
{
24+
match *self {
25+
A { a: ref __self_0_0 } => {
26+
::clone::assert_receiver_is_clone(&(*__self_0_0));
27+
}
28+
};
29+
*self
30+
}
31+
}
2332
}
2433
#[automatically_derived]
2534
#[allow(unused_qualifications)]

0 commit comments

Comments
 (0)