]>
Commit | Line | Data |
---|---|---|
9fa01778 | 1 | use crate::deriving::generic::ty::*; |
dfeec247 XL |
2 | use crate::deriving::generic::*; |
3 | use crate::deriving::path_std; | |
9cc50fc6 | 4 | |
74b04a01 | 5 | use rustc_ast::ptr::P; |
3dfed10e | 6 | use rustc_ast::{self as ast, Expr, GenericArg, Generics, ItemKind, MetaItem, VariantData}; |
dfeec247 | 7 | use rustc_expand::base::{Annotatable, ExtCtxt}; |
f9f354fc | 8 | use rustc_span::symbol::{kw, sym, Ident, Symbol}; |
dfeec247 | 9 | use rustc_span::Span; |
223e47cc | 10 | |
dfeec247 XL |
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 | ) { | |
a7813a04 XL |
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. | |
9e0c209e SL |
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. | |
5e7ed085 | 31 | // Wherever Clone is implemented for fields is irrelevant so we don't assert it. |
a7813a04 | 32 | let bounds; |
a7813a04 | 33 | let substructure; |
9e0c209e | 34 | let is_shallow; |
a7813a04 | 35 | match *item { |
dfeec247 XL |
36 | Annotatable::Item(ref annitem) => match annitem.kind { |
37 | ItemKind::Struct(_, Generics { ref params, .. }) | |
38 | | ItemKind::Enum(_, Generics { ref params, .. }) => { | |
136023e0 XL |
39 | let container_id = cx.current_expansion.id.expn_data().parent.expect_local(); |
40 | let has_derive_copy = cx.resolver.has_derive_copy(container_id); | |
41 | if has_derive_copy | |
5869c6ff XL |
42 | && !params |
43 | .iter() | |
44 | .any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })) | |
dfeec247 XL |
45 | { |
46 | bounds = vec![]; | |
9e0c209e | 47 | is_shallow = true; |
a7813a04 | 48 | substructure = combine_substructure(Box::new(|c, s, sub| { |
dfeec247 | 49 | cs_clone_shallow("Clone", c, s, sub, false) |
a7813a04 | 50 | })); |
dfeec247 | 51 | } else { |
a7813a04 | 52 | bounds = vec![]; |
9e0c209e | 53 | is_shallow = false; |
dfeec247 XL |
54 | substructure = |
55 | combine_substructure(Box::new(|c, s, sub| cs_clone("Clone", c, s, sub))); | |
a7813a04 XL |
56 | } |
57 | } | |
dfeec247 | 58 | ItemKind::Union(..) => { |
3dfed10e | 59 | bounds = vec![Literal(path_std!(marker::Copy))]; |
dfeec247 XL |
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 | }, | |
a7813a04 | 72 | |
416331ca | 73 | _ => cx.span_bug(span, "`#[derive(Clone)]` on trait item or impl item"), |
a7813a04 XL |
74 | } |
75 | ||
dc9dc135 | 76 | let inline = cx.meta_word(span, sym::inline); |
416331ca | 77 | let attrs = vec![cx.attribute(inline)]; |
970d7e83 | 78 | let trait_def = TraitDef { |
3b2f2976 | 79 | span, |
1a4d82fc | 80 | attributes: Vec::new(), |
3dfed10e | 81 | path: path_std!(clone::Clone), |
a7813a04 | 82 | additional_bounds: bounds, |
3dfed10e | 83 | generics: Bounds::empty(), |
e9174d1e | 84 | is_unsafe: false, |
9e0c209e | 85 | supports_unions: true, |
5bcae85e | 86 | methods: vec![MethodDef { |
3dfed10e XL |
87 | name: sym::clone, |
88 | generics: Bounds::empty(), | |
dfeec247 XL |
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 | }], | |
85aaf69f | 97 | associated_types: Vec::new(), |
223e47cc LB |
98 | }; |
99 | ||
9e0c209e SL |
100 | trait_def.expand_ext(cx, mitem, item, push, is_shallow) |
101 | } | |
102 | ||
dfeec247 XL |
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 | ) { | |
9e0c209e SL |
117 | // Generate statement `let _: helper_name<ty>;`, |
118 | // set the expn ID so we can use the unstable struct. | |
e1599b0c | 119 | let span = cx.with_def_site_ctxt(span); |
dfeec247 XL |
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 | ); | |
9e0c209e SL |
126 | stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path))); |
127 | } | |
9fa01778 | 128 | fn process_variant(cx: &mut ExtCtxt<'_>, stmts: &mut Vec<ast::Stmt>, variant: &VariantData) { |
9e0c209e SL |
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>; | |
f9f354fc | 138 | let self_ty = cx.ty_path(cx.path_ident(trait_span, Ident::with_dummy_span(kw::SelfUpper))); |
9e0c209e SL |
139 | assert_ty_bounds(cx, &mut stmts, self_ty, trait_span, "AssertParamIsCopy"); |
140 | } else { | |
141 | match *substr.fields { | |
142 | StaticStruct(vdata, ..) => { | |
143 | process_variant(cx, &mut stmts, vdata); | |
144 | } | |
145 | StaticEnum(enum_def, ..) => { | |
146 | for variant in &enum_def.variants { | |
e1599b0c | 147 | process_variant(cx, &mut stmts, &variant.data); |
9e0c209e SL |
148 | } |
149 | } | |
dfeec247 XL |
150 | _ => cx.span_bug( |
151 | trait_span, | |
136023e0 | 152 | &format!("unexpected substructure in shallow `derive({})`", name), |
dfeec247 | 153 | ), |
9e0c209e SL |
154 | } |
155 | } | |
156 | stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span)))); | |
157 | cx.expr_block(cx.block(trait_span, stmts)) | |
223e47cc LB |
158 | } |
159 | ||
dfeec247 XL |
160 | fn cs_clone( |
161 | name: &str, | |
162 | cx: &mut ExtCtxt<'_>, | |
163 | trait_span: Span, | |
164 | substr: &Substructure<'_>, | |
165 | ) -> P<Expr> { | |
1a4d82fc | 166 | let ctor_path; |
970d7e83 | 167 | let all_fields; |
dc9dc135 | 168 | let fn_path = cx.std_path(&[sym::clone, sym::Clone, sym::clone]); |
9fa01778 | 169 | let subcall = |cx: &mut ExtCtxt<'_>, field: &FieldInfo<'_>| { |
1a4d82fc | 170 | let args = vec![cx.expr_addr_of(field.span, field.self_.clone())]; |
9e0c209e | 171 | cx.expr_call_global(field.span, fn_path.clone(), args) |
1a4d82fc | 172 | }; |
970d7e83 | 173 | |
7453a54e | 174 | let vdata; |
970d7e83 | 175 | match *substr.fields { |
7453a54e | 176 | Struct(vdata_, ref af) => { |
1a4d82fc | 177 | ctor_path = cx.path(trait_span, vec![substr.type_ident]); |
970d7e83 | 178 | all_fields = af; |
7453a54e | 179 | vdata = vdata_; |
223e47cc | 180 | } |
041b39d2 | 181 | EnumMatching(.., variant, ref af) => { |
e1599b0c | 182 | ctor_path = cx.path(trait_span, vec![substr.type_ident, variant.ident]); |
970d7e83 | 183 | all_fields = af; |
e1599b0c | 184 | vdata = &variant.data; |
5bcae85e | 185 | } |
dfeec247 XL |
186 | EnumNonMatchingCollapsed(..) => { |
187 | cx.span_bug(trait_span, &format!("non-matching enum variants in `derive({})`", name,)) | |
188 | } | |
1a4d82fc | 189 | StaticEnum(..) | StaticStruct(..) => { |
e74abb32 | 190 | cx.span_bug(trait_span, &format!("associated function in `derive({})`", name)) |
1a4d82fc | 191 | } |
223e47cc LB |
192 | } |
193 | ||
9e0c209e SL |
194 | match *vdata { |
195 | VariantData::Struct(..) => { | |
dfeec247 XL |
196 | let fields = all_fields |
197 | .iter() | |
9e0c209e | 198 | .map(|field| { |
5e7ed085 FG |
199 | let Some(ident) = field.name else { |
200 | cx.span_bug( | |
dfeec247 XL |
201 | trait_span, |
202 | &format!("unnamed field in normal struct in `derive({})`", name,), | |
5e7ed085 | 203 | ); |
9e0c209e SL |
204 | }; |
205 | let call = subcall(cx, field); | |
206 | cx.field_imm(field.span, ident, call) | |
207 | }) | |
208 | .collect::<Vec<_>>(); | |
a7813a04 | 209 | |
9e0c209e SL |
210 | cx.expr_struct(trait_span, ctor_path, fields) |
211 | } | |
212 | VariantData::Tuple(..) => { | |
213 | let subcalls = all_fields.iter().map(|f| subcall(cx, f)).collect(); | |
214 | let path = cx.expr_path(ctor_path); | |
215 | cx.expr_call(trait_span, path, subcalls) | |
7453a54e | 216 | } |
9e0c209e | 217 | VariantData::Unit(..) => cx.expr_path(ctor_path), |
970d7e83 | 218 | } |
223e47cc | 219 | } |