]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | //! Some code that abstracts away much of the boilerplate of writing |
2 | //! `derive` instances for traits. Among other things it manages getting | |
3 | //! access to the fields of the 4 different sorts of structs and enum | |
4 | //! variants, as well as creating the method and impl ast instances. | |
5 | //! | |
6 | //! Supported features (fairly exhaustive): | |
7 | //! | |
8 | //! - Methods taking any number of parameters of any type, and returning | |
9 | //! any type, other than vectors, bottom and closures. | |
10 | //! - Generating `impl`s for types with type parameters and lifetimes | |
0731742a | 11 | //! (e.g., `Option<T>`), the parameters are automatically given the |
1a4d82fc JJ |
12 | //! current trait as a bound. (This includes separate type parameters |
13 | //! and lifetimes for methods.) | |
85aaf69f | 14 | //! - Additional bounds on the type parameters (`TraitDef.additional_bounds`) |
1a4d82fc | 15 | //! |
3b2f2976 | 16 | //! The most important thing for implementors is the `Substructure` and |
1a4d82fc JJ |
17 | //! `SubstructureFields` objects. The latter groups 5 possibilities of the |
18 | //! arguments: | |
19 | //! | |
20 | //! - `Struct`, when `Self` is a struct (including tuple structs, e.g | |
85aaf69f | 21 | //! `struct T(i32, char)`). |
1a4d82fc | 22 | //! - `EnumMatching`, when `Self` is an enum and all the arguments are the |
0731742a | 23 | //! same variant of the enum (e.g., `Some(1)`, `Some(3)` and `Some(4)`) |
1a4d82fc | 24 | //! - `EnumNonMatchingCollapsed` when `Self` is an enum and the arguments |
0731742a | 25 | //! are not the same variant (e.g., `None`, `Some(1)` and `None`). |
1a4d82fc JJ |
26 | //! - `StaticEnum` and `StaticStruct` for static methods, where the type |
27 | //! being derived upon is either an enum or struct respectively. (Any | |
28 | //! argument with type Self is just grouped among the non-self | |
29 | //! arguments.) | |
30 | //! | |
31 | //! In the first two cases, the values from the corresponding fields in | |
32 | //! all the arguments are grouped together. For `EnumNonMatchingCollapsed` | |
33 | //! this isn't possible (different variants have different fields), so the | |
34 | //! fields are inaccessible. (Previous versions of the deriving infrastructure | |
35 | //! had a way to expand into code that could access them, at the cost of | |
36 | //! generating exponential amounts of code; see issue #15375). There are no | |
37 | //! fields with values in the static cases, so these are treated entirely | |
38 | //! differently. | |
39 | //! | |
40 | //! The non-static cases have `Option<ident>` in several places associated | |
41 | //! with field `expr`s. This represents the name of the field it is | |
42 | //! associated with. It is only not `None` when the associated field has | |
43 | //! an identifier in the source code. For example, the `x`s in the | |
44 | //! following snippet | |
45 | //! | |
46 | //! ```rust | |
92a42be0 | 47 | //! # #![allow(dead_code)] |
85aaf69f | 48 | //! struct A { x : i32 } |
1a4d82fc | 49 | //! |
85aaf69f | 50 | //! struct B(i32); |
1a4d82fc JJ |
51 | //! |
52 | //! enum C { | |
85aaf69f SL |
53 | //! C0(i32), |
54 | //! C1 { x: i32 } | |
1a4d82fc JJ |
55 | //! } |
56 | //! ``` | |
57 | //! | |
85aaf69f | 58 | //! The `i32`s in `B` and `C0` don't have an identifier, so the |
1a4d82fc JJ |
59 | //! `Option<ident>`s would be `None` for them. |
60 | //! | |
a1dfa0c6 | 61 | //! In the static cases, the structure is summarized, either into the just |
1a4d82fc JJ |
62 | //! spans of the fields or a list of spans and the field idents (for tuple |
63 | //! structs and record structs, respectively), or a list of these, for | |
64 | //! enums (one for each variant). For empty struct and empty enum | |
65 | //! variants, it is represented as a count of 0. | |
66 | //! | |
85aaf69f SL |
67 | //! # "`cs`" functions |
68 | //! | |
69 | //! The `cs_...` functions ("combine substructure) are designed to | |
70 | //! make life easier by providing some pre-made recipes for common | |
bd371182 | 71 | //! threads; mostly calling the function being derived on all the |
85aaf69f SL |
72 | //! arguments and then combining them back together in some way (or |
73 | //! letting the user chose that). They are not meant to be the only | |
74 | //! way to handle the structures that this code creates. | |
75 | //! | |
1a4d82fc JJ |
76 | //! # Examples |
77 | //! | |
78 | //! The following simplified `PartialEq` is used for in-code examples: | |
79 | //! | |
80 | //! ```rust | |
81 | //! trait PartialEq { | |
92a42be0 | 82 | //! fn eq(&self, other: &Self) -> bool; |
1a4d82fc | 83 | //! } |
85aaf69f SL |
84 | //! impl PartialEq for i32 { |
85 | //! fn eq(&self, other: &i32) -> bool { | |
1a4d82fc JJ |
86 | //! *self == *other |
87 | //! } | |
88 | //! } | |
89 | //! ``` | |
90 | //! | |
91 | //! Some examples of the values of `SubstructureFields` follow, using the | |
92 | //! above `PartialEq`, `A`, `B` and `C`. | |
93 | //! | |
94 | //! ## Structs | |
95 | //! | |
96 | //! When generating the `expr` for the `A` impl, the `SubstructureFields` is | |
97 | //! | |
98 | //! ```{.text} | |
85aaf69f | 99 | //! Struct(vec![FieldInfo { |
1a4d82fc JJ |
100 | //! span: <span of x> |
101 | //! name: Some(<ident of x>), | |
102 | //! self_: <expr for &self.x>, | |
85aaf69f | 103 | //! other: vec![<expr for &other.x] |
1a4d82fc JJ |
104 | //! }]) |
105 | //! ``` | |
106 | //! | |
107 | //! For the `B` impl, called with `B(a)` and `B(b)`, | |
108 | //! | |
109 | //! ```{.text} | |
85aaf69f SL |
110 | //! Struct(vec![FieldInfo { |
111 | //! span: <span of `i32`>, | |
1a4d82fc | 112 | //! name: None, |
85aaf69f SL |
113 | //! self_: <expr for &a> |
114 | //! other: vec![<expr for &b>] | |
1a4d82fc JJ |
115 | //! }]) |
116 | //! ``` | |
117 | //! | |
118 | //! ## Enums | |
119 | //! | |
120 | //! When generating the `expr` for a call with `self == C0(a)` and `other | |
121 | //! == C0(b)`, the SubstructureFields is | |
122 | //! | |
123 | //! ```{.text} | |
124 | //! EnumMatching(0, <ast::Variant for C0>, | |
85aaf69f SL |
125 | //! vec![FieldInfo { |
126 | //! span: <span of i32> | |
1a4d82fc JJ |
127 | //! name: None, |
128 | //! self_: <expr for &a>, | |
85aaf69f | 129 | //! other: vec![<expr for &b>] |
1a4d82fc JJ |
130 | //! }]) |
131 | //! ``` | |
132 | //! | |
133 | //! For `C1 {x}` and `C1 {x}`, | |
134 | //! | |
135 | //! ```{.text} | |
136 | //! EnumMatching(1, <ast::Variant for C1>, | |
85aaf69f | 137 | //! vec![FieldInfo { |
1a4d82fc JJ |
138 | //! span: <span of x> |
139 | //! name: Some(<ident of x>), | |
140 | //! self_: <expr for &self.x>, | |
85aaf69f | 141 | //! other: vec![<expr for &other.x>] |
1a4d82fc JJ |
142 | //! }]) |
143 | //! ``` | |
144 | //! | |
145 | //! For `C0(a)` and `C1 {x}` , | |
146 | //! | |
147 | //! ```{.text} | |
148 | //! EnumNonMatchingCollapsed( | |
85aaf69f | 149 | //! vec![<ident of self>, <ident of __arg_1>], |
1a4d82fc JJ |
150 | //! &[<ast::Variant for C0>, <ast::Variant for C1>], |
151 | //! &[<ident for self index value>, <ident of __arg_1 index value>]) | |
152 | //! ``` | |
153 | //! | |
154 | //! It is the same for when the arguments are flipped to `C1 {x}` and | |
155 | //! `C0(a)`; the only difference is what the values of the identifiers | |
156 | //! <ident for self index value> and <ident of __arg_1 index value> will | |
157 | //! be in the generated code. | |
158 | //! | |
159 | //! `EnumNonMatchingCollapsed` deliberately provides far less information | |
160 | //! than is generally available for a given pair of variants; see #15375 | |
161 | //! for discussion. | |
162 | //! | |
163 | //! ## Static | |
164 | //! | |
85aaf69f | 165 | //! A static method on the types above would result in, |
1a4d82fc JJ |
166 | //! |
167 | //! ```{.text} | |
b039eaaf | 168 | //! StaticStruct(<ast::VariantData of A>, Named(vec![(<ident of x>, <span of x>)])) |
1a4d82fc | 169 | //! |
b039eaaf | 170 | //! StaticStruct(<ast::VariantData of B>, Unnamed(vec![<span of x>])) |
1a4d82fc | 171 | //! |
85aaf69f SL |
172 | //! StaticEnum(<ast::EnumDef of C>, |
173 | //! vec![(<ident of C0>, <span of C0>, Unnamed(vec![<span of i32>])), | |
174 | //! (<ident of C1>, <span of C1>, Named(vec![(<ident of x>, <span of x>)]))]) | |
1a4d82fc JJ |
175 | //! ``` |
176 | ||
9fa01778 XL |
177 | pub use StaticFields::*; |
178 | pub use SubstructureFields::*; | |
1a4d82fc JJ |
179 | |
180 | use std::cell::RefCell; | |
8faf50e0 | 181 | use std::iter; |
1a4d82fc JJ |
182 | use std::vec; |
183 | ||
74b04a01 | 184 | use rustc_ast::ptr::P; |
3dfed10e XL |
185 | use rustc_ast::{self as ast, BinOpKind, EnumDef, Expr, Generics, PatKind}; |
186 | use rustc_ast::{GenericArg, GenericParamKind, VariantData}; | |
74b04a01 | 187 | use rustc_attr as attr; |
ba9703b0 | 188 | use rustc_data_structures::map_in_place::MapInPlace; |
dfeec247 | 189 | use rustc_expand::base::{Annotatable, ExtCtxt}; |
f9f354fc | 190 | use rustc_span::symbol::{kw, sym, Ident, Symbol}; |
dfeec247 | 191 | use rustc_span::Span; |
1a4d82fc | 192 | |
3dfed10e | 193 | use ty::{Bounds, Path, Ptr, PtrTy, Self_, Ty}; |
1a4d82fc | 194 | |
9fa01778 | 195 | use crate::deriving; |
54a0048b | 196 | |
1a4d82fc JJ |
197 | pub mod ty; |
198 | ||
199 | pub struct TraitDef<'a> { | |
200 | /// The span for the current #[derive(Foo)] header. | |
201 | pub span: Span, | |
202 | ||
203 | pub attributes: Vec<ast::Attribute>, | |
204 | ||
205 | /// Path of the trait, including any type parameters | |
3dfed10e | 206 | pub path: Path, |
1a4d82fc JJ |
207 | |
208 | /// Additional bounds required of any type parameters of the type, | |
209 | /// other than the current trait | |
3dfed10e | 210 | pub additional_bounds: Vec<Ty>, |
1a4d82fc | 211 | |
0731742a | 212 | /// Any extra lifetimes and/or bounds, e.g., `D: serialize::Decoder` |
3dfed10e | 213 | pub generics: Bounds, |
1a4d82fc | 214 | |
e9174d1e SL |
215 | /// Is it an `unsafe` trait? |
216 | pub is_unsafe: bool, | |
217 | ||
9e0c209e SL |
218 | /// Can this trait be derived for unions? |
219 | pub supports_unions: bool, | |
220 | ||
1a4d82fc | 221 | pub methods: Vec<MethodDef<'a>>, |
85aaf69f | 222 | |
3dfed10e | 223 | pub associated_types: Vec<(Ident, Ty)>, |
1a4d82fc JJ |
224 | } |
225 | ||
1a4d82fc JJ |
226 | pub struct MethodDef<'a> { |
227 | /// name of the method | |
3dfed10e | 228 | pub name: Symbol, |
0731742a | 229 | /// List of generics, e.g., `R: rand::Rng` |
3dfed10e | 230 | pub generics: Bounds, |
1a4d82fc | 231 | |
0731742a | 232 | /// Whether there is a self argument (outer Option) i.e., whether |
1a4d82fc JJ |
233 | /// this is a static function, and whether it is a pointer (inner |
234 | /// Option) | |
e1599b0c | 235 | pub explicit_self: Option<Option<PtrTy>>, |
1a4d82fc JJ |
236 | |
237 | /// Arguments other than the self argument | |
3dfed10e | 238 | pub args: Vec<(Ty, Symbol)>, |
1a4d82fc | 239 | |
9fa01778 | 240 | /// Returns type |
3dfed10e | 241 | pub ret_ty: Ty, |
1a4d82fc JJ |
242 | |
243 | pub attributes: Vec<ast::Attribute>, | |
244 | ||
62682a34 SL |
245 | // Is it an `unsafe fn`? |
246 | pub is_unsafe: bool, | |
247 | ||
a7813a04 XL |
248 | /// Can we combine fieldless variants for enums into a single match arm? |
249 | pub unify_fieldless_variants: bool, | |
250 | ||
1a4d82fc JJ |
251 | pub combine_substructure: RefCell<CombineSubstructureFunc<'a>>, |
252 | } | |
253 | ||
254 | /// All the data about the data structure/method being derived upon. | |
255 | pub struct Substructure<'a> { | |
256 | /// ident of self | |
257 | pub type_ident: Ident, | |
258 | /// ident of the method | |
259 | pub method_ident: Ident, | |
fc512014 XL |
260 | /// dereferenced access to any [`Self_`] or [`Ptr(Self_, _)][ptr]` arguments |
261 | /// | |
262 | /// [`Self_`]: ty::Ty::Self_ | |
263 | /// [ptr]: ty::Ty::Ptr | |
1a4d82fc JJ |
264 | pub self_args: &'a [P<Expr>], |
265 | /// verbatim access to any other arguments | |
266 | pub nonself_args: &'a [P<Expr>], | |
5bcae85e | 267 | pub fields: &'a SubstructureFields<'a>, |
1a4d82fc JJ |
268 | } |
269 | ||
270 | /// Summary of the relevant parts of a struct/enum field. | |
d9579d0f | 271 | pub struct FieldInfo<'a> { |
1a4d82fc JJ |
272 | pub span: Span, |
273 | /// None for tuple structs/normal enum variants, Some for normal | |
274 | /// structs/struct enum variants. | |
275 | pub name: Option<Ident>, | |
276 | /// The expression corresponding to this field of `self` | |
277 | /// (specifically, a reference to it). | |
278 | pub self_: P<Expr>, | |
279 | /// The expressions corresponding to references to this field in | |
280 | /// the other `Self` arguments. | |
281 | pub other: Vec<P<Expr>>, | |
d9579d0f AL |
282 | /// The attributes on the field |
283 | pub attrs: &'a [ast::Attribute], | |
1a4d82fc JJ |
284 | } |
285 | ||
286 | /// Fields for a static method | |
287 | pub enum StaticFields { | |
9e0c209e SL |
288 | /// Tuple and unit structs/enum variants like this. |
289 | Unnamed(Vec<Span>, bool /*is tuple*/), | |
1a4d82fc JJ |
290 | /// Normal structs/struct variants. |
291 | Named(Vec<(Ident, Span)>), | |
292 | } | |
293 | ||
294 | /// A summary of the possible sets of fields. | |
295 | pub enum SubstructureFields<'a> { | |
7453a54e | 296 | Struct(&'a ast::VariantData, Vec<FieldInfo<'a>>), |
041b39d2 | 297 | /// Matching variants of the enum: variant index, variant count, ast::Variant, |
1a4d82fc JJ |
298 | /// fields: the field name is only non-`None` in the case of a struct |
299 | /// variant. | |
041b39d2 | 300 | EnumMatching(usize, usize, &'a ast::Variant, Vec<FieldInfo<'a>>), |
1a4d82fc JJ |
301 | |
302 | /// Non-matching variants of the enum, but with all state hidden from | |
9fa01778 | 303 | /// the consequent code. The first component holds `Ident`s for all of |
1a4d82fc JJ |
304 | /// the `Self` arguments; the second component is a slice of all of the |
305 | /// variants for the enum itself, and the third component is a list of | |
306 | /// `Ident`s bound to the variant index values for each of the actual | |
307 | /// input `Self` arguments. | |
7453a54e | 308 | EnumNonMatchingCollapsed(Vec<Ident>, &'a [ast::Variant], &'a [Ident]), |
1a4d82fc JJ |
309 | |
310 | /// A static method where `Self` is a struct. | |
b039eaaf | 311 | StaticStruct(&'a ast::VariantData, StaticFields), |
1a4d82fc JJ |
312 | /// A static method where `Self` is an enum. |
313 | StaticEnum(&'a ast::EnumDef, Vec<(Ident, Span, StaticFields)>), | |
314 | } | |
315 | ||
1a4d82fc JJ |
316 | /// Combine the values of all the fields together. The last argument is |
317 | /// all the fields of all the structures. | |
318 | pub type CombineSubstructureFunc<'a> = | |
9fa01778 | 319 | Box<dyn FnMut(&mut ExtCtxt<'_>, Span, &Substructure<'_>) -> P<Expr> + 'a>; |
1a4d82fc | 320 | |
9fa01778 | 321 | /// Deal with non-matching enum variants. The tuple is a list of |
1a4d82fc JJ |
322 | /// identifiers (one for each `Self` argument, which could be any of the |
323 | /// variants since they have been collapsed together) and the identifiers | |
9fa01778 | 324 | /// holding the variant index value for each of the `Self` arguments. The |
1a4d82fc JJ |
325 | /// last argument is all the non-`Self` args of the method being derived. |
326 | pub type EnumNonMatchCollapsedFunc<'a> = | |
9fa01778 | 327 | Box<dyn FnMut(&mut ExtCtxt<'_>, Span, (&[Ident], &[Ident]), &[P<Expr>]) -> P<Expr> + 'a>; |
1a4d82fc | 328 | |
dfeec247 XL |
329 | pub fn combine_substructure( |
330 | f: CombineSubstructureFunc<'_>, | |
331 | ) -> RefCell<CombineSubstructureFunc<'_>> { | |
1a4d82fc JJ |
332 | RefCell::new(f) |
333 | } | |
334 | ||
c295e0f8 XL |
335 | struct TypeParameter { |
336 | bound_generic_params: Vec<ast::GenericParam>, | |
337 | ty: P<ast::Ty>, | |
338 | } | |
339 | ||
c34b1796 AL |
340 | /// This method helps to extract all the type parameters referenced from a |
341 | /// type. For a type parameter `<T>`, it looks for either a `TyPath` that | |
342 | /// is not global and starts with `T`, or a `TyQPath`. | |
c295e0f8 | 343 | /// Also include bound generic params from the input type. |
dc9dc135 XL |
344 | fn find_type_parameters( |
345 | ty: &ast::Ty, | |
f9f354fc | 346 | ty_param_names: &[Symbol], |
dc9dc135 | 347 | cx: &ExtCtxt<'_>, |
c295e0f8 | 348 | ) -> Vec<TypeParameter> { |
74b04a01 | 349 | use rustc_ast::visit; |
c34b1796 | 350 | |
dc9dc135 | 351 | struct Visitor<'a, 'b> { |
3157f602 | 352 | cx: &'a ExtCtxt<'b>, |
f9f354fc | 353 | ty_param_names: &'a [Symbol], |
c295e0f8 XL |
354 | bound_generic_params_stack: Vec<ast::GenericParam>, |
355 | type_params: Vec<TypeParameter>, | |
c34b1796 AL |
356 | } |
357 | ||
476ff2be SL |
358 | impl<'a, 'b> visit::Visitor<'a> for Visitor<'a, 'b> { |
359 | fn visit_ty(&mut self, ty: &'a ast::Ty) { | |
e74abb32 | 360 | if let ast::TyKind::Path(_, ref path) = ty.kind { |
32a655c1 | 361 | if let Some(segment) = path.segments.first() { |
83c7162d | 362 | if self.ty_param_names.contains(&segment.ident.name) { |
c295e0f8 XL |
363 | self.type_params.push(TypeParameter { |
364 | bound_generic_params: self.bound_generic_params_stack.clone(), | |
365 | ty: P(ty.clone()), | |
366 | }); | |
c34b1796 AL |
367 | } |
368 | } | |
c34b1796 AL |
369 | } |
370 | ||
371 | visit::walk_ty(self, ty) | |
372 | } | |
3157f602 | 373 | |
c295e0f8 XL |
374 | // Place bound generic params on a stack, to extract them when a type is encountered. |
375 | fn visit_poly_trait_ref( | |
376 | &mut self, | |
377 | trait_ref: &'a ast::PolyTraitRef, | |
378 | modifier: &'a ast::TraitBoundModifier, | |
379 | ) { | |
380 | let stack_len = self.bound_generic_params_stack.len(); | |
381 | self.bound_generic_params_stack | |
382 | .extend(trait_ref.bound_generic_params.clone().into_iter()); | |
383 | ||
384 | visit::walk_poly_trait_ref(self, trait_ref, modifier); | |
385 | ||
386 | self.bound_generic_params_stack.truncate(stack_len); | |
387 | } | |
388 | ||
29967ef6 | 389 | fn visit_mac_call(&mut self, mac: &ast::MacCall) { |
60c5eb7d | 390 | self.cx.span_err(mac.span(), "`derive` cannot be used on items with type macros"); |
3157f602 | 391 | } |
c34b1796 AL |
392 | } |
393 | ||
c295e0f8 XL |
394 | let mut visitor = Visitor { |
395 | cx, | |
396 | ty_param_names, | |
397 | bound_generic_params_stack: Vec::new(), | |
398 | type_params: Vec::new(), | |
399 | }; | |
c34b1796 AL |
400 | visit::Visitor::visit_ty(&mut visitor, ty); |
401 | ||
c295e0f8 | 402 | visitor.type_params |
c34b1796 | 403 | } |
1a4d82fc JJ |
404 | |
405 | impl<'a> TraitDef<'a> { | |
dfeec247 XL |
406 | pub fn expand( |
407 | self, | |
408 | cx: &mut ExtCtxt<'_>, | |
409 | mitem: &ast::MetaItem, | |
410 | item: &'a Annotatable, | |
411 | push: &mut dyn FnMut(Annotatable), | |
412 | ) { | |
9e0c209e SL |
413 | self.expand_ext(cx, mitem, item, push, false); |
414 | } | |
415 | ||
dfeec247 XL |
416 | pub fn expand_ext( |
417 | self, | |
418 | cx: &mut ExtCtxt<'_>, | |
419 | mitem: &ast::MetaItem, | |
420 | item: &'a Annotatable, | |
421 | push: &mut dyn FnMut(Annotatable), | |
422 | from_scratch: bool, | |
423 | ) { | |
d9579d0f AL |
424 | match *item { |
425 | Annotatable::Item(ref item) => { | |
ff7c6d11 | 426 | let is_packed = item.attrs.iter().any(|attr| { |
3dfed10e | 427 | for r in attr::find_repr_attrs(&cx.sess, attr) { |
83c7162d XL |
428 | if let attr::ReprPacked(_) = r { |
429 | return true; | |
430 | } | |
431 | } | |
432 | false | |
ff7c6d11 | 433 | }); |
e74abb32 | 434 | let has_no_type_params = match item.kind { |
dfeec247 XL |
435 | ast::ItemKind::Struct(_, ref generics) |
436 | | ast::ItemKind::Enum(_, ref generics) | |
5869c6ff XL |
437 | | ast::ItemKind::Union(_, ref generics) => !generics |
438 | .params | |
439 | .iter() | |
440 | .any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })), | |
fc512014 | 441 | _ => unreachable!(), |
ff7c6d11 | 442 | }; |
136023e0 | 443 | let container_id = cx.current_expansion.id.expn_data().parent.expect_local(); |
60c5eb7d XL |
444 | let always_copy = has_no_type_params && cx.resolver.has_derive_copy(container_id); |
445 | let use_temporaries = is_packed && always_copy; | |
ff7c6d11 | 446 | |
e74abb32 | 447 | let newitem = match item.kind { |
dfeec247 XL |
448 | ast::ItemKind::Struct(ref struct_def, ref generics) => self.expand_struct_def( |
449 | cx, | |
450 | &struct_def, | |
451 | item.ident, | |
452 | generics, | |
453 | from_scratch, | |
454 | use_temporaries, | |
455 | ), | |
7453a54e | 456 | ast::ItemKind::Enum(ref enum_def, ref generics) => { |
ff7c6d11 XL |
457 | // We ignore `use_temporaries` here, because |
458 | // `repr(packed)` enums cause an error later on. | |
459 | // | |
460 | // This can only cause further compilation errors | |
461 | // downstream in blatantly illegal code, so it | |
462 | // is fine. | |
f9f354fc | 463 | self.expand_enum_def(cx, enum_def, item.ident, generics, from_scratch) |
9e0c209e SL |
464 | } |
465 | ast::ItemKind::Union(ref struct_def, ref generics) => { | |
466 | if self.supports_unions { | |
dfeec247 XL |
467 | self.expand_struct_def( |
468 | cx, | |
469 | &struct_def, | |
470 | item.ident, | |
471 | generics, | |
472 | from_scratch, | |
473 | use_temporaries, | |
474 | ) | |
9e0c209e | 475 | } else { |
dfeec247 | 476 | cx.span_err(mitem.span, "this trait cannot be derived for unions"); |
9e0c209e SL |
477 | return; |
478 | } | |
d9579d0f | 479 | } |
ff7c6d11 | 480 | _ => unreachable!(), |
d9579d0f AL |
481 | }; |
482 | // Keep the lint attributes of the previous item to control how the | |
483 | // generated implementations are linted | |
484 | let mut attrs = newitem.attrs.clone(); | |
dfeec247 XL |
485 | attrs.extend( |
486 | item.attrs | |
487 | .iter() | |
488 | .filter(|a| { | |
489 | [ | |
490 | sym::allow, | |
491 | sym::warn, | |
492 | sym::deny, | |
493 | sym::forbid, | |
494 | sym::stable, | |
495 | sym::unstable, | |
496 | ] | |
48663c56 | 497 | .contains(&a.name_or_empty()) |
dfeec247 XL |
498 | }) |
499 | .cloned(), | |
500 | ); | |
74b04a01 | 501 | push(Annotatable::Item(P(ast::Item { attrs, ..(*newitem).clone() }))) |
1a4d82fc | 502 | } |
fc512014 | 503 | _ => unreachable!(), |
d9579d0f | 504 | } |
1a4d82fc JJ |
505 | } |
506 | ||
c34b1796 | 507 | /// Given that we are deriving a trait `DerivedTrait` for a type like: |
1a4d82fc | 508 | /// |
041b39d2 | 509 | /// ```ignore (only-for-syntax-highlight) |
c34b1796 AL |
510 | /// struct Struct<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> where C: WhereTrait { |
511 | /// a: A, | |
512 | /// b: B::Item, | |
513 | /// b1: <B as DeclaredTrait>::Item, | |
514 | /// c1: <C as WhereTrait>::Item, | |
515 | /// c2: Option<<C as WhereTrait>::Item>, | |
516 | /// ... | |
517 | /// } | |
1a4d82fc JJ |
518 | /// ``` |
519 | /// | |
c34b1796 AL |
520 | /// create an impl like: |
521 | /// | |
041b39d2 | 522 | /// ```ignore (only-for-syntax-highlight) |
9fa01778 | 523 | /// impl<'a, ..., 'z, A, B: DeclaredTrait, C, ... Z> where |
c34b1796 AL |
524 | /// C: WhereTrait, |
525 | /// A: DerivedTrait + B1 + ... + BN, | |
526 | /// B: DerivedTrait + B1 + ... + BN, | |
527 | /// C: DerivedTrait + B1 + ... + BN, | |
528 | /// B::Item: DerivedTrait + B1 + ... + BN, | |
529 | /// <C as WhereTrait>::Item: DerivedTrait + B1 + ... + BN, | |
530 | /// ... | |
531 | /// { | |
532 | /// ... | |
533 | /// } | |
534 | /// ``` | |
535 | /// | |
536 | /// where B1, ..., BN are the bounds given by `bounds_paths`.'. Z is a phantom type, and | |
537 | /// therefore does not get bound by the derived trait. | |
dfeec247 XL |
538 | fn create_derived_impl( |
539 | &self, | |
540 | cx: &mut ExtCtxt<'_>, | |
541 | type_ident: Ident, | |
542 | generics: &Generics, | |
543 | field_tys: Vec<P<ast::Ty>>, | |
74b04a01 | 544 | methods: Vec<P<ast::AssocItem>>, |
dfeec247 | 545 | ) -> P<ast::Item> { |
1a4d82fc JJ |
546 | let trait_path = self.path.to_path(cx, self.span, type_ident, generics); |
547 | ||
dfeec247 | 548 | // Transform associated types from `deriving::ty::Ty` into `ast::AssocItem` |
74b04a01 XL |
549 | let associated_types = self.associated_types.iter().map(|&(ident, ref type_def)| { |
550 | P(ast::AssocItem { | |
85aaf69f SL |
551 | id: ast::DUMMY_NODE_ID, |
552 | span: self.span, | |
3b2f2976 | 553 | ident, |
1b1a35ee XL |
554 | vis: ast::Visibility { |
555 | span: self.span.shrink_to_lo(), | |
556 | kind: ast::VisibilityKind::Inherited, | |
557 | tokens: None, | |
558 | }, | |
85aaf69f | 559 | attrs: Vec::new(), |
3c0e092e XL |
560 | kind: ast::AssocItemKind::TyAlias(Box::new(ast::TyAlias { |
561 | defaultness: ast::Defaultness::Final, | |
562 | generics: Generics::default(), | |
5e7ed085 FG |
563 | where_clauses: ( |
564 | ast::TyAliasWhereClause::default(), | |
565 | ast::TyAliasWhereClause::default(), | |
566 | ), | |
567 | where_predicates_split: 0, | |
3c0e092e XL |
568 | bounds: Vec::new(), |
569 | ty: Some(type_def.to_ty(cx, self.span, type_ident, generics)), | |
570 | })), | |
3b2f2976 | 571 | tokens: None, |
74b04a01 XL |
572 | }) |
573 | }); | |
85aaf69f | 574 | |
a2a8927a | 575 | let Generics { mut params, mut where_clause, .. } = |
dfeec247 | 576 | self.generics.to_generics(cx, self.span, type_ident, generics); |
a2a8927a XL |
577 | where_clause.span = generics.where_clause.span; |
578 | let ctxt = self.span.ctxt(); | |
579 | let span = generics.span.with_ctxt(ctxt); | |
1a4d82fc | 580 | |
ff7c6d11 | 581 | // Create the generic parameters |
cdc7bbd5 | 582 | params.extend(generics.params.iter().map(|param| match ¶m.kind { |
8faf50e0 XL |
583 | GenericParamKind::Lifetime { .. } => param.clone(), |
584 | GenericParamKind::Type { .. } => { | |
585 | // I don't think this can be moved out of the loop, since | |
586 | // a GenericBound requires an ast id | |
b7449926 | 587 | let bounds: Vec<_> = |
8faf50e0 XL |
588 | // extra restrictions on the generics parameters to the |
589 | // type being derived upon | |
590 | self.additional_bounds.iter().map(|p| { | |
591 | cx.trait_bound(p.to_path(cx, self.span, type_ident, generics)) | |
592 | }).chain( | |
593 | // require the current trait | |
594 | iter::once(cx.trait_bound(trait_path.clone())) | |
595 | ).chain( | |
596 | // also add in any bounds from the declaration | |
597 | param.bounds.iter().cloned() | |
598 | ).collect(); | |
1a4d82fc | 599 | |
a2a8927a | 600 | cx.typaram(param.ident.span.with_ctxt(ctxt), param.ident, vec![], bounds, None) |
ff7c6d11 | 601 | } |
cdc7bbd5 XL |
602 | GenericParamKind::Const { ty, kw_span, .. } => { |
603 | let const_nodefault_kind = GenericParamKind::Const { | |
604 | ty: ty.clone(), | |
a2a8927a | 605 | kw_span: kw_span.with_ctxt(ctxt), |
cdc7bbd5 XL |
606 | |
607 | // We can't have default values inside impl block | |
608 | default: None, | |
609 | }; | |
610 | let mut param_clone = param.clone(); | |
611 | param_clone.kind = const_nodefault_kind; | |
612 | param_clone | |
613 | } | |
1a4d82fc JJ |
614 | })); |
615 | ||
616 | // and similarly for where clauses | |
617 | where_clause.predicates.extend(generics.where_clause.predicates.iter().map(|clause| { | |
a2a8927a XL |
618 | match clause { |
619 | ast::WherePredicate::BoundPredicate(wb) => { | |
620 | let span = wb.span.with_ctxt(ctxt); | |
1a4d82fc | 621 | ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { |
a2a8927a XL |
622 | span, |
623 | ..wb.clone() | |
1a4d82fc JJ |
624 | }) |
625 | } | |
a2a8927a XL |
626 | ast::WherePredicate::RegionPredicate(wr) => { |
627 | let span = wr.span.with_ctxt(ctxt); | |
1a4d82fc | 628 | ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { |
a2a8927a XL |
629 | span, |
630 | ..wr.clone() | |
1a4d82fc JJ |
631 | }) |
632 | } | |
a2a8927a XL |
633 | ast::WherePredicate::EqPredicate(we) => { |
634 | let span = we.span.with_ctxt(ctxt); | |
1a4d82fc JJ |
635 | ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { |
636 | id: ast::DUMMY_NODE_ID, | |
a2a8927a XL |
637 | span, |
638 | ..we.clone() | |
1a4d82fc JJ |
639 | }) |
640 | } | |
641 | } | |
642 | })); | |
643 | ||
ff7c6d11 XL |
644 | { |
645 | // Extra scope required here so ty_params goes out of scope before params is moved | |
c34b1796 | 646 | |
dfeec247 XL |
647 | let mut ty_params = params |
648 | .iter() | |
5869c6ff | 649 | .filter(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })) |
ff7c6d11 | 650 | .peekable(); |
c34b1796 | 651 | |
ff7c6d11 | 652 | if ty_params.peek().is_some() { |
f9f354fc | 653 | let ty_param_names: Vec<Symbol> = |
dfeec247 | 654 | ty_params.map(|ty_param| ty_param.ident.name).collect(); |
ff7c6d11 | 655 | |
ff7c6d11 | 656 | for field_ty in field_tys { |
c295e0f8 | 657 | let field_ty_params = find_type_parameters(&field_ty, &ty_param_names, cx); |
ff7c6d11 | 658 | |
c295e0f8 | 659 | for field_ty_param in field_ty_params { |
ff7c6d11 | 660 | // if we have already handled this type, skip it |
c295e0f8 | 661 | if let ast::TyKind::Path(_, ref p) = field_ty_param.ty.kind { |
dfeec247 XL |
662 | if p.segments.len() == 1 |
663 | && ty_param_names.contains(&p.segments[0].ident.name) | |
664 | { | |
ff7c6d11 XL |
665 | continue; |
666 | }; | |
ff7c6d11 | 667 | } |
dfeec247 XL |
668 | let mut bounds: Vec<_> = self |
669 | .additional_bounds | |
ff7c6d11 | 670 | .iter() |
dfeec247 | 671 | .map(|p| cx.trait_bound(p.to_path(cx, self.span, type_ident, generics))) |
ff7c6d11 XL |
672 | .collect(); |
673 | ||
674 | // require the current trait | |
8faf50e0 | 675 | bounds.push(cx.trait_bound(trait_path.clone())); |
ff7c6d11 XL |
676 | |
677 | let predicate = ast::WhereBoundPredicate { | |
678 | span: self.span, | |
c295e0f8 XL |
679 | bound_generic_params: field_ty_param.bound_generic_params, |
680 | bounded_ty: field_ty_param.ty, | |
ff7c6d11 XL |
681 | bounds, |
682 | }; | |
c34b1796 | 683 | |
ff7c6d11 XL |
684 | let predicate = ast::WherePredicate::BoundPredicate(predicate); |
685 | where_clause.predicates.push(predicate); | |
686 | } | |
c34b1796 AL |
687 | } |
688 | } | |
689 | } | |
690 | ||
dfeec247 | 691 | let trait_generics = Generics { params, where_clause, span }; |
1a4d82fc JJ |
692 | |
693 | // Create the reference to the trait. | |
694 | let trait_ref = cx.trait_ref(trait_path); | |
695 | ||
dfeec247 XL |
696 | let self_params: Vec<_> = generics |
697 | .params | |
698 | .iter() | |
699 | .map(|param| match param.kind { | |
700 | GenericParamKind::Lifetime { .. } => { | |
a2a8927a | 701 | GenericArg::Lifetime(cx.lifetime(param.ident.span.with_ctxt(ctxt), param.ident)) |
dfeec247 XL |
702 | } |
703 | GenericParamKind::Type { .. } => { | |
a2a8927a | 704 | GenericArg::Type(cx.ty_ident(param.ident.span.with_ctxt(ctxt), param.ident)) |
dfeec247 XL |
705 | } |
706 | GenericParamKind::Const { .. } => { | |
a2a8927a | 707 | GenericArg::Const(cx.const_ident(param.ident.span.with_ctxt(ctxt), param.ident)) |
dfeec247 XL |
708 | } |
709 | }) | |
710 | .collect(); | |
1a4d82fc JJ |
711 | |
712 | // Create the type of `self`. | |
e1599b0c | 713 | let path = cx.path_all(self.span, false, vec![type_ident], self_params); |
8faf50e0 | 714 | let self_type = cx.ty_path(path); |
5bcae85e | 715 | |
416331ca | 716 | let attr = cx.attribute(cx.meta_word(self.span, sym::automatically_derived)); |
1a4d82fc | 717 | let opt_trait_ref = Some(trait_ref); |
476ff2be | 718 | let unused_qual = { |
74b04a01 | 719 | let word = rustc_ast::attr::mk_nested_word_item(Ident::new( |
3dfed10e | 720 | sym::unused_qualifications, |
dfeec247 XL |
721 | self.span, |
722 | )); | |
74b04a01 | 723 | let list = rustc_ast::attr::mk_list_item(Ident::new(sym::allow, self.span), vec![word]); |
e1599b0c | 724 | cx.attribute(list) |
476ff2be SL |
725 | }; |
726 | ||
e9174d1e | 727 | let mut a = vec![attr, unused_qual]; |
85aaf69f | 728 | a.extend(self.attributes.iter().cloned()); |
e9174d1e | 729 | |
74b04a01 | 730 | let unsafety = if self.is_unsafe { ast::Unsafe::Yes(self.span) } else { ast::Unsafe::No }; |
e9174d1e | 731 | |
dfeec247 XL |
732 | cx.item( |
733 | self.span, | |
3c0e092e | 734 | Ident::empty(), |
dfeec247 | 735 | a, |
3c0e092e | 736 | ast::ItemKind::Impl(Box::new(ast::Impl { |
dfeec247 XL |
737 | unsafety, |
738 | polarity: ast::ImplPolarity::Positive, | |
739 | defaultness: ast::Defaultness::Final, | |
74b04a01 | 740 | constness: ast::Const::No, |
dfeec247 XL |
741 | generics: trait_generics, |
742 | of_trait: opt_trait_ref, | |
743 | self_ty: self_type, | |
744 | items: methods.into_iter().chain(associated_types).collect(), | |
94222f64 | 745 | })), |
dfeec247 | 746 | ) |
1a4d82fc JJ |
747 | } |
748 | ||
dfeec247 XL |
749 | fn expand_struct_def( |
750 | &self, | |
751 | cx: &mut ExtCtxt<'_>, | |
752 | struct_def: &'a VariantData, | |
753 | type_ident: Ident, | |
754 | generics: &Generics, | |
755 | from_scratch: bool, | |
756 | use_temporaries: bool, | |
757 | ) -> P<ast::Item> { | |
758 | let field_tys: Vec<P<ast::Ty>> = | |
759 | struct_def.fields().iter().map(|field| field.ty.clone()).collect(); | |
760 | ||
761 | let methods = self | |
762 | .methods | |
5bcae85e SL |
763 | .iter() |
764 | .map(|method_def| { | |
765 | let (explicit_self, self_args, nonself_args, tys) = | |
766 | method_def.split_self_nonself_args(cx, self, type_ident, generics); | |
767 | ||
9e0c209e | 768 | let body = if from_scratch || method_def.is_static() { |
dfeec247 XL |
769 | method_def.expand_static_struct_method_body( |
770 | cx, | |
771 | self, | |
772 | struct_def, | |
773 | type_ident, | |
a2a8927a XL |
774 | &self_args, |
775 | &nonself_args, | |
dfeec247 | 776 | ) |
5bcae85e | 777 | } else { |
dfeec247 XL |
778 | method_def.expand_struct_method_body( |
779 | cx, | |
780 | self, | |
781 | struct_def, | |
782 | type_ident, | |
a2a8927a XL |
783 | &self_args, |
784 | &nonself_args, | |
dfeec247 XL |
785 | use_temporaries, |
786 | ) | |
5bcae85e SL |
787 | }; |
788 | ||
dfeec247 | 789 | method_def.create_method(cx, self, type_ident, generics, explicit_self, tys, body) |
5bcae85e SL |
790 | }) |
791 | .collect(); | |
1a4d82fc | 792 | |
c34b1796 | 793 | self.create_derived_impl(cx, type_ident, generics, field_tys, methods) |
1a4d82fc JJ |
794 | } |
795 | ||
dfeec247 XL |
796 | fn expand_enum_def( |
797 | &self, | |
798 | cx: &mut ExtCtxt<'_>, | |
799 | enum_def: &'a EnumDef, | |
dfeec247 XL |
800 | type_ident: Ident, |
801 | generics: &Generics, | |
802 | from_scratch: bool, | |
803 | ) -> P<ast::Item> { | |
c34b1796 AL |
804 | let mut field_tys = Vec::new(); |
805 | ||
62682a34 | 806 | for variant in &enum_def.variants { |
dfeec247 | 807 | field_tys.extend(variant.data.fields().iter().map(|field| field.ty.clone())); |
c34b1796 AL |
808 | } |
809 | ||
dfeec247 XL |
810 | let methods = self |
811 | .methods | |
5bcae85e SL |
812 | .iter() |
813 | .map(|method_def| { | |
814 | let (explicit_self, self_args, nonself_args, tys) = | |
815 | method_def.split_self_nonself_args(cx, self, type_ident, generics); | |
816 | ||
9e0c209e | 817 | let body = if from_scratch || method_def.is_static() { |
dfeec247 XL |
818 | method_def.expand_static_enum_method_body( |
819 | cx, | |
820 | self, | |
821 | enum_def, | |
822 | type_ident, | |
a2a8927a XL |
823 | &self_args, |
824 | &nonself_args, | |
dfeec247 | 825 | ) |
5bcae85e | 826 | } else { |
dfeec247 XL |
827 | method_def.expand_enum_method_body( |
828 | cx, | |
829 | self, | |
830 | enum_def, | |
dfeec247 XL |
831 | type_ident, |
832 | self_args, | |
a2a8927a | 833 | &nonself_args, |
dfeec247 | 834 | ) |
5bcae85e SL |
835 | }; |
836 | ||
dfeec247 | 837 | method_def.create_method(cx, self, type_ident, generics, explicit_self, tys, body) |
5bcae85e SL |
838 | }) |
839 | .collect(); | |
1a4d82fc | 840 | |
c34b1796 | 841 | self.create_derived_impl(cx, type_ident, generics, field_tys, methods) |
1a4d82fc JJ |
842 | } |
843 | } | |
844 | ||
1a4d82fc | 845 | impl<'a> MethodDef<'a> { |
dfeec247 XL |
846 | fn call_substructure_method( |
847 | &self, | |
848 | cx: &mut ExtCtxt<'_>, | |
849 | trait_: &TraitDef<'_>, | |
850 | type_ident: Ident, | |
851 | self_args: &[P<Expr>], | |
852 | nonself_args: &[P<Expr>], | |
853 | fields: &SubstructureFields<'_>, | |
854 | ) -> P<Expr> { | |
a2a8927a | 855 | let span = trait_.span; |
1a4d82fc | 856 | let substructure = Substructure { |
3b2f2976 | 857 | type_ident, |
a2a8927a | 858 | method_ident: Ident::new(self.name, span), |
3b2f2976 XL |
859 | self_args, |
860 | nonself_args, | |
861 | fields, | |
1a4d82fc JJ |
862 | }; |
863 | let mut f = self.combine_substructure.borrow_mut(); | |
9fa01778 | 864 | let f: &mut CombineSubstructureFunc<'_> = &mut *f; |
a2a8927a | 865 | f(cx, span, &substructure) |
1a4d82fc JJ |
866 | } |
867 | ||
dfeec247 XL |
868 | fn get_ret_ty( |
869 | &self, | |
870 | cx: &mut ExtCtxt<'_>, | |
871 | trait_: &TraitDef<'_>, | |
872 | generics: &Generics, | |
873 | type_ident: Ident, | |
874 | ) -> P<ast::Ty> { | |
1a4d82fc JJ |
875 | self.ret_ty.to_ty(cx, trait_.span, type_ident, generics) |
876 | } | |
877 | ||
878 | fn is_static(&self) -> bool { | |
879 | self.explicit_self.is_none() | |
880 | } | |
881 | ||
dfeec247 XL |
882 | fn split_self_nonself_args( |
883 | &self, | |
884 | cx: &mut ExtCtxt<'_>, | |
885 | trait_: &TraitDef<'_>, | |
886 | type_ident: Ident, | |
887 | generics: &Generics, | |
888 | ) -> (Option<ast::ExplicitSelf>, Vec<P<Expr>>, Vec<P<Expr>>, Vec<(Ident, P<ast::Ty>)>) { | |
1a4d82fc JJ |
889 | let mut self_args = Vec::new(); |
890 | let mut nonself_args = Vec::new(); | |
891 | let mut arg_tys = Vec::new(); | |
892 | let mut nonstatic = false; | |
a2a8927a | 893 | let span = trait_.span; |
1a4d82fc | 894 | |
3157f602 | 895 | let ast_explicit_self = self.explicit_self.as_ref().map(|self_ptr| { |
a2a8927a | 896 | let (self_expr, explicit_self) = ty::get_explicit_self(cx, span, self_ptr); |
1a4d82fc | 897 | |
3157f602 XL |
898 | self_args.push(self_expr); |
899 | nonstatic = true; | |
1a4d82fc | 900 | |
3157f602 XL |
901 | explicit_self |
902 | }); | |
1a4d82fc | 903 | |
83c7162d | 904 | for (ty, name) in self.args.iter() { |
a2a8927a XL |
905 | let ast_ty = ty.to_ty(cx, span, type_ident, generics); |
906 | let ident = Ident::new(*name, span); | |
1a4d82fc JJ |
907 | arg_tys.push((ident, ast_ty)); |
908 | ||
a2a8927a | 909 | let arg_expr = cx.expr_ident(span, ident); |
1a4d82fc JJ |
910 | |
911 | match *ty { | |
912 | // for static methods, just treat any Self | |
913 | // arguments as a normal arg | |
5bcae85e | 914 | Self_ if nonstatic => { |
1a4d82fc JJ |
915 | self_args.push(arg_expr); |
916 | } | |
5869c6ff | 917 | Ptr(ref ty, _) if matches!(**ty, Self_) && nonstatic => { |
a2a8927a | 918 | self_args.push(cx.expr_deref(span, arg_expr)) |
1a4d82fc JJ |
919 | } |
920 | _ => { | |
921 | nonself_args.push(arg_expr); | |
922 | } | |
923 | } | |
924 | } | |
925 | ||
926 | (ast_explicit_self, self_args, nonself_args, arg_tys) | |
927 | } | |
928 | ||
dfeec247 XL |
929 | fn create_method( |
930 | &self, | |
931 | cx: &mut ExtCtxt<'_>, | |
932 | trait_: &TraitDef<'_>, | |
933 | type_ident: Ident, | |
934 | generics: &Generics, | |
935 | explicit_self: Option<ast::ExplicitSelf>, | |
936 | arg_types: Vec<(Ident, P<ast::Ty>)>, | |
937 | body: P<Expr>, | |
74b04a01 | 938 | ) -> P<ast::AssocItem> { |
a2a8927a | 939 | let span = trait_.span; |
dc9dc135 | 940 | // Create the generics that aren't for `Self`. |
a2a8927a | 941 | let fn_generics = self.generics.to_generics(cx, span, type_ident, generics); |
1a4d82fc | 942 | |
1a4d82fc | 943 | let args = { |
3157f602 | 944 | let self_args = explicit_self.map(|explicit_self| { |
a2a8927a | 945 | let ident = Ident::with_dummy_span(kw::SelfLower).with_span_pos(span); |
dfeec247 | 946 | ast::Param::from_self(ast::AttrVec::default(), explicit_self, ident) |
3157f602 | 947 | }); |
a2a8927a | 948 | let nonself_args = arg_types.into_iter().map(|(name, ty)| cx.param(span, name, ty)); |
3157f602 | 949 | self_args.into_iter().chain(nonself_args).collect() |
1a4d82fc JJ |
950 | }; |
951 | ||
952 | let ret_type = self.get_ret_ty(cx, trait_, generics, type_ident); | |
953 | ||
a2a8927a | 954 | let method_ident = Ident::new(self.name, span); |
74b04a01 | 955 | let fn_decl = cx.fn_decl(args, ast::FnRetTy::Ty(ret_type)); |
1a4d82fc JJ |
956 | let body_block = cx.block_expr(body); |
957 | ||
a2a8927a | 958 | let unsafety = if self.is_unsafe { ast::Unsafe::Yes(span) } else { ast::Unsafe::No }; |
62682a34 | 959 | |
a2a8927a | 960 | let trait_lo_sp = span.shrink_to_lo(); |
60c5eb7d XL |
961 | |
962 | let sig = ast::FnSig { | |
dfeec247 | 963 | header: ast::FnHeader { unsafety, ext: ast::Extern::None, ..ast::FnHeader::default() }, |
60c5eb7d | 964 | decl: fn_decl, |
a2a8927a | 965 | span, |
60c5eb7d | 966 | }; |
3c0e092e | 967 | let defaultness = ast::Defaultness::Final; |
60c5eb7d | 968 | |
1a4d82fc | 969 | // Create the method. |
74b04a01 | 970 | P(ast::AssocItem { |
1a4d82fc | 971 | id: ast::DUMMY_NODE_ID, |
c34b1796 | 972 | attrs: self.attributes.clone(), |
a2a8927a | 973 | span, |
1b1a35ee XL |
974 | vis: ast::Visibility { |
975 | span: trait_lo_sp, | |
976 | kind: ast::VisibilityKind::Inherited, | |
977 | tokens: None, | |
978 | }, | |
c34b1796 | 979 | ident: method_ident, |
3c0e092e XL |
980 | kind: ast::AssocItemKind::Fn(Box::new(ast::Fn { |
981 | defaultness, | |
94222f64 | 982 | sig, |
3c0e092e XL |
983 | generics: fn_generics, |
984 | body: Some(body_block), | |
985 | })), | |
3b2f2976 | 986 | tokens: None, |
74b04a01 | 987 | }) |
1a4d82fc JJ |
988 | } |
989 | ||
041b39d2 | 990 | /// ``` |
1a4d82fc | 991 | /// #[derive(PartialEq)] |
041b39d2 | 992 | /// # struct Dummy; |
85aaf69f | 993 | /// struct A { x: i32, y: i32 } |
1a4d82fc JJ |
994 | /// |
995 | /// // equivalent to: | |
996 | /// impl PartialEq for A { | |
83c7162d | 997 | /// fn eq(&self, other: &A) -> bool { |
1a4d82fc JJ |
998 | /// match *self { |
999 | /// A {x: ref __self_0_0, y: ref __self_0_1} => { | |
83c7162d | 1000 | /// match *other { |
1a4d82fc JJ |
1001 | /// A {x: ref __self_1_0, y: ref __self_1_1} => { |
1002 | /// __self_0_0.eq(__self_1_0) && __self_0_1.eq(__self_1_1) | |
1003 | /// } | |
1004 | /// } | |
1005 | /// } | |
1006 | /// } | |
1007 | /// } | |
1008 | /// } | |
ff7c6d11 XL |
1009 | /// |
1010 | /// // or if A is repr(packed) - note fields are matched by-value | |
1011 | /// // instead of by-reference. | |
1012 | /// impl PartialEq for A { | |
83c7162d | 1013 | /// fn eq(&self, other: &A) -> bool { |
ff7c6d11 XL |
1014 | /// match *self { |
1015 | /// A {x: __self_0_0, y: __self_0_1} => { | |
83c7162d | 1016 | /// match other { |
ff7c6d11 XL |
1017 | /// A {x: __self_1_0, y: __self_1_1} => { |
1018 | /// __self_0_0.eq(&__self_1_0) && __self_0_1.eq(&__self_1_1) | |
1019 | /// } | |
1020 | /// } | |
1021 | /// } | |
1022 | /// } | |
1023 | /// } | |
1024 | /// } | |
1a4d82fc | 1025 | /// ``` |
dfeec247 XL |
1026 | fn expand_struct_method_body<'b>( |
1027 | &self, | |
1028 | cx: &mut ExtCtxt<'_>, | |
1029 | trait_: &TraitDef<'b>, | |
1030 | struct_def: &'b VariantData, | |
1031 | type_ident: Ident, | |
1032 | self_args: &[P<Expr>], | |
1033 | nonself_args: &[P<Expr>], | |
1034 | use_temporaries: bool, | |
1035 | ) -> P<Expr> { | |
a2a8927a XL |
1036 | let mut raw_fields = Vec::new(); // Vec<[fields of self], [fields of next Self arg], [etc]> |
1037 | let span = trait_.span; | |
1a4d82fc | 1038 | let mut patterns = Vec::new(); |
85aaf69f | 1039 | for i in 0..self_args.len() { |
a2a8927a | 1040 | let struct_path = cx.path(span, vec![type_ident]); |
dfeec247 XL |
1041 | let (pat, ident_expr) = trait_.create_struct_pattern( |
1042 | cx, | |
1043 | struct_path, | |
1044 | struct_def, | |
1045 | &format!("__self_{}", i), | |
1046 | ast::Mutability::Not, | |
1047 | use_temporaries, | |
1048 | ); | |
1a4d82fc JJ |
1049 | patterns.push(pat); |
1050 | raw_fields.push(ident_expr); | |
1051 | } | |
1052 | ||
1053 | // transpose raw_fields | |
9346a6ac | 1054 | let fields = if !raw_fields.is_empty() { |
1a4d82fc JJ |
1055 | let mut raw_fields = raw_fields.into_iter().map(|v| v.into_iter()); |
1056 | let first_field = raw_fields.next().unwrap(); | |
5bcae85e | 1057 | let mut other_fields: Vec<vec::IntoIter<_>> = raw_fields.collect(); |
dfeec247 XL |
1058 | first_field |
1059 | .map(|(span, opt_id, field, attrs)| FieldInfo { | |
a2a8927a | 1060 | span: span.with_ctxt(trait_.span.ctxt()), |
dfeec247 XL |
1061 | name: opt_id, |
1062 | self_: field, | |
1063 | other: other_fields | |
1064 | .iter_mut() | |
ba9703b0 XL |
1065 | .map(|l| { |
1066 | let (.., ex, _) = l.next().unwrap(); | |
1067 | ex | |
dfeec247 XL |
1068 | }) |
1069 | .collect(), | |
1070 | attrs, | |
5bcae85e SL |
1071 | }) |
1072 | .collect() | |
1a4d82fc | 1073 | } else { |
a2a8927a | 1074 | cx.span_bug(span, "no `self` parameter for method in generic `derive`") |
1a4d82fc JJ |
1075 | }; |
1076 | ||
1077 | // body of the inner most destructuring match | |
dfeec247 XL |
1078 | let mut body = self.call_substructure_method( |
1079 | cx, | |
1080 | trait_, | |
1081 | type_ident, | |
1082 | self_args, | |
1083 | nonself_args, | |
1084 | &Struct(struct_def, fields), | |
1085 | ); | |
1a4d82fc JJ |
1086 | |
1087 | // make a series of nested matches, to destructure the | |
1088 | // structs. This is actually right-to-left, but it shouldn't | |
1089 | // matter. | |
cdc7bbd5 | 1090 | for (arg_expr, pat) in iter::zip(self_args, patterns) { |
a2a8927a | 1091 | body = cx.expr_match(span, arg_expr.clone(), vec![cx.arm(span, pat.clone(), body)]) |
1a4d82fc | 1092 | } |
a7813a04 | 1093 | |
1a4d82fc JJ |
1094 | body |
1095 | } | |
1096 | ||
dfeec247 XL |
1097 | fn expand_static_struct_method_body( |
1098 | &self, | |
1099 | cx: &mut ExtCtxt<'_>, | |
1100 | trait_: &TraitDef<'_>, | |
1101 | struct_def: &VariantData, | |
1102 | type_ident: Ident, | |
1103 | self_args: &[P<Expr>], | |
1104 | nonself_args: &[P<Expr>], | |
1105 | ) -> P<Expr> { | |
1a4d82fc JJ |
1106 | let summary = trait_.summarise_struct(cx, struct_def); |
1107 | ||
dfeec247 XL |
1108 | self.call_substructure_method( |
1109 | cx, | |
1110 | trait_, | |
1111 | type_ident, | |
1112 | self_args, | |
1113 | nonself_args, | |
1114 | &StaticStruct(struct_def, summary), | |
1115 | ) | |
1a4d82fc JJ |
1116 | } |
1117 | ||
041b39d2 | 1118 | /// ``` |
1a4d82fc | 1119 | /// #[derive(PartialEq)] |
041b39d2 | 1120 | /// # struct Dummy; |
1a4d82fc JJ |
1121 | /// enum A { |
1122 | /// A1, | |
85aaf69f | 1123 | /// A2(i32) |
1a4d82fc JJ |
1124 | /// } |
1125 | /// | |
1126 | /// // is equivalent to | |
1127 | /// | |
1128 | /// impl PartialEq for A { | |
83c7162d XL |
1129 | /// fn eq(&self, other: &A) -> ::bool { |
1130 | /// match (&*self, &*other) { | |
1a4d82fc | 1131 | /// (&A1, &A1) => true, |
54a0048b SL |
1132 | /// (&A2(ref self_0), |
1133 | /// &A2(ref __arg_1_0)) => (*self_0).eq(&(*__arg_1_0)), | |
1a4d82fc | 1134 | /// _ => { |
85aaf69f | 1135 | /// let __self_vi = match *self { A1(..) => 0, A2(..) => 1 }; |
83c7162d | 1136 | /// let __arg_1_vi = match *other { A1(..) => 0, A2(..) => 1 }; |
1a4d82fc JJ |
1137 | /// false |
1138 | /// } | |
1139 | /// } | |
1140 | /// } | |
1141 | /// } | |
1142 | /// ``` | |
1143 | /// | |
1144 | /// (Of course `__self_vi` and `__arg_1_vi` are unused for | |
1145 | /// `PartialEq`, and those subcomputations will hopefully be removed | |
9fa01778 | 1146 | /// as their results are unused. The point of `__self_vi` and |
1a4d82fc | 1147 | /// `__arg_1_vi` is for `PartialOrd`; see #15503.) |
dfeec247 XL |
1148 | fn expand_enum_method_body<'b>( |
1149 | &self, | |
1150 | cx: &mut ExtCtxt<'_>, | |
1151 | trait_: &TraitDef<'b>, | |
1152 | enum_def: &'b EnumDef, | |
dfeec247 XL |
1153 | type_ident: Ident, |
1154 | self_args: Vec<P<Expr>>, | |
1155 | nonself_args: &[P<Expr>], | |
1156 | ) -> P<Expr> { | |
f9f354fc | 1157 | self.build_enum_match_tuple(cx, trait_, enum_def, type_ident, self_args, nonself_args) |
1a4d82fc JJ |
1158 | } |
1159 | ||
1a4d82fc JJ |
1160 | /// Creates a match for a tuple of all `self_args`, where either all |
1161 | /// variants match, or it falls into a catch-all for when one variant | |
1162 | /// does not match. | |
1163 | ||
1164 | /// There are N + 1 cases because is a case for each of the N | |
1165 | /// variants where all of the variants match, and one catch-all for | |
1166 | /// when one does not match. | |
1167 | ||
62682a34 SL |
1168 | /// As an optimization we generate code which checks whether all variants |
1169 | /// match first which makes llvm see that C-like enums can be compiled into | |
1170 | /// a simple equality check (for PartialEq). | |
1171 | ||
1a4d82fc | 1172 | /// The catch-all handler is provided access the variant index values |
62682a34 | 1173 | /// for each of the self-args, carried in precomputed variables. |
1a4d82fc JJ |
1174 | |
1175 | /// ```{.text} | |
29967ef6 XL |
1176 | /// let __self0_vi = std::intrinsics::discriminant_value(&self); |
1177 | /// let __self1_vi = std::intrinsics::discriminant_value(&arg1); | |
1178 | /// let __self2_vi = std::intrinsics::discriminant_value(&arg2); | |
62682a34 SL |
1179 | /// |
1180 | /// if __self0_vi == __self1_vi && __self0_vi == __self2_vi && ... { | |
1181 | /// match (...) { | |
1182 | /// (Variant1, Variant1, ...) => Body1 | |
1183 | /// (Variant2, Variant2, ...) => Body2, | |
1184 | /// ... | |
1185 | /// _ => ::core::intrinsics::unreachable() | |
1186 | /// } | |
1187 | /// } | |
1188 | /// else { | |
1a4d82fc | 1189 | /// ... // catch-all remainder can inspect above variant index values. |
1a4d82fc JJ |
1190 | /// } |
1191 | /// ``` | |
dfeec247 XL |
1192 | fn build_enum_match_tuple<'b>( |
1193 | &self, | |
1194 | cx: &mut ExtCtxt<'_>, | |
1195 | trait_: &TraitDef<'b>, | |
1196 | enum_def: &'b EnumDef, | |
dfeec247 XL |
1197 | type_ident: Ident, |
1198 | mut self_args: Vec<P<Expr>>, | |
1199 | nonself_args: &[P<Expr>], | |
1200 | ) -> P<Expr> { | |
a2a8927a | 1201 | let span = trait_.span; |
1a4d82fc JJ |
1202 | let variants = &enum_def.variants; |
1203 | ||
dfeec247 XL |
1204 | let self_arg_names = iter::once("__self".to_string()) |
1205 | .chain( | |
1206 | self_args | |
1207 | .iter() | |
1208 | .enumerate() | |
1209 | .skip(1) | |
1210 | .map(|(arg_count, _self_arg)| format!("__arg_{}", arg_count)), | |
1211 | ) | |
1212 | .collect::<Vec<String>>(); | |
1213 | ||
3dfed10e XL |
1214 | let self_arg_idents = self_arg_names |
1215 | .iter() | |
a2a8927a | 1216 | .map(|name| Ident::from_str_and_span(name, span)) |
3dfed10e | 1217 | .collect::<Vec<Ident>>(); |
1a4d82fc JJ |
1218 | |
1219 | // The `vi_idents` will be bound, solely in the catch-all, to | |
9346a6ac AL |
1220 | // a series of let statements mapping each self_arg to an int |
1221 | // value corresponding to its discriminant. | |
dfeec247 XL |
1222 | let vi_idents = self_arg_names |
1223 | .iter() | |
5bcae85e | 1224 | .map(|name| { |
a2a8927a XL |
1225 | let vi_suffix = format!("{}_vi", name); |
1226 | Ident::from_str_and_span(&vi_suffix, span) | |
5bcae85e | 1227 | }) |
f9f354fc | 1228 | .collect::<Vec<Ident>>(); |
1a4d82fc JJ |
1229 | |
1230 | // Builds, via callback to call_substructure_method, the | |
1231 | // delegated expression that handles the catch-all case, | |
1232 | // using `__variants_tuple` to drive logic if necessary. | |
5bcae85e | 1233 | let catch_all_substructure = |
a2a8927a | 1234 | EnumNonMatchingCollapsed(self_arg_idents, &variants, &vi_idents); |
1a4d82fc | 1235 | |
e1599b0c | 1236 | let first_fieldless = variants.iter().find(|v| v.data.fields().is_empty()); |
a7813a04 | 1237 | |
1a4d82fc JJ |
1238 | // These arms are of the form: |
1239 | // (Variant1, Variant1, ...) => Body1 | |
1240 | // (Variant2, Variant2, ...) => Body2 | |
1241 | // ... | |
1242 | // where each tuple has length = self_args.len() | |
dfeec247 XL |
1243 | let mut match_arms: Vec<ast::Arm> = variants |
1244 | .iter() | |
5bcae85e | 1245 | .enumerate() |
e1599b0c | 1246 | .filter(|&(_, v)| !(self.unify_fieldless_variants && v.data.fields().is_empty())) |
1a4d82fc | 1247 | .map(|(index, variant)| { |
9fa01778 | 1248 | let mk_self_pat = |cx: &mut ExtCtxt<'_>, self_arg_name: &str| { |
dfeec247 XL |
1249 | let (p, idents) = trait_.create_enum_variant_pattern( |
1250 | cx, | |
1251 | type_ident, | |
1252 | variant, | |
1253 | self_arg_name, | |
1254 | ast::Mutability::Not, | |
1255 | ); | |
a2a8927a | 1256 | (cx.pat(span, PatKind::Ref(p, ast::Mutability::Not)), idents) |
1a4d82fc JJ |
1257 | }; |
1258 | ||
1259 | // A single arm has form (&VariantK, &VariantK, ...) => BodyK | |
1260 | // (see "Final wrinkle" note below for why.) | |
1261 | let mut subpats = Vec::with_capacity(self_arg_names.len()); | |
1262 | let mut self_pats_idents = Vec::with_capacity(self_arg_names.len() - 1); | |
1263 | let first_self_pat_idents = { | |
c34b1796 | 1264 | let (p, idents) = mk_self_pat(cx, &self_arg_names[0]); |
1a4d82fc JJ |
1265 | subpats.push(p); |
1266 | idents | |
1267 | }; | |
d9579d0f | 1268 | for self_arg_name in &self_arg_names[1..] { |
a2a8927a | 1269 | let (p, idents) = mk_self_pat(cx, &self_arg_name); |
1a4d82fc JJ |
1270 | subpats.push(p); |
1271 | self_pats_idents.push(idents); | |
1272 | } | |
1273 | ||
1274 | // Here is the pat = `(&VariantK, &VariantK, ...)` | |
a2a8927a | 1275 | let single_pat = cx.pat_tuple(span, subpats); |
1a4d82fc JJ |
1276 | |
1277 | // For the BodyK, we need to delegate to our caller, | |
1278 | // passing it an EnumMatching to indicate which case | |
1279 | // we are in. | |
1280 | ||
1281 | // All of the Self args have the same variant in these | |
1282 | // cases. So we transpose the info in self_pats_idents | |
1283 | // to gather the getter expressions together, in the | |
1284 | // form that EnumMatching expects. | |
1285 | ||
1286 | // The transposition is driven by walking across the | |
1287 | // arg fields of the variant for the first self pat. | |
dfeec247 XL |
1288 | let field_tuples = first_self_pat_idents |
1289 | .into_iter() | |
1290 | .enumerate() | |
1a4d82fc | 1291 | // For each arg field of self, pull out its getter expr ... |
a2a8927a | 1292 | .map(|(field_index, (span, opt_ident, self_getter_expr, attrs))| { |
1a4d82fc JJ |
1293 | // ... but FieldInfo also wants getter expr |
1294 | // for matching other arguments of Self type; | |
1295 | // so walk across the *other* self_pats_idents | |
1296 | // and pull out getter for same field in each | |
1297 | // of them (using `field_index` tracked above). | |
1298 | // That is the heart of the transposition. | |
dfeec247 XL |
1299 | let others = self_pats_idents |
1300 | .iter() | |
1301 | .map(|fields| { | |
1302 | let (_, _opt_ident, ref other_getter_expr, _) = fields[field_index]; | |
1303 | ||
1304 | // All Self args have same variant, so | |
1305 | // opt_idents are the same. (Assert | |
1306 | // here to make it self-evident that | |
1307 | // it is okay to ignore `_opt_ident`.) | |
1308 | assert!(opt_ident == _opt_ident); | |
1309 | ||
1310 | other_getter_expr.clone() | |
1311 | }) | |
1312 | .collect::<Vec<P<Expr>>>(); | |
1313 | ||
1314 | FieldInfo { | |
a2a8927a | 1315 | span, |
dfeec247 XL |
1316 | name: opt_ident, |
1317 | self_: self_getter_expr, | |
1318 | other: others, | |
1319 | attrs, | |
1a4d82fc | 1320 | } |
dfeec247 XL |
1321 | }) |
1322 | .collect::<Vec<FieldInfo<'_>>>(); | |
1a4d82fc JJ |
1323 | |
1324 | // Now, for some given VariantK, we have built up | |
1325 | // expressions for referencing every field of every | |
1326 | // Self arg, assuming all are instances of VariantK. | |
1327 | // Build up code associated with such a case. | |
041b39d2 | 1328 | let substructure = EnumMatching(index, variants.len(), variant, field_tuples); |
dfeec247 XL |
1329 | let arm_expr = self.call_substructure_method( |
1330 | cx, | |
1331 | trait_, | |
1332 | type_ident, | |
1333 | &self_args[..], | |
1334 | nonself_args, | |
1335 | &substructure, | |
1336 | ); | |
1a4d82fc | 1337 | |
a2a8927a | 1338 | cx.arm(span, single_pat, arm_expr) |
5bcae85e SL |
1339 | }) |
1340 | .collect(); | |
a7813a04 XL |
1341 | |
1342 | let default = match first_fieldless { | |
1343 | Some(v) if self.unify_fieldless_variants => { | |
1344 | // We need a default case that handles the fieldless variants. | |
1345 | // The index and actual variant aren't meaningful in this case, | |
1346 | // so just use whatever | |
041b39d2 | 1347 | let substructure = EnumMatching(0, variants.len(), v, Vec::new()); |
dfeec247 XL |
1348 | Some(self.call_substructure_method( |
1349 | cx, | |
1350 | trait_, | |
1351 | type_ident, | |
1352 | &self_args[..], | |
1353 | nonself_args, | |
1354 | &substructure, | |
1355 | )) | |
a7813a04 XL |
1356 | } |
1357 | _ if variants.len() > 1 && self_args.len() > 1 => { | |
1358 | // Since we know that all the arguments will match if we reach | |
1359 | // the match expression we add the unreachable intrinsics as the | |
1360 | // result of the catch all which should help llvm in optimizing it | |
a2a8927a | 1361 | Some(deriving::call_unreachable(cx, span)) |
a7813a04 | 1362 | } |
5bcae85e | 1363 | _ => None, |
a7813a04 XL |
1364 | }; |
1365 | if let Some(arm) = default { | |
a2a8927a | 1366 | match_arms.push(cx.arm(span, cx.pat_wild(span), arm)); |
a7813a04 XL |
1367 | } |
1368 | ||
1a4d82fc JJ |
1369 | // We will usually need the catch-all after matching the |
1370 | // tuples `(VariantK, VariantK, ...)` for each VariantK of the | |
1371 | // enum. But: | |
1372 | // | |
1373 | // * when there is only one Self arg, the arms above suffice | |
1374 | // (and the deriving we call back into may not be prepared to | |
1375 | // handle EnumNonMatchCollapsed), and, | |
1376 | // | |
1377 | // * when the enum has only one variant, the single arm that | |
1378 | // is already present always suffices. | |
1379 | // | |
1380 | // * In either of the two cases above, if we *did* add a | |
1381 | // catch-all `_` match, it would trigger the | |
1382 | // unreachable-pattern error. | |
1383 | // | |
1384 | if variants.len() > 1 && self_args.len() > 1 { | |
1a4d82fc | 1385 | // Build a series of let statements mapping each self_arg |
f9f354fc | 1386 | // to its discriminant value. |
9346a6ac | 1387 | // |
0731742a | 1388 | // i.e., for `enum E<T> { A, B(1), C(T, T) }`, and a deriving |
1a4d82fc JJ |
1389 | // with three Self args, builds three statements: |
1390 | // | |
1391 | // ``` | |
29967ef6 XL |
1392 | // let __self0_vi = std::intrinsics::discriminant_value(&self); |
1393 | // let __self1_vi = std::intrinsics::discriminant_value(&arg1); | |
1394 | // let __self2_vi = std::intrinsics::discriminant_value(&arg2); | |
1a4d82fc | 1395 | // ``` |
a1dfa0c6 | 1396 | let mut index_let_stmts: Vec<ast::Stmt> = Vec::with_capacity(vi_idents.len() + 1); |
9346a6ac | 1397 | |
5bcae85e | 1398 | // We also build an expression which checks whether all discriminants are equal |
62682a34 | 1399 | // discriminant_test = __self0_vi == __self1_vi && __self0_vi == __self2_vi && ... |
a2a8927a | 1400 | let mut discriminant_test = cx.expr_bool(span, true); |
62682a34 | 1401 | |
62682a34 | 1402 | let mut first_ident = None; |
cdc7bbd5 | 1403 | for (&ident, self_arg) in iter::zip(&vi_idents, &self_args) { |
a2a8927a | 1404 | let self_addr = cx.expr_addr_of(span, self_arg.clone()); |
5bcae85e | 1405 | let variant_value = |
a2a8927a XL |
1406 | deriving::call_intrinsic(cx, span, sym::discriminant_value, vec![self_addr]); |
1407 | let let_stmt = cx.stmt_let(span, false, ident, variant_value); | |
1a4d82fc | 1408 | index_let_stmts.push(let_stmt); |
62682a34 SL |
1409 | |
1410 | match first_ident { | |
1411 | Some(first) => { | |
a2a8927a XL |
1412 | let first_expr = cx.expr_ident(span, first); |
1413 | let id = cx.expr_ident(span, ident); | |
1414 | let test = cx.expr_binary(span, BinOpKind::Eq, first_expr, id); | |
5bcae85e | 1415 | discriminant_test = |
a2a8927a | 1416 | cx.expr_binary(span, BinOpKind::And, discriminant_test, test) |
62682a34 SL |
1417 | } |
1418 | None => { | |
1419 | first_ident = Some(ident); | |
1420 | } | |
1421 | } | |
1a4d82fc JJ |
1422 | } |
1423 | ||
dfeec247 XL |
1424 | let arm_expr = self.call_substructure_method( |
1425 | cx, | |
1426 | trait_, | |
1427 | type_ident, | |
1428 | &self_args[..], | |
1429 | nonself_args, | |
1430 | &catch_all_substructure, | |
1431 | ); | |
1a4d82fc | 1432 | |
62682a34 | 1433 | // Final wrinkle: the self_args are expressions that deref |
2c00a5a8 | 1434 | // down to desired places, but we cannot actually deref |
62682a34 SL |
1435 | // them when they are fed as r-values into a tuple |
1436 | // expression; here add a layer of borrowing, turning | |
1437 | // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`. | |
a2a8927a XL |
1438 | self_args.map_in_place(|self_arg| cx.expr_addr_of(span, self_arg)); |
1439 | let match_arg = cx.expr(span, ast::ExprKind::Tup(self_args)); | |
62682a34 | 1440 | |
5bcae85e | 1441 | // Lastly we create an expression which branches on all discriminants being equal |
62682a34 SL |
1442 | // if discriminant_test { |
1443 | // match (...) { | |
1444 | // (Variant1, Variant1, ...) => Body1 | |
1445 | // (Variant2, Variant2, ...) => Body2, | |
1446 | // ... | |
1447 | // _ => ::core::intrinsics::unreachable() | |
1448 | // } | |
1449 | // } | |
1450 | // else { | |
1451 | // <delegated expression referring to __self0_vi, et al.> | |
1452 | // } | |
a2a8927a XL |
1453 | let all_match = cx.expr_match(span, match_arg, match_arms); |
1454 | let arm_expr = cx.expr_if(span, discriminant_test, all_match, Some(arm_expr)); | |
3157f602 | 1455 | index_let_stmts.push(cx.stmt_expr(arm_expr)); |
a2a8927a | 1456 | cx.expr_block(cx.block(span, index_let_stmts)) |
9346a6ac | 1457 | } else if variants.is_empty() { |
1a4d82fc JJ |
1458 | // As an additional wrinkle, For a zero-variant enum A, |
1459 | // currently the compiler | |
1460 | // will accept `fn (a: &Self) { match *a { } }` | |
1461 | // but rejects `fn (a: &Self) { match (&*a,) { } }` | |
1462 | // as well as `fn (a: &Self) { match ( *a,) { } }` | |
1463 | // | |
1464 | // This means that the strategy of building up a tuple of | |
1465 | // all Self arguments fails when Self is a zero variant | |
1466 | // enum: rustc rejects the expanded program, even though | |
1467 | // the actual code tends to be impossible to execute (at | |
1468 | // least safely), according to the type system. | |
1469 | // | |
1470 | // The most expedient fix for this is to just let the | |
1471 | // code fall through to the catch-all. But even this is | |
1472 | // error-prone, since the catch-all as defined above would | |
1473 | // generate code like this: | |
1474 | // | |
1475 | // _ => { let __self0 = match *self { }; | |
1476 | // let __self1 = match *__arg_0 { }; | |
1477 | // <catch-all-expr> } | |
1478 | // | |
1479 | // Which is yields bindings for variables which type | |
1480 | // inference cannot resolve to unique types. | |
1481 | // | |
1482 | // One option to the above might be to add explicit type | |
1483 | // annotations. But the *only* reason to go down that path | |
1484 | // would be to try to make the expanded output consistent | |
1485 | // with the case when the number of enum variants >= 1. | |
1486 | // | |
1487 | // That just isn't worth it. In fact, trying to generate | |
1488 | // sensible code for *any* deriving on a zero-variant enum | |
1489 | // does not make sense. But at the same time, for now, we | |
1490 | // do not want to cause a compile failure just because the | |
1491 | // user happened to attach a deriving to their | |
1492 | // zero-variant enum. | |
1493 | // | |
1494 | // Instead, just generate a failing expression for the | |
1495 | // zero variant case, skipping matches and also skipping | |
1496 | // delegating back to the end user code entirely. | |
1497 | // | |
1498 | // (See also #4499 and #12609; note that some of the | |
1499 | // discussions there influence what choice we make here; | |
0731742a XL |
1500 | // e.g., if we feature-gate `match x { ... }` when x refers |
1501 | // to an uninhabited type (e.g., a zero-variant enum or a | |
1a4d82fc JJ |
1502 | // type holding such an enum), but do not feature-gate |
1503 | // zero-variant enums themselves, then attempting to | |
85aaf69f | 1504 | // derive Debug on such a type could here generate code |
1a4d82fc JJ |
1505 | // that needs the feature gate enabled.) |
1506 | ||
a2a8927a | 1507 | deriving::call_unreachable(cx, span) |
5bcae85e | 1508 | } else { |
62682a34 | 1509 | // Final wrinkle: the self_args are expressions that deref |
2c00a5a8 | 1510 | // down to desired places, but we cannot actually deref |
62682a34 SL |
1511 | // them when they are fed as r-values into a tuple |
1512 | // expression; here add a layer of borrowing, turning | |
1513 | // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`. | |
a2a8927a XL |
1514 | self_args.map_in_place(|self_arg| cx.expr_addr_of(span, self_arg)); |
1515 | let match_arg = cx.expr(span, ast::ExprKind::Tup(self_args)); | |
1516 | cx.expr_match(span, match_arg, match_arms) | |
1a4d82fc | 1517 | } |
1a4d82fc JJ |
1518 | } |
1519 | ||
dfeec247 XL |
1520 | fn expand_static_enum_method_body( |
1521 | &self, | |
1522 | cx: &mut ExtCtxt<'_>, | |
1523 | trait_: &TraitDef<'_>, | |
1524 | enum_def: &EnumDef, | |
1525 | type_ident: Ident, | |
1526 | self_args: &[P<Expr>], | |
1527 | nonself_args: &[P<Expr>], | |
1528 | ) -> P<Expr> { | |
1529 | let summary = enum_def | |
1530 | .variants | |
5bcae85e SL |
1531 | .iter() |
1532 | .map(|v| { | |
ea8adc8c | 1533 | let sp = v.span.with_ctxt(trait_.span.ctxt()); |
e1599b0c XL |
1534 | let summary = trait_.summarise_struct(cx, &v.data); |
1535 | (v.ident, sp, summary) | |
5bcae85e SL |
1536 | }) |
1537 | .collect(); | |
dfeec247 XL |
1538 | self.call_substructure_method( |
1539 | cx, | |
1540 | trait_, | |
1541 | type_ident, | |
1542 | self_args, | |
1543 | nonself_args, | |
1544 | &StaticEnum(enum_def, summary), | |
1545 | ) | |
1a4d82fc JJ |
1546 | } |
1547 | } | |
1548 | ||
1a4d82fc JJ |
1549 | // general helper methods. |
1550 | impl<'a> TraitDef<'a> { | |
9fa01778 | 1551 | fn summarise_struct(&self, cx: &mut ExtCtxt<'_>, struct_def: &VariantData) -> StaticFields { |
1a4d82fc JJ |
1552 | let mut named_idents = Vec::new(); |
1553 | let mut just_spans = Vec::new(); | |
5bcae85e | 1554 | for field in struct_def.fields() { |
ea8adc8c | 1555 | let sp = field.span.with_ctxt(self.span.ctxt()); |
54a0048b SL |
1556 | match field.ident { |
1557 | Some(ident) => named_idents.push((ident, sp)), | |
1558 | _ => just_spans.push(sp), | |
1a4d82fc JJ |
1559 | } |
1560 | } | |
1561 | ||
1b1a35ee | 1562 | let is_tuple = matches!(struct_def, ast::VariantData::Tuple(..)); |
1a4d82fc | 1563 | match (just_spans.is_empty(), named_idents.is_empty()) { |
a2a8927a XL |
1564 | (false, false) => { |
1565 | cx.span_bug(self.span, "a struct with named and unnamed fields in generic `derive`") | |
1566 | } | |
1a4d82fc JJ |
1567 | // named fields |
1568 | (_, false) => Named(named_idents), | |
532ac7d7 XL |
1569 | // unnamed fields |
1570 | (false, _) => Unnamed(just_spans, is_tuple), | |
1571 | // empty | |
1572 | _ => Named(Vec::new()), | |
1a4d82fc JJ |
1573 | } |
1574 | } | |
1575 | ||
dfeec247 XL |
1576 | fn create_subpatterns( |
1577 | &self, | |
1578 | cx: &mut ExtCtxt<'_>, | |
f9f354fc | 1579 | field_paths: Vec<Ident>, |
dfeec247 XL |
1580 | mutbl: ast::Mutability, |
1581 | use_temporaries: bool, | |
1582 | ) -> Vec<P<ast::Pat>> { | |
1583 | field_paths | |
1584 | .iter() | |
5bcae85e | 1585 | .map(|path| { |
ff7c6d11 | 1586 | let binding_mode = if use_temporaries { |
dfeec247 | 1587 | ast::BindingMode::ByValue(ast::Mutability::Not) |
ff7c6d11 XL |
1588 | } else { |
1589 | ast::BindingMode::ByRef(mutbl) | |
1590 | }; | |
dfeec247 | 1591 | cx.pat(path.span, PatKind::Ident(binding_mode, *path, None)) |
5bcae85e SL |
1592 | }) |
1593 | .collect() | |
1a4d82fc JJ |
1594 | } |
1595 | ||
dfeec247 XL |
1596 | fn create_struct_pattern( |
1597 | &self, | |
1598 | cx: &mut ExtCtxt<'_>, | |
1599 | struct_path: ast::Path, | |
1600 | struct_def: &'a VariantData, | |
1601 | prefix: &str, | |
1602 | mutbl: ast::Mutability, | |
1603 | use_temporaries: bool, | |
1604 | ) -> (P<ast::Pat>, Vec<(Span, Option<Ident>, P<Expr>, &'a [ast::Attribute])>) { | |
1a4d82fc | 1605 | let mut paths = Vec::new(); |
54a0048b | 1606 | let mut ident_exprs = Vec::new(); |
b039eaaf | 1607 | for (i, struct_field) in struct_def.fields().iter().enumerate() { |
ea8adc8c | 1608 | let sp = struct_field.span.with_ctxt(self.span.ctxt()); |
3dfed10e | 1609 | let ident = Ident::from_str_and_span(&format!("{}_{}", prefix, i), self.span); |
83c7162d | 1610 | paths.push(ident.with_span_pos(sp)); |
ff7c6d11 | 1611 | let val = cx.expr_path(cx.path_ident(sp, ident)); |
dfeec247 | 1612 | let val = if use_temporaries { val } else { cx.expr_deref(sp, val) }; |
7453a54e | 1613 | let val = cx.expr(sp, ast::ExprKind::Paren(val)); |
ff7c6d11 | 1614 | |
54a0048b | 1615 | ident_exprs.push((sp, struct_field.ident, val, &struct_field.attrs[..])); |
1a4d82fc JJ |
1616 | } |
1617 | ||
ff7c6d11 | 1618 | let subpats = self.create_subpatterns(cx, paths, mutbl, use_temporaries); |
9e0c209e SL |
1619 | let pattern = match *struct_def { |
1620 | VariantData::Struct(..) => { | |
cdc7bbd5 | 1621 | let field_pats = iter::zip(subpats, &ident_exprs) |
9e0c209e SL |
1622 | .map(|(pat, &(sp, ident, ..))| { |
1623 | if ident.is_none() { | |
1624 | cx.span_bug(sp, "a braced struct with unnamed fields in `derive`"); | |
1625 | } | |
6a06907d | 1626 | ast::PatField { |
e1599b0c XL |
1627 | ident: ident.unwrap(), |
1628 | is_shorthand: false, | |
dfeec247 | 1629 | attrs: ast::AttrVec::new(), |
e1599b0c | 1630 | id: ast::DUMMY_NODE_ID, |
ea8adc8c | 1631 | span: pat.span.with_ctxt(self.span.ctxt()), |
e1599b0c | 1632 | pat, |
dfeec247 | 1633 | is_placeholder: false, |
9e0c209e SL |
1634 | } |
1635 | }) | |
1636 | .collect(); | |
1637 | cx.pat_struct(self.span, struct_path, field_pats) | |
1638 | } | |
dfeec247 XL |
1639 | VariantData::Tuple(..) => cx.pat_tuple_struct(self.span, struct_path, subpats), |
1640 | VariantData::Unit(..) => cx.pat_path(self.span, struct_path), | |
1a4d82fc JJ |
1641 | }; |
1642 | ||
54a0048b | 1643 | (pattern, ident_exprs) |
1a4d82fc JJ |
1644 | } |
1645 | ||
dfeec247 XL |
1646 | fn create_enum_variant_pattern( |
1647 | &self, | |
1648 | cx: &mut ExtCtxt<'_>, | |
f9f354fc | 1649 | enum_ident: Ident, |
dfeec247 XL |
1650 | variant: &'a ast::Variant, |
1651 | prefix: &str, | |
1652 | mutbl: ast::Mutability, | |
1653 | ) -> (P<ast::Pat>, Vec<(Span, Option<Ident>, P<Expr>, &'a [ast::Attribute])>) { | |
ea8adc8c | 1654 | let sp = variant.span.with_ctxt(self.span.ctxt()); |
e1599b0c | 1655 | let variant_path = cx.path(sp, vec![enum_ident, variant.ident]); |
ff7c6d11 | 1656 | let use_temporaries = false; // enums can't be repr(packed) |
dfeec247 | 1657 | self.create_struct_pattern(cx, variant_path, &variant.data, prefix, mutbl, use_temporaries) |
1a4d82fc JJ |
1658 | } |
1659 | } | |
1660 | ||
5bcae85e | 1661 | // helpful premade recipes |
1a4d82fc | 1662 | |
dfeec247 XL |
1663 | pub fn cs_fold_fields<'a, F>( |
1664 | use_foldl: bool, | |
1665 | mut f: F, | |
1666 | base: P<Expr>, | |
1667 | cx: &mut ExtCtxt<'_>, | |
1668 | all_fields: &[FieldInfo<'a>], | |
1669 | ) -> P<Expr> | |
1670 | where | |
1671 | F: FnMut(&mut ExtCtxt<'_>, Span, P<Expr>, P<Expr>, &[P<Expr>]) -> P<Expr>, | |
83c7162d XL |
1672 | { |
1673 | if use_foldl { | |
dfeec247 XL |
1674 | all_fields |
1675 | .iter() | |
1676 | .fold(base, |old, field| f(cx, field.span, old, field.self_.clone(), &field.other)) | |
83c7162d | 1677 | } else { |
dfeec247 XL |
1678 | all_fields |
1679 | .iter() | |
1680 | .rev() | |
1681 | .fold(base, |old, field| f(cx, field.span, old, field.self_.clone(), &field.other)) | |
83c7162d XL |
1682 | } |
1683 | } | |
1684 | ||
dfeec247 XL |
1685 | pub fn cs_fold_enumnonmatch( |
1686 | mut enum_nonmatch_f: EnumNonMatchCollapsedFunc<'_>, | |
1687 | cx: &mut ExtCtxt<'_>, | |
1688 | trait_span: Span, | |
1689 | substructure: &Substructure<'_>, | |
1690 | ) -> P<Expr> { | |
83c7162d XL |
1691 | match *substructure.fields { |
1692 | EnumNonMatchingCollapsed(ref all_args, _, tuple) => { | |
dfeec247 | 1693 | enum_nonmatch_f(cx, trait_span, (&all_args[..], tuple), substructure.nonself_args) |
83c7162d | 1694 | } |
dfeec247 | 1695 | _ => cx.span_bug(trait_span, "cs_fold_enumnonmatch expected an EnumNonMatchingCollapsed"), |
83c7162d XL |
1696 | } |
1697 | } | |
1698 | ||
dfeec247 | 1699 | pub fn cs_fold_static(cx: &mut ExtCtxt<'_>, trait_span: Span) -> P<Expr> { |
83c7162d XL |
1700 | cx.span_bug(trait_span, "static function in `derive`") |
1701 | } | |
1702 | ||
1a4d82fc JJ |
1703 | /// Fold the fields. `use_foldl` controls whether this is done |
1704 | /// left-to-right (`true`) or right-to-left (`false`). | |
dfeec247 XL |
1705 | pub fn cs_fold<F>( |
1706 | use_foldl: bool, | |
1707 | f: F, | |
1708 | base: P<Expr>, | |
1709 | enum_nonmatch_f: EnumNonMatchCollapsedFunc<'_>, | |
1710 | cx: &mut ExtCtxt<'_>, | |
1711 | trait_span: Span, | |
1712 | substructure: &Substructure<'_>, | |
1713 | ) -> P<Expr> | |
1714 | where | |
1715 | F: FnMut(&mut ExtCtxt<'_>, Span, P<Expr>, P<Expr>, &[P<Expr>]) -> P<Expr>, | |
1a4d82fc JJ |
1716 | { |
1717 | match *substructure.fields { | |
dfeec247 | 1718 | EnumMatching(.., ref all_fields) | Struct(_, ref all_fields) => { |
83c7162d | 1719 | cs_fold_fields(use_foldl, f, base, cx, all_fields) |
1a4d82fc | 1720 | } |
83c7162d XL |
1721 | EnumNonMatchingCollapsed(..) => { |
1722 | cs_fold_enumnonmatch(enum_nonmatch_f, cx, trait_span, substructure) | |
1723 | } | |
dfeec247 | 1724 | StaticEnum(..) | StaticStruct(..) => cs_fold_static(cx, trait_span), |
1a4d82fc JJ |
1725 | } |
1726 | } | |
1727 | ||
83c7162d XL |
1728 | /// Function to fold over fields, with three cases, to generate more efficient and concise code. |
1729 | /// When the `substructure` has grouped fields, there are two cases: | |
9fa01778 | 1730 | /// Zero fields: call the base case function with `None` (like the usual base case of `cs_fold`). |
83c7162d XL |
1731 | /// One or more fields: call the base case function on the first value (which depends on |
1732 | /// `use_fold`), and use that as the base case. Then perform `cs_fold` on the remainder of the | |
1733 | /// fields. | |
94222f64 | 1734 | /// When the `substructure` is an `EnumNonMatchingCollapsed`, the result of `enum_nonmatch_f` |
83c7162d XL |
1735 | /// is returned. Statics may not be folded over. |
1736 | /// See `cs_op` in `partial_ord.rs` for a model example. | |
dfeec247 XL |
1737 | pub fn cs_fold1<F, B>( |
1738 | use_foldl: bool, | |
1739 | f: F, | |
1740 | mut b: B, | |
1741 | enum_nonmatch_f: EnumNonMatchCollapsedFunc<'_>, | |
1742 | cx: &mut ExtCtxt<'_>, | |
1743 | trait_span: Span, | |
1744 | substructure: &Substructure<'_>, | |
1745 | ) -> P<Expr> | |
1746 | where | |
1747 | F: FnMut(&mut ExtCtxt<'_>, Span, P<Expr>, P<Expr>, &[P<Expr>]) -> P<Expr>, | |
1748 | B: FnMut(&mut ExtCtxt<'_>, Option<(Span, P<Expr>, &[P<Expr>])>) -> P<Expr>, | |
83c7162d XL |
1749 | { |
1750 | match *substructure.fields { | |
dfeec247 | 1751 | EnumMatching(.., ref all_fields) | Struct(_, ref all_fields) => { |
83c7162d XL |
1752 | let (base, all_fields) = match (all_fields.is_empty(), use_foldl) { |
1753 | (false, true) => { | |
1754 | let field = &all_fields[0]; | |
1755 | let args = (field.span, field.self_.clone(), &field.other[..]); | |
1756 | (b(cx, Some(args)), &all_fields[1..]) | |
1757 | } | |
1758 | (false, false) => { | |
1759 | let idx = all_fields.len() - 1; | |
1760 | let field = &all_fields[idx]; | |
1761 | let args = (field.span, field.self_.clone(), &field.other[..]); | |
1762 | (b(cx, Some(args)), &all_fields[..idx]) | |
1763 | } | |
dfeec247 | 1764 | (true, _) => (b(cx, None), &all_fields[..]), |
83c7162d XL |
1765 | }; |
1766 | ||
1767 | cs_fold_fields(use_foldl, f, base, cx, all_fields) | |
1768 | } | |
1769 | EnumNonMatchingCollapsed(..) => { | |
1770 | cs_fold_enumnonmatch(enum_nonmatch_f, cx, trait_span, substructure) | |
1771 | } | |
dfeec247 | 1772 | StaticEnum(..) | StaticStruct(..) => cs_fold_static(cx, trait_span), |
83c7162d XL |
1773 | } |
1774 | } | |
1a4d82fc | 1775 | |
9fa01778 | 1776 | /// Returns `true` if the type has no value fields |
54a0048b SL |
1777 | /// (for an enum, no variant has any fields) |
1778 | pub fn is_type_without_fields(item: &Annotatable) -> bool { | |
1779 | if let Annotatable::Item(ref item) = *item { | |
e74abb32 | 1780 | match item.kind { |
54a0048b | 1781 | ast::ItemKind::Enum(ref enum_def, _) => { |
e1599b0c | 1782 | enum_def.variants.iter().all(|v| v.data.fields().is_empty()) |
54a0048b | 1783 | } |
5bcae85e SL |
1784 | ast::ItemKind::Struct(ref variant_data, _) => variant_data.fields().is_empty(), |
1785 | _ => false, | |
54a0048b SL |
1786 | } |
1787 | } else { | |
1788 | false | |
1789 | } | |
1790 | } |