11
11
use deriving:: generic:: * ;
12
12
use deriving:: generic:: ty:: * ;
13
13
14
- use syntax:: ast:: { MetaItem , Expr , VariantData } ;
14
+ use syntax:: ast:: { Expr , ItemKind , Generics , MetaItem , VariantData } ;
15
+ use syntax:: attr:: { self , AttrMetaMethods } ;
15
16
use syntax:: codemap:: Span ;
16
17
use syntax:: ext:: base:: { ExtCtxt , Annotatable } ;
17
18
use syntax:: ext:: build:: AstBuilder ;
18
19
use syntax:: parse:: token:: InternedString ;
19
20
use syntax:: ptr:: P ;
20
21
22
+ #[ derive( PartialEq ) ]
23
+ enum Mode { Deep , Shallow }
24
+
21
25
pub fn expand_deriving_clone ( cx : & mut ExtCtxt ,
22
26
span : Span ,
23
27
mitem : & MetaItem ,
24
28
item : & Annotatable ,
25
29
push : & mut FnMut ( Annotatable ) )
26
30
{
31
+ // check if we can use a short form
32
+ //
33
+ // the short form is `fn clone(&self) -> Self { *self }`
34
+ //
35
+ // we can use the short form if:
36
+ // - the item is Copy (unfortunately, all we can check is whether it's also deriving Copy)
37
+ // - there are no generic parameters (after specialization this limitation can be removed)
38
+ // if we used the short form with generics, we'd have to bound the generics with
39
+ // Clone + Copy, and then there'd be no Clone impl at all if the user fills in something
40
+ // that is Clone but not Copy. and until specialization we can't write both impls.
41
+ let bounds;
42
+ let substructure;
43
+ match * item {
44
+ Annotatable :: Item ( ref annitem) => {
45
+ match annitem. node {
46
+ ItemKind :: Struct ( _, Generics { ref ty_params, .. } ) |
47
+ ItemKind :: Enum ( _, Generics { ref ty_params, .. } )
48
+ if ty_params. is_empty ( )
49
+ && attr:: contains_name ( & annitem. attrs , "derive_Copy" ) => {
50
+
51
+ bounds = vec ! [ Literal ( path_std!( cx, core:: marker:: Copy ) ) ] ;
52
+ substructure = combine_substructure ( Box :: new ( |c, s, sub| {
53
+ cs_clone ( "Clone" , c, s, sub, Mode :: Shallow )
54
+ } ) ) ;
55
+ }
56
+
57
+ _ => {
58
+ bounds = vec ! [ ] ;
59
+ substructure = combine_substructure ( Box :: new ( |c, s, sub| {
60
+ cs_clone ( "Clone" , c, s, sub, Mode :: Deep )
61
+ } ) ) ;
62
+ }
63
+ }
64
+ }
65
+
66
+ _ => cx. span_bug ( span, "#[derive(Clone)] on trait item or impl item" )
67
+ }
68
+
27
69
let inline = cx. meta_word ( span, InternedString :: new ( "inline" ) ) ;
28
70
let attrs = vec ! ( cx. attribute( span, inline) ) ;
29
71
let trait_def = TraitDef {
30
72
span : span,
31
73
attributes : Vec :: new ( ) ,
32
74
path : path_std ! ( cx, core:: clone:: Clone ) ,
33
- additional_bounds : Vec :: new ( ) ,
75
+ additional_bounds : bounds ,
34
76
generics : LifetimeBounds :: empty ( ) ,
35
77
is_unsafe : false ,
36
78
methods : vec ! (
@@ -42,9 +84,7 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt,
42
84
ret_ty: Self_ ,
43
85
attributes: attrs,
44
86
is_unsafe: false ,
45
- combine_substructure: combine_substructure( Box :: new( |c, s, sub| {
46
- cs_clone( "Clone" , c, s, sub)
47
- } ) ) ,
87
+ combine_substructure: substructure,
48
88
}
49
89
) ,
50
90
associated_types : Vec :: new ( ) ,
@@ -56,14 +96,24 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt,
56
96
fn cs_clone (
57
97
name : & str ,
58
98
cx : & mut ExtCtxt , trait_span : Span ,
59
- substr : & Substructure ) -> P < Expr > {
99
+ substr : & Substructure ,
100
+ mode : Mode ) -> P < Expr > {
60
101
let ctor_path;
61
102
let all_fields;
62
- let fn_path = cx. std_path ( & [ "clone" , "Clone" , "clone" ] ) ;
103
+ let fn_path = match mode {
104
+ Mode :: Shallow => cx. std_path ( & [ "clone" , "assert_receiver_is_clone" ] ) ,
105
+ Mode :: Deep => cx. std_path ( & [ "clone" , "Clone" , "clone" ] ) ,
106
+ } ;
63
107
let subcall = |field : & FieldInfo | {
64
108
let args = vec ! [ cx. expr_addr_of( field. span, field. self_. clone( ) ) ] ;
65
109
66
- cx. expr_call_global ( field. span , fn_path. clone ( ) , args)
110
+ let span = if mode == Mode :: Shallow {
111
+ // set the expn ID so we can call the unstable method
112
+ Span { expn_id : cx. backtrace ( ) , .. trait_span }
113
+ } else {
114
+ field. span
115
+ } ;
116
+ cx. expr_call_global ( span, fn_path. clone ( ) , args)
67
117
} ;
68
118
69
119
let vdata;
@@ -89,29 +139,41 @@ fn cs_clone(
89
139
}
90
140
}
91
141
92
- match * vdata {
93
- VariantData :: Struct ( ..) => {
94
- let fields = all_fields. iter ( ) . map ( |field| {
95
- let ident = match field. name {
96
- Some ( i) => i,
97
- None => {
98
- cx. span_bug ( trait_span,
99
- & format ! ( "unnamed field in normal struct in \
100
- `derive({})`", name) )
101
- }
102
- } ;
103
- cx. field_imm ( field. span , ident, subcall ( field) )
104
- } ) . collect :: < Vec < _ > > ( ) ;
105
-
106
- cx. expr_struct ( trait_span, ctor_path, fields)
142
+ match mode {
143
+ Mode :: Shallow => {
144
+ cx. expr_block ( cx. block ( trait_span,
145
+ all_fields. iter ( )
146
+ . map ( subcall)
147
+ . map ( |e| cx. stmt_expr ( e) )
148
+ . collect ( ) ,
149
+ Some ( cx. expr_deref ( trait_span, cx. expr_self ( trait_span) ) ) ) )
107
150
}
108
- VariantData :: Tuple ( ..) => {
109
- let subcalls = all_fields. iter ( ) . map ( subcall) . collect ( ) ;
110
- let path = cx. expr_path ( ctor_path) ;
111
- cx. expr_call ( trait_span, path, subcalls)
112
- }
113
- VariantData :: Unit ( ..) => {
114
- cx. expr_path ( ctor_path)
151
+ Mode :: Deep => {
152
+ match * vdata {
153
+ VariantData :: Struct ( ..) => {
154
+ let fields = all_fields. iter ( ) . map ( |field| {
155
+ let ident = match field. name {
156
+ Some ( i) => i,
157
+ None => {
158
+ cx. span_bug ( trait_span,
159
+ & format ! ( "unnamed field in normal struct in \
160
+ `derive({})`", name) )
161
+ }
162
+ } ;
163
+ cx. field_imm ( field. span , ident, subcall ( field) )
164
+ } ) . collect :: < Vec < _ > > ( ) ;
165
+
166
+ cx. expr_struct ( trait_span, ctor_path, fields)
167
+ }
168
+ VariantData :: Tuple ( ..) => {
169
+ let subcalls = all_fields. iter ( ) . map ( subcall) . collect ( ) ;
170
+ let path = cx. expr_path ( ctor_path) ;
171
+ cx. expr_call ( trait_span, path, subcalls)
172
+ }
173
+ VariantData :: Unit ( ..) => {
174
+ cx. expr_path ( ctor_path)
175
+ }
176
+ }
115
177
}
116
178
}
117
179
}
0 commit comments