]> git.proxmox.com Git - rustc.git/blob - src/libsyntax_ext/deriving/clone.rs
New upstream version 1.34.2+dfsg1
[rustc.git] / src / libsyntax_ext / deriving / clone.rs
1 use crate::deriving::path_std;
2 use crate::deriving::generic::*;
3 use crate::deriving::generic::ty::*;
4
5 use syntax::ast::{self, Expr, GenericArg, Generics, ItemKind, MetaItem, VariantData};
6 use syntax::attr;
7 use syntax::ext::base::{Annotatable, ExtCtxt};
8 use syntax::ext::build::AstBuilder;
9 use syntax::ptr::P;
10 use syntax::symbol::{Symbol, keywords};
11 use syntax_pos::Span;
12
13 pub fn expand_deriving_clone(cx: &mut ExtCtxt<'_>,
14 span: Span,
15 mitem: &MetaItem,
16 item: &Annotatable,
17 push: &mut dyn FnMut(Annotatable)) {
18 // check if we can use a short form
19 //
20 // the short form is `fn clone(&self) -> Self { *self }`
21 //
22 // we can use the short form if:
23 // - the item is Copy (unfortunately, all we can check is whether it's also deriving Copy)
24 // - there are no generic parameters (after specialization this limitation can be removed)
25 // if we used the short form with generics, we'd have to bound the generics with
26 // Clone + Copy, and then there'd be no Clone impl at all if the user fills in something
27 // that is Clone but not Copy. and until specialization we can't write both impls.
28 // - the item is a union with Copy fields
29 // Unions with generic parameters still can derive Clone because they require Copy
30 // for deriving, Clone alone is not enough.
31 // Whever Clone is implemented for fields is irrelevant so we don't assert it.
32 let bounds;
33 let substructure;
34 let is_shallow;
35 match *item {
36 Annotatable::Item(ref annitem) => {
37 match annitem.node {
38 ItemKind::Struct(_, Generics { ref params, .. }) |
39 ItemKind::Enum(_, Generics { ref params, .. }) => {
40 if attr::contains_name(&annitem.attrs, "rustc_copy_clone_marker") &&
41 !params.iter().any(|param| match param.kind {
42 ast::GenericParamKind::Type { .. } => true,
43 _ => false,
44 })
45 {
46 bounds = vec![];
47 is_shallow = true;
48 substructure = combine_substructure(Box::new(|c, s, sub| {
49 cs_clone_shallow("Clone", c, s, sub, false)
50 }));
51 } else {
52 bounds = vec![];
53 is_shallow = false;
54 substructure = combine_substructure(Box::new(|c, s, sub| {
55 cs_clone("Clone", c, s, sub)
56 }));
57 }
58 }
59 ItemKind::Union(..) => {
60 bounds = vec![Literal(path_std!(cx, marker::Copy))];
61 is_shallow = true;
62 substructure = combine_substructure(Box::new(|c, s, sub| {
63 cs_clone_shallow("Clone", c, s, sub, true)
64 }));
65 }
66 _ => {
67 bounds = vec![];
68 is_shallow = false;
69 substructure = combine_substructure(Box::new(|c, s, sub| {
70 cs_clone("Clone", c, s, sub)
71 }));
72 }
73 }
74 }
75
76 _ => cx.span_bug(span, "#[derive(Clone)] on trait item or impl item"),
77 }
78
79 let inline = cx.meta_word(span, Symbol::intern("inline"));
80 let attrs = vec![cx.attribute(span, inline)];
81 let trait_def = TraitDef {
82 span,
83 attributes: Vec::new(),
84 path: path_std!(cx, clone::Clone),
85 additional_bounds: bounds,
86 generics: LifetimeBounds::empty(),
87 is_unsafe: false,
88 supports_unions: true,
89 methods: vec![MethodDef {
90 name: "clone",
91 generics: LifetimeBounds::empty(),
92 explicit_self: borrowed_explicit_self(),
93 args: Vec::new(),
94 ret_ty: Self_,
95 attributes: attrs,
96 is_unsafe: false,
97 unify_fieldless_variants: false,
98 combine_substructure: substructure,
99 }],
100 associated_types: Vec::new(),
101 };
102
103 trait_def.expand_ext(cx, mitem, item, push, is_shallow)
104 }
105
106 fn cs_clone_shallow(name: &str,
107 cx: &mut ExtCtxt<'_>,
108 trait_span: Span,
109 substr: &Substructure<'_>,
110 is_union: bool)
111 -> P<Expr> {
112 fn assert_ty_bounds(cx: &mut ExtCtxt<'_>, stmts: &mut Vec<ast::Stmt>,
113 ty: P<ast::Ty>, span: Span, helper_name: &str) {
114 // Generate statement `let _: helper_name<ty>;`,
115 // set the expn ID so we can use the unstable struct.
116 let span = span.with_ctxt(cx.backtrace());
117 let assert_path = cx.path_all(span, true,
118 cx.std_path(&["clone", helper_name]),
119 vec![GenericArg::Type(ty)], vec![]);
120 stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path)));
121 }
122 fn process_variant(cx: &mut ExtCtxt<'_>, stmts: &mut Vec<ast::Stmt>, variant: &VariantData) {
123 for field in variant.fields() {
124 // let _: AssertParamIsClone<FieldTy>;
125 assert_ty_bounds(cx, stmts, field.ty.clone(), field.span, "AssertParamIsClone");
126 }
127 }
128
129 let mut stmts = Vec::new();
130 if is_union {
131 // let _: AssertParamIsCopy<Self>;
132 let self_ty = cx.ty_path(cx.path_ident(trait_span, keywords::SelfUpper.ident()));
133 assert_ty_bounds(cx, &mut stmts, self_ty, trait_span, "AssertParamIsCopy");
134 } else {
135 match *substr.fields {
136 StaticStruct(vdata, ..) => {
137 process_variant(cx, &mut stmts, vdata);
138 }
139 StaticEnum(enum_def, ..) => {
140 for variant in &enum_def.variants {
141 process_variant(cx, &mut stmts, &variant.node.data);
142 }
143 }
144 _ => cx.span_bug(trait_span, &format!("unexpected substructure in \
145 shallow `derive({})`", name))
146 }
147 }
148 stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span))));
149 cx.expr_block(cx.block(trait_span, stmts))
150 }
151
152 fn cs_clone(name: &str,
153 cx: &mut ExtCtxt<'_>,
154 trait_span: Span,
155 substr: &Substructure<'_>)
156 -> P<Expr> {
157 let ctor_path;
158 let all_fields;
159 let fn_path = cx.std_path(&["clone", "Clone", "clone"]);
160 let subcall = |cx: &mut ExtCtxt<'_>, field: &FieldInfo<'_>| {
161 let args = vec![cx.expr_addr_of(field.span, field.self_.clone())];
162 cx.expr_call_global(field.span, fn_path.clone(), args)
163 };
164
165 let vdata;
166 match *substr.fields {
167 Struct(vdata_, ref af) => {
168 ctor_path = cx.path(trait_span, vec![substr.type_ident]);
169 all_fields = af;
170 vdata = vdata_;
171 }
172 EnumMatching(.., variant, ref af) => {
173 ctor_path = cx.path(trait_span, vec![substr.type_ident, variant.node.ident]);
174 all_fields = af;
175 vdata = &variant.node.data;
176 }
177 EnumNonMatchingCollapsed(..) => {
178 cx.span_bug(trait_span,
179 &format!("non-matching enum variants in \
180 `derive({})`",
181 name))
182 }
183 StaticEnum(..) | StaticStruct(..) => {
184 cx.span_bug(trait_span, &format!("static method in `derive({})`", name))
185 }
186 }
187
188 match *vdata {
189 VariantData::Struct(..) => {
190 let fields = all_fields.iter()
191 .map(|field| {
192 let ident = match field.name {
193 Some(i) => i,
194 None => {
195 cx.span_bug(trait_span,
196 &format!("unnamed field in normal struct in \
197 `derive({})`",
198 name))
199 }
200 };
201 let call = subcall(cx, field);
202 cx.field_imm(field.span, ident, call)
203 })
204 .collect::<Vec<_>>();
205
206 cx.expr_struct(trait_span, ctor_path, fields)
207 }
208 VariantData::Tuple(..) => {
209 let subcalls = all_fields.iter().map(|f| subcall(cx, f)).collect();
210 let path = cx.expr_path(ctor_path);
211 cx.expr_call(trait_span, path, subcalls)
212 }
213 VariantData::Unit(..) => cx.expr_path(ctor_path),
214 }
215 }