]> git.proxmox.com Git - rustc.git/blob - src/librustc_builtin_macros/deriving/clone.rs
New upstream version 1.43.0+dfsg1
[rustc.git] / src / librustc_builtin_macros / deriving / clone.rs
1 use crate::deriving::generic::ty::*;
2 use crate::deriving::generic::*;
3 use crate::deriving::path_std;
4
5 use rustc_ast::ast::{self, Expr, GenericArg, Generics, ItemKind, MetaItem, VariantData};
6 use rustc_ast::ptr::P;
7 use rustc_expand::base::{Annotatable, ExtCtxt};
8 use rustc_span::symbol::{kw, sym, Symbol};
9 use rustc_span::Span;
10
11 pub fn expand_deriving_clone(
12 cx: &mut ExtCtxt<'_>,
13 span: Span,
14 mitem: &MetaItem,
15 item: &Annotatable,
16 push: &mut dyn FnMut(Annotatable),
17 ) {
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) => match annitem.kind {
37 ItemKind::Struct(_, Generics { ref params, .. })
38 | ItemKind::Enum(_, Generics { ref params, .. }) => {
39 let container_id = cx.current_expansion.id.expn_data().parent;
40 if cx.resolver.has_derive_copy(container_id)
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 =
55 combine_substructure(Box::new(|c, s, sub| cs_clone("Clone", c, s, sub)));
56 }
57 }
58 ItemKind::Union(..) => {
59 bounds = vec![Literal(path_std!(cx, marker::Copy))];
60 is_shallow = true;
61 substructure = combine_substructure(Box::new(|c, s, sub| {
62 cs_clone_shallow("Clone", c, s, sub, true)
63 }));
64 }
65 _ => {
66 bounds = vec![];
67 is_shallow = false;
68 substructure =
69 combine_substructure(Box::new(|c, s, sub| cs_clone("Clone", c, s, sub)));
70 }
71 },
72
73 _ => cx.span_bug(span, "`#[derive(Clone)]` on trait item or impl item"),
74 }
75
76 let inline = cx.meta_word(span, sym::inline);
77 let attrs = vec![cx.attribute(inline)];
78 let trait_def = TraitDef {
79 span,
80 attributes: Vec::new(),
81 path: path_std!(cx, clone::Clone),
82 additional_bounds: bounds,
83 generics: LifetimeBounds::empty(),
84 is_unsafe: false,
85 supports_unions: true,
86 methods: vec![MethodDef {
87 name: "clone",
88 generics: LifetimeBounds::empty(),
89 explicit_self: borrowed_explicit_self(),
90 args: Vec::new(),
91 ret_ty: Self_,
92 attributes: attrs,
93 is_unsafe: false,
94 unify_fieldless_variants: false,
95 combine_substructure: substructure,
96 }],
97 associated_types: Vec::new(),
98 };
99
100 trait_def.expand_ext(cx, mitem, item, push, is_shallow)
101 }
102
103 fn cs_clone_shallow(
104 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(
111 cx: &mut ExtCtxt<'_>,
112 stmts: &mut Vec<ast::Stmt>,
113 ty: P<ast::Ty>,
114 span: Span,
115 helper_name: &str,
116 ) {
117 // Generate statement `let _: helper_name<ty>;`,
118 // set the expn ID so we can use the unstable struct.
119 let span = cx.with_def_site_ctxt(span);
120 let assert_path = cx.path_all(
121 span,
122 true,
123 cx.std_path(&[sym::clone, Symbol::intern(helper_name)]),
124 vec![GenericArg::Type(ty)],
125 );
126 stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path)));
127 }
128 fn process_variant(cx: &mut ExtCtxt<'_>, stmts: &mut Vec<ast::Stmt>, variant: &VariantData) {
129 for field in variant.fields() {
130 // let _: AssertParamIsClone<FieldTy>;
131 assert_ty_bounds(cx, stmts, field.ty.clone(), field.span, "AssertParamIsClone");
132 }
133 }
134
135 let mut stmts = Vec::new();
136 if is_union {
137 // let _: AssertParamIsCopy<Self>;
138 let self_ty =
139 cx.ty_path(cx.path_ident(trait_span, ast::Ident::with_dummy_span(kw::SelfUpper)));
140 assert_ty_bounds(cx, &mut stmts, self_ty, trait_span, "AssertParamIsCopy");
141 } else {
142 match *substr.fields {
143 StaticStruct(vdata, ..) => {
144 process_variant(cx, &mut stmts, vdata);
145 }
146 StaticEnum(enum_def, ..) => {
147 for variant in &enum_def.variants {
148 process_variant(cx, &mut stmts, &variant.data);
149 }
150 }
151 _ => cx.span_bug(
152 trait_span,
153 &format!(
154 "unexpected substructure in \
155 shallow `derive({})`",
156 name
157 ),
158 ),
159 }
160 }
161 stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span))));
162 cx.expr_block(cx.block(trait_span, stmts))
163 }
164
165 fn cs_clone(
166 name: &str,
167 cx: &mut ExtCtxt<'_>,
168 trait_span: Span,
169 substr: &Substructure<'_>,
170 ) -> P<Expr> {
171 let ctor_path;
172 let all_fields;
173 let fn_path = cx.std_path(&[sym::clone, sym::Clone, sym::clone]);
174 let subcall = |cx: &mut ExtCtxt<'_>, field: &FieldInfo<'_>| {
175 let args = vec![cx.expr_addr_of(field.span, field.self_.clone())];
176 cx.expr_call_global(field.span, fn_path.clone(), args)
177 };
178
179 let vdata;
180 match *substr.fields {
181 Struct(vdata_, ref af) => {
182 ctor_path = cx.path(trait_span, vec![substr.type_ident]);
183 all_fields = af;
184 vdata = vdata_;
185 }
186 EnumMatching(.., variant, ref af) => {
187 ctor_path = cx.path(trait_span, vec![substr.type_ident, variant.ident]);
188 all_fields = af;
189 vdata = &variant.data;
190 }
191 EnumNonMatchingCollapsed(..) => {
192 cx.span_bug(trait_span, &format!("non-matching enum variants in `derive({})`", name,))
193 }
194 StaticEnum(..) | StaticStruct(..) => {
195 cx.span_bug(trait_span, &format!("associated function in `derive({})`", name))
196 }
197 }
198
199 match *vdata {
200 VariantData::Struct(..) => {
201 let fields = all_fields
202 .iter()
203 .map(|field| {
204 let ident = match field.name {
205 Some(i) => i,
206 None => cx.span_bug(
207 trait_span,
208 &format!("unnamed field in normal struct in `derive({})`", name,),
209 ),
210 };
211 let call = subcall(cx, field);
212 cx.field_imm(field.span, ident, call)
213 })
214 .collect::<Vec<_>>();
215
216 cx.expr_struct(trait_span, ctor_path, fields)
217 }
218 VariantData::Tuple(..) => {
219 let subcalls = all_fields.iter().map(|f| subcall(cx, f)).collect();
220 let path = cx.expr_path(ctor_path);
221 cx.expr_call(trait_span, path, subcalls)
222 }
223 VariantData::Unit(..) => cx.expr_path(ctor_path),
224 }
225 }