]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_ext/deriving/clone.rs
Rollup merge of #62696 - chocol4te:fix_#62194, r=estebank
[rust.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::{kw, sym, Symbol};
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, sym::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, sym::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(&[sym::clone, Symbol::intern(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 =
133             cx.ty_path(cx.path_ident(trait_span, ast::Ident::with_empty_ctxt(kw::SelfUpper)));
134         assert_ty_bounds(cx, &mut stmts, self_ty, trait_span, "AssertParamIsCopy");
135     } else {
136         match *substr.fields {
137             StaticStruct(vdata, ..) => {
138                 process_variant(cx, &mut stmts, vdata);
139             }
140             StaticEnum(enum_def, ..) => {
141                 for variant in &enum_def.variants {
142                     process_variant(cx, &mut stmts, &variant.node.data);
143                 }
144             }
145             _ => cx.span_bug(trait_span, &format!("unexpected substructure in \
146                                                     shallow `derive({})`", name))
147         }
148     }
149     stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span))));
150     cx.expr_block(cx.block(trait_span, stmts))
151 }
152
153 fn cs_clone(name: &str,
154             cx: &mut ExtCtxt<'_>,
155             trait_span: Span,
156             substr: &Substructure<'_>)
157             -> P<Expr> {
158     let ctor_path;
159     let all_fields;
160     let fn_path = cx.std_path(&[sym::clone, sym::Clone, sym::clone]);
161     let subcall = |cx: &mut ExtCtxt<'_>, field: &FieldInfo<'_>| {
162         let args = vec![cx.expr_addr_of(field.span, field.self_.clone())];
163         cx.expr_call_global(field.span, fn_path.clone(), args)
164     };
165
166     let vdata;
167     match *substr.fields {
168         Struct(vdata_, ref af) => {
169             ctor_path = cx.path(trait_span, vec![substr.type_ident]);
170             all_fields = af;
171             vdata = vdata_;
172         }
173         EnumMatching(.., variant, ref af) => {
174             ctor_path = cx.path(trait_span, vec![substr.type_ident, variant.node.ident]);
175             all_fields = af;
176             vdata = &variant.node.data;
177         }
178         EnumNonMatchingCollapsed(..) => {
179             cx.span_bug(trait_span,
180                         &format!("non-matching enum variants in \
181                                  `derive({})`",
182                                  name))
183         }
184         StaticEnum(..) | StaticStruct(..) => {
185             cx.span_bug(trait_span, &format!("static method in `derive({})`", name))
186         }
187     }
188
189     match *vdata {
190         VariantData::Struct(..) => {
191             let fields = all_fields.iter()
192                 .map(|field| {
193                     let ident = match field.name {
194                         Some(i) => i,
195                         None => {
196                             cx.span_bug(trait_span,
197                                         &format!("unnamed field in normal struct in \
198                                                 `derive({})`",
199                                                     name))
200                         }
201                     };
202                     let call = subcall(cx, field);
203                     cx.field_imm(field.span, ident, call)
204                 })
205                 .collect::<Vec<_>>();
206
207             cx.expr_struct(trait_span, ctor_path, fields)
208         }
209         VariantData::Tuple(..) => {
210             let subcalls = all_fields.iter().map(|f| subcall(cx, f)).collect();
211             let path = cx.expr_path(ctor_path);
212             cx.expr_call(trait_span, path, subcalls)
213         }
214         VariantData::Unit(..) => cx.expr_path(ctor_path),
215     }
216 }