1111use deriving:: generic:: * ;
1212use deriving:: generic:: ty:: * ;
1313
14- use syntax:: ast:: { MetaItem , Expr , VariantData } ;
14+ use syntax:: ast:: { Expr , ItemKind , Generics , MetaItem , VariantData } ;
15+ use syntax:: attr:: { self , AttrMetaMethods } ;
1516use syntax:: codemap:: Span ;
1617use syntax:: ext:: base:: { ExtCtxt , Annotatable } ;
1718use syntax:: ext:: build:: AstBuilder ;
1819use syntax:: parse:: token:: InternedString ;
1920use syntax:: ptr:: P ;
2021
22+ #[ derive( PartialEq ) ]
23+ enum Mode { Deep , Shallow }
24+
2125pub fn expand_deriving_clone ( cx : & mut ExtCtxt ,
2226 span : Span ,
2327 mitem : & MetaItem ,
2428 item : & Annotatable ,
2529 push : & mut FnMut ( Annotatable ) )
2630{
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+
2769 let inline = cx. meta_word ( span, InternedString :: new ( "inline" ) ) ;
2870 let attrs = vec ! ( cx. attribute( span, inline) ) ;
2971 let trait_def = TraitDef {
3072 span : span,
3173 attributes : Vec :: new ( ) ,
3274 path : path_std ! ( cx, core:: clone:: Clone ) ,
33- additional_bounds : Vec :: new ( ) ,
75+ additional_bounds : bounds ,
3476 generics : LifetimeBounds :: empty ( ) ,
3577 is_unsafe : false ,
3678 methods : vec ! (
@@ -42,9 +84,7 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt,
4284 ret_ty: Self_ ,
4385 attributes: attrs,
4486 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,
4888 }
4989 ) ,
5090 associated_types : Vec :: new ( ) ,
@@ -56,14 +96,24 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt,
5696fn cs_clone (
5797 name : & str ,
5898 cx : & mut ExtCtxt , trait_span : Span ,
59- substr : & Substructure ) -> P < Expr > {
99+ substr : & Substructure ,
100+ mode : Mode ) -> P < Expr > {
60101 let ctor_path;
61102 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+ } ;
63107 let subcall = |field : & FieldInfo | {
64108 let args = vec ! [ cx. expr_addr_of( field. span, field. self_. clone( ) ) ] ;
65109
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)
67117 } ;
68118
69119 let vdata;
@@ -89,29 +139,41 @@ fn cs_clone(
89139 }
90140 }
91141
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) ) ) ) )
107150 }
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+ }
115177 }
116178 }
117179}
0 commit comments