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