diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /compiler/rustc_builtin_macros/src/deriving/generic | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_builtin_macros/src/deriving/generic')
-rw-r--r-- | compiler/rustc_builtin_macros/src/deriving/generic/mod.rs | 1655 | ||||
-rw-r--r-- | compiler/rustc_builtin_macros/src/deriving/generic/ty.rs | 203 |
2 files changed, 1858 insertions, 0 deletions
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs new file mode 100644 index 000000000..735017aa5 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -0,0 +1,1655 @@ +//! Some code that abstracts away much of the boilerplate of writing +//! `derive` instances for traits. Among other things it manages getting +//! access to the fields of the 4 different sorts of structs and enum +//! variants, as well as creating the method and impl ast instances. +//! +//! Supported features (fairly exhaustive): +//! +//! - Methods taking any number of parameters of any type, and returning +//! any type, other than vectors, bottom and closures. +//! - Generating `impl`s for types with type parameters and lifetimes +//! (e.g., `Option<T>`), the parameters are automatically given the +//! current trait as a bound. (This includes separate type parameters +//! and lifetimes for methods.) +//! - Additional bounds on the type parameters (`TraitDef.additional_bounds`) +//! +//! The most important thing for implementors is the `Substructure` and +//! `SubstructureFields` objects. The latter groups 5 possibilities of the +//! arguments: +//! +//! - `Struct`, when `Self` is a struct (including tuple structs, e.g +//! `struct T(i32, char)`). +//! - `EnumMatching`, when `Self` is an enum and all the arguments are the +//! same variant of the enum (e.g., `Some(1)`, `Some(3)` and `Some(4)`) +//! - `EnumTag` when `Self` is an enum, for comparing the enum tags. +//! - `StaticEnum` and `StaticStruct` for static methods, where the type +//! being derived upon is either an enum or struct respectively. (Any +//! argument with type Self is just grouped among the non-self +//! arguments.) +//! +//! In the first two cases, the values from the corresponding fields in +//! all the arguments are grouped together. +//! +//! The non-static cases have `Option<ident>` in several places associated +//! with field `expr`s. This represents the name of the field it is +//! associated with. It is only not `None` when the associated field has +//! an identifier in the source code. For example, the `x`s in the +//! following snippet +//! +//! ```rust +//! # #![allow(dead_code)] +//! struct A { x : i32 } +//! +//! struct B(i32); +//! +//! enum C { +//! C0(i32), +//! C1 { x: i32 } +//! } +//! ``` +//! +//! The `i32`s in `B` and `C0` don't have an identifier, so the +//! `Option<ident>`s would be `None` for them. +//! +//! In the static cases, the structure is summarized, either into the just +//! spans of the fields or a list of spans and the field idents (for tuple +//! structs and record structs, respectively), or a list of these, for +//! enums (one for each variant). For empty struct and empty enum +//! variants, it is represented as a count of 0. +//! +//! # "`cs`" functions +//! +//! The `cs_...` functions ("combine substructure") are designed to +//! make life easier by providing some pre-made recipes for common +//! threads; mostly calling the function being derived on all the +//! arguments and then combining them back together in some way (or +//! letting the user chose that). They are not meant to be the only +//! way to handle the structures that this code creates. +//! +//! # Examples +//! +//! The following simplified `PartialEq` is used for in-code examples: +//! +//! ```rust +//! trait PartialEq { +//! fn eq(&self, other: &Self) -> bool; +//! } +//! impl PartialEq for i32 { +//! fn eq(&self, other: &i32) -> bool { +//! *self == *other +//! } +//! } +//! ``` +//! +//! Some examples of the values of `SubstructureFields` follow, using the +//! above `PartialEq`, `A`, `B` and `C`. +//! +//! ## Structs +//! +//! When generating the `expr` for the `A` impl, the `SubstructureFields` is +//! +//! ```{.text} +//! Struct(vec![FieldInfo { +//! span: <span of x> +//! name: Some(<ident of x>), +//! self_: <expr for &self.x>, +//! other: vec![<expr for &other.x] +//! }]) +//! ``` +//! +//! For the `B` impl, called with `B(a)` and `B(b)`, +//! +//! ```{.text} +//! Struct(vec![FieldInfo { +//! span: <span of `i32`>, +//! name: None, +//! self_: <expr for &a> +//! other: vec![<expr for &b>] +//! }]) +//! ``` +//! +//! ## Enums +//! +//! When generating the `expr` for a call with `self == C0(a)` and `other +//! == C0(b)`, the SubstructureFields is +//! +//! ```{.text} +//! EnumMatching(0, <ast::Variant for C0>, +//! vec![FieldInfo { +//! span: <span of i32> +//! name: None, +//! self_: <expr for &a>, +//! other: vec![<expr for &b>] +//! }]) +//! ``` +//! +//! For `C1 {x}` and `C1 {x}`, +//! +//! ```{.text} +//! EnumMatching(1, <ast::Variant for C1>, +//! vec![FieldInfo { +//! span: <span of x> +//! name: Some(<ident of x>), +//! self_: <expr for &self.x>, +//! other: vec![<expr for &other.x>] +//! }]) +//! ``` +//! +//! For the tags, +//! +//! ```{.text} +//! EnumTag( +//! &[<ident of self tag>, <ident of other tag>], <expr to combine with>) +//! ``` +//! Note that this setup doesn't allow for the brute-force "match every variant +//! against every other variant" approach, which is bad because it produces a +//! quadratic amount of code (see #15375). +//! +//! ## Static +//! +//! A static method on the types above would result in, +//! +//! ```{.text} +//! StaticStruct(<ast::VariantData of A>, Named(vec![(<ident of x>, <span of x>)])) +//! +//! StaticStruct(<ast::VariantData of B>, Unnamed(vec![<span of x>])) +//! +//! StaticEnum(<ast::EnumDef of C>, +//! vec![(<ident of C0>, <span of C0>, Unnamed(vec![<span of i32>])), +//! (<ident of C1>, <span of C1>, Named(vec![(<ident of x>, <span of x>)]))]) +//! ``` + +pub use StaticFields::*; +pub use SubstructureFields::*; + +use std::cell::RefCell; +use std::iter; +use std::vec; + +use rustc_ast::ptr::P; +use rustc_ast::{self as ast, EnumDef, Expr, Generics, PatKind}; +use rustc_ast::{GenericArg, GenericParamKind, VariantData}; +use rustc_attr as attr; +use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; +use rustc_span::Span; + +use ty::{Bounds, Path, Ref, Self_, Ty}; + +use crate::deriving; + +pub mod ty; + +pub struct TraitDef<'a> { + /// The span for the current #[derive(Foo)] header. + pub span: Span, + + pub attributes: Vec<ast::Attribute>, + + /// Path of the trait, including any type parameters + pub path: Path, + + /// Additional bounds required of any type parameters of the type, + /// other than the current trait + pub additional_bounds: Vec<Ty>, + + /// Any extra lifetimes and/or bounds, e.g., `D: serialize::Decoder` + pub generics: Bounds, + + /// Can this trait be derived for unions? + pub supports_unions: bool, + + pub methods: Vec<MethodDef<'a>>, + + pub associated_types: Vec<(Ident, Ty)>, +} + +pub struct MethodDef<'a> { + /// name of the method + pub name: Symbol, + /// List of generics, e.g., `R: rand::Rng` + pub generics: Bounds, + + /// Is there is a `&self` argument? If not, it is a static function. + pub explicit_self: bool, + + /// Arguments other than the self argument. + pub nonself_args: Vec<(Ty, Symbol)>, + + /// Returns type + pub ret_ty: Ty, + + pub attributes: Vec<ast::Attribute>, + + /// Can we combine fieldless variants for enums into a single match arm? + /// If true, indicates that the trait operation uses the enum tag in some + /// way. + pub unify_fieldless_variants: bool, + + pub combine_substructure: RefCell<CombineSubstructureFunc<'a>>, +} + +/// All the data about the data structure/method being derived upon. +pub struct Substructure<'a> { + /// ident of self + pub type_ident: Ident, + /// Verbatim access to any non-selflike arguments, i.e. arguments that + /// don't have type `&Self`. + pub nonselflike_args: &'a [P<Expr>], + pub fields: &'a SubstructureFields<'a>, +} + +/// Summary of the relevant parts of a struct/enum field. +pub struct FieldInfo { + pub span: Span, + /// None for tuple structs/normal enum variants, Some for normal + /// structs/struct enum variants. + pub name: Option<Ident>, + /// The expression corresponding to this field of `self` + /// (specifically, a reference to it). + pub self_expr: P<Expr>, + /// The expressions corresponding to references to this field in + /// the other selflike arguments. + pub other_selflike_exprs: Vec<P<Expr>>, +} + +/// Fields for a static method +pub enum StaticFields { + /// Tuple and unit structs/enum variants like this. + Unnamed(Vec<Span>, bool /*is tuple*/), + /// Normal structs/struct variants. + Named(Vec<(Ident, Span)>), +} + +/// A summary of the possible sets of fields. +pub enum SubstructureFields<'a> { + /// A non-static method with `Self` is a struct. + Struct(&'a ast::VariantData, Vec<FieldInfo>), + + /// Matching variants of the enum: variant index, variant count, ast::Variant, + /// fields: the field name is only non-`None` in the case of a struct + /// variant. + EnumMatching(usize, usize, &'a ast::Variant, Vec<FieldInfo>), + + /// The tag of an enum. The first field is a `FieldInfo` for the tags, as + /// if they were fields. The second field is the expression to combine the + /// tag expression with; it will be `None` if no match is necessary. + EnumTag(FieldInfo, Option<P<Expr>>), + + /// A static method where `Self` is a struct. + StaticStruct(&'a ast::VariantData, StaticFields), + + /// A static method where `Self` is an enum. + StaticEnum(&'a ast::EnumDef, Vec<(Ident, Span, StaticFields)>), +} + +/// Combine the values of all the fields together. The last argument is +/// all the fields of all the structures. +pub type CombineSubstructureFunc<'a> = + Box<dyn FnMut(&mut ExtCtxt<'_>, Span, &Substructure<'_>) -> BlockOrExpr + 'a>; + +pub fn combine_substructure( + f: CombineSubstructureFunc<'_>, +) -> RefCell<CombineSubstructureFunc<'_>> { + RefCell::new(f) +} + +struct TypeParameter { + bound_generic_params: Vec<ast::GenericParam>, + ty: P<ast::Ty>, +} + +// The code snippets built up for derived code are sometimes used as blocks +// (e.g. in a function body) and sometimes used as expressions (e.g. in a match +// arm). This structure avoids committing to either form until necessary, +// avoiding the insertion of any unnecessary blocks. +// +// The statements come before the expression. +pub struct BlockOrExpr(Vec<ast::Stmt>, Option<P<Expr>>); + +impl BlockOrExpr { + pub fn new_stmts(stmts: Vec<ast::Stmt>) -> BlockOrExpr { + BlockOrExpr(stmts, None) + } + + pub fn new_expr(expr: P<Expr>) -> BlockOrExpr { + BlockOrExpr(vec![], Some(expr)) + } + + pub fn new_mixed(stmts: Vec<ast::Stmt>, expr: Option<P<Expr>>) -> BlockOrExpr { + BlockOrExpr(stmts, expr) + } + + // Converts it into a block. + fn into_block(mut self, cx: &ExtCtxt<'_>, span: Span) -> P<ast::Block> { + if let Some(expr) = self.1 { + self.0.push(cx.stmt_expr(expr)); + } + cx.block(span, self.0) + } + + // Converts it into an expression. + fn into_expr(self, cx: &ExtCtxt<'_>, span: Span) -> P<Expr> { + if self.0.is_empty() { + match self.1 { + None => cx.expr_block(cx.block(span, vec![])), + Some(expr) => expr, + } + } else if self.0.len() == 1 + && let ast::StmtKind::Expr(expr) = &self.0[0].kind + && self.1.is_none() + { + // There's only a single statement expression. Pull it out. + expr.clone() + } else { + // Multiple statements and/or expressions. + cx.expr_block(self.into_block(cx, span)) + } + } +} + +/// This method helps to extract all the type parameters referenced from a +/// type. For a type parameter `<T>`, it looks for either a `TyPath` that +/// is not global and starts with `T`, or a `TyQPath`. +/// Also include bound generic params from the input type. +fn find_type_parameters( + ty: &ast::Ty, + ty_param_names: &[Symbol], + cx: &ExtCtxt<'_>, +) -> Vec<TypeParameter> { + use rustc_ast::visit; + + struct Visitor<'a, 'b> { + cx: &'a ExtCtxt<'b>, + ty_param_names: &'a [Symbol], + bound_generic_params_stack: Vec<ast::GenericParam>, + type_params: Vec<TypeParameter>, + } + + impl<'a, 'b> visit::Visitor<'a> for Visitor<'a, 'b> { + fn visit_ty(&mut self, ty: &'a ast::Ty) { + if let ast::TyKind::Path(_, ref path) = ty.kind { + if let Some(segment) = path.segments.first() { + if self.ty_param_names.contains(&segment.ident.name) { + self.type_params.push(TypeParameter { + bound_generic_params: self.bound_generic_params_stack.clone(), + ty: P(ty.clone()), + }); + } + } + } + + visit::walk_ty(self, ty) + } + + // Place bound generic params on a stack, to extract them when a type is encountered. + fn visit_poly_trait_ref( + &mut self, + trait_ref: &'a ast::PolyTraitRef, + modifier: &'a ast::TraitBoundModifier, + ) { + let stack_len = self.bound_generic_params_stack.len(); + self.bound_generic_params_stack + .extend(trait_ref.bound_generic_params.clone().into_iter()); + + visit::walk_poly_trait_ref(self, trait_ref, modifier); + + self.bound_generic_params_stack.truncate(stack_len); + } + + fn visit_mac_call(&mut self, mac: &ast::MacCall) { + self.cx.span_err(mac.span(), "`derive` cannot be used on items with type macros"); + } + } + + let mut visitor = Visitor { + cx, + ty_param_names, + bound_generic_params_stack: Vec::new(), + type_params: Vec::new(), + }; + visit::Visitor::visit_ty(&mut visitor, ty); + + visitor.type_params +} + +impl<'a> TraitDef<'a> { + pub fn expand( + self, + cx: &mut ExtCtxt<'_>, + mitem: &ast::MetaItem, + item: &'a Annotatable, + push: &mut dyn FnMut(Annotatable), + ) { + self.expand_ext(cx, mitem, item, push, false); + } + + pub fn expand_ext( + self, + cx: &mut ExtCtxt<'_>, + mitem: &ast::MetaItem, + item: &'a Annotatable, + push: &mut dyn FnMut(Annotatable), + from_scratch: bool, + ) { + match *item { + Annotatable::Item(ref item) => { + let is_packed = item.attrs.iter().any(|attr| { + for r in attr::find_repr_attrs(&cx.sess, attr) { + if let attr::ReprPacked(_) = r { + return true; + } + } + false + }); + let has_no_type_params = match item.kind { + ast::ItemKind::Struct(_, ref generics) + | ast::ItemKind::Enum(_, ref generics) + | ast::ItemKind::Union(_, ref generics) => !generics + .params + .iter() + .any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })), + _ => unreachable!(), + }; + let container_id = cx.current_expansion.id.expn_data().parent.expect_local(); + let always_copy = has_no_type_params && cx.resolver.has_derive_copy(container_id); + + let newitem = match item.kind { + ast::ItemKind::Struct(ref struct_def, ref generics) => self.expand_struct_def( + cx, + &struct_def, + item.ident, + generics, + from_scratch, + is_packed, + always_copy, + ), + ast::ItemKind::Enum(ref enum_def, ref generics) => { + // We ignore `is_packed`/`always_copy` here, because + // `repr(packed)` enums cause an error later on. + // + // This can only cause further compilation errors + // downstream in blatantly illegal code, so it + // is fine. + self.expand_enum_def(cx, enum_def, item.ident, generics, from_scratch) + } + ast::ItemKind::Union(ref struct_def, ref generics) => { + if self.supports_unions { + self.expand_struct_def( + cx, + &struct_def, + item.ident, + generics, + from_scratch, + is_packed, + always_copy, + ) + } else { + cx.span_err(mitem.span, "this trait cannot be derived for unions"); + return; + } + } + _ => unreachable!(), + }; + // Keep the lint attributes of the previous item to control how the + // generated implementations are linted + let mut attrs = newitem.attrs.clone(); + attrs.extend( + item.attrs + .iter() + .filter(|a| { + [ + sym::allow, + sym::warn, + sym::deny, + sym::forbid, + sym::stable, + sym::unstable, + ] + .contains(&a.name_or_empty()) + }) + .cloned(), + ); + push(Annotatable::Item(P(ast::Item { attrs, ..(*newitem).clone() }))) + } + _ => unreachable!(), + } + } + + /// Given that we are deriving a trait `DerivedTrait` for a type like: + /// + /// ```ignore (only-for-syntax-highlight) + /// struct Struct<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> where C: WhereTrait { + /// a: A, + /// b: B::Item, + /// b1: <B as DeclaredTrait>::Item, + /// c1: <C as WhereTrait>::Item, + /// c2: Option<<C as WhereTrait>::Item>, + /// ... + /// } + /// ``` + /// + /// create an impl like: + /// + /// ```ignore (only-for-syntax-highlight) + /// impl<'a, ..., 'z, A, B: DeclaredTrait, C, ... Z> where + /// C: WhereTrait, + /// A: DerivedTrait + B1 + ... + BN, + /// B: DerivedTrait + B1 + ... + BN, + /// C: DerivedTrait + B1 + ... + BN, + /// B::Item: DerivedTrait + B1 + ... + BN, + /// <C as WhereTrait>::Item: DerivedTrait + B1 + ... + BN, + /// ... + /// { + /// ... + /// } + /// ``` + /// + /// where B1, ..., BN are the bounds given by `bounds_paths`.'. Z is a phantom type, and + /// therefore does not get bound by the derived trait. + fn create_derived_impl( + &self, + cx: &mut ExtCtxt<'_>, + type_ident: Ident, + generics: &Generics, + field_tys: Vec<P<ast::Ty>>, + methods: Vec<P<ast::AssocItem>>, + ) -> P<ast::Item> { + let trait_path = self.path.to_path(cx, self.span, type_ident, generics); + + // Transform associated types from `deriving::ty::Ty` into `ast::AssocItem` + let associated_types = self.associated_types.iter().map(|&(ident, ref type_def)| { + P(ast::AssocItem { + id: ast::DUMMY_NODE_ID, + span: self.span, + ident, + vis: ast::Visibility { + span: self.span.shrink_to_lo(), + kind: ast::VisibilityKind::Inherited, + tokens: None, + }, + attrs: Vec::new(), + kind: ast::AssocItemKind::TyAlias(Box::new(ast::TyAlias { + defaultness: ast::Defaultness::Final, + generics: Generics::default(), + where_clauses: ( + ast::TyAliasWhereClause::default(), + ast::TyAliasWhereClause::default(), + ), + where_predicates_split: 0, + bounds: Vec::new(), + ty: Some(type_def.to_ty(cx, self.span, type_ident, generics)), + })), + tokens: None, + }) + }); + + let Generics { mut params, mut where_clause, .. } = + self.generics.to_generics(cx, self.span, type_ident, generics); + where_clause.span = generics.where_clause.span; + let ctxt = self.span.ctxt(); + let span = generics.span.with_ctxt(ctxt); + + // Create the generic parameters + params.extend(generics.params.iter().map(|param| match ¶m.kind { + GenericParamKind::Lifetime { .. } => param.clone(), + GenericParamKind::Type { .. } => { + // I don't think this can be moved out of the loop, since + // a GenericBound requires an ast id + let bounds: Vec<_> = + // extra restrictions on the generics parameters to the + // type being derived upon + self.additional_bounds.iter().map(|p| { + cx.trait_bound(p.to_path(cx, self.span, type_ident, generics)) + }).chain( + // require the current trait + iter::once(cx.trait_bound(trait_path.clone())) + ).chain( + // also add in any bounds from the declaration + param.bounds.iter().cloned() + ).collect(); + + cx.typaram(param.ident.span.with_ctxt(ctxt), param.ident, vec![], bounds, None) + } + GenericParamKind::Const { ty, kw_span, .. } => { + let const_nodefault_kind = GenericParamKind::Const { + ty: ty.clone(), + kw_span: kw_span.with_ctxt(ctxt), + + // We can't have default values inside impl block + default: None, + }; + let mut param_clone = param.clone(); + param_clone.kind = const_nodefault_kind; + param_clone + } + })); + + // and similarly for where clauses + where_clause.predicates.extend(generics.where_clause.predicates.iter().map(|clause| { + match clause { + ast::WherePredicate::BoundPredicate(wb) => { + let span = wb.span.with_ctxt(ctxt); + ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { + span, + ..wb.clone() + }) + } + ast::WherePredicate::RegionPredicate(wr) => { + let span = wr.span.with_ctxt(ctxt); + ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { + span, + ..wr.clone() + }) + } + ast::WherePredicate::EqPredicate(we) => { + let span = we.span.with_ctxt(ctxt); + ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { + id: ast::DUMMY_NODE_ID, + span, + ..we.clone() + }) + } + } + })); + + { + // Extra scope required here so ty_params goes out of scope before params is moved + + let mut ty_params = params + .iter() + .filter(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })) + .peekable(); + + if ty_params.peek().is_some() { + let ty_param_names: Vec<Symbol> = + ty_params.map(|ty_param| ty_param.ident.name).collect(); + + for field_ty in field_tys { + let field_ty_params = find_type_parameters(&field_ty, &ty_param_names, cx); + + for field_ty_param in field_ty_params { + // if we have already handled this type, skip it + if let ast::TyKind::Path(_, ref p) = field_ty_param.ty.kind { + if p.segments.len() == 1 + && ty_param_names.contains(&p.segments[0].ident.name) + { + continue; + }; + } + let mut bounds: Vec<_> = self + .additional_bounds + .iter() + .map(|p| cx.trait_bound(p.to_path(cx, self.span, type_ident, generics))) + .collect(); + + // require the current trait + bounds.push(cx.trait_bound(trait_path.clone())); + + let predicate = ast::WhereBoundPredicate { + span: self.span, + bound_generic_params: field_ty_param.bound_generic_params, + bounded_ty: field_ty_param.ty, + bounds, + }; + + let predicate = ast::WherePredicate::BoundPredicate(predicate); + where_clause.predicates.push(predicate); + } + } + } + } + + let trait_generics = Generics { params, where_clause, span }; + + // Create the reference to the trait. + let trait_ref = cx.trait_ref(trait_path); + + let self_params: Vec<_> = generics + .params + .iter() + .map(|param| match param.kind { + GenericParamKind::Lifetime { .. } => { + GenericArg::Lifetime(cx.lifetime(param.ident.span.with_ctxt(ctxt), param.ident)) + } + GenericParamKind::Type { .. } => { + GenericArg::Type(cx.ty_ident(param.ident.span.with_ctxt(ctxt), param.ident)) + } + GenericParamKind::Const { .. } => { + GenericArg::Const(cx.const_ident(param.ident.span.with_ctxt(ctxt), param.ident)) + } + }) + .collect(); + + // Create the type of `self`. + let path = cx.path_all(self.span, false, vec![type_ident], self_params); + let self_type = cx.ty_path(path); + + let attr = cx.attribute(cx.meta_word(self.span, sym::automatically_derived)); + let opt_trait_ref = Some(trait_ref); + + let mut a = vec![attr]; + a.extend(self.attributes.iter().cloned()); + + cx.item( + self.span, + Ident::empty(), + a, + ast::ItemKind::Impl(Box::new(ast::Impl { + unsafety: ast::Unsafe::No, + polarity: ast::ImplPolarity::Positive, + defaultness: ast::Defaultness::Final, + constness: ast::Const::No, + generics: trait_generics, + of_trait: opt_trait_ref, + self_ty: self_type, + items: methods.into_iter().chain(associated_types).collect(), + })), + ) + } + + fn expand_struct_def( + &self, + cx: &mut ExtCtxt<'_>, + struct_def: &'a VariantData, + type_ident: Ident, + generics: &Generics, + from_scratch: bool, + is_packed: bool, + always_copy: bool, + ) -> P<ast::Item> { + let field_tys: Vec<P<ast::Ty>> = + struct_def.fields().iter().map(|field| field.ty.clone()).collect(); + + let methods = self + .methods + .iter() + .map(|method_def| { + let (explicit_self, selflike_args, nonselflike_args, nonself_arg_tys) = + method_def.extract_arg_details(cx, self, type_ident, generics); + + let body = if from_scratch || method_def.is_static() { + method_def.expand_static_struct_method_body( + cx, + self, + struct_def, + type_ident, + &nonselflike_args, + ) + } else { + method_def.expand_struct_method_body( + cx, + self, + struct_def, + type_ident, + &selflike_args, + &nonselflike_args, + is_packed, + always_copy, + ) + }; + + method_def.create_method( + cx, + self, + type_ident, + generics, + explicit_self, + nonself_arg_tys, + body, + ) + }) + .collect(); + + self.create_derived_impl(cx, type_ident, generics, field_tys, methods) + } + + fn expand_enum_def( + &self, + cx: &mut ExtCtxt<'_>, + enum_def: &'a EnumDef, + type_ident: Ident, + generics: &Generics, + from_scratch: bool, + ) -> P<ast::Item> { + let mut field_tys = Vec::new(); + + for variant in &enum_def.variants { + field_tys.extend(variant.data.fields().iter().map(|field| field.ty.clone())); + } + + let methods = self + .methods + .iter() + .map(|method_def| { + let (explicit_self, selflike_args, nonselflike_args, nonself_arg_tys) = + method_def.extract_arg_details(cx, self, type_ident, generics); + + let body = if from_scratch || method_def.is_static() { + method_def.expand_static_enum_method_body( + cx, + self, + enum_def, + type_ident, + &nonselflike_args, + ) + } else { + method_def.expand_enum_method_body( + cx, + self, + enum_def, + type_ident, + selflike_args, + &nonselflike_args, + ) + }; + + method_def.create_method( + cx, + self, + type_ident, + generics, + explicit_self, + nonself_arg_tys, + body, + ) + }) + .collect(); + + self.create_derived_impl(cx, type_ident, generics, field_tys, methods) + } +} + +impl<'a> MethodDef<'a> { + fn call_substructure_method( + &self, + cx: &mut ExtCtxt<'_>, + trait_: &TraitDef<'_>, + type_ident: Ident, + nonselflike_args: &[P<Expr>], + fields: &SubstructureFields<'_>, + ) -> BlockOrExpr { + let span = trait_.span; + let substructure = Substructure { type_ident, nonselflike_args, fields }; + let mut f = self.combine_substructure.borrow_mut(); + let f: &mut CombineSubstructureFunc<'_> = &mut *f; + f(cx, span, &substructure) + } + + fn get_ret_ty( + &self, + cx: &mut ExtCtxt<'_>, + trait_: &TraitDef<'_>, + generics: &Generics, + type_ident: Ident, + ) -> P<ast::Ty> { + self.ret_ty.to_ty(cx, trait_.span, type_ident, generics) + } + + fn is_static(&self) -> bool { + !self.explicit_self + } + + // The return value includes: + // - explicit_self: The `&self` arg, if present. + // - selflike_args: Expressions for `&self` (if present) and also any other + // args with the same type (e.g. the `other` arg in `PartialEq::eq`). + // - nonselflike_args: Expressions for all the remaining args. + // - nonself_arg_tys: Additional information about all the args other than + // `&self`. + fn extract_arg_details( + &self, + cx: &mut ExtCtxt<'_>, + trait_: &TraitDef<'_>, + type_ident: Ident, + generics: &Generics, + ) -> (Option<ast::ExplicitSelf>, Vec<P<Expr>>, Vec<P<Expr>>, Vec<(Ident, P<ast::Ty>)>) { + let mut selflike_args = Vec::new(); + let mut nonselflike_args = Vec::new(); + let mut nonself_arg_tys = Vec::new(); + let span = trait_.span; + + let explicit_self = if self.explicit_self { + let (self_expr, explicit_self) = ty::get_explicit_self(cx, span); + selflike_args.push(self_expr); + Some(explicit_self) + } else { + None + }; + + for (ty, name) in self.nonself_args.iter() { + let ast_ty = ty.to_ty(cx, span, type_ident, generics); + let ident = Ident::new(*name, span); + nonself_arg_tys.push((ident, ast_ty)); + + let arg_expr = cx.expr_ident(span, ident); + + match ty { + // Selflike (`&Self`) arguments only occur in non-static methods. + Ref(box Self_, _) if !self.is_static() => selflike_args.push(arg_expr), + Self_ => cx.span_bug(span, "`Self` in non-return position"), + _ => nonselflike_args.push(arg_expr), + } + } + + (explicit_self, selflike_args, nonselflike_args, nonself_arg_tys) + } + + fn create_method( + &self, + cx: &mut ExtCtxt<'_>, + trait_: &TraitDef<'_>, + type_ident: Ident, + generics: &Generics, + explicit_self: Option<ast::ExplicitSelf>, + nonself_arg_tys: Vec<(Ident, P<ast::Ty>)>, + body: BlockOrExpr, + ) -> P<ast::AssocItem> { + let span = trait_.span; + // Create the generics that aren't for `Self`. + let fn_generics = self.generics.to_generics(cx, span, type_ident, generics); + + let args = { + let self_arg = explicit_self.map(|explicit_self| { + let ident = Ident::with_dummy_span(kw::SelfLower).with_span_pos(span); + ast::Param::from_self(ast::AttrVec::default(), explicit_self, ident) + }); + let nonself_args = + nonself_arg_tys.into_iter().map(|(name, ty)| cx.param(span, name, ty)); + self_arg.into_iter().chain(nonself_args).collect() + }; + + let ret_type = self.get_ret_ty(cx, trait_, generics, type_ident); + + let method_ident = Ident::new(self.name, span); + let fn_decl = cx.fn_decl(args, ast::FnRetTy::Ty(ret_type)); + let body_block = body.into_block(cx, span); + + let trait_lo_sp = span.shrink_to_lo(); + + let sig = ast::FnSig { header: ast::FnHeader::default(), decl: fn_decl, span }; + let defaultness = ast::Defaultness::Final; + + // Create the method. + P(ast::AssocItem { + id: ast::DUMMY_NODE_ID, + attrs: self.attributes.clone(), + span, + vis: ast::Visibility { + span: trait_lo_sp, + kind: ast::VisibilityKind::Inherited, + tokens: None, + }, + ident: method_ident, + kind: ast::AssocItemKind::Fn(Box::new(ast::Fn { + defaultness, + sig, + generics: fn_generics, + body: Some(body_block), + })), + tokens: None, + }) + } + + /// The normal case uses field access. + /// ``` + /// #[derive(PartialEq)] + /// # struct Dummy; + /// struct A { x: u8, y: u8 } + /// + /// // equivalent to: + /// impl PartialEq for A { + /// fn eq(&self, other: &A) -> bool { + /// self.x == other.x && self.y == other.y + /// } + /// } + /// ``` + /// But if the struct is `repr(packed)`, we can't use something like + /// `&self.x` because that might cause an unaligned ref. So for any trait + /// method that takes a reference, if the struct impls `Copy` then we use a + /// local block to force a copy: + /// ``` + /// # struct A { x: u8, y: u8 } + /// impl PartialEq for A { + /// fn eq(&self, other: &A) -> bool { + /// // Desugars to `{ self.x }.eq(&{ other.y }) && ...` + /// { self.x } == { other.y } && { self.y } == { other.y } + /// } + /// } + /// impl Hash for A { + /// fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + /// ::core::hash::Hash::hash(&{ self.x }, state); + /// ::core::hash::Hash::hash(&{ self.y }, state) + /// } + /// } + /// ``` + /// If the struct doesn't impl `Copy`, we use let-destructuring with `ref`: + /// ``` + /// # struct A { x: u8, y: u8 } + /// impl PartialEq for A { + /// fn eq(&self, other: &A) -> bool { + /// let Self { x: ref __self_0_0, y: ref __self_0_1 } = *self; + /// let Self { x: ref __self_1_0, y: ref __self_1_1 } = *other; + /// *__self_0_0 == *__self_1_0 && *__self_0_1 == *__self_1_1 + /// } + /// } + /// ``` + /// This latter case only works if the fields match the alignment required + /// by the `packed(N)` attribute. (We'll get errors later on if not.) + fn expand_struct_method_body<'b>( + &self, + cx: &mut ExtCtxt<'_>, + trait_: &TraitDef<'b>, + struct_def: &'b VariantData, + type_ident: Ident, + selflike_args: &[P<Expr>], + nonselflike_args: &[P<Expr>], + is_packed: bool, + always_copy: bool, + ) -> BlockOrExpr { + let span = trait_.span; + assert!(selflike_args.len() == 1 || selflike_args.len() == 2); + + let mk_body = |cx, selflike_fields| { + self.call_substructure_method( + cx, + trait_, + type_ident, + nonselflike_args, + &Struct(struct_def, selflike_fields), + ) + }; + + if !is_packed { + let selflike_fields = + trait_.create_struct_field_access_fields(cx, selflike_args, struct_def, false); + mk_body(cx, selflike_fields) + } else if always_copy { + let selflike_fields = + trait_.create_struct_field_access_fields(cx, selflike_args, struct_def, true); + mk_body(cx, selflike_fields) + } else { + // Neither packed nor copy. Need to use ref patterns. + let prefixes: Vec<_> = + (0..selflike_args.len()).map(|i| format!("__self_{}", i)).collect(); + let addr_of = always_copy; + let selflike_fields = + trait_.create_struct_pattern_fields(cx, struct_def, &prefixes, addr_of); + let mut body = mk_body(cx, selflike_fields); + + let struct_path = cx.path(span, vec![Ident::new(kw::SelfUpper, type_ident.span)]); + let use_ref_pat = is_packed && !always_copy; + let patterns = + trait_.create_struct_patterns(cx, struct_path, struct_def, &prefixes, use_ref_pat); + + // Do the let-destructuring. + let mut stmts: Vec<_> = iter::zip(selflike_args, patterns) + .map(|(selflike_arg_expr, pat)| { + let selflike_arg_expr = cx.expr_deref(span, selflike_arg_expr.clone()); + cx.stmt_let_pat(span, pat, selflike_arg_expr) + }) + .collect(); + stmts.extend(std::mem::take(&mut body.0)); + BlockOrExpr(stmts, body.1) + } + } + + fn expand_static_struct_method_body( + &self, + cx: &mut ExtCtxt<'_>, + trait_: &TraitDef<'_>, + struct_def: &VariantData, + type_ident: Ident, + nonselflike_args: &[P<Expr>], + ) -> BlockOrExpr { + let summary = trait_.summarise_struct(cx, struct_def); + + self.call_substructure_method( + cx, + trait_, + type_ident, + nonselflike_args, + &StaticStruct(struct_def, summary), + ) + } + + /// ``` + /// #[derive(PartialEq)] + /// # struct Dummy; + /// enum A { + /// A1, + /// A2(i32) + /// } + /// ``` + /// is equivalent to: + /// ``` + /// impl ::core::cmp::PartialEq for A { + /// #[inline] + /// fn eq(&self, other: &A) -> bool { + /// let __self_tag = ::core::intrinsics::discriminant_value(self); + /// let __arg1_tag = ::core::intrinsics::discriminant_value(other); + /// __self_tag == __arg1_tag && + /// match (self, other) { + /// (A::A2(__self_0), A::A2(__arg1_0)) => + /// *__self_0 == *__arg1_0, + /// _ => true, + /// } + /// } + /// } + /// ``` + /// Creates a tag check combined with a match for a tuple of all + /// `selflike_args`, with an arm for each variant with fields, possibly an + /// arm for each fieldless variant (if `!unify_fieldless_variants` is not + /// true), and possibly a default arm. + fn expand_enum_method_body<'b>( + &self, + cx: &mut ExtCtxt<'_>, + trait_: &TraitDef<'b>, + enum_def: &'b EnumDef, + type_ident: Ident, + selflike_args: Vec<P<Expr>>, + nonselflike_args: &[P<Expr>], + ) -> BlockOrExpr { + let span = trait_.span; + let variants = &enum_def.variants; + + // Traits that unify fieldless variants always use the tag(s). + let uses_tags = self.unify_fieldless_variants; + + // There is no sensible code to be generated for *any* deriving on a + // zero-variant enum. So we just generate a failing expression. + if variants.is_empty() { + return BlockOrExpr(vec![], Some(deriving::call_unreachable(cx, span))); + } + + let prefixes = iter::once("__self".to_string()) + .chain( + selflike_args + .iter() + .enumerate() + .skip(1) + .map(|(arg_count, _selflike_arg)| format!("__arg{}", arg_count)), + ) + .collect::<Vec<String>>(); + + // Build a series of let statements mapping each selflike_arg + // to its discriminant value. + // + // e.g. for `PartialEq::eq` builds two statements: + // ``` + // let __self_tag = ::core::intrinsics::discriminant_value(self); + // let __arg1_tag = ::core::intrinsics::discriminant_value(other); + // ``` + let get_tag_pieces = |cx: &ExtCtxt<'_>| { + let tag_idents: Vec<_> = prefixes + .iter() + .map(|name| Ident::from_str_and_span(&format!("{}_tag", name), span)) + .collect(); + + let mut tag_exprs: Vec<_> = tag_idents + .iter() + .map(|&ident| cx.expr_addr_of(span, cx.expr_ident(span, ident))) + .collect(); + + let self_expr = tag_exprs.remove(0); + let other_selflike_exprs = tag_exprs; + let tag_field = FieldInfo { span, name: None, self_expr, other_selflike_exprs }; + + let tag_let_stmts: Vec<_> = iter::zip(&tag_idents, &selflike_args) + .map(|(&ident, selflike_arg)| { + let variant_value = deriving::call_intrinsic( + cx, + span, + sym::discriminant_value, + vec![selflike_arg.clone()], + ); + cx.stmt_let(span, false, ident, variant_value) + }) + .collect(); + + (tag_field, tag_let_stmts) + }; + + // There are some special cases involving fieldless enums where no + // match is necessary. + let all_fieldless = variants.iter().all(|v| v.data.fields().is_empty()); + if all_fieldless { + if uses_tags && variants.len() > 1 { + // If the type is fieldless and the trait uses the tag and + // there are multiple variants, we need just an operation on + // the tag(s). + let (tag_field, mut tag_let_stmts) = get_tag_pieces(cx); + let mut tag_check = self.call_substructure_method( + cx, + trait_, + type_ident, + nonselflike_args, + &EnumTag(tag_field, None), + ); + tag_let_stmts.append(&mut tag_check.0); + return BlockOrExpr(tag_let_stmts, tag_check.1); + } + + if variants.len() == 1 { + // If there is a single variant, we don't need an operation on + // the tag(s). Just use the most degenerate result. + return self.call_substructure_method( + cx, + trait_, + type_ident, + nonselflike_args, + &EnumMatching(0, 1, &variants[0], Vec::new()), + ); + }; + } + + // These arms are of the form: + // (Variant1, Variant1, ...) => Body1 + // (Variant2, Variant2, ...) => Body2 + // ... + // where each tuple has length = selflike_args.len() + let mut match_arms: Vec<ast::Arm> = variants + .iter() + .enumerate() + .filter(|&(_, v)| !(self.unify_fieldless_variants && v.data.fields().is_empty())) + .map(|(index, variant)| { + // A single arm has form (&VariantK, &VariantK, ...) => BodyK + // (see "Final wrinkle" note below for why.) + + let addr_of = false; // because enums can't be repr(packed) + let fields = + trait_.create_struct_pattern_fields(cx, &variant.data, &prefixes, addr_of); + + let sp = variant.span.with_ctxt(trait_.span.ctxt()); + let variant_path = cx.path(sp, vec![type_ident, variant.ident]); + let use_ref_pat = false; // because enums can't be repr(packed) + let mut subpats: Vec<_> = trait_.create_struct_patterns( + cx, + variant_path, + &variant.data, + &prefixes, + use_ref_pat, + ); + + // `(VariantK, VariantK, ...)` or just `VariantK`. + let single_pat = if subpats.len() == 1 { + subpats.pop().unwrap() + } else { + cx.pat_tuple(span, subpats) + }; + + // For the BodyK, we need to delegate to our caller, + // passing it an EnumMatching to indicate which case + // we are in. + // + // Now, for some given VariantK, we have built up + // expressions for referencing every field of every + // Self arg, assuming all are instances of VariantK. + // Build up code associated with such a case. + let substructure = EnumMatching(index, variants.len(), variant, fields); + let arm_expr = self + .call_substructure_method( + cx, + trait_, + type_ident, + nonselflike_args, + &substructure, + ) + .into_expr(cx, span); + + cx.arm(span, single_pat, arm_expr) + }) + .collect(); + + // Add a default arm to the match, if necessary. + let first_fieldless = variants.iter().find(|v| v.data.fields().is_empty()); + let default = match first_fieldless { + Some(v) if self.unify_fieldless_variants => { + // We need a default case that handles all the fieldless + // variants. The index and actual variant aren't meaningful in + // this case, so just use dummy values. + Some( + self.call_substructure_method( + cx, + trait_, + type_ident, + nonselflike_args, + &EnumMatching(0, variants.len(), v, Vec::new()), + ) + .into_expr(cx, span), + ) + } + _ if variants.len() > 1 && selflike_args.len() > 1 => { + // Because we know that all the arguments will match if we reach + // the match expression we add the unreachable intrinsics as the + // result of the default which should help llvm in optimizing it. + Some(deriving::call_unreachable(cx, span)) + } + _ => None, + }; + if let Some(arm) = default { + match_arms.push(cx.arm(span, cx.pat_wild(span), arm)); + } + + // Create a match expression with one arm per discriminant plus + // possibly a default arm, e.g.: + // match (self, other) { + // (Variant1, Variant1, ...) => Body1 + // (Variant2, Variant2, ...) => Body2, + // ... + // _ => ::core::intrinsics::unreachable() + // } + let get_match_expr = |mut selflike_args: Vec<P<Expr>>| { + let match_arg = if selflike_args.len() == 1 { + selflike_args.pop().unwrap() + } else { + cx.expr(span, ast::ExprKind::Tup(selflike_args)) + }; + cx.expr_match(span, match_arg, match_arms) + }; + + // If the trait uses the tag and there are multiple variants, we need + // to add a tag check operation before the match. Otherwise, the match + // is enough. + if uses_tags && variants.len() > 1 { + let (tag_field, mut tag_let_stmts) = get_tag_pieces(cx); + + // Combine a tag check with the match. + let mut tag_check_plus_match = self.call_substructure_method( + cx, + trait_, + type_ident, + nonselflike_args, + &EnumTag(tag_field, Some(get_match_expr(selflike_args))), + ); + tag_let_stmts.append(&mut tag_check_plus_match.0); + BlockOrExpr(tag_let_stmts, tag_check_plus_match.1) + } else { + BlockOrExpr(vec![], Some(get_match_expr(selflike_args))) + } + } + + fn expand_static_enum_method_body( + &self, + cx: &mut ExtCtxt<'_>, + trait_: &TraitDef<'_>, + enum_def: &EnumDef, + type_ident: Ident, + nonselflike_args: &[P<Expr>], + ) -> BlockOrExpr { + let summary = enum_def + .variants + .iter() + .map(|v| { + let sp = v.span.with_ctxt(trait_.span.ctxt()); + let summary = trait_.summarise_struct(cx, &v.data); + (v.ident, sp, summary) + }) + .collect(); + self.call_substructure_method( + cx, + trait_, + type_ident, + nonselflike_args, + &StaticEnum(enum_def, summary), + ) + } +} + +// general helper methods. +impl<'a> TraitDef<'a> { + fn summarise_struct(&self, cx: &mut ExtCtxt<'_>, struct_def: &VariantData) -> StaticFields { + let mut named_idents = Vec::new(); + let mut just_spans = Vec::new(); + for field in struct_def.fields() { + let sp = field.span.with_ctxt(self.span.ctxt()); + match field.ident { + Some(ident) => named_idents.push((ident, sp)), + _ => just_spans.push(sp), + } + } + + let is_tuple = matches!(struct_def, ast::VariantData::Tuple(..)); + match (just_spans.is_empty(), named_idents.is_empty()) { + (false, false) => { + cx.span_bug(self.span, "a struct with named and unnamed fields in generic `derive`") + } + // named fields + (_, false) => Named(named_idents), + // unnamed fields + (false, _) => Unnamed(just_spans, is_tuple), + // empty + _ => Named(Vec::new()), + } + } + + fn create_struct_patterns( + &self, + cx: &mut ExtCtxt<'_>, + struct_path: ast::Path, + struct_def: &'a VariantData, + prefixes: &[String], + use_ref_pat: bool, + ) -> Vec<P<ast::Pat>> { + prefixes + .iter() + .map(|prefix| { + let pieces_iter = + struct_def.fields().iter().enumerate().map(|(i, struct_field)| { + let sp = struct_field.span.with_ctxt(self.span.ctxt()); + let binding_mode = if use_ref_pat { + ast::BindingMode::ByRef(ast::Mutability::Not) + } else { + ast::BindingMode::ByValue(ast::Mutability::Not) + }; + let ident = self.mk_pattern_ident(prefix, i); + let path = ident.with_span_pos(sp); + ( + sp, + struct_field.ident, + cx.pat(path.span, PatKind::Ident(binding_mode, path, None)), + ) + }); + + let struct_path = struct_path.clone(); + match *struct_def { + VariantData::Struct(..) => { + let field_pats = pieces_iter + .map(|(sp, ident, pat)| { + if ident.is_none() { + cx.span_bug( + sp, + "a braced struct with unnamed fields in `derive`", + ); + } + ast::PatField { + ident: ident.unwrap(), + is_shorthand: false, + attrs: ast::AttrVec::new(), + id: ast::DUMMY_NODE_ID, + span: pat.span.with_ctxt(self.span.ctxt()), + pat, + is_placeholder: false, + } + }) + .collect(); + cx.pat_struct(self.span, struct_path, field_pats) + } + VariantData::Tuple(..) => { + let subpats = pieces_iter.map(|(_, _, subpat)| subpat).collect(); + cx.pat_tuple_struct(self.span, struct_path, subpats) + } + VariantData::Unit(..) => cx.pat_path(self.span, struct_path), + } + }) + .collect() + } + + fn create_fields<F>(&self, struct_def: &'a VariantData, mk_exprs: F) -> Vec<FieldInfo> + where + F: Fn(usize, &ast::FieldDef, Span) -> Vec<P<ast::Expr>>, + { + struct_def + .fields() + .iter() + .enumerate() + .map(|(i, struct_field)| { + // For this field, get an expr for each selflike_arg. E.g. for + // `PartialEq::eq`, one for each of `&self` and `other`. + let sp = struct_field.span.with_ctxt(self.span.ctxt()); + let mut exprs: Vec<_> = mk_exprs(i, struct_field, sp); + let self_expr = exprs.remove(0); + let other_selflike_exprs = exprs; + FieldInfo { + span: sp.with_ctxt(self.span.ctxt()), + name: struct_field.ident, + self_expr, + other_selflike_exprs, + } + }) + .collect() + } + + fn mk_pattern_ident(&self, prefix: &str, i: usize) -> Ident { + Ident::from_str_and_span(&format!("{}_{}", prefix, i), self.span) + } + + fn create_struct_pattern_fields( + &self, + cx: &mut ExtCtxt<'_>, + struct_def: &'a VariantData, + prefixes: &[String], + addr_of: bool, + ) -> Vec<FieldInfo> { + self.create_fields(struct_def, |i, _struct_field, sp| { + prefixes + .iter() + .map(|prefix| { + let ident = self.mk_pattern_ident(prefix, i); + let expr = cx.expr_path(cx.path_ident(sp, ident)); + if addr_of { cx.expr_addr_of(sp, expr) } else { expr } + }) + .collect() + }) + } + + fn create_struct_field_access_fields( + &self, + cx: &mut ExtCtxt<'_>, + selflike_args: &[P<Expr>], + struct_def: &'a VariantData, + copy: bool, + ) -> Vec<FieldInfo> { + self.create_fields(struct_def, |i, struct_field, sp| { + selflike_args + .iter() + .map(|selflike_arg| { + // Note: we must use `struct_field.span` rather than `sp` in the + // `unwrap_or_else` case otherwise the hygiene is wrong and we get + // "field `0` of struct `Point` is private" errors on tuple + // structs. + let mut field_expr = cx.expr( + sp, + ast::ExprKind::Field( + selflike_arg.clone(), + struct_field.ident.unwrap_or_else(|| { + Ident::from_str_and_span(&i.to_string(), struct_field.span) + }), + ), + ); + if copy { + field_expr = cx.expr_block( + cx.block(struct_field.span, vec![cx.stmt_expr(field_expr)]), + ); + } + cx.expr_addr_of(sp, field_expr) + }) + .collect() + }) + } +} + +/// The function passed to `cs_fold` is called repeatedly with a value of this +/// type. It describes one part of the code generation. The result is always an +/// expression. +pub enum CsFold<'a> { + /// The basic case: a field expression for one or more selflike args. E.g. + /// for `PartialEq::eq` this is something like `self.x == other.x`. + Single(&'a FieldInfo), + + /// The combination of two field expressions. E.g. for `PartialEq::eq` this + /// is something like `<field1 equality> && <field2 equality>`. + Combine(Span, P<Expr>, P<Expr>), + + // The fallback case for a struct or enum variant with no fields. + Fieldless, +} + +/// Folds over fields, combining the expressions for each field in a sequence. +/// Statics may not be folded over. +pub fn cs_fold<F>( + use_foldl: bool, + cx: &mut ExtCtxt<'_>, + trait_span: Span, + substructure: &Substructure<'_>, + mut f: F, +) -> P<Expr> +where + F: FnMut(&mut ExtCtxt<'_>, CsFold<'_>) -> P<Expr>, +{ + match substructure.fields { + EnumMatching(.., all_fields) | Struct(_, all_fields) => { + if all_fields.is_empty() { + return f(cx, CsFold::Fieldless); + } + + let (base_field, rest) = if use_foldl { + all_fields.split_first().unwrap() + } else { + all_fields.split_last().unwrap() + }; + + let base_expr = f(cx, CsFold::Single(base_field)); + + let op = |old, field: &FieldInfo| { + let new = f(cx, CsFold::Single(field)); + f(cx, CsFold::Combine(field.span, old, new)) + }; + + if use_foldl { + rest.iter().fold(base_expr, op) + } else { + rest.iter().rfold(base_expr, op) + } + } + EnumTag(tag_field, match_expr) => { + let tag_check_expr = f(cx, CsFold::Single(tag_field)); + if let Some(match_expr) = match_expr { + if use_foldl { + f(cx, CsFold::Combine(trait_span, tag_check_expr, match_expr.clone())) + } else { + f(cx, CsFold::Combine(trait_span, match_expr.clone(), tag_check_expr)) + } + } else { + tag_check_expr + } + } + StaticEnum(..) | StaticStruct(..) => cx.span_bug(trait_span, "static function in `derive`"), + } +} + +/// Returns `true` if the type has no value fields +/// (for an enum, no variant has any fields) +pub fn is_type_without_fields(item: &Annotatable) -> bool { + if let Annotatable::Item(ref item) = *item { + match item.kind { + ast::ItemKind::Enum(ref enum_def, _) => { + enum_def.variants.iter().all(|v| v.data.fields().is_empty()) + } + ast::ItemKind::Struct(ref variant_data, _) => variant_data.fields().is_empty(), + _ => false, + } + } else { + false + } +} diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs new file mode 100644 index 000000000..4d46f7cd4 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs @@ -0,0 +1,203 @@ +//! A mini version of ast::Ty, which is easier to use, and features an explicit `Self` type to use +//! when specifying impls to be derived. + +pub use Ty::*; + +use rustc_ast::ptr::P; +use rustc_ast::{self as ast, Expr, GenericArg, GenericParamKind, Generics, SelfKind}; +use rustc_expand::base::ExtCtxt; +use rustc_span::source_map::{respan, DUMMY_SP}; +use rustc_span::symbol::{kw, Ident, Symbol}; +use rustc_span::Span; + +/// A path, e.g., `::std::option::Option::<i32>` (global). Has support +/// for type parameters. +#[derive(Clone)] +pub struct Path { + path: Vec<Symbol>, + params: Vec<Box<Ty>>, + kind: PathKind, +} + +#[derive(Clone)] +pub enum PathKind { + Local, + Global, + Std, +} + +impl Path { + pub fn new(path: Vec<Symbol>) -> Path { + Path::new_(path, Vec::new(), PathKind::Std) + } + pub fn new_local(path: Symbol) -> Path { + Path::new_(vec![path], Vec::new(), PathKind::Local) + } + pub fn new_(path: Vec<Symbol>, params: Vec<Box<Ty>>, kind: PathKind) -> Path { + Path { path, params, kind } + } + + pub fn to_ty( + &self, + cx: &ExtCtxt<'_>, + span: Span, + self_ty: Ident, + self_generics: &Generics, + ) -> P<ast::Ty> { + cx.ty_path(self.to_path(cx, span, self_ty, self_generics)) + } + pub fn to_path( + &self, + cx: &ExtCtxt<'_>, + span: Span, + self_ty: Ident, + self_generics: &Generics, + ) -> ast::Path { + let mut idents = self.path.iter().map(|s| Ident::new(*s, span)).collect(); + let tys = self.params.iter().map(|t| t.to_ty(cx, span, self_ty, self_generics)); + let params = tys.map(GenericArg::Type).collect(); + + match self.kind { + PathKind::Global => cx.path_all(span, true, idents, params), + PathKind::Local => cx.path_all(span, false, idents, params), + PathKind::Std => { + let def_site = cx.with_def_site_ctxt(DUMMY_SP); + idents.insert(0, Ident::new(kw::DollarCrate, def_site)); + cx.path_all(span, false, idents, params) + } + } + } +} + +/// A type. Supports pointers, Self, and literals. +#[derive(Clone)] +pub enum Ty { + Self_, + /// A reference. + Ref(Box<Ty>, ast::Mutability), + /// `mod::mod::Type<[lifetime], [Params...]>`, including a plain type + /// parameter, and things like `i32` + Path(Path), + /// For () return types. + Unit, +} + +pub fn self_ref() -> Ty { + Ref(Box::new(Self_), ast::Mutability::Not) +} + +impl Ty { + pub fn to_ty( + &self, + cx: &ExtCtxt<'_>, + span: Span, + self_ty: Ident, + self_generics: &Generics, + ) -> P<ast::Ty> { + match self { + Ref(ty, mutbl) => { + let raw_ty = ty.to_ty(cx, span, self_ty, self_generics); + cx.ty_rptr(span, raw_ty, None, *mutbl) + } + Path(p) => p.to_ty(cx, span, self_ty, self_generics), + Self_ => cx.ty_path(self.to_path(cx, span, self_ty, self_generics)), + Unit => { + let ty = ast::TyKind::Tup(vec![]); + cx.ty(span, ty) + } + } + } + + pub fn to_path( + &self, + cx: &ExtCtxt<'_>, + span: Span, + self_ty: Ident, + generics: &Generics, + ) -> ast::Path { + match *self { + Self_ => { + let params: Vec<_> = generics + .params + .iter() + .map(|param| match param.kind { + GenericParamKind::Lifetime { .. } => { + GenericArg::Lifetime(ast::Lifetime { id: param.id, ident: param.ident }) + } + GenericParamKind::Type { .. } => { + GenericArg::Type(cx.ty_ident(span, param.ident)) + } + GenericParamKind::Const { .. } => { + GenericArg::Const(cx.const_ident(span, param.ident)) + } + }) + .collect(); + + cx.path_all(span, false, vec![self_ty], params) + } + Path(ref p) => p.to_path(cx, span, self_ty, generics), + Ref(..) => cx.span_bug(span, "ref in a path in generic `derive`"), + Unit => cx.span_bug(span, "unit in a path in generic `derive`"), + } + } +} + +fn mk_ty_param( + cx: &ExtCtxt<'_>, + span: Span, + name: Symbol, + attrs: &[ast::Attribute], + bounds: &[Path], + self_ident: Ident, + self_generics: &Generics, +) -> ast::GenericParam { + let bounds = bounds + .iter() + .map(|b| { + let path = b.to_path(cx, span, self_ident, self_generics); + cx.trait_bound(path) + }) + .collect(); + cx.typaram(span, Ident::new(name, span), attrs.to_owned(), bounds, None) +} + +/// Bounds on type parameters. +#[derive(Clone)] +pub struct Bounds { + pub bounds: Vec<(Symbol, Vec<Path>)>, +} + +impl Bounds { + pub fn empty() -> Bounds { + Bounds { bounds: Vec::new() } + } + pub fn to_generics( + &self, + cx: &ExtCtxt<'_>, + span: Span, + self_ty: Ident, + self_generics: &Generics, + ) -> Generics { + let params = self + .bounds + .iter() + .map(|t| { + let (name, ref bounds) = *t; + mk_ty_param(cx, span, name, &[], &bounds, self_ty, self_generics) + }) + .collect(); + + Generics { + params, + where_clause: ast::WhereClause { has_where_token: false, predicates: Vec::new(), span }, + span, + } + } +} + +pub fn get_explicit_self(cx: &ExtCtxt<'_>, span: Span) -> (P<Expr>, ast::ExplicitSelf) { + // This constructs a fresh `self` path. + let self_path = cx.expr_self(span); + let self_ty = respan(span, SelfKind::Region(None, ast::Mutability::Not)); + (self_path, self_ty) +} |