]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_ext/deriving/clone.rs
Rollup merge of #63146 - Mark-Simulacrum:clean-attr, r=petrochenkov
[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::ptr::P;
9 use syntax::symbol::{kw, sym, Symbol};
10 use syntax_pos::Span;
11
12 pub fn expand_deriving_clone(cx: &mut ExtCtxt<'_>,
13                              span: Span,
14                              mitem: &MetaItem,
15                              item: &Annotatable,
16                              push: &mut dyn FnMut(Annotatable)) {
17     // check if we can use a short form
18     //
19     // the short form is `fn clone(&self) -> Self { *self }`
20     //
21     // we can use the short form if:
22     // - the item is Copy (unfortunately, all we can check is whether it's also deriving Copy)
23     // - there are no generic parameters (after specialization this limitation can be removed)
24     //      if we used the short form with generics, we'd have to bound the generics with
25     //      Clone + Copy, and then there'd be no Clone impl at all if the user fills in something
26     //      that is Clone but not Copy. and until specialization we can't write both impls.
27     // - the item is a union with Copy fields
28     //      Unions with generic parameters still can derive Clone because they require Copy
29     //      for deriving, Clone alone is not enough.
30     //      Whever Clone is implemented for fields is irrelevant so we don't assert it.
31     let bounds;
32     let substructure;
33     let is_shallow;
34     match *item {
35         Annotatable::Item(ref annitem) => {
36             match annitem.node {
37                 ItemKind::Struct(_, Generics { ref params, .. }) |
38                 ItemKind::Enum(_, Generics { ref params, .. }) => {
39                     if attr::contains_name(&annitem.attrs, sym::rustc_copy_clone_marker) &&
40                         !params.iter().any(|param| match param.kind {
41                             ast::GenericParamKind::Type { .. } => true,
42                             _ => false,
43                         })
44                     {
45                         bounds = vec![];
46                         is_shallow = true;
47                         substructure = combine_substructure(Box::new(|c, s, sub| {
48                             cs_clone_shallow("Clone", c, s, sub, false)
49                         }));
50                     } else {
51                         bounds = vec![];
52                         is_shallow = false;
53                         substructure = combine_substructure(Box::new(|c, s, sub| {
54                             cs_clone("Clone", c, s, sub)
55                         }));
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 = combine_substructure(Box::new(|c, s, sub| {
69                         cs_clone("Clone", c, s, sub)
70                     }));
71                 }
72             }
73         }
74
75         _ => cx.span_bug(span, "`#[derive(Clone)]` on trait item or impl item"),
76     }
77
78     let inline = cx.meta_word(span, sym::inline);
79     let attrs = vec![cx.attribute(inline)];
80     let trait_def = TraitDef {
81         span,
82         attributes: Vec::new(),
83         path: path_std!(cx, clone::Clone),
84         additional_bounds: bounds,
85         generics: LifetimeBounds::empty(),
86         is_unsafe: false,
87         supports_unions: true,
88         methods: vec![MethodDef {
89                           name: "clone",
90                           generics: LifetimeBounds::empty(),
91                           explicit_self: borrowed_explicit_self(),
92                           args: Vec::new(),
93                           ret_ty: Self_,
94                           attributes: attrs,
95                           is_unsafe: false,
96                           unify_fieldless_variants: false,
97                           combine_substructure: substructure,
98                       }],
99         associated_types: Vec::new(),
100     };
101
102     trait_def.expand_ext(cx, mitem, item, push, is_shallow)
103 }
104
105 fn cs_clone_shallow(name: &str,
106                     cx: &mut ExtCtxt<'_>,
107                     trait_span: Span,
108                     substr: &Substructure<'_>,
109                     is_union: bool)
110                     -> P<Expr> {
111     fn assert_ty_bounds(cx: &mut ExtCtxt<'_>, stmts: &mut Vec<ast::Stmt>,
112                         ty: P<ast::Ty>, span: Span, helper_name: &str) {
113         // Generate statement `let _: helper_name<ty>;`,
114         // set the expn ID so we can use the unstable struct.
115         let span = span.with_ctxt(cx.backtrace());
116         let assert_path = cx.path_all(span, true,
117                                         cx.std_path(&[sym::clone, Symbol::intern(helper_name)]),
118                                         vec![GenericArg::Type(ty)], vec![]);
119         stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path)));
120     }
121     fn process_variant(cx: &mut ExtCtxt<'_>, stmts: &mut Vec<ast::Stmt>, variant: &VariantData) {
122         for field in variant.fields() {
123             // let _: AssertParamIsClone<FieldTy>;
124             assert_ty_bounds(cx, stmts, field.ty.clone(), field.span, "AssertParamIsClone");
125         }
126     }
127
128     let mut stmts = Vec::new();
129     if is_union {
130         // let _: AssertParamIsCopy<Self>;
131         let self_ty =
132             cx.ty_path(cx.path_ident(trait_span, ast::Ident::with_empty_ctxt(kw::SelfUpper)));
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(&[sym::clone, sym::Clone, sym::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 }