]>
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)`) |
064997fb | 24 | //! - `EnumTag` when `Self` is an enum, for comparing the enum tags. |
1a4d82fc JJ |
25 | //! - `StaticEnum` and `StaticStruct` for static methods, where the type |
26 | //! being derived upon is either an enum or struct respectively. (Any | |
27 | //! argument with type Self is just grouped among the non-self | |
28 | //! arguments.) | |
29 | //! | |
30 | //! In the first two cases, the values from the corresponding fields in | |
064997fb | 31 | //! all the arguments are grouped together. |
1a4d82fc JJ |
32 | //! |
33 | //! The non-static cases have `Option<ident>` in several places associated | |
34 | //! with field `expr`s. This represents the name of the field it is | |
35 | //! associated with. It is only not `None` when the associated field has | |
36 | //! an identifier in the source code. For example, the `x`s in the | |
37 | //! following snippet | |
38 | //! | |
39 | //! ```rust | |
92a42be0 | 40 | //! # #![allow(dead_code)] |
85aaf69f | 41 | //! struct A { x : i32 } |
1a4d82fc | 42 | //! |
85aaf69f | 43 | //! struct B(i32); |
1a4d82fc JJ |
44 | //! |
45 | //! enum C { | |
85aaf69f SL |
46 | //! C0(i32), |
47 | //! C1 { x: i32 } | |
1a4d82fc JJ |
48 | //! } |
49 | //! ``` | |
50 | //! | |
85aaf69f | 51 | //! The `i32`s in `B` and `C0` don't have an identifier, so the |
1a4d82fc JJ |
52 | //! `Option<ident>`s would be `None` for them. |
53 | //! | |
a1dfa0c6 | 54 | //! In the static cases, the structure is summarized, either into the just |
1a4d82fc JJ |
55 | //! spans of the fields or a list of spans and the field idents (for tuple |
56 | //! structs and record structs, respectively), or a list of these, for | |
57 | //! enums (one for each variant). For empty struct and empty enum | |
58 | //! variants, it is represented as a count of 0. | |
59 | //! | |
85aaf69f SL |
60 | //! # "`cs`" functions |
61 | //! | |
064997fb | 62 | //! The `cs_...` functions ("combine substructure") are designed to |
85aaf69f | 63 | //! make life easier by providing some pre-made recipes for common |
bd371182 | 64 | //! threads; mostly calling the function being derived on all the |
85aaf69f SL |
65 | //! arguments and then combining them back together in some way (or |
66 | //! letting the user chose that). They are not meant to be the only | |
67 | //! way to handle the structures that this code creates. | |
68 | //! | |
1a4d82fc JJ |
69 | //! # Examples |
70 | //! | |
71 | //! The following simplified `PartialEq` is used for in-code examples: | |
72 | //! | |
73 | //! ```rust | |
74 | //! trait PartialEq { | |
92a42be0 | 75 | //! fn eq(&self, other: &Self) -> bool; |
1a4d82fc | 76 | //! } |
85aaf69f SL |
77 | //! impl PartialEq for i32 { |
78 | //! fn eq(&self, other: &i32) -> bool { | |
1a4d82fc JJ |
79 | //! *self == *other |
80 | //! } | |
81 | //! } | |
82 | //! ``` | |
83 | //! | |
84 | //! Some examples of the values of `SubstructureFields` follow, using the | |
85 | //! above `PartialEq`, `A`, `B` and `C`. | |
86 | //! | |
87 | //! ## Structs | |
88 | //! | |
89 | //! When generating the `expr` for the `A` impl, the `SubstructureFields` is | |
90 | //! | |
91 | //! ```{.text} | |
85aaf69f | 92 | //! Struct(vec![FieldInfo { |
1a4d82fc JJ |
93 | //! span: <span of x> |
94 | //! name: Some(<ident of x>), | |
95 | //! self_: <expr for &self.x>, | |
85aaf69f | 96 | //! other: vec![<expr for &other.x] |
1a4d82fc JJ |
97 | //! }]) |
98 | //! ``` | |
99 | //! | |
100 | //! For the `B` impl, called with `B(a)` and `B(b)`, | |
101 | //! | |
102 | //! ```{.text} | |
85aaf69f SL |
103 | //! Struct(vec![FieldInfo { |
104 | //! span: <span of `i32`>, | |
1a4d82fc | 105 | //! name: None, |
85aaf69f SL |
106 | //! self_: <expr for &a> |
107 | //! other: vec![<expr for &b>] | |
1a4d82fc JJ |
108 | //! }]) |
109 | //! ``` | |
110 | //! | |
111 | //! ## Enums | |
112 | //! | |
113 | //! When generating the `expr` for a call with `self == C0(a)` and `other | |
114 | //! == C0(b)`, the SubstructureFields is | |
115 | //! | |
116 | //! ```{.text} | |
117 | //! EnumMatching(0, <ast::Variant for C0>, | |
85aaf69f SL |
118 | //! vec![FieldInfo { |
119 | //! span: <span of i32> | |
1a4d82fc JJ |
120 | //! name: None, |
121 | //! self_: <expr for &a>, | |
85aaf69f | 122 | //! other: vec![<expr for &b>] |
1a4d82fc JJ |
123 | //! }]) |
124 | //! ``` | |
125 | //! | |
126 | //! For `C1 {x}` and `C1 {x}`, | |
127 | //! | |
128 | //! ```{.text} | |
129 | //! EnumMatching(1, <ast::Variant for C1>, | |
85aaf69f | 130 | //! vec![FieldInfo { |
1a4d82fc JJ |
131 | //! span: <span of x> |
132 | //! name: Some(<ident of x>), | |
133 | //! self_: <expr for &self.x>, | |
85aaf69f | 134 | //! other: vec![<expr for &other.x>] |
1a4d82fc JJ |
135 | //! }]) |
136 | //! ``` | |
137 | //! | |
064997fb | 138 | //! For the tags, |
1a4d82fc JJ |
139 | //! |
140 | //! ```{.text} | |
064997fb FG |
141 | //! EnumTag( |
142 | //! &[<ident of self tag>, <ident of other tag>], <expr to combine with>) | |
1a4d82fc | 143 | //! ``` |
064997fb FG |
144 | //! Note that this setup doesn't allow for the brute-force "match every variant |
145 | //! against every other variant" approach, which is bad because it produces a | |
146 | //! quadratic amount of code (see #15375). | |
1a4d82fc JJ |
147 | //! |
148 | //! ## Static | |
149 | //! | |
85aaf69f | 150 | //! A static method on the types above would result in, |
1a4d82fc JJ |
151 | //! |
152 | //! ```{.text} | |
b039eaaf | 153 | //! StaticStruct(<ast::VariantData of A>, Named(vec![(<ident of x>, <span of x>)])) |
1a4d82fc | 154 | //! |
b039eaaf | 155 | //! StaticStruct(<ast::VariantData of B>, Unnamed(vec![<span of x>])) |
1a4d82fc | 156 | //! |
85aaf69f SL |
157 | //! StaticEnum(<ast::EnumDef of C>, |
158 | //! vec![(<ident of C0>, <span of C0>, Unnamed(vec![<span of i32>])), | |
159 | //! (<ident of C1>, <span of C1>, Named(vec![(<ident of x>, <span of x>)]))]) | |
1a4d82fc JJ |
160 | //! ``` |
161 | ||
9fa01778 XL |
162 | pub use StaticFields::*; |
163 | pub use SubstructureFields::*; | |
1a4d82fc | 164 | |
f2b60f7d | 165 | use crate::deriving; |
74b04a01 | 166 | use rustc_ast::ptr::P; |
f2b60f7d FG |
167 | use rustc_ast::{ |
168 | self as ast, BindingAnnotation, ByRef, EnumDef, Expr, Generics, Mutability, PatKind, | |
169 | }; | |
3dfed10e | 170 | use rustc_ast::{GenericArg, GenericParamKind, VariantData}; |
74b04a01 | 171 | use rustc_attr as attr; |
dfeec247 | 172 | use rustc_expand::base::{Annotatable, ExtCtxt}; |
f9f354fc | 173 | use rustc_span::symbol::{kw, sym, Ident, Symbol}; |
dfeec247 | 174 | use rustc_span::Span; |
f2b60f7d FG |
175 | use std::cell::RefCell; |
176 | use std::iter; | |
177 | use std::vec; | |
178 | use thin_vec::thin_vec; | |
064997fb | 179 | use ty::{Bounds, Path, Ref, Self_, Ty}; |
1a4d82fc JJ |
180 | |
181 | pub mod ty; | |
182 | ||
183 | pub struct TraitDef<'a> { | |
184 | /// The span for the current #[derive(Foo)] header. | |
185 | pub span: Span, | |
186 | ||
1a4d82fc | 187 | /// Path of the trait, including any type parameters |
3dfed10e | 188 | pub path: Path, |
1a4d82fc JJ |
189 | |
190 | /// Additional bounds required of any type parameters of the type, | |
191 | /// other than the current trait | |
3dfed10e | 192 | pub additional_bounds: Vec<Ty>, |
1a4d82fc | 193 | |
0731742a | 194 | /// Any extra lifetimes and/or bounds, e.g., `D: serialize::Decoder` |
3dfed10e | 195 | pub generics: Bounds, |
1a4d82fc | 196 | |
9e0c209e SL |
197 | /// Can this trait be derived for unions? |
198 | pub supports_unions: bool, | |
199 | ||
1a4d82fc | 200 | pub methods: Vec<MethodDef<'a>>, |
85aaf69f | 201 | |
3dfed10e | 202 | pub associated_types: Vec<(Ident, Ty)>, |
1a4d82fc JJ |
203 | } |
204 | ||
1a4d82fc JJ |
205 | pub struct MethodDef<'a> { |
206 | /// name of the method | |
3dfed10e | 207 | pub name: Symbol, |
0731742a | 208 | /// List of generics, e.g., `R: rand::Rng` |
3dfed10e | 209 | pub generics: Bounds, |
1a4d82fc | 210 | |
064997fb FG |
211 | /// Is there is a `&self` argument? If not, it is a static function. |
212 | pub explicit_self: bool, | |
1a4d82fc | 213 | |
064997fb FG |
214 | /// Arguments other than the self argument. |
215 | pub nonself_args: Vec<(Ty, Symbol)>, | |
1a4d82fc | 216 | |
9fa01778 | 217 | /// Returns type |
3dfed10e | 218 | pub ret_ty: Ty, |
1a4d82fc | 219 | |
f2b60f7d | 220 | pub attributes: ast::AttrVec, |
1a4d82fc | 221 | |
a7813a04 | 222 | /// Can we combine fieldless variants for enums into a single match arm? |
064997fb FG |
223 | /// If true, indicates that the trait operation uses the enum tag in some |
224 | /// way. | |
a7813a04 XL |
225 | pub unify_fieldless_variants: bool, |
226 | ||
1a4d82fc JJ |
227 | pub combine_substructure: RefCell<CombineSubstructureFunc<'a>>, |
228 | } | |
229 | ||
230 | /// All the data about the data structure/method being derived upon. | |
231 | pub struct Substructure<'a> { | |
232 | /// ident of self | |
233 | pub type_ident: Ident, | |
064997fb FG |
234 | /// Verbatim access to any non-selflike arguments, i.e. arguments that |
235 | /// don't have type `&Self`. | |
236 | pub nonselflike_args: &'a [P<Expr>], | |
5bcae85e | 237 | pub fields: &'a SubstructureFields<'a>, |
1a4d82fc JJ |
238 | } |
239 | ||
240 | /// Summary of the relevant parts of a struct/enum field. | |
064997fb | 241 | pub struct FieldInfo { |
1a4d82fc JJ |
242 | pub span: Span, |
243 | /// None for tuple structs/normal enum variants, Some for normal | |
244 | /// structs/struct enum variants. | |
245 | pub name: Option<Ident>, | |
246 | /// The expression corresponding to this field of `self` | |
247 | /// (specifically, a reference to it). | |
064997fb | 248 | pub self_expr: P<Expr>, |
1a4d82fc | 249 | /// The expressions corresponding to references to this field in |
064997fb FG |
250 | /// the other selflike arguments. |
251 | pub other_selflike_exprs: Vec<P<Expr>>, | |
1a4d82fc JJ |
252 | } |
253 | ||
254 | /// Fields for a static method | |
255 | pub enum StaticFields { | |
9e0c209e SL |
256 | /// Tuple and unit structs/enum variants like this. |
257 | Unnamed(Vec<Span>, bool /*is tuple*/), | |
1a4d82fc JJ |
258 | /// Normal structs/struct variants. |
259 | Named(Vec<(Ident, Span)>), | |
260 | } | |
261 | ||
262 | /// A summary of the possible sets of fields. | |
263 | pub enum SubstructureFields<'a> { | |
064997fb FG |
264 | /// A non-static method with `Self` is a struct. |
265 | Struct(&'a ast::VariantData, Vec<FieldInfo>), | |
266 | ||
041b39d2 | 267 | /// Matching variants of the enum: variant index, variant count, ast::Variant, |
1a4d82fc JJ |
268 | /// fields: the field name is only non-`None` in the case of a struct |
269 | /// variant. | |
064997fb | 270 | EnumMatching(usize, usize, &'a ast::Variant, Vec<FieldInfo>), |
1a4d82fc | 271 | |
064997fb FG |
272 | /// The tag of an enum. The first field is a `FieldInfo` for the tags, as |
273 | /// if they were fields. The second field is the expression to combine the | |
274 | /// tag expression with; it will be `None` if no match is necessary. | |
275 | EnumTag(FieldInfo, Option<P<Expr>>), | |
1a4d82fc JJ |
276 | |
277 | /// A static method where `Self` is a struct. | |
b039eaaf | 278 | StaticStruct(&'a ast::VariantData, StaticFields), |
064997fb | 279 | |
1a4d82fc JJ |
280 | /// A static method where `Self` is an enum. |
281 | StaticEnum(&'a ast::EnumDef, Vec<(Ident, Span, StaticFields)>), | |
282 | } | |
283 | ||
1a4d82fc JJ |
284 | /// Combine the values of all the fields together. The last argument is |
285 | /// all the fields of all the structures. | |
286 | pub type CombineSubstructureFunc<'a> = | |
064997fb | 287 | Box<dyn FnMut(&mut ExtCtxt<'_>, Span, &Substructure<'_>) -> BlockOrExpr + 'a>; |
1a4d82fc | 288 | |
dfeec247 XL |
289 | pub fn combine_substructure( |
290 | f: CombineSubstructureFunc<'_>, | |
291 | ) -> RefCell<CombineSubstructureFunc<'_>> { | |
1a4d82fc JJ |
292 | RefCell::new(f) |
293 | } | |
294 | ||
c295e0f8 XL |
295 | struct TypeParameter { |
296 | bound_generic_params: Vec<ast::GenericParam>, | |
297 | ty: P<ast::Ty>, | |
298 | } | |
299 | ||
064997fb FG |
300 | // The code snippets built up for derived code are sometimes used as blocks |
301 | // (e.g. in a function body) and sometimes used as expressions (e.g. in a match | |
302 | // arm). This structure avoids committing to either form until necessary, | |
303 | // avoiding the insertion of any unnecessary blocks. | |
304 | // | |
305 | // The statements come before the expression. | |
306 | pub struct BlockOrExpr(Vec<ast::Stmt>, Option<P<Expr>>); | |
307 | ||
308 | impl BlockOrExpr { | |
309 | pub fn new_stmts(stmts: Vec<ast::Stmt>) -> BlockOrExpr { | |
310 | BlockOrExpr(stmts, None) | |
311 | } | |
312 | ||
313 | pub fn new_expr(expr: P<Expr>) -> BlockOrExpr { | |
314 | BlockOrExpr(vec![], Some(expr)) | |
315 | } | |
316 | ||
317 | pub fn new_mixed(stmts: Vec<ast::Stmt>, expr: Option<P<Expr>>) -> BlockOrExpr { | |
318 | BlockOrExpr(stmts, expr) | |
319 | } | |
320 | ||
321 | // Converts it into a block. | |
322 | fn into_block(mut self, cx: &ExtCtxt<'_>, span: Span) -> P<ast::Block> { | |
323 | if let Some(expr) = self.1 { | |
324 | self.0.push(cx.stmt_expr(expr)); | |
325 | } | |
326 | cx.block(span, self.0) | |
327 | } | |
328 | ||
329 | // Converts it into an expression. | |
330 | fn into_expr(self, cx: &ExtCtxt<'_>, span: Span) -> P<Expr> { | |
331 | if self.0.is_empty() { | |
332 | match self.1 { | |
333 | None => cx.expr_block(cx.block(span, vec![])), | |
334 | Some(expr) => expr, | |
335 | } | |
336 | } else if self.0.len() == 1 | |
337 | && let ast::StmtKind::Expr(expr) = &self.0[0].kind | |
338 | && self.1.is_none() | |
339 | { | |
340 | // There's only a single statement expression. Pull it out. | |
341 | expr.clone() | |
342 | } else { | |
343 | // Multiple statements and/or expressions. | |
344 | cx.expr_block(self.into_block(cx, span)) | |
345 | } | |
346 | } | |
347 | } | |
348 | ||
c34b1796 AL |
349 | /// This method helps to extract all the type parameters referenced from a |
350 | /// type. For a type parameter `<T>`, it looks for either a `TyPath` that | |
351 | /// is not global and starts with `T`, or a `TyQPath`. | |
c295e0f8 | 352 | /// Also include bound generic params from the input type. |
dc9dc135 XL |
353 | fn find_type_parameters( |
354 | ty: &ast::Ty, | |
f9f354fc | 355 | ty_param_names: &[Symbol], |
dc9dc135 | 356 | cx: &ExtCtxt<'_>, |
c295e0f8 | 357 | ) -> Vec<TypeParameter> { |
74b04a01 | 358 | use rustc_ast::visit; |
c34b1796 | 359 | |
dc9dc135 | 360 | struct Visitor<'a, 'b> { |
3157f602 | 361 | cx: &'a ExtCtxt<'b>, |
f9f354fc | 362 | ty_param_names: &'a [Symbol], |
c295e0f8 XL |
363 | bound_generic_params_stack: Vec<ast::GenericParam>, |
364 | type_params: Vec<TypeParameter>, | |
c34b1796 AL |
365 | } |
366 | ||
476ff2be SL |
367 | impl<'a, 'b> visit::Visitor<'a> for Visitor<'a, 'b> { |
368 | fn visit_ty(&mut self, ty: &'a ast::Ty) { | |
e74abb32 | 369 | if let ast::TyKind::Path(_, ref path) = ty.kind { |
32a655c1 | 370 | if let Some(segment) = path.segments.first() { |
83c7162d | 371 | if self.ty_param_names.contains(&segment.ident.name) { |
c295e0f8 XL |
372 | self.type_params.push(TypeParameter { |
373 | bound_generic_params: self.bound_generic_params_stack.clone(), | |
374 | ty: P(ty.clone()), | |
375 | }); | |
c34b1796 AL |
376 | } |
377 | } | |
c34b1796 AL |
378 | } |
379 | ||
380 | visit::walk_ty(self, ty) | |
381 | } | |
3157f602 | 382 | |
c295e0f8 | 383 | // Place bound generic params on a stack, to extract them when a type is encountered. |
f2b60f7d | 384 | fn visit_poly_trait_ref(&mut self, trait_ref: &'a ast::PolyTraitRef) { |
c295e0f8 | 385 | let stack_len = self.bound_generic_params_stack.len(); |
f2b60f7d | 386 | self.bound_generic_params_stack.extend(trait_ref.bound_generic_params.iter().cloned()); |
c295e0f8 | 387 | |
f2b60f7d | 388 | visit::walk_poly_trait_ref(self, trait_ref); |
c295e0f8 XL |
389 | |
390 | self.bound_generic_params_stack.truncate(stack_len); | |
391 | } | |
392 | ||
29967ef6 | 393 | fn visit_mac_call(&mut self, mac: &ast::MacCall) { |
60c5eb7d | 394 | self.cx.span_err(mac.span(), "`derive` cannot be used on items with type macros"); |
3157f602 | 395 | } |
c34b1796 AL |
396 | } |
397 | ||
c295e0f8 XL |
398 | let mut visitor = Visitor { |
399 | cx, | |
400 | ty_param_names, | |
401 | bound_generic_params_stack: Vec::new(), | |
402 | type_params: Vec::new(), | |
403 | }; | |
c34b1796 AL |
404 | visit::Visitor::visit_ty(&mut visitor, ty); |
405 | ||
c295e0f8 | 406 | visitor.type_params |
c34b1796 | 407 | } |
1a4d82fc JJ |
408 | |
409 | impl<'a> TraitDef<'a> { | |
dfeec247 XL |
410 | pub fn expand( |
411 | self, | |
412 | cx: &mut ExtCtxt<'_>, | |
413 | mitem: &ast::MetaItem, | |
414 | item: &'a Annotatable, | |
415 | push: &mut dyn FnMut(Annotatable), | |
416 | ) { | |
9e0c209e SL |
417 | self.expand_ext(cx, mitem, item, push, false); |
418 | } | |
419 | ||
dfeec247 XL |
420 | pub fn expand_ext( |
421 | self, | |
422 | cx: &mut ExtCtxt<'_>, | |
423 | mitem: &ast::MetaItem, | |
424 | item: &'a Annotatable, | |
425 | push: &mut dyn FnMut(Annotatable), | |
426 | from_scratch: bool, | |
427 | ) { | |
d9579d0f AL |
428 | match *item { |
429 | Annotatable::Item(ref item) => { | |
ff7c6d11 | 430 | let is_packed = item.attrs.iter().any(|attr| { |
3dfed10e | 431 | for r in attr::find_repr_attrs(&cx.sess, attr) { |
83c7162d XL |
432 | if let attr::ReprPacked(_) = r { |
433 | return true; | |
434 | } | |
435 | } | |
436 | false | |
ff7c6d11 | 437 | }); |
e74abb32 | 438 | let has_no_type_params = match item.kind { |
dfeec247 XL |
439 | ast::ItemKind::Struct(_, ref generics) |
440 | | ast::ItemKind::Enum(_, ref generics) | |
5869c6ff XL |
441 | | ast::ItemKind::Union(_, ref generics) => !generics |
442 | .params | |
443 | .iter() | |
444 | .any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })), | |
fc512014 | 445 | _ => unreachable!(), |
ff7c6d11 | 446 | }; |
136023e0 | 447 | let container_id = cx.current_expansion.id.expn_data().parent.expect_local(); |
60c5eb7d | 448 | let always_copy = has_no_type_params && cx.resolver.has_derive_copy(container_id); |
ff7c6d11 | 449 | |
e74abb32 | 450 | let newitem = match item.kind { |
dfeec247 XL |
451 | ast::ItemKind::Struct(ref struct_def, ref generics) => self.expand_struct_def( |
452 | cx, | |
453 | &struct_def, | |
454 | item.ident, | |
455 | generics, | |
456 | from_scratch, | |
064997fb FG |
457 | is_packed, |
458 | always_copy, | |
dfeec247 | 459 | ), |
7453a54e | 460 | ast::ItemKind::Enum(ref enum_def, ref generics) => { |
064997fb | 461 | // We ignore `is_packed`/`always_copy` here, because |
ff7c6d11 XL |
462 | // `repr(packed)` enums cause an error later on. |
463 | // | |
464 | // This can only cause further compilation errors | |
465 | // downstream in blatantly illegal code, so it | |
466 | // is fine. | |
f9f354fc | 467 | self.expand_enum_def(cx, enum_def, item.ident, generics, from_scratch) |
9e0c209e SL |
468 | } |
469 | ast::ItemKind::Union(ref struct_def, ref generics) => { | |
470 | if self.supports_unions { | |
dfeec247 XL |
471 | self.expand_struct_def( |
472 | cx, | |
473 | &struct_def, | |
474 | item.ident, | |
475 | generics, | |
476 | from_scratch, | |
064997fb FG |
477 | is_packed, |
478 | always_copy, | |
dfeec247 | 479 | ) |
9e0c209e | 480 | } else { |
dfeec247 | 481 | cx.span_err(mitem.span, "this trait cannot be derived for unions"); |
9e0c209e SL |
482 | return; |
483 | } | |
d9579d0f | 484 | } |
ff7c6d11 | 485 | _ => unreachable!(), |
d9579d0f AL |
486 | }; |
487 | // Keep the lint attributes of the previous item to control how the | |
488 | // generated implementations are linted | |
489 | let mut attrs = newitem.attrs.clone(); | |
dfeec247 XL |
490 | attrs.extend( |
491 | item.attrs | |
492 | .iter() | |
493 | .filter(|a| { | |
494 | [ | |
495 | sym::allow, | |
496 | sym::warn, | |
497 | sym::deny, | |
498 | sym::forbid, | |
499 | sym::stable, | |
500 | sym::unstable, | |
501 | ] | |
48663c56 | 502 | .contains(&a.name_or_empty()) |
dfeec247 XL |
503 | }) |
504 | .cloned(), | |
505 | ); | |
74b04a01 | 506 | push(Annotatable::Item(P(ast::Item { attrs, ..(*newitem).clone() }))) |
1a4d82fc | 507 | } |
fc512014 | 508 | _ => unreachable!(), |
d9579d0f | 509 | } |
1a4d82fc JJ |
510 | } |
511 | ||
c34b1796 | 512 | /// Given that we are deriving a trait `DerivedTrait` for a type like: |
1a4d82fc | 513 | /// |
041b39d2 | 514 | /// ```ignore (only-for-syntax-highlight) |
c34b1796 AL |
515 | /// struct Struct<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> where C: WhereTrait { |
516 | /// a: A, | |
517 | /// b: B::Item, | |
518 | /// b1: <B as DeclaredTrait>::Item, | |
519 | /// c1: <C as WhereTrait>::Item, | |
520 | /// c2: Option<<C as WhereTrait>::Item>, | |
521 | /// ... | |
522 | /// } | |
1a4d82fc JJ |
523 | /// ``` |
524 | /// | |
c34b1796 AL |
525 | /// create an impl like: |
526 | /// | |
041b39d2 | 527 | /// ```ignore (only-for-syntax-highlight) |
9fa01778 | 528 | /// impl<'a, ..., 'z, A, B: DeclaredTrait, C, ... Z> where |
c34b1796 AL |
529 | /// C: WhereTrait, |
530 | /// A: DerivedTrait + B1 + ... + BN, | |
531 | /// B: DerivedTrait + B1 + ... + BN, | |
532 | /// C: DerivedTrait + B1 + ... + BN, | |
533 | /// B::Item: DerivedTrait + B1 + ... + BN, | |
534 | /// <C as WhereTrait>::Item: DerivedTrait + B1 + ... + BN, | |
535 | /// ... | |
536 | /// { | |
537 | /// ... | |
538 | /// } | |
539 | /// ``` | |
540 | /// | |
541 | /// where B1, ..., BN are the bounds given by `bounds_paths`.'. Z is a phantom type, and | |
542 | /// therefore does not get bound by the derived trait. | |
dfeec247 XL |
543 | fn create_derived_impl( |
544 | &self, | |
545 | cx: &mut ExtCtxt<'_>, | |
546 | type_ident: Ident, | |
547 | generics: &Generics, | |
548 | field_tys: Vec<P<ast::Ty>>, | |
74b04a01 | 549 | methods: Vec<P<ast::AssocItem>>, |
dfeec247 | 550 | ) -> P<ast::Item> { |
1a4d82fc JJ |
551 | let trait_path = self.path.to_path(cx, self.span, type_ident, generics); |
552 | ||
dfeec247 | 553 | // Transform associated types from `deriving::ty::Ty` into `ast::AssocItem` |
74b04a01 XL |
554 | let associated_types = self.associated_types.iter().map(|&(ident, ref type_def)| { |
555 | P(ast::AssocItem { | |
85aaf69f SL |
556 | id: ast::DUMMY_NODE_ID, |
557 | span: self.span, | |
3b2f2976 | 558 | ident, |
1b1a35ee XL |
559 | vis: ast::Visibility { |
560 | span: self.span.shrink_to_lo(), | |
561 | kind: ast::VisibilityKind::Inherited, | |
562 | tokens: None, | |
563 | }, | |
f2b60f7d | 564 | attrs: ast::AttrVec::new(), |
3c0e092e XL |
565 | kind: ast::AssocItemKind::TyAlias(Box::new(ast::TyAlias { |
566 | defaultness: ast::Defaultness::Final, | |
567 | generics: Generics::default(), | |
5e7ed085 FG |
568 | where_clauses: ( |
569 | ast::TyAliasWhereClause::default(), | |
570 | ast::TyAliasWhereClause::default(), | |
571 | ), | |
572 | where_predicates_split: 0, | |
3c0e092e XL |
573 | bounds: Vec::new(), |
574 | ty: Some(type_def.to_ty(cx, self.span, type_ident, generics)), | |
575 | })), | |
3b2f2976 | 576 | tokens: None, |
74b04a01 XL |
577 | }) |
578 | }); | |
85aaf69f | 579 | |
a2a8927a | 580 | let Generics { mut params, mut where_clause, .. } = |
dfeec247 | 581 | self.generics.to_generics(cx, self.span, type_ident, generics); |
a2a8927a XL |
582 | where_clause.span = generics.where_clause.span; |
583 | let ctxt = self.span.ctxt(); | |
584 | let span = generics.span.with_ctxt(ctxt); | |
1a4d82fc | 585 | |
ff7c6d11 | 586 | // Create the generic parameters |
cdc7bbd5 | 587 | params.extend(generics.params.iter().map(|param| match ¶m.kind { |
8faf50e0 XL |
588 | GenericParamKind::Lifetime { .. } => param.clone(), |
589 | GenericParamKind::Type { .. } => { | |
590 | // I don't think this can be moved out of the loop, since | |
591 | // a GenericBound requires an ast id | |
b7449926 | 592 | let bounds: Vec<_> = |
8faf50e0 XL |
593 | // extra restrictions on the generics parameters to the |
594 | // type being derived upon | |
595 | self.additional_bounds.iter().map(|p| { | |
596 | cx.trait_bound(p.to_path(cx, self.span, type_ident, generics)) | |
597 | }).chain( | |
598 | // require the current trait | |
599 | iter::once(cx.trait_bound(trait_path.clone())) | |
600 | ).chain( | |
601 | // also add in any bounds from the declaration | |
602 | param.bounds.iter().cloned() | |
603 | ).collect(); | |
1a4d82fc | 604 | |
f2b60f7d | 605 | cx.typaram(param.ident.span.with_ctxt(ctxt), param.ident, bounds, None) |
ff7c6d11 | 606 | } |
cdc7bbd5 XL |
607 | GenericParamKind::Const { ty, kw_span, .. } => { |
608 | let const_nodefault_kind = GenericParamKind::Const { | |
609 | ty: ty.clone(), | |
a2a8927a | 610 | kw_span: kw_span.with_ctxt(ctxt), |
cdc7bbd5 XL |
611 | |
612 | // We can't have default values inside impl block | |
613 | default: None, | |
614 | }; | |
615 | let mut param_clone = param.clone(); | |
616 | param_clone.kind = const_nodefault_kind; | |
617 | param_clone | |
618 | } | |
1a4d82fc JJ |
619 | })); |
620 | ||
621 | // and similarly for where clauses | |
622 | where_clause.predicates.extend(generics.where_clause.predicates.iter().map(|clause| { | |
a2a8927a XL |
623 | match clause { |
624 | ast::WherePredicate::BoundPredicate(wb) => { | |
625 | let span = wb.span.with_ctxt(ctxt); | |
1a4d82fc | 626 | ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { |
a2a8927a XL |
627 | span, |
628 | ..wb.clone() | |
1a4d82fc JJ |
629 | }) |
630 | } | |
a2a8927a XL |
631 | ast::WherePredicate::RegionPredicate(wr) => { |
632 | let span = wr.span.with_ctxt(ctxt); | |
1a4d82fc | 633 | ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { |
a2a8927a XL |
634 | span, |
635 | ..wr.clone() | |
1a4d82fc JJ |
636 | }) |
637 | } | |
a2a8927a XL |
638 | ast::WherePredicate::EqPredicate(we) => { |
639 | let span = we.span.with_ctxt(ctxt); | |
f2b60f7d | 640 | ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { span, ..we.clone() }) |
1a4d82fc JJ |
641 | } |
642 | } | |
643 | })); | |
644 | ||
ff7c6d11 XL |
645 | { |
646 | // Extra scope required here so ty_params goes out of scope before params is moved | |
c34b1796 | 647 | |
dfeec247 XL |
648 | let mut ty_params = params |
649 | .iter() | |
5869c6ff | 650 | .filter(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })) |
ff7c6d11 | 651 | .peekable(); |
c34b1796 | 652 | |
ff7c6d11 | 653 | if ty_params.peek().is_some() { |
f9f354fc | 654 | let ty_param_names: Vec<Symbol> = |
dfeec247 | 655 | ty_params.map(|ty_param| ty_param.ident.name).collect(); |
ff7c6d11 | 656 | |
ff7c6d11 | 657 | for field_ty in field_tys { |
c295e0f8 | 658 | let field_ty_params = find_type_parameters(&field_ty, &ty_param_names, cx); |
ff7c6d11 | 659 | |
c295e0f8 | 660 | for field_ty_param in field_ty_params { |
ff7c6d11 | 661 | // if we have already handled this type, skip it |
c295e0f8 | 662 | if let ast::TyKind::Path(_, ref p) = field_ty_param.ty.kind { |
dfeec247 XL |
663 | if p.segments.len() == 1 |
664 | && ty_param_names.contains(&p.segments[0].ident.name) | |
665 | { | |
ff7c6d11 XL |
666 | continue; |
667 | }; | |
ff7c6d11 | 668 | } |
dfeec247 XL |
669 | let mut bounds: Vec<_> = self |
670 | .additional_bounds | |
ff7c6d11 | 671 | .iter() |
dfeec247 | 672 | .map(|p| cx.trait_bound(p.to_path(cx, self.span, type_ident, generics))) |
ff7c6d11 XL |
673 | .collect(); |
674 | ||
675 | // require the current trait | |
8faf50e0 | 676 | bounds.push(cx.trait_bound(trait_path.clone())); |
ff7c6d11 XL |
677 | |
678 | let predicate = ast::WhereBoundPredicate { | |
679 | span: self.span, | |
c295e0f8 XL |
680 | bound_generic_params: field_ty_param.bound_generic_params, |
681 | bounded_ty: field_ty_param.ty, | |
ff7c6d11 XL |
682 | bounds, |
683 | }; | |
c34b1796 | 684 | |
ff7c6d11 XL |
685 | let predicate = ast::WherePredicate::BoundPredicate(predicate); |
686 | where_clause.predicates.push(predicate); | |
687 | } | |
c34b1796 AL |
688 | } |
689 | } | |
690 | } | |
691 | ||
dfeec247 | 692 | let trait_generics = Generics { params, where_clause, span }; |
1a4d82fc JJ |
693 | |
694 | // Create the reference to the trait. | |
695 | let trait_ref = cx.trait_ref(trait_path); | |
696 | ||
dfeec247 XL |
697 | let self_params: Vec<_> = generics |
698 | .params | |
699 | .iter() | |
700 | .map(|param| match param.kind { | |
701 | GenericParamKind::Lifetime { .. } => { | |
a2a8927a | 702 | GenericArg::Lifetime(cx.lifetime(param.ident.span.with_ctxt(ctxt), param.ident)) |
dfeec247 XL |
703 | } |
704 | GenericParamKind::Type { .. } => { | |
a2a8927a | 705 | GenericArg::Type(cx.ty_ident(param.ident.span.with_ctxt(ctxt), param.ident)) |
dfeec247 XL |
706 | } |
707 | GenericParamKind::Const { .. } => { | |
a2a8927a | 708 | GenericArg::Const(cx.const_ident(param.ident.span.with_ctxt(ctxt), param.ident)) |
dfeec247 XL |
709 | } |
710 | }) | |
711 | .collect(); | |
1a4d82fc JJ |
712 | |
713 | // Create the type of `self`. | |
e1599b0c | 714 | let path = cx.path_all(self.span, false, vec![type_ident], self_params); |
8faf50e0 | 715 | let self_type = cx.ty_path(path); |
5bcae85e | 716 | |
416331ca | 717 | let attr = cx.attribute(cx.meta_word(self.span, sym::automatically_derived)); |
f2b60f7d | 718 | let attrs = thin_vec![attr]; |
1a4d82fc | 719 | let opt_trait_ref = Some(trait_ref); |
476ff2be | 720 | |
dfeec247 XL |
721 | cx.item( |
722 | self.span, | |
3c0e092e | 723 | Ident::empty(), |
f2b60f7d | 724 | attrs, |
3c0e092e | 725 | ast::ItemKind::Impl(Box::new(ast::Impl { |
064997fb | 726 | unsafety: ast::Unsafe::No, |
dfeec247 XL |
727 | polarity: ast::ImplPolarity::Positive, |
728 | defaultness: ast::Defaultness::Final, | |
74b04a01 | 729 | constness: ast::Const::No, |
dfeec247 XL |
730 | generics: trait_generics, |
731 | of_trait: opt_trait_ref, | |
732 | self_ty: self_type, | |
733 | items: methods.into_iter().chain(associated_types).collect(), | |
94222f64 | 734 | })), |
dfeec247 | 735 | ) |
1a4d82fc JJ |
736 | } |
737 | ||
dfeec247 XL |
738 | fn expand_struct_def( |
739 | &self, | |
740 | cx: &mut ExtCtxt<'_>, | |
741 | struct_def: &'a VariantData, | |
742 | type_ident: Ident, | |
743 | generics: &Generics, | |
744 | from_scratch: bool, | |
064997fb FG |
745 | is_packed: bool, |
746 | always_copy: bool, | |
dfeec247 XL |
747 | ) -> P<ast::Item> { |
748 | let field_tys: Vec<P<ast::Ty>> = | |
749 | struct_def.fields().iter().map(|field| field.ty.clone()).collect(); | |
750 | ||
751 | let methods = self | |
752 | .methods | |
5bcae85e SL |
753 | .iter() |
754 | .map(|method_def| { | |
064997fb FG |
755 | let (explicit_self, selflike_args, nonselflike_args, nonself_arg_tys) = |
756 | method_def.extract_arg_details(cx, self, type_ident, generics); | |
5bcae85e | 757 | |
9e0c209e | 758 | let body = if from_scratch || method_def.is_static() { |
dfeec247 XL |
759 | method_def.expand_static_struct_method_body( |
760 | cx, | |
761 | self, | |
762 | struct_def, | |
763 | type_ident, | |
064997fb | 764 | &nonselflike_args, |
dfeec247 | 765 | ) |
5bcae85e | 766 | } else { |
dfeec247 XL |
767 | method_def.expand_struct_method_body( |
768 | cx, | |
769 | self, | |
770 | struct_def, | |
771 | type_ident, | |
064997fb FG |
772 | &selflike_args, |
773 | &nonselflike_args, | |
774 | is_packed, | |
775 | always_copy, | |
dfeec247 | 776 | ) |
5bcae85e SL |
777 | }; |
778 | ||
064997fb FG |
779 | method_def.create_method( |
780 | cx, | |
781 | self, | |
782 | type_ident, | |
783 | generics, | |
784 | explicit_self, | |
785 | nonself_arg_tys, | |
786 | body, | |
787 | ) | |
5bcae85e SL |
788 | }) |
789 | .collect(); | |
1a4d82fc | 790 | |
c34b1796 | 791 | self.create_derived_impl(cx, type_ident, generics, field_tys, methods) |
1a4d82fc JJ |
792 | } |
793 | ||
dfeec247 XL |
794 | fn expand_enum_def( |
795 | &self, | |
796 | cx: &mut ExtCtxt<'_>, | |
797 | enum_def: &'a EnumDef, | |
dfeec247 XL |
798 | type_ident: Ident, |
799 | generics: &Generics, | |
800 | from_scratch: bool, | |
801 | ) -> P<ast::Item> { | |
c34b1796 AL |
802 | let mut field_tys = Vec::new(); |
803 | ||
62682a34 | 804 | for variant in &enum_def.variants { |
dfeec247 | 805 | field_tys.extend(variant.data.fields().iter().map(|field| field.ty.clone())); |
c34b1796 AL |
806 | } |
807 | ||
dfeec247 XL |
808 | let methods = self |
809 | .methods | |
5bcae85e SL |
810 | .iter() |
811 | .map(|method_def| { | |
064997fb FG |
812 | let (explicit_self, selflike_args, nonselflike_args, nonself_arg_tys) = |
813 | method_def.extract_arg_details(cx, self, type_ident, generics); | |
5bcae85e | 814 | |
9e0c209e | 815 | let body = if from_scratch || method_def.is_static() { |
dfeec247 XL |
816 | method_def.expand_static_enum_method_body( |
817 | cx, | |
818 | self, | |
819 | enum_def, | |
820 | type_ident, | |
064997fb | 821 | &nonselflike_args, |
dfeec247 | 822 | ) |
5bcae85e | 823 | } else { |
dfeec247 XL |
824 | method_def.expand_enum_method_body( |
825 | cx, | |
826 | self, | |
827 | enum_def, | |
dfeec247 | 828 | type_ident, |
064997fb FG |
829 | selflike_args, |
830 | &nonselflike_args, | |
dfeec247 | 831 | ) |
5bcae85e SL |
832 | }; |
833 | ||
064997fb FG |
834 | method_def.create_method( |
835 | cx, | |
836 | self, | |
837 | type_ident, | |
838 | generics, | |
839 | explicit_self, | |
840 | nonself_arg_tys, | |
841 | body, | |
842 | ) | |
5bcae85e SL |
843 | }) |
844 | .collect(); | |
1a4d82fc | 845 | |
c34b1796 | 846 | self.create_derived_impl(cx, type_ident, generics, field_tys, methods) |
1a4d82fc JJ |
847 | } |
848 | } | |
849 | ||
1a4d82fc | 850 | impl<'a> MethodDef<'a> { |
dfeec247 XL |
851 | fn call_substructure_method( |
852 | &self, | |
853 | cx: &mut ExtCtxt<'_>, | |
854 | trait_: &TraitDef<'_>, | |
855 | type_ident: Ident, | |
064997fb | 856 | nonselflike_args: &[P<Expr>], |
dfeec247 | 857 | fields: &SubstructureFields<'_>, |
064997fb | 858 | ) -> BlockOrExpr { |
a2a8927a | 859 | let span = trait_.span; |
064997fb | 860 | let substructure = Substructure { type_ident, nonselflike_args, fields }; |
1a4d82fc | 861 | let mut f = self.combine_substructure.borrow_mut(); |
9fa01778 | 862 | let f: &mut CombineSubstructureFunc<'_> = &mut *f; |
a2a8927a | 863 | f(cx, span, &substructure) |
1a4d82fc JJ |
864 | } |
865 | ||
dfeec247 XL |
866 | fn get_ret_ty( |
867 | &self, | |
868 | cx: &mut ExtCtxt<'_>, | |
869 | trait_: &TraitDef<'_>, | |
870 | generics: &Generics, | |
871 | type_ident: Ident, | |
872 | ) -> P<ast::Ty> { | |
1a4d82fc JJ |
873 | self.ret_ty.to_ty(cx, trait_.span, type_ident, generics) |
874 | } | |
875 | ||
876 | fn is_static(&self) -> bool { | |
064997fb | 877 | !self.explicit_self |
1a4d82fc JJ |
878 | } |
879 | ||
064997fb FG |
880 | // The return value includes: |
881 | // - explicit_self: The `&self` arg, if present. | |
882 | // - selflike_args: Expressions for `&self` (if present) and also any other | |
883 | // args with the same type (e.g. the `other` arg in `PartialEq::eq`). | |
884 | // - nonselflike_args: Expressions for all the remaining args. | |
885 | // - nonself_arg_tys: Additional information about all the args other than | |
886 | // `&self`. | |
887 | fn extract_arg_details( | |
dfeec247 XL |
888 | &self, |
889 | cx: &mut ExtCtxt<'_>, | |
890 | trait_: &TraitDef<'_>, | |
891 | type_ident: Ident, | |
892 | generics: &Generics, | |
893 | ) -> (Option<ast::ExplicitSelf>, Vec<P<Expr>>, Vec<P<Expr>>, Vec<(Ident, P<ast::Ty>)>) { | |
064997fb FG |
894 | let mut selflike_args = Vec::new(); |
895 | let mut nonselflike_args = Vec::new(); | |
896 | let mut nonself_arg_tys = Vec::new(); | |
a2a8927a | 897 | let span = trait_.span; |
1a4d82fc | 898 | |
064997fb FG |
899 | let explicit_self = if self.explicit_self { |
900 | let (self_expr, explicit_self) = ty::get_explicit_self(cx, span); | |
901 | selflike_args.push(self_expr); | |
902 | Some(explicit_self) | |
903 | } else { | |
904 | None | |
905 | }; | |
1a4d82fc | 906 | |
064997fb | 907 | for (ty, name) in self.nonself_args.iter() { |
a2a8927a XL |
908 | let ast_ty = ty.to_ty(cx, span, type_ident, generics); |
909 | let ident = Ident::new(*name, span); | |
064997fb | 910 | nonself_arg_tys.push((ident, ast_ty)); |
1a4d82fc | 911 | |
a2a8927a | 912 | let arg_expr = cx.expr_ident(span, ident); |
1a4d82fc | 913 | |
064997fb FG |
914 | match ty { |
915 | // Selflike (`&Self`) arguments only occur in non-static methods. | |
916 | Ref(box Self_, _) if !self.is_static() => selflike_args.push(arg_expr), | |
917 | Self_ => cx.span_bug(span, "`Self` in non-return position"), | |
918 | _ => nonselflike_args.push(arg_expr), | |
1a4d82fc JJ |
919 | } |
920 | } | |
921 | ||
064997fb | 922 | (explicit_self, selflike_args, nonselflike_args, nonself_arg_tys) |
1a4d82fc JJ |
923 | } |
924 | ||
dfeec247 XL |
925 | fn create_method( |
926 | &self, | |
927 | cx: &mut ExtCtxt<'_>, | |
928 | trait_: &TraitDef<'_>, | |
929 | type_ident: Ident, | |
930 | generics: &Generics, | |
931 | explicit_self: Option<ast::ExplicitSelf>, | |
064997fb FG |
932 | nonself_arg_tys: Vec<(Ident, P<ast::Ty>)>, |
933 | body: BlockOrExpr, | |
74b04a01 | 934 | ) -> P<ast::AssocItem> { |
a2a8927a | 935 | let span = trait_.span; |
dc9dc135 | 936 | // Create the generics that aren't for `Self`. |
a2a8927a | 937 | let fn_generics = self.generics.to_generics(cx, span, type_ident, generics); |
1a4d82fc | 938 | |
1a4d82fc | 939 | let args = { |
064997fb | 940 | let self_arg = explicit_self.map(|explicit_self| { |
a2a8927a | 941 | let ident = Ident::with_dummy_span(kw::SelfLower).with_span_pos(span); |
dfeec247 | 942 | ast::Param::from_self(ast::AttrVec::default(), explicit_self, ident) |
3157f602 | 943 | }); |
064997fb FG |
944 | let nonself_args = |
945 | nonself_arg_tys.into_iter().map(|(name, ty)| cx.param(span, name, ty)); | |
946 | self_arg.into_iter().chain(nonself_args).collect() | |
1a4d82fc JJ |
947 | }; |
948 | ||
949 | let ret_type = self.get_ret_ty(cx, trait_, generics, type_ident); | |
950 | ||
a2a8927a | 951 | let method_ident = Ident::new(self.name, span); |
74b04a01 | 952 | let fn_decl = cx.fn_decl(args, ast::FnRetTy::Ty(ret_type)); |
064997fb | 953 | let body_block = body.into_block(cx, span); |
62682a34 | 954 | |
a2a8927a | 955 | let trait_lo_sp = span.shrink_to_lo(); |
60c5eb7d | 956 | |
064997fb | 957 | let sig = ast::FnSig { header: ast::FnHeader::default(), decl: fn_decl, span }; |
3c0e092e | 958 | let defaultness = ast::Defaultness::Final; |
60c5eb7d | 959 | |
1a4d82fc | 960 | // Create the method. |
74b04a01 | 961 | P(ast::AssocItem { |
1a4d82fc | 962 | id: ast::DUMMY_NODE_ID, |
c34b1796 | 963 | attrs: self.attributes.clone(), |
a2a8927a | 964 | span, |
1b1a35ee XL |
965 | vis: ast::Visibility { |
966 | span: trait_lo_sp, | |
967 | kind: ast::VisibilityKind::Inherited, | |
968 | tokens: None, | |
969 | }, | |
c34b1796 | 970 | ident: method_ident, |
3c0e092e XL |
971 | kind: ast::AssocItemKind::Fn(Box::new(ast::Fn { |
972 | defaultness, | |
94222f64 | 973 | sig, |
3c0e092e XL |
974 | generics: fn_generics, |
975 | body: Some(body_block), | |
976 | })), | |
3b2f2976 | 977 | tokens: None, |
74b04a01 | 978 | }) |
1a4d82fc JJ |
979 | } |
980 | ||
064997fb | 981 | /// The normal case uses field access. |
041b39d2 | 982 | /// ``` |
1a4d82fc | 983 | /// #[derive(PartialEq)] |
041b39d2 | 984 | /// # struct Dummy; |
064997fb | 985 | /// struct A { x: u8, y: u8 } |
1a4d82fc JJ |
986 | /// |
987 | /// // equivalent to: | |
988 | /// impl PartialEq for A { | |
83c7162d | 989 | /// fn eq(&self, other: &A) -> bool { |
064997fb | 990 | /// self.x == other.x && self.y == other.y |
1a4d82fc JJ |
991 | /// } |
992 | /// } | |
04454e1e | 993 | /// ``` |
064997fb FG |
994 | /// But if the struct is `repr(packed)`, we can't use something like |
995 | /// `&self.x` because that might cause an unaligned ref. So for any trait | |
996 | /// method that takes a reference, if the struct impls `Copy` then we use a | |
997 | /// local block to force a copy: | |
04454e1e | 998 | /// ``` |
064997fb | 999 | /// # struct A { x: u8, y: u8 } |
ff7c6d11 | 1000 | /// impl PartialEq for A { |
83c7162d | 1001 | /// fn eq(&self, other: &A) -> bool { |
064997fb FG |
1002 | /// // Desugars to `{ self.x }.eq(&{ other.y }) && ...` |
1003 | /// { self.x } == { other.y } && { self.y } == { other.y } | |
1004 | /// } | |
1005 | /// } | |
1006 | /// impl Hash for A { | |
1007 | /// fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { | |
1008 | /// ::core::hash::Hash::hash(&{ self.x }, state); | |
1009 | /// ::core::hash::Hash::hash(&{ self.y }, state) | |
1010 | /// } | |
1011 | /// } | |
1012 | /// ``` | |
1013 | /// If the struct doesn't impl `Copy`, we use let-destructuring with `ref`: | |
1014 | /// ``` | |
1015 | /// # struct A { x: u8, y: u8 } | |
1016 | /// impl PartialEq for A { | |
1017 | /// fn eq(&self, other: &A) -> bool { | |
1018 | /// let Self { x: ref __self_0_0, y: ref __self_0_1 } = *self; | |
1019 | /// let Self { x: ref __self_1_0, y: ref __self_1_1 } = *other; | |
1020 | /// *__self_0_0 == *__self_1_0 && *__self_0_1 == *__self_1_1 | |
ff7c6d11 XL |
1021 | /// } |
1022 | /// } | |
1a4d82fc | 1023 | /// ``` |
064997fb FG |
1024 | /// This latter case only works if the fields match the alignment required |
1025 | /// by the `packed(N)` attribute. (We'll get errors later on if not.) | |
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, | |
064997fb FG |
1032 | selflike_args: &[P<Expr>], |
1033 | nonselflike_args: &[P<Expr>], | |
1034 | is_packed: bool, | |
1035 | always_copy: bool, | |
1036 | ) -> BlockOrExpr { | |
a2a8927a | 1037 | let span = trait_.span; |
064997fb FG |
1038 | assert!(selflike_args.len() == 1 || selflike_args.len() == 2); |
1039 | ||
1040 | let mk_body = |cx, selflike_fields| { | |
1041 | self.call_substructure_method( | |
dfeec247 | 1042 | cx, |
064997fb FG |
1043 | trait_, |
1044 | type_ident, | |
1045 | nonselflike_args, | |
1046 | &Struct(struct_def, selflike_fields), | |
1047 | ) | |
1048 | }; | |
1a4d82fc | 1049 | |
064997fb FG |
1050 | if !is_packed { |
1051 | let selflike_fields = | |
1052 | trait_.create_struct_field_access_fields(cx, selflike_args, struct_def, false); | |
1053 | mk_body(cx, selflike_fields) | |
1054 | } else if always_copy { | |
1055 | let selflike_fields = | |
1056 | trait_.create_struct_field_access_fields(cx, selflike_args, struct_def, true); | |
1057 | mk_body(cx, selflike_fields) | |
1a4d82fc | 1058 | } else { |
064997fb FG |
1059 | // Neither packed nor copy. Need to use ref patterns. |
1060 | let prefixes: Vec<_> = | |
1061 | (0..selflike_args.len()).map(|i| format!("__self_{}", i)).collect(); | |
1062 | let addr_of = always_copy; | |
1063 | let selflike_fields = | |
1064 | trait_.create_struct_pattern_fields(cx, struct_def, &prefixes, addr_of); | |
1065 | let mut body = mk_body(cx, selflike_fields); | |
1a4d82fc | 1066 | |
064997fb | 1067 | let struct_path = cx.path(span, vec![Ident::new(kw::SelfUpper, type_ident.span)]); |
f2b60f7d | 1068 | let by_ref = ByRef::from(is_packed && !always_copy); |
064997fb | 1069 | let patterns = |
f2b60f7d | 1070 | trait_.create_struct_patterns(cx, struct_path, struct_def, &prefixes, by_ref); |
064997fb FG |
1071 | |
1072 | // Do the let-destructuring. | |
1073 | let mut stmts: Vec<_> = iter::zip(selflike_args, patterns) | |
1074 | .map(|(selflike_arg_expr, pat)| { | |
1075 | let selflike_arg_expr = cx.expr_deref(span, selflike_arg_expr.clone()); | |
1076 | cx.stmt_let_pat(span, pat, selflike_arg_expr) | |
1077 | }) | |
1078 | .collect(); | |
1079 | stmts.extend(std::mem::take(&mut body.0)); | |
1080 | BlockOrExpr(stmts, body.1) | |
1a4d82fc | 1081 | } |
1a4d82fc JJ |
1082 | } |
1083 | ||
dfeec247 XL |
1084 | fn expand_static_struct_method_body( |
1085 | &self, | |
1086 | cx: &mut ExtCtxt<'_>, | |
1087 | trait_: &TraitDef<'_>, | |
1088 | struct_def: &VariantData, | |
1089 | type_ident: Ident, | |
064997fb FG |
1090 | nonselflike_args: &[P<Expr>], |
1091 | ) -> BlockOrExpr { | |
1a4d82fc JJ |
1092 | let summary = trait_.summarise_struct(cx, struct_def); |
1093 | ||
dfeec247 XL |
1094 | self.call_substructure_method( |
1095 | cx, | |
1096 | trait_, | |
1097 | type_ident, | |
064997fb | 1098 | nonselflike_args, |
dfeec247 XL |
1099 | &StaticStruct(struct_def, summary), |
1100 | ) | |
1a4d82fc JJ |
1101 | } |
1102 | ||
041b39d2 | 1103 | /// ``` |
1a4d82fc | 1104 | /// #[derive(PartialEq)] |
041b39d2 | 1105 | /// # struct Dummy; |
1a4d82fc JJ |
1106 | /// enum A { |
1107 | /// A1, | |
85aaf69f | 1108 | /// A2(i32) |
1a4d82fc | 1109 | /// } |
064997fb FG |
1110 | /// ``` |
1111 | /// is equivalent to: | |
1112 | /// ``` | |
1113 | /// impl ::core::cmp::PartialEq for A { | |
1114 | /// #[inline] | |
04454e1e | 1115 | /// fn eq(&self, other: &A) -> bool { |
064997fb FG |
1116 | /// let __self_tag = ::core::intrinsics::discriminant_value(self); |
1117 | /// let __arg1_tag = ::core::intrinsics::discriminant_value(other); | |
1118 | /// __self_tag == __arg1_tag && | |
1119 | /// match (self, other) { | |
1120 | /// (A::A2(__self_0), A::A2(__arg1_0)) => | |
1121 | /// *__self_0 == *__arg1_0, | |
1122 | /// _ => true, | |
1a4d82fc | 1123 | /// } |
1a4d82fc JJ |
1124 | /// } |
1125 | /// } | |
1126 | /// ``` | |
064997fb FG |
1127 | /// Creates a tag check combined with a match for a tuple of all |
1128 | /// `selflike_args`, with an arm for each variant with fields, possibly an | |
1129 | /// arm for each fieldless variant (if `!unify_fieldless_variants` is not | |
1130 | /// true), and possibly a default arm. | |
dfeec247 XL |
1131 | fn expand_enum_method_body<'b>( |
1132 | &self, | |
1133 | cx: &mut ExtCtxt<'_>, | |
1134 | trait_: &TraitDef<'b>, | |
1135 | enum_def: &'b EnumDef, | |
dfeec247 | 1136 | type_ident: Ident, |
064997fb FG |
1137 | selflike_args: Vec<P<Expr>>, |
1138 | nonselflike_args: &[P<Expr>], | |
1139 | ) -> BlockOrExpr { | |
a2a8927a | 1140 | let span = trait_.span; |
1a4d82fc JJ |
1141 | let variants = &enum_def.variants; |
1142 | ||
064997fb FG |
1143 | // Traits that unify fieldless variants always use the tag(s). |
1144 | let uses_tags = self.unify_fieldless_variants; | |
1145 | ||
1146 | // There is no sensible code to be generated for *any* deriving on a | |
1147 | // zero-variant enum. So we just generate a failing expression. | |
1148 | if variants.is_empty() { | |
1149 | return BlockOrExpr(vec![], Some(deriving::call_unreachable(cx, span))); | |
1150 | } | |
1151 | ||
1152 | let prefixes = iter::once("__self".to_string()) | |
dfeec247 | 1153 | .chain( |
064997fb | 1154 | selflike_args |
dfeec247 XL |
1155 | .iter() |
1156 | .enumerate() | |
1157 | .skip(1) | |
064997fb | 1158 | .map(|(arg_count, _selflike_arg)| format!("__arg{}", arg_count)), |
dfeec247 XL |
1159 | ) |
1160 | .collect::<Vec<String>>(); | |
1161 | ||
064997fb FG |
1162 | // Build a series of let statements mapping each selflike_arg |
1163 | // to its discriminant value. | |
1164 | // | |
1165 | // e.g. for `PartialEq::eq` builds two statements: | |
1166 | // ``` | |
1167 | // let __self_tag = ::core::intrinsics::discriminant_value(self); | |
1168 | // let __arg1_tag = ::core::intrinsics::discriminant_value(other); | |
1169 | // ``` | |
1170 | let get_tag_pieces = |cx: &ExtCtxt<'_>| { | |
1171 | let tag_idents: Vec<_> = prefixes | |
1172 | .iter() | |
1173 | .map(|name| Ident::from_str_and_span(&format!("{}_tag", name), span)) | |
1174 | .collect(); | |
1a4d82fc | 1175 | |
064997fb FG |
1176 | let mut tag_exprs: Vec<_> = tag_idents |
1177 | .iter() | |
1178 | .map(|&ident| cx.expr_addr_of(span, cx.expr_ident(span, ident))) | |
1179 | .collect(); | |
1a4d82fc | 1180 | |
064997fb FG |
1181 | let self_expr = tag_exprs.remove(0); |
1182 | let other_selflike_exprs = tag_exprs; | |
1183 | let tag_field = FieldInfo { span, name: None, self_expr, other_selflike_exprs }; | |
1a4d82fc | 1184 | |
064997fb FG |
1185 | let tag_let_stmts: Vec<_> = iter::zip(&tag_idents, &selflike_args) |
1186 | .map(|(&ident, selflike_arg)| { | |
1187 | let variant_value = deriving::call_intrinsic( | |
1188 | cx, | |
1189 | span, | |
1190 | sym::discriminant_value, | |
1191 | vec![selflike_arg.clone()], | |
1192 | ); | |
1193 | cx.stmt_let(span, false, ident, variant_value) | |
1194 | }) | |
1195 | .collect(); | |
1196 | ||
1197 | (tag_field, tag_let_stmts) | |
1198 | }; | |
1199 | ||
1200 | // There are some special cases involving fieldless enums where no | |
1201 | // match is necessary. | |
1202 | let all_fieldless = variants.iter().all(|v| v.data.fields().is_empty()); | |
1203 | if all_fieldless { | |
1204 | if uses_tags && variants.len() > 1 { | |
1205 | // If the type is fieldless and the trait uses the tag and | |
1206 | // there are multiple variants, we need just an operation on | |
1207 | // the tag(s). | |
1208 | let (tag_field, mut tag_let_stmts) = get_tag_pieces(cx); | |
1209 | let mut tag_check = self.call_substructure_method( | |
1210 | cx, | |
1211 | trait_, | |
1212 | type_ident, | |
1213 | nonselflike_args, | |
1214 | &EnumTag(tag_field, None), | |
1215 | ); | |
1216 | tag_let_stmts.append(&mut tag_check.0); | |
1217 | return BlockOrExpr(tag_let_stmts, tag_check.1); | |
1218 | } | |
1219 | ||
1220 | if variants.len() == 1 { | |
1221 | // If there is a single variant, we don't need an operation on | |
1222 | // the tag(s). Just use the most degenerate result. | |
1223 | return self.call_substructure_method( | |
1224 | cx, | |
1225 | trait_, | |
1226 | type_ident, | |
1227 | nonselflike_args, | |
1228 | &EnumMatching(0, 1, &variants[0], Vec::new()), | |
1229 | ); | |
1230 | }; | |
1231 | } | |
a7813a04 | 1232 | |
1a4d82fc JJ |
1233 | // These arms are of the form: |
1234 | // (Variant1, Variant1, ...) => Body1 | |
1235 | // (Variant2, Variant2, ...) => Body2 | |
1236 | // ... | |
064997fb | 1237 | // where each tuple has length = selflike_args.len() |
dfeec247 XL |
1238 | let mut match_arms: Vec<ast::Arm> = variants |
1239 | .iter() | |
5bcae85e | 1240 | .enumerate() |
e1599b0c | 1241 | .filter(|&(_, v)| !(self.unify_fieldless_variants && v.data.fields().is_empty())) |
1a4d82fc | 1242 | .map(|(index, variant)| { |
1a4d82fc JJ |
1243 | // A single arm has form (&VariantK, &VariantK, ...) => BodyK |
1244 | // (see "Final wrinkle" note below for why.) | |
1a4d82fc | 1245 | |
064997fb FG |
1246 | let addr_of = false; // because enums can't be repr(packed) |
1247 | let fields = | |
1248 | trait_.create_struct_pattern_fields(cx, &variant.data, &prefixes, addr_of); | |
1249 | ||
1250 | let sp = variant.span.with_ctxt(trait_.span.ctxt()); | |
1251 | let variant_path = cx.path(sp, vec![type_ident, variant.ident]); | |
f2b60f7d | 1252 | let by_ref = ByRef::No; // because enums can't be repr(packed) |
064997fb FG |
1253 | let mut subpats: Vec<_> = trait_.create_struct_patterns( |
1254 | cx, | |
1255 | variant_path, | |
1256 | &variant.data, | |
1257 | &prefixes, | |
f2b60f7d | 1258 | by_ref, |
064997fb FG |
1259 | ); |
1260 | ||
1261 | // `(VariantK, VariantK, ...)` or just `VariantK`. | |
1262 | let single_pat = if subpats.len() == 1 { | |
1263 | subpats.pop().unwrap() | |
1264 | } else { | |
1265 | cx.pat_tuple(span, subpats) | |
1266 | }; | |
1a4d82fc JJ |
1267 | |
1268 | // For the BodyK, we need to delegate to our caller, | |
1269 | // passing it an EnumMatching to indicate which case | |
1270 | // we are in. | |
064997fb | 1271 | // |
1a4d82fc JJ |
1272 | // Now, for some given VariantK, we have built up |
1273 | // expressions for referencing every field of every | |
1274 | // Self arg, assuming all are instances of VariantK. | |
1275 | // Build up code associated with such a case. | |
064997fb FG |
1276 | let substructure = EnumMatching(index, variants.len(), variant, fields); |
1277 | let arm_expr = self | |
1278 | .call_substructure_method( | |
1279 | cx, | |
1280 | trait_, | |
1281 | type_ident, | |
1282 | nonselflike_args, | |
1283 | &substructure, | |
1284 | ) | |
1285 | .into_expr(cx, span); | |
1a4d82fc | 1286 | |
a2a8927a | 1287 | cx.arm(span, single_pat, arm_expr) |
5bcae85e SL |
1288 | }) |
1289 | .collect(); | |
a7813a04 | 1290 | |
064997fb FG |
1291 | // Add a default arm to the match, if necessary. |
1292 | let first_fieldless = variants.iter().find(|v| v.data.fields().is_empty()); | |
a7813a04 XL |
1293 | let default = match first_fieldless { |
1294 | Some(v) if self.unify_fieldless_variants => { | |
064997fb FG |
1295 | // We need a default case that handles all the fieldless |
1296 | // variants. The index and actual variant aren't meaningful in | |
1297 | // this case, so just use dummy values. | |
1298 | Some( | |
1299 | self.call_substructure_method( | |
1300 | cx, | |
1301 | trait_, | |
1302 | type_ident, | |
1303 | nonselflike_args, | |
1304 | &EnumMatching(0, variants.len(), v, Vec::new()), | |
1305 | ) | |
1306 | .into_expr(cx, span), | |
1307 | ) | |
a7813a04 | 1308 | } |
064997fb FG |
1309 | _ if variants.len() > 1 && selflike_args.len() > 1 => { |
1310 | // Because we know that all the arguments will match if we reach | |
a7813a04 | 1311 | // the match expression we add the unreachable intrinsics as the |
064997fb | 1312 | // result of the default which should help llvm in optimizing it. |
a2a8927a | 1313 | Some(deriving::call_unreachable(cx, span)) |
a7813a04 | 1314 | } |
5bcae85e | 1315 | _ => None, |
a7813a04 XL |
1316 | }; |
1317 | if let Some(arm) = default { | |
a2a8927a | 1318 | match_arms.push(cx.arm(span, cx.pat_wild(span), arm)); |
a7813a04 XL |
1319 | } |
1320 | ||
064997fb FG |
1321 | // Create a match expression with one arm per discriminant plus |
1322 | // possibly a default arm, e.g.: | |
1323 | // match (self, other) { | |
1324 | // (Variant1, Variant1, ...) => Body1 | |
1325 | // (Variant2, Variant2, ...) => Body2, | |
1326 | // ... | |
1327 | // _ => ::core::intrinsics::unreachable() | |
1328 | // } | |
1329 | let get_match_expr = |mut selflike_args: Vec<P<Expr>>| { | |
1330 | let match_arg = if selflike_args.len() == 1 { | |
1331 | selflike_args.pop().unwrap() | |
1332 | } else { | |
1333 | cx.expr(span, ast::ExprKind::Tup(selflike_args)) | |
1334 | }; | |
1335 | cx.expr_match(span, match_arg, match_arms) | |
1336 | }; | |
1337 | ||
1338 | // If the trait uses the tag and there are multiple variants, we need | |
1339 | // to add a tag check operation before the match. Otherwise, the match | |
1340 | // is enough. | |
1341 | if uses_tags && variants.len() > 1 { | |
1342 | let (tag_field, mut tag_let_stmts) = get_tag_pieces(cx); | |
1a4d82fc | 1343 | |
064997fb FG |
1344 | // Combine a tag check with the match. |
1345 | let mut tag_check_plus_match = self.call_substructure_method( | |
dfeec247 XL |
1346 | cx, |
1347 | trait_, | |
1348 | type_ident, | |
064997fb FG |
1349 | nonselflike_args, |
1350 | &EnumTag(tag_field, Some(get_match_expr(selflike_args))), | |
dfeec247 | 1351 | ); |
064997fb FG |
1352 | tag_let_stmts.append(&mut tag_check_plus_match.0); |
1353 | BlockOrExpr(tag_let_stmts, tag_check_plus_match.1) | |
5bcae85e | 1354 | } else { |
064997fb | 1355 | BlockOrExpr(vec![], Some(get_match_expr(selflike_args))) |
1a4d82fc | 1356 | } |
1a4d82fc JJ |
1357 | } |
1358 | ||
dfeec247 XL |
1359 | fn expand_static_enum_method_body( |
1360 | &self, | |
1361 | cx: &mut ExtCtxt<'_>, | |
1362 | trait_: &TraitDef<'_>, | |
1363 | enum_def: &EnumDef, | |
1364 | type_ident: Ident, | |
064997fb FG |
1365 | nonselflike_args: &[P<Expr>], |
1366 | ) -> BlockOrExpr { | |
dfeec247 XL |
1367 | let summary = enum_def |
1368 | .variants | |
5bcae85e SL |
1369 | .iter() |
1370 | .map(|v| { | |
ea8adc8c | 1371 | let sp = v.span.with_ctxt(trait_.span.ctxt()); |
e1599b0c XL |
1372 | let summary = trait_.summarise_struct(cx, &v.data); |
1373 | (v.ident, sp, summary) | |
5bcae85e SL |
1374 | }) |
1375 | .collect(); | |
dfeec247 XL |
1376 | self.call_substructure_method( |
1377 | cx, | |
1378 | trait_, | |
1379 | type_ident, | |
064997fb | 1380 | nonselflike_args, |
dfeec247 XL |
1381 | &StaticEnum(enum_def, summary), |
1382 | ) | |
1a4d82fc JJ |
1383 | } |
1384 | } | |
1385 | ||
1a4d82fc JJ |
1386 | // general helper methods. |
1387 | impl<'a> TraitDef<'a> { | |
9fa01778 | 1388 | fn summarise_struct(&self, cx: &mut ExtCtxt<'_>, struct_def: &VariantData) -> StaticFields { |
1a4d82fc JJ |
1389 | let mut named_idents = Vec::new(); |
1390 | let mut just_spans = Vec::new(); | |
5bcae85e | 1391 | for field in struct_def.fields() { |
ea8adc8c | 1392 | let sp = field.span.with_ctxt(self.span.ctxt()); |
54a0048b SL |
1393 | match field.ident { |
1394 | Some(ident) => named_idents.push((ident, sp)), | |
1395 | _ => just_spans.push(sp), | |
1a4d82fc JJ |
1396 | } |
1397 | } | |
1398 | ||
1b1a35ee | 1399 | let is_tuple = matches!(struct_def, ast::VariantData::Tuple(..)); |
1a4d82fc | 1400 | match (just_spans.is_empty(), named_idents.is_empty()) { |
a2a8927a XL |
1401 | (false, false) => { |
1402 | cx.span_bug(self.span, "a struct with named and unnamed fields in generic `derive`") | |
1403 | } | |
1a4d82fc JJ |
1404 | // named fields |
1405 | (_, false) => Named(named_idents), | |
532ac7d7 XL |
1406 | // unnamed fields |
1407 | (false, _) => Unnamed(just_spans, is_tuple), | |
1408 | // empty | |
1409 | _ => Named(Vec::new()), | |
1a4d82fc JJ |
1410 | } |
1411 | } | |
1412 | ||
064997fb | 1413 | fn create_struct_patterns( |
dfeec247 XL |
1414 | &self, |
1415 | cx: &mut ExtCtxt<'_>, | |
064997fb FG |
1416 | struct_path: ast::Path, |
1417 | struct_def: &'a VariantData, | |
1418 | prefixes: &[String], | |
f2b60f7d | 1419 | by_ref: ByRef, |
dfeec247 | 1420 | ) -> Vec<P<ast::Pat>> { |
064997fb | 1421 | prefixes |
dfeec247 | 1422 | .iter() |
064997fb FG |
1423 | .map(|prefix| { |
1424 | let pieces_iter = | |
1425 | struct_def.fields().iter().enumerate().map(|(i, struct_field)| { | |
1426 | let sp = struct_field.span.with_ctxt(self.span.ctxt()); | |
064997fb FG |
1427 | let ident = self.mk_pattern_ident(prefix, i); |
1428 | let path = ident.with_span_pos(sp); | |
1429 | ( | |
1430 | sp, | |
1431 | struct_field.ident, | |
f2b60f7d FG |
1432 | cx.pat( |
1433 | path.span, | |
1434 | PatKind::Ident( | |
1435 | BindingAnnotation(by_ref, Mutability::Not), | |
1436 | path, | |
1437 | None, | |
1438 | ), | |
1439 | ), | |
064997fb FG |
1440 | ) |
1441 | }); | |
1442 | ||
1443 | let struct_path = struct_path.clone(); | |
1444 | match *struct_def { | |
1445 | VariantData::Struct(..) => { | |
1446 | let field_pats = pieces_iter | |
1447 | .map(|(sp, ident, pat)| { | |
1448 | if ident.is_none() { | |
1449 | cx.span_bug( | |
1450 | sp, | |
1451 | "a braced struct with unnamed fields in `derive`", | |
1452 | ); | |
1453 | } | |
1454 | ast::PatField { | |
1455 | ident: ident.unwrap(), | |
1456 | is_shorthand: false, | |
1457 | attrs: ast::AttrVec::new(), | |
1458 | id: ast::DUMMY_NODE_ID, | |
1459 | span: pat.span.with_ctxt(self.span.ctxt()), | |
1460 | pat, | |
1461 | is_placeholder: false, | |
1462 | } | |
1463 | }) | |
1464 | .collect(); | |
1465 | cx.pat_struct(self.span, struct_path, field_pats) | |
1466 | } | |
1467 | VariantData::Tuple(..) => { | |
1468 | let subpats = pieces_iter.map(|(_, _, subpat)| subpat).collect(); | |
1469 | cx.pat_tuple_struct(self.span, struct_path, subpats) | |
1470 | } | |
1471 | VariantData::Unit(..) => cx.pat_path(self.span, struct_path), | |
1472 | } | |
1473 | }) | |
1474 | .collect() | |
1475 | } | |
1476 | ||
1477 | fn create_fields<F>(&self, struct_def: &'a VariantData, mk_exprs: F) -> Vec<FieldInfo> | |
1478 | where | |
1479 | F: Fn(usize, &ast::FieldDef, Span) -> Vec<P<ast::Expr>>, | |
1480 | { | |
1481 | struct_def | |
1482 | .fields() | |
1483 | .iter() | |
1484 | .enumerate() | |
1485 | .map(|(i, struct_field)| { | |
1486 | // For this field, get an expr for each selflike_arg. E.g. for | |
1487 | // `PartialEq::eq`, one for each of `&self` and `other`. | |
1488 | let sp = struct_field.span.with_ctxt(self.span.ctxt()); | |
1489 | let mut exprs: Vec<_> = mk_exprs(i, struct_field, sp); | |
1490 | let self_expr = exprs.remove(0); | |
1491 | let other_selflike_exprs = exprs; | |
1492 | FieldInfo { | |
1493 | span: sp.with_ctxt(self.span.ctxt()), | |
1494 | name: struct_field.ident, | |
1495 | self_expr, | |
1496 | other_selflike_exprs, | |
1497 | } | |
5bcae85e SL |
1498 | }) |
1499 | .collect() | |
1a4d82fc JJ |
1500 | } |
1501 | ||
064997fb FG |
1502 | fn mk_pattern_ident(&self, prefix: &str, i: usize) -> Ident { |
1503 | Ident::from_str_and_span(&format!("{}_{}", prefix, i), self.span) | |
1504 | } | |
1505 | ||
1506 | fn create_struct_pattern_fields( | |
dfeec247 XL |
1507 | &self, |
1508 | cx: &mut ExtCtxt<'_>, | |
dfeec247 | 1509 | struct_def: &'a VariantData, |
064997fb FG |
1510 | prefixes: &[String], |
1511 | addr_of: bool, | |
1512 | ) -> Vec<FieldInfo> { | |
1513 | self.create_fields(struct_def, |i, _struct_field, sp| { | |
1514 | prefixes | |
1515 | .iter() | |
1516 | .map(|prefix| { | |
1517 | let ident = self.mk_pattern_ident(prefix, i); | |
1518 | let expr = cx.expr_path(cx.path_ident(sp, ident)); | |
1519 | if addr_of { cx.expr_addr_of(sp, expr) } else { expr } | |
1520 | }) | |
1521 | .collect() | |
1522 | }) | |
1a4d82fc JJ |
1523 | } |
1524 | ||
064997fb | 1525 | fn create_struct_field_access_fields( |
dfeec247 XL |
1526 | &self, |
1527 | cx: &mut ExtCtxt<'_>, | |
064997fb FG |
1528 | selflike_args: &[P<Expr>], |
1529 | struct_def: &'a VariantData, | |
1530 | copy: bool, | |
1531 | ) -> Vec<FieldInfo> { | |
1532 | self.create_fields(struct_def, |i, struct_field, sp| { | |
1533 | selflike_args | |
1534 | .iter() | |
1535 | .map(|selflike_arg| { | |
1536 | // Note: we must use `struct_field.span` rather than `sp` in the | |
1537 | // `unwrap_or_else` case otherwise the hygiene is wrong and we get | |
1538 | // "field `0` of struct `Point` is private" errors on tuple | |
1539 | // structs. | |
1540 | let mut field_expr = cx.expr( | |
1541 | sp, | |
1542 | ast::ExprKind::Field( | |
1543 | selflike_arg.clone(), | |
1544 | struct_field.ident.unwrap_or_else(|| { | |
1545 | Ident::from_str_and_span(&i.to_string(), struct_field.span) | |
1546 | }), | |
1547 | ), | |
1548 | ); | |
1549 | if copy { | |
1550 | field_expr = cx.expr_block( | |
1551 | cx.block(struct_field.span, vec![cx.stmt_expr(field_expr)]), | |
1552 | ); | |
1553 | } | |
1554 | cx.expr_addr_of(sp, field_expr) | |
1555 | }) | |
1556 | .collect() | |
1557 | }) | |
1a4d82fc JJ |
1558 | } |
1559 | } | |
1560 | ||
064997fb FG |
1561 | /// The function passed to `cs_fold` is called repeatedly with a value of this |
1562 | /// type. It describes one part of the code generation. The result is always an | |
1563 | /// expression. | |
1564 | pub enum CsFold<'a> { | |
1565 | /// The basic case: a field expression for one or more selflike args. E.g. | |
1566 | /// for `PartialEq::eq` this is something like `self.x == other.x`. | |
1567 | Single(&'a FieldInfo), | |
1a4d82fc | 1568 | |
064997fb FG |
1569 | /// The combination of two field expressions. E.g. for `PartialEq::eq` this |
1570 | /// is something like `<field1 equality> && <field2 equality>`. | |
1571 | Combine(Span, P<Expr>, P<Expr>), | |
83c7162d | 1572 | |
064997fb FG |
1573 | // The fallback case for a struct or enum variant with no fields. |
1574 | Fieldless, | |
83c7162d XL |
1575 | } |
1576 | ||
064997fb FG |
1577 | /// Folds over fields, combining the expressions for each field in a sequence. |
1578 | /// Statics may not be folded over. | |
dfeec247 XL |
1579 | pub fn cs_fold<F>( |
1580 | use_foldl: bool, | |
dfeec247 XL |
1581 | cx: &mut ExtCtxt<'_>, |
1582 | trait_span: Span, | |
1583 | substructure: &Substructure<'_>, | |
064997fb | 1584 | mut f: F, |
dfeec247 XL |
1585 | ) -> P<Expr> |
1586 | where | |
064997fb | 1587 | F: FnMut(&mut ExtCtxt<'_>, CsFold<'_>) -> P<Expr>, |
1a4d82fc | 1588 | { |
064997fb FG |
1589 | match substructure.fields { |
1590 | EnumMatching(.., all_fields) | Struct(_, all_fields) => { | |
1591 | if all_fields.is_empty() { | |
1592 | return f(cx, CsFold::Fieldless); | |
1593 | } | |
1a4d82fc | 1594 | |
064997fb FG |
1595 | let (base_field, rest) = if use_foldl { |
1596 | all_fields.split_first().unwrap() | |
1597 | } else { | |
1598 | all_fields.split_last().unwrap() | |
83c7162d XL |
1599 | }; |
1600 | ||
064997fb FG |
1601 | let base_expr = f(cx, CsFold::Single(base_field)); |
1602 | ||
1603 | let op = |old, field: &FieldInfo| { | |
1604 | let new = f(cx, CsFold::Single(field)); | |
1605 | f(cx, CsFold::Combine(field.span, old, new)) | |
1606 | }; | |
1607 | ||
1608 | if use_foldl { | |
1609 | rest.iter().fold(base_expr, op) | |
1610 | } else { | |
1611 | rest.iter().rfold(base_expr, op) | |
1612 | } | |
83c7162d | 1613 | } |
064997fb FG |
1614 | EnumTag(tag_field, match_expr) => { |
1615 | let tag_check_expr = f(cx, CsFold::Single(tag_field)); | |
1616 | if let Some(match_expr) = match_expr { | |
1617 | if use_foldl { | |
1618 | f(cx, CsFold::Combine(trait_span, tag_check_expr, match_expr.clone())) | |
1619 | } else { | |
1620 | f(cx, CsFold::Combine(trait_span, match_expr.clone(), tag_check_expr)) | |
1621 | } | |
1622 | } else { | |
1623 | tag_check_expr | |
1624 | } | |
83c7162d | 1625 | } |
064997fb | 1626 | StaticEnum(..) | StaticStruct(..) => cx.span_bug(trait_span, "static function in `derive`"), |
83c7162d XL |
1627 | } |
1628 | } |