]> git.proxmox.com Git - rustc.git/blob - src/libsyntax_ext/deriving/clone.rs
New upstream version 1.18.0+dfsg1
[rustc.git] / src / libsyntax_ext / deriving / clone.rs
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.
4 //
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.
10
11 use deriving::generic::*;
12 use deriving::generic::ty::*;
13
14 use syntax::ast::{self, Expr, Generics, ItemKind, MetaItem, VariantData};
15 use syntax::attr;
16 use syntax::ext::base::{Annotatable, ExtCtxt};
17 use syntax::ext::build::AstBuilder;
18 use syntax::ptr::P;
19 use syntax::symbol::{Symbol, keywords};
20 use syntax_pos::Span;
21
22 pub fn expand_deriving_clone(cx: &mut ExtCtxt,
23 span: Span,
24 mitem: &MetaItem,
25 item: &Annotatable,
26 push: &mut FnMut(Annotatable)) {
27 // check if we can use a short form
28 //
29 // the short form is `fn clone(&self) -> Self { *self }`
30 //
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.
41 let bounds;
42 let substructure;
43 let is_shallow;
44 match *item {
45 Annotatable::Item(ref annitem) => {
46 match annitem.node {
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() => {
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(..) => {
58 bounds = vec![Literal(path_std!(cx, core::marker::Copy))];
59 is_shallow = true;
60 substructure = combine_substructure(Box::new(|c, s, sub| {
61 cs_clone_shallow("Clone", c, s, sub, true)
62 }));
63 }
64 _ => {
65 bounds = vec![];
66 is_shallow = false;
67 substructure = combine_substructure(Box::new(|c, s, sub| {
68 cs_clone("Clone", c, s, sub)
69 }));
70 }
71 }
72 }
73
74 _ => cx.span_bug(span, "#[derive(Clone)] on trait item or impl item"),
75 }
76
77 let inline = cx.meta_word(span, Symbol::intern("inline"));
78 let attrs = vec![cx.attribute(span, inline)];
79 let trait_def = TraitDef {
80 span: span,
81 attributes: Vec::new(),
82 path: path_std!(cx, core::clone::Clone),
83 additional_bounds: bounds,
84 generics: LifetimeBounds::empty(),
85 is_unsafe: false,
86 supports_unions: true,
87 methods: vec![MethodDef {
88 name: "clone",
89 generics: LifetimeBounds::empty(),
90 explicit_self: borrowed_explicit_self(),
91 args: Vec::new(),
92 ret_ty: Self_,
93 attributes: attrs,
94 is_unsafe: false,
95 unify_fieldless_variants: false,
96 combine_substructure: substructure,
97 }],
98 associated_types: Vec::new(),
99 };
100
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 = 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)));
119 }
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");
124 }
125 }
126
127 let mut stmts = Vec::new();
128 if is_union {
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");
132 } else {
133 match *substr.fields {
134 StaticStruct(vdata, ..) => {
135 process_variant(cx, &mut stmts, vdata);
136 }
137 StaticEnum(enum_def, ..) => {
138 for variant in &enum_def.variants {
139 process_variant(cx, &mut stmts, &variant.node.data);
140 }
141 }
142 _ => cx.span_bug(trait_span, &format!("unexpected substructure in \
143 shallow `derive({})`", name))
144 }
145 }
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))
148 }
149
150 fn cs_clone(name: &str,
151 cx: &mut ExtCtxt,
152 trait_span: Span,
153 substr: &Substructure)
154 -> P<Expr> {
155 let ctor_path;
156 let all_fields;
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)
161 };
162
163 let vdata;
164 match *substr.fields {
165 Struct(vdata_, ref af) => {
166 ctor_path = cx.path(trait_span, vec![substr.type_ident]);
167 all_fields = af;
168 vdata = vdata_;
169 }
170 EnumMatching(_, variant, ref af) => {
171 ctor_path = cx.path(trait_span, vec![substr.type_ident, variant.node.name]);
172 all_fields = af;
173 vdata = &variant.node.data;
174 }
175 EnumNonMatchingCollapsed(..) => {
176 cx.span_bug(trait_span,
177 &format!("non-matching enum variants in \
178 `derive({})`",
179 name))
180 }
181 StaticEnum(..) | StaticStruct(..) => {
182 cx.span_bug(trait_span, &format!("static method in `derive({})`", name))
183 }
184 }
185
186 match *vdata {
187 VariantData::Struct(..) => {
188 let fields = all_fields.iter()
189 .map(|field| {
190 let ident = match field.name {
191 Some(i) => i,
192 None => {
193 cx.span_bug(trait_span,
194 &format!("unnamed field in normal struct in \
195 `derive({})`",
196 name))
197 }
198 };
199 let call = subcall(cx, field);
200 cx.field_imm(field.span, ident, call)
201 })
202 .collect::<Vec<_>>();
203
204 cx.expr_struct(trait_span, ctor_path, fields)
205 }
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)
210 }
211 VariantData::Unit(..) => cx.expr_path(ctor_path),
212 }
213 }