1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use deriving
::generic
::*;
12 use deriving
::generic
::ty
::*;
14 use syntax
::ast
::{self, Expr, Generics, ItemKind, MetaItem, VariantData}
;
16 use syntax
::ext
::base
::{Annotatable, ExtCtxt}
;
17 use syntax
::ext
::build
::AstBuilder
;
19 use syntax
::symbol
::{Symbol, keywords}
;
22 pub fn expand_deriving_clone(cx
: &mut ExtCtxt
,
26 push
: &mut FnMut(Annotatable
)) {
27 // check if we can use a short form
29 // the short form is `fn clone(&self) -> Self { *self }`
31 // we can use the short form if:
32 // - the item is Copy (unfortunately, all we can check is whether it's also deriving Copy)
33 // - there are no generic parameters (after specialization this limitation can be removed)
34 // if we used the short form with generics, we'd have to bound the generics with
35 // Clone + Copy, and then there'd be no Clone impl at all if the user fills in something
36 // 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.
45 Annotatable
::Item(ref annitem
) => {
47 ItemKind
::Struct(_
, Generics { ref ty_params, .. }
) |
48 ItemKind
::Enum(_
, Generics { ref ty_params, .. }
)
49 if attr
::contains_name(&annitem
.attrs
, "rustc_copy_clone_marker") &&
50 ty_params
.is_empty() => {
53 substructure
= combine_substructure(Box
::new(|c
, s
, sub
| {
54 cs_clone_shallow("Clone", c
, s
, sub
, false)
57 ItemKind
::Union(..) => {
58 bounds
= vec
![Literal(path_std
!(cx
, core
::marker
::Copy
))];
60 substructure
= combine_substructure(Box
::new(|c
, s
, sub
| {
61 cs_clone_shallow("Clone", c
, s
, sub
, true)
67 substructure
= combine_substructure(Box
::new(|c
, s
, sub
| {
68 cs_clone("Clone", c
, s
, sub
)
74 _
=> cx
.span_bug(span
, "#[derive(Clone)] on trait item or impl item"),
77 let inline
= cx
.meta_word(span
, Symbol
::intern("inline"));
78 let attrs
= vec
![cx
.attribute(span
, inline
)];
79 let trait_def
= TraitDef
{
81 attributes
: Vec
::new(),
82 path
: path_std
!(cx
, core
::clone
::Clone
),
83 additional_bounds
: bounds
,
84 generics
: LifetimeBounds
::empty(),
86 supports_unions
: true,
87 methods
: vec
![MethodDef
{
89 generics
: LifetimeBounds
::empty(),
90 explicit_self
: borrowed_explicit_self(),
95 unify_fieldless_variants
: false,
96 combine_substructure
: substructure
,
98 associated_types
: Vec
::new(),
101 trait_def
.expand_ext(cx
, mitem
, item
, push
, is_shallow
)
104 fn cs_clone_shallow(name
: &str,
107 substr
: &Substructure
,
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
= Span { ctxt: cx.backtrace(), ..span}
;
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
)));
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");
127 let mut stmts
= Vec
::new();
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");
133 match *substr
.fields
{
134 StaticStruct(vdata
, ..) => {
135 process_variant(cx
, &mut stmts
, vdata
);
137 StaticEnum(enum_def
, ..) => {
138 for variant
in &enum_def
.variants
{
139 process_variant(cx
, &mut stmts
, &variant
.node
.data
);
142 _
=> cx
.span_bug(trait_span
, &format
!("unexpected substructure in \
143 shallow `derive({})`", name
))
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
))
150 fn cs_clone(name
: &str,
153 substr
: &Substructure
)
157 let fn_path
= cx
.std_path(&["clone", "Clone", "clone"]);
158 let subcall
= |cx
: &mut ExtCtxt
, field
: &FieldInfo
| {
159 let args
= vec
![cx
.expr_addr_of(field
.span
, field
.self_
.clone())];
160 cx
.expr_call_global(field
.span
, fn_path
.clone(), args
)
164 match *substr
.fields
{
165 Struct(vdata_
, ref af
) => {
166 ctor_path
= cx
.path(trait_span
, vec
![substr
.type_ident
]);
170 EnumMatching(_
, variant
, ref af
) => {
171 ctor_path
= cx
.path(trait_span
, vec
![substr
.type_ident
, variant
.node
.name
]);
173 vdata
= &variant
.node
.data
;
175 EnumNonMatchingCollapsed(..) => {
176 cx
.span_bug(trait_span
,
177 &format
!("non-matching enum variants in \
181 StaticEnum(..) | StaticStruct(..) => {
182 cx
.span_bug(trait_span
, &format
!("static method in `derive({})`", name
))
187 VariantData
::Struct(..) => {
188 let fields
= all_fields
.iter()
190 let ident
= match field
.name
{
193 cx
.span_bug(trait_span
,
194 &format
!("unnamed field in normal struct in \
199 let call
= subcall(cx
, field
);
200 cx
.field_imm(field
.span
, ident
, call
)
202 .collect
::<Vec
<_
>>();
204 cx
.expr_struct(trait_span
, ctor_path
, fields
)
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
)
211 VariantData
::Unit(..) => cx
.expr_path(ctor_path
),