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 | |
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 '')
-rw-r--r-- | compiler/rustc_builtin_macros/src/deriving/bounds.rs | 28 | ||||
-rw-r--r-- | compiler/rustc_builtin_macros/src/deriving/clone.rs | 212 | ||||
-rw-r--r-- | compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs | 90 | ||||
-rw-r--r-- | compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs | 79 | ||||
-rw-r--r-- | compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs | 110 | ||||
-rw-r--r-- | compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs | 88 | ||||
-rw-r--r-- | compiler/rustc_builtin_macros/src/deriving/debug.rs | 181 | ||||
-rw-r--r-- | compiler/rustc_builtin_macros/src/deriving/decodable.rs | 224 | ||||
-rw-r--r-- | compiler/rustc_builtin_macros/src/deriving/default.rs | 267 | ||||
-rw-r--r-- | compiler/rustc_builtin_macros/src/deriving/encodable.rs | 295 | ||||
-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 | ||||
-rw-r--r-- | compiler/rustc_builtin_macros/src/deriving/hash.rs | 80 | ||||
-rw-r--r-- | compiler/rustc_builtin_macros/src/deriving/mod.rs | 208 |
14 files changed, 3720 insertions, 0 deletions
diff --git a/compiler/rustc_builtin_macros/src/deriving/bounds.rs b/compiler/rustc_builtin_macros/src/deriving/bounds.rs new file mode 100644 index 000000000..5ef68c6ae --- /dev/null +++ b/compiler/rustc_builtin_macros/src/deriving/bounds.rs @@ -0,0 +1,28 @@ +use crate::deriving::generic::ty::*; +use crate::deriving::generic::*; +use crate::deriving::path_std; + +use rustc_ast::MetaItem; +use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_span::Span; + +pub fn expand_deriving_copy( + cx: &mut ExtCtxt<'_>, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut dyn FnMut(Annotatable), +) { + let trait_def = TraitDef { + span, + attributes: Vec::new(), + path: path_std!(marker::Copy), + additional_bounds: Vec::new(), + generics: Bounds::empty(), + supports_unions: true, + methods: Vec::new(), + associated_types: Vec::new(), + }; + + trait_def.expand(cx, mitem, item, push); +} diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs new file mode 100644 index 000000000..7755ff779 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -0,0 +1,212 @@ +use crate::deriving::generic::ty::*; +use crate::deriving::generic::*; +use crate::deriving::path_std; + +use rustc_ast::{self as ast, Generics, ItemKind, MetaItem, VariantData}; +use rustc_data_structures::fx::FxHashSet; +use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_span::symbol::{kw, sym, Ident}; +use rustc_span::Span; + +pub fn expand_deriving_clone( + cx: &mut ExtCtxt<'_>, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut dyn FnMut(Annotatable), +) { + // The simple form is `fn clone(&self) -> Self { *self }`, possibly with + // some additional `AssertParamIsClone` assertions. + // + // We can use the simple form if either of the following are true. + // - The type derives Copy and there are no generic parameters. (If we + // used the simple form with generics, we'd have to bound the generics + // with Clone + Copy, and then there'd be no Clone impl at all if the + // user fills in something that is Clone but not Copy. After + // specialization we can remove this no-generics limitation.) + // - The item is a union. (Unions with generic parameters still can derive + // Clone because they require Copy for deriving, Clone alone is not + // enough. Whether Clone is implemented for fields is irrelevant so we + // don't assert it.) + let bounds; + let substructure; + let is_simple; + match *item { + Annotatable::Item(ref annitem) => match annitem.kind { + ItemKind::Struct(_, Generics { ref params, .. }) + | ItemKind::Enum(_, Generics { ref params, .. }) => { + let container_id = cx.current_expansion.id.expn_data().parent.expect_local(); + let has_derive_copy = cx.resolver.has_derive_copy(container_id); + if has_derive_copy + && !params + .iter() + .any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })) + { + bounds = vec![]; + is_simple = true; + substructure = combine_substructure(Box::new(|c, s, sub| { + cs_clone_simple("Clone", c, s, sub, false) + })); + } else { + bounds = vec![]; + is_simple = false; + substructure = + combine_substructure(Box::new(|c, s, sub| cs_clone("Clone", c, s, sub))); + } + } + ItemKind::Union(..) => { + bounds = vec![Path(path_std!(marker::Copy))]; + is_simple = true; + substructure = combine_substructure(Box::new(|c, s, sub| { + cs_clone_simple("Clone", c, s, sub, true) + })); + } + _ => cx.span_bug(span, "`#[derive(Clone)]` on wrong item kind"), + }, + + _ => cx.span_bug(span, "`#[derive(Clone)]` on trait item or impl item"), + } + + let inline = cx.meta_word(span, sym::inline); + let attrs = vec![cx.attribute(inline)]; + let trait_def = TraitDef { + span, + attributes: Vec::new(), + path: path_std!(clone::Clone), + additional_bounds: bounds, + generics: Bounds::empty(), + supports_unions: true, + methods: vec![MethodDef { + name: sym::clone, + generics: Bounds::empty(), + explicit_self: true, + nonself_args: Vec::new(), + ret_ty: Self_, + attributes: attrs, + unify_fieldless_variants: false, + combine_substructure: substructure, + }], + associated_types: Vec::new(), + }; + + trait_def.expand_ext(cx, mitem, item, push, is_simple) +} + +fn cs_clone_simple( + name: &str, + cx: &mut ExtCtxt<'_>, + trait_span: Span, + substr: &Substructure<'_>, + is_union: bool, +) -> BlockOrExpr { + let mut stmts = Vec::new(); + let mut seen_type_names = FxHashSet::default(); + let mut process_variant = |variant: &VariantData| { + for field in variant.fields() { + // This basic redundancy checking only prevents duplication of + // assertions like `AssertParamIsClone<Foo>` where the type is a + // simple name. That's enough to get a lot of cases, though. + if let Some(name) = field.ty.kind.is_simple_path() && !seen_type_names.insert(name) { + // Already produced an assertion for this type. + } else { + // let _: AssertParamIsClone<FieldTy>; + super::assert_ty_bounds( + cx, + &mut stmts, + field.ty.clone(), + field.span, + &[sym::clone, sym::AssertParamIsClone], + ); + } + } + }; + + if is_union { + // Just a single assertion for unions, that the union impls `Copy`. + // let _: AssertParamIsCopy<Self>; + let self_ty = cx.ty_path(cx.path_ident(trait_span, Ident::with_dummy_span(kw::SelfUpper))); + super::assert_ty_bounds( + cx, + &mut stmts, + self_ty, + trait_span, + &[sym::clone, sym::AssertParamIsCopy], + ); + } else { + match *substr.fields { + StaticStruct(vdata, ..) => { + process_variant(vdata); + } + StaticEnum(enum_def, ..) => { + for variant in &enum_def.variants { + process_variant(&variant.data); + } + } + _ => cx.span_bug( + trait_span, + &format!("unexpected substructure in simple `derive({})`", name), + ), + } + } + BlockOrExpr::new_mixed(stmts, Some(cx.expr_deref(trait_span, cx.expr_self(trait_span)))) +} + +fn cs_clone( + name: &str, + cx: &mut ExtCtxt<'_>, + trait_span: Span, + substr: &Substructure<'_>, +) -> BlockOrExpr { + let ctor_path; + let all_fields; + let fn_path = cx.std_path(&[sym::clone, sym::Clone, sym::clone]); + let subcall = |cx: &mut ExtCtxt<'_>, field: &FieldInfo| { + let args = vec![field.self_expr.clone()]; + cx.expr_call_global(field.span, fn_path.clone(), args) + }; + + let vdata; + match *substr.fields { + Struct(vdata_, ref af) => { + ctor_path = cx.path(trait_span, vec![substr.type_ident]); + all_fields = af; + vdata = vdata_; + } + EnumMatching(.., variant, ref af) => { + ctor_path = cx.path(trait_span, vec![substr.type_ident, variant.ident]); + all_fields = af; + vdata = &variant.data; + } + EnumTag(..) => cx.span_bug(trait_span, &format!("enum tags in `derive({})`", name,)), + StaticEnum(..) | StaticStruct(..) => { + cx.span_bug(trait_span, &format!("associated function in `derive({})`", name)) + } + } + + let expr = match *vdata { + VariantData::Struct(..) => { + let fields = all_fields + .iter() + .map(|field| { + let Some(ident) = field.name else { + cx.span_bug( + trait_span, + &format!("unnamed field in normal struct in `derive({})`", name,), + ); + }; + let call = subcall(cx, field); + cx.field_imm(field.span, ident, call) + }) + .collect::<Vec<_>>(); + + cx.expr_struct(trait_span, ctor_path, fields) + } + VariantData::Tuple(..) => { + let subcalls = all_fields.iter().map(|f| subcall(cx, f)).collect(); + let path = cx.expr_path(ctor_path); + cx.expr_call(trait_span, path, subcalls) + } + VariantData::Unit(..) => cx.expr_path(ctor_path), + }; + BlockOrExpr::new_expr(expr) +} diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs new file mode 100644 index 000000000..4e798bf6a --- /dev/null +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs @@ -0,0 +1,90 @@ +use crate::deriving::generic::ty::*; +use crate::deriving::generic::*; +use crate::deriving::path_std; + +use rustc_ast::{self as ast, MetaItem}; +use rustc_data_structures::fx::FxHashSet; +use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_span::symbol::{sym, Ident}; +use rustc_span::Span; + +pub fn expand_deriving_eq( + cx: &mut ExtCtxt<'_>, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut dyn FnMut(Annotatable), +) { + let span = cx.with_def_site_ctxt(span); + let inline = cx.meta_word(span, sym::inline); + let hidden = rustc_ast::attr::mk_nested_word_item(Ident::new(sym::hidden, span)); + let doc = rustc_ast::attr::mk_list_item(Ident::new(sym::doc, span), vec![hidden]); + let no_coverage = cx.meta_word(span, sym::no_coverage); + let attrs = vec![cx.attribute(inline), cx.attribute(doc), cx.attribute(no_coverage)]; + let trait_def = TraitDef { + span, + attributes: Vec::new(), + path: path_std!(cmp::Eq), + additional_bounds: Vec::new(), + generics: Bounds::empty(), + supports_unions: true, + methods: vec![MethodDef { + name: sym::assert_receiver_is_total_eq, + generics: Bounds::empty(), + explicit_self: true, + nonself_args: vec![], + ret_ty: Unit, + attributes: attrs, + unify_fieldless_variants: true, + combine_substructure: combine_substructure(Box::new(|a, b, c| { + cs_total_eq_assert(a, b, c) + })), + }], + associated_types: Vec::new(), + }; + + super::inject_impl_of_structural_trait(cx, span, item, path_std!(marker::StructuralEq), push); + + trait_def.expand_ext(cx, mitem, item, push, true) +} + +fn cs_total_eq_assert( + cx: &mut ExtCtxt<'_>, + trait_span: Span, + substr: &Substructure<'_>, +) -> BlockOrExpr { + let mut stmts = Vec::new(); + let mut seen_type_names = FxHashSet::default(); + let mut process_variant = |variant: &ast::VariantData| { + for field in variant.fields() { + // This basic redundancy checking only prevents duplication of + // assertions like `AssertParamIsEq<Foo>` where the type is a + // simple name. That's enough to get a lot of cases, though. + if let Some(name) = field.ty.kind.is_simple_path() && !seen_type_names.insert(name) { + // Already produced an assertion for this type. + } else { + // let _: AssertParamIsEq<FieldTy>; + super::assert_ty_bounds( + cx, + &mut stmts, + field.ty.clone(), + field.span, + &[sym::cmp, sym::AssertParamIsEq], + ); + } + } + }; + + match *substr.fields { + StaticStruct(vdata, ..) => { + process_variant(vdata); + } + StaticEnum(enum_def, ..) => { + for variant in &enum_def.variants { + process_variant(&variant.data); + } + } + _ => cx.span_bug(trait_span, "unexpected substructure in `derive(Eq)`"), + } + BlockOrExpr::new_stmts(stmts) +} diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs new file mode 100644 index 000000000..1612be862 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs @@ -0,0 +1,79 @@ +use crate::deriving::generic::ty::*; +use crate::deriving::generic::*; +use crate::deriving::path_std; + +use rustc_ast::MetaItem; +use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_span::symbol::{sym, Ident}; +use rustc_span::Span; + +pub fn expand_deriving_ord( + cx: &mut ExtCtxt<'_>, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut dyn FnMut(Annotatable), +) { + let inline = cx.meta_word(span, sym::inline); + let attrs = vec![cx.attribute(inline)]; + let trait_def = TraitDef { + span, + attributes: Vec::new(), + path: path_std!(cmp::Ord), + additional_bounds: Vec::new(), + generics: Bounds::empty(), + supports_unions: false, + methods: vec![MethodDef { + name: sym::cmp, + generics: Bounds::empty(), + explicit_self: true, + nonself_args: vec![(self_ref(), sym::other)], + ret_ty: Path(path_std!(cmp::Ordering)), + attributes: attrs, + unify_fieldless_variants: true, + combine_substructure: combine_substructure(Box::new(|a, b, c| cs_cmp(a, b, c))), + }], + associated_types: Vec::new(), + }; + + trait_def.expand(cx, mitem, item, push) +} + +pub fn cs_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { + let test_id = Ident::new(sym::cmp, span); + let equal_path = cx.path_global(span, cx.std_path(&[sym::cmp, sym::Ordering, sym::Equal])); + let cmp_path = cx.std_path(&[sym::cmp, sym::Ord, sym::cmp]); + + // Builds: + // + // match ::core::cmp::Ord::cmp(&self.x, &other.x) { + // ::std::cmp::Ordering::Equal => + // ::core::cmp::Ord::cmp(&self.y, &other.y), + // cmp => cmp, + // } + let expr = cs_fold( + // foldr nests the if-elses correctly, leaving the first field + // as the outermost one, and the last as the innermost. + false, + cx, + span, + substr, + |cx, fold| match fold { + CsFold::Single(field) => { + let [other_expr] = &field.other_selflike_exprs[..] else { + cx.span_bug(field.span, "not exactly 2 arguments in `derive(Ord)`"); + }; + let args = vec![field.self_expr.clone(), other_expr.clone()]; + cx.expr_call_global(field.span, cmp_path.clone(), args) + } + CsFold::Combine(span, expr1, expr2) => { + let eq_arm = cx.arm(span, cx.pat_path(span, equal_path.clone()), expr1); + let neq_arm = + cx.arm(span, cx.pat_ident(span, test_id), cx.expr_ident(span, test_id)); + cx.expr_match(span, expr2, vec![eq_arm, neq_arm]) + } + CsFold::Fieldless => cx.expr_path(equal_path.clone()), + }, + ); + BlockOrExpr::new_expr(expr) +} diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs new file mode 100644 index 000000000..0141b3377 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs @@ -0,0 +1,110 @@ +use crate::deriving::generic::ty::*; +use crate::deriving::generic::*; +use crate::deriving::{path_local, path_std}; + +use rustc_ast::ptr::P; +use rustc_ast::{BinOpKind, BorrowKind, Expr, ExprKind, MetaItem, Mutability}; +use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_span::symbol::sym; +use rustc_span::Span; + +pub fn expand_deriving_partial_eq( + cx: &mut ExtCtxt<'_>, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut dyn FnMut(Annotatable), +) { + fn cs_op( + cx: &mut ExtCtxt<'_>, + span: Span, + substr: &Substructure<'_>, + op: BinOpKind, + combiner: BinOpKind, + base: bool, + ) -> BlockOrExpr { + let expr = cs_fold( + true, // use foldl + cx, + span, + substr, + |cx, fold| match fold { + CsFold::Single(field) => { + let [other_expr] = &field.other_selflike_exprs[..] else { + cx.span_bug(field.span, "not exactly 2 arguments in `derive(PartialEq)`"); + }; + + // We received `&T` arguments. Convert them to `T` by + // stripping `&` or adding `*`. This isn't necessary for + // type checking, but it results in much better error + // messages if something goes wrong. + let convert = |expr: &P<Expr>| { + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = + &expr.kind + { + inner.clone() + } else { + cx.expr_deref(field.span, expr.clone()) + } + }; + cx.expr_binary(field.span, op, convert(&field.self_expr), convert(other_expr)) + } + CsFold::Combine(span, expr1, expr2) => cx.expr_binary(span, combiner, expr1, expr2), + CsFold::Fieldless => cx.expr_bool(span, base), + }, + ); + BlockOrExpr::new_expr(expr) + } + + fn cs_eq(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { + cs_op(cx, span, substr, BinOpKind::Eq, BinOpKind::And, true) + } + fn cs_ne(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { + cs_op(cx, span, substr, BinOpKind::Ne, BinOpKind::Or, false) + } + + macro_rules! md { + ($name:expr, $f:ident) => {{ + let inline = cx.meta_word(span, sym::inline); + let attrs = vec![cx.attribute(inline)]; + MethodDef { + name: $name, + generics: Bounds::empty(), + explicit_self: true, + nonself_args: vec![(self_ref(), sym::other)], + ret_ty: Path(path_local!(bool)), + attributes: attrs, + unify_fieldless_variants: true, + combine_substructure: combine_substructure(Box::new(|a, b, c| $f(a, b, c))), + } + }}; + } + + super::inject_impl_of_structural_trait( + cx, + span, + item, + path_std!(marker::StructuralPartialEq), + push, + ); + + // avoid defining `ne` if we can + // c-like enums, enums without any fields and structs without fields + // can safely define only `eq`. + let mut methods = vec![md!(sym::eq, cs_eq)]; + if !is_type_without_fields(item) { + methods.push(md!(sym::ne, cs_ne)); + } + + let trait_def = TraitDef { + span, + attributes: Vec::new(), + path: path_std!(cmp::PartialEq), + additional_bounds: Vec::new(), + generics: Bounds::empty(), + supports_unions: false, + methods, + associated_types: Vec::new(), + }; + trait_def.expand(cx, mitem, item, push) +} diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs new file mode 100644 index 000000000..2ebb01cc8 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs @@ -0,0 +1,88 @@ +use crate::deriving::generic::ty::*; +use crate::deriving::generic::*; +use crate::deriving::{path_std, pathvec_std}; + +use rustc_ast::MetaItem; +use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_span::symbol::{sym, Ident}; +use rustc_span::Span; + +pub fn expand_deriving_partial_ord( + cx: &mut ExtCtxt<'_>, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut dyn FnMut(Annotatable), +) { + let ordering_ty = Path(path_std!(cmp::Ordering)); + let ret_ty = + Path(Path::new_(pathvec_std!(option::Option), vec![Box::new(ordering_ty)], PathKind::Std)); + + let inline = cx.meta_word(span, sym::inline); + let attrs = vec![cx.attribute(inline)]; + + let partial_cmp_def = MethodDef { + name: sym::partial_cmp, + generics: Bounds::empty(), + explicit_self: true, + nonself_args: vec![(self_ref(), sym::other)], + ret_ty, + attributes: attrs, + unify_fieldless_variants: true, + combine_substructure: combine_substructure(Box::new(|cx, span, substr| { + cs_partial_cmp(cx, span, substr) + })), + }; + + let trait_def = TraitDef { + span, + attributes: vec![], + path: path_std!(cmp::PartialOrd), + additional_bounds: vec![], + generics: Bounds::empty(), + supports_unions: false, + methods: vec![partial_cmp_def], + associated_types: Vec::new(), + }; + trait_def.expand(cx, mitem, item, push) +} + +pub fn cs_partial_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { + let test_id = Ident::new(sym::cmp, span); + let equal_path = cx.path_global(span, cx.std_path(&[sym::cmp, sym::Ordering, sym::Equal])); + let partial_cmp_path = cx.std_path(&[sym::cmp, sym::PartialOrd, sym::partial_cmp]); + + // Builds: + // + // match ::core::cmp::PartialOrd::partial_cmp(&self.x, &other.x) { + // ::core::option::Option::Some(::core::cmp::Ordering::Equal) => + // ::core::cmp::PartialOrd::partial_cmp(&self.y, &other.y), + // cmp => cmp, + // } + let expr = cs_fold( + // foldr nests the if-elses correctly, leaving the first field + // as the outermost one, and the last as the innermost. + false, + cx, + span, + substr, + |cx, fold| match fold { + CsFold::Single(field) => { + let [other_expr] = &field.other_selflike_exprs[..] else { + cx.span_bug(field.span, "not exactly 2 arguments in `derive(Ord)`"); + }; + let args = vec![field.self_expr.clone(), other_expr.clone()]; + cx.expr_call_global(field.span, partial_cmp_path.clone(), args) + } + CsFold::Combine(span, expr1, expr2) => { + let eq_arm = + cx.arm(span, cx.pat_some(span, cx.pat_path(span, equal_path.clone())), expr1); + let neq_arm = + cx.arm(span, cx.pat_ident(span, test_id), cx.expr_ident(span, test_id)); + cx.expr_match(span, expr2, vec![eq_arm, neq_arm]) + } + CsFold::Fieldless => cx.expr_some(span, cx.expr_path(equal_path.clone())), + }, + ); + BlockOrExpr::new_expr(expr) +} diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs new file mode 100644 index 000000000..ceef893e8 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -0,0 +1,181 @@ +use crate::deriving::generic::ty::*; +use crate::deriving::generic::*; +use crate::deriving::path_std; + +use rustc_ast::{self as ast, MetaItem}; +use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_span::symbol::{sym, Ident, Symbol}; +use rustc_span::Span; + +pub fn expand_deriving_debug( + cx: &mut ExtCtxt<'_>, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut dyn FnMut(Annotatable), +) { + // &mut ::std::fmt::Formatter + let fmtr = Ref(Box::new(Path(path_std!(fmt::Formatter))), ast::Mutability::Mut); + + let trait_def = TraitDef { + span, + attributes: Vec::new(), + path: path_std!(fmt::Debug), + additional_bounds: Vec::new(), + generics: Bounds::empty(), + supports_unions: false, + methods: vec![MethodDef { + name: sym::fmt, + generics: Bounds::empty(), + explicit_self: true, + nonself_args: vec![(fmtr, sym::f)], + ret_ty: Path(path_std!(fmt::Result)), + attributes: Vec::new(), + unify_fieldless_variants: false, + combine_substructure: combine_substructure(Box::new(|a, b, c| { + show_substructure(a, b, c) + })), + }], + associated_types: Vec::new(), + }; + trait_def.expand(cx, mitem, item, push) +} + +fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { + let (ident, vdata, fields) = match substr.fields { + Struct(vdata, fields) => (substr.type_ident, *vdata, fields), + EnumMatching(_, _, v, fields) => (v.ident, &v.data, fields), + EnumTag(..) | StaticStruct(..) | StaticEnum(..) => { + cx.span_bug(span, "nonsensical .fields in `#[derive(Debug)]`") + } + }; + + // We want to make sure we have the ctxt set so that we can use unstable methods + let span = cx.with_def_site_ctxt(span); + let name = cx.expr_lit(span, ast::LitKind::Str(ident.name, ast::StrStyle::Cooked)); + let fmt = substr.nonselflike_args[0].clone(); + + // Struct and tuples are similar enough that we use the same code for both, + // with some extra pieces for structs due to the field names. + let (is_struct, args_per_field) = match vdata { + ast::VariantData::Unit(..) => { + // Special fast path for unit variants. + assert!(fields.is_empty()); + (false, 0) + } + ast::VariantData::Tuple(..) => (false, 1), + ast::VariantData::Struct(..) => (true, 2), + }; + + // The number of fields that can be handled without an array. + const CUTOFF: usize = 5; + + if fields.is_empty() { + // Special case for no fields. + let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]); + let expr = cx.expr_call_global(span, fn_path_write_str, vec![fmt, name]); + BlockOrExpr::new_expr(expr) + } else if fields.len() <= CUTOFF { + // Few enough fields that we can use a specific-length method. + let debug = if is_struct { + format!("debug_struct_field{}_finish", fields.len()) + } else { + format!("debug_tuple_field{}_finish", fields.len()) + }; + let fn_path_debug = cx.std_path(&[sym::fmt, sym::Formatter, Symbol::intern(&debug)]); + + let mut args = Vec::with_capacity(2 + fields.len() * args_per_field); + args.extend([fmt, name]); + for i in 0..fields.len() { + let field = &fields[i]; + if is_struct { + let name = cx.expr_lit( + field.span, + ast::LitKind::Str(field.name.unwrap().name, ast::StrStyle::Cooked), + ); + args.push(name); + } + // Use an extra indirection to make sure this works for unsized types. + let field = cx.expr_addr_of(field.span, field.self_expr.clone()); + args.push(field); + } + let expr = cx.expr_call_global(span, fn_path_debug, args); + BlockOrExpr::new_expr(expr) + } else { + // Enough fields that we must use the any-length method. + let mut name_exprs = Vec::with_capacity(fields.len()); + let mut value_exprs = Vec::with_capacity(fields.len()); + + for field in fields { + if is_struct { + name_exprs.push(cx.expr_lit( + field.span, + ast::LitKind::Str(field.name.unwrap().name, ast::StrStyle::Cooked), + )); + } + + // Use an extra indirection to make sure this works for unsized types. + let field = cx.expr_addr_of(field.span, field.self_expr.clone()); + value_exprs.push(field); + } + + // `let names: &'static _ = &["field1", "field2"];` + let names_let = if is_struct { + let lt_static = Some(cx.lifetime_static(span)); + let ty_static_ref = + cx.ty_rptr(span, cx.ty_infer(span), lt_static, ast::Mutability::Not); + Some(cx.stmt_let_ty( + span, + false, + Ident::new(sym::names, span), + Some(ty_static_ref), + cx.expr_array_ref(span, name_exprs), + )) + } else { + None + }; + + // `let values: &[&dyn Debug] = &[&&self.field1, &&self.field2];` + let path_debug = cx.path_global(span, cx.std_path(&[sym::fmt, sym::Debug])); + let ty_dyn_debug = cx.ty( + span, + ast::TyKind::TraitObject(vec![cx.trait_bound(path_debug)], ast::TraitObjectSyntax::Dyn), + ); + let ty_slice = cx.ty( + span, + ast::TyKind::Slice(cx.ty_rptr(span, ty_dyn_debug, None, ast::Mutability::Not)), + ); + let values_let = cx.stmt_let_ty( + span, + false, + Ident::new(sym::values, span), + Some(cx.ty_rptr(span, ty_slice, None, ast::Mutability::Not)), + cx.expr_array_ref(span, value_exprs), + ); + + // `fmt::Formatter::debug_struct_fields_finish(fmt, name, names, values)` or + // `fmt::Formatter::debug_tuple_fields_finish(fmt, name, values)` + let sym_debug = if is_struct { + sym::debug_struct_fields_finish + } else { + sym::debug_tuple_fields_finish + }; + let fn_path_debug_internal = cx.std_path(&[sym::fmt, sym::Formatter, sym_debug]); + + let mut args = Vec::with_capacity(4); + args.push(fmt); + args.push(name); + if is_struct { + args.push(cx.expr_ident(span, Ident::new(sym::names, span))); + } + args.push(cx.expr_ident(span, Ident::new(sym::values, span))); + let expr = cx.expr_call_global(span, fn_path_debug_internal, args); + + let mut stmts = Vec::with_capacity(3); + if is_struct { + stmts.push(names_let.unwrap()); + } + stmts.push(values_let); + BlockOrExpr::new_mixed(stmts, Some(expr)) + } +} diff --git a/compiler/rustc_builtin_macros/src/deriving/decodable.rs b/compiler/rustc_builtin_macros/src/deriving/decodable.rs new file mode 100644 index 000000000..d688143a2 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/deriving/decodable.rs @@ -0,0 +1,224 @@ +//! The compiler code necessary for `#[derive(RustcDecodable)]`. See encodable.rs for more. + +use crate::deriving::generic::ty::*; +use crate::deriving::generic::*; +use crate::deriving::pathvec_std; + +use rustc_ast::ptr::P; +use rustc_ast::{self as ast, Expr, MetaItem, Mutability}; +use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_span::symbol::{sym, Ident, Symbol}; +use rustc_span::Span; + +pub fn expand_deriving_rustc_decodable( + cx: &mut ExtCtxt<'_>, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut dyn FnMut(Annotatable), +) { + let krate = sym::rustc_serialize; + let typaram = sym::__D; + + let trait_def = TraitDef { + span, + attributes: Vec::new(), + path: Path::new_(vec![krate, sym::Decodable], vec![], PathKind::Global), + additional_bounds: Vec::new(), + generics: Bounds::empty(), + supports_unions: false, + methods: vec![MethodDef { + name: sym::decode, + generics: Bounds { + bounds: vec![( + typaram, + vec![Path::new_(vec![krate, sym::Decoder], vec![], PathKind::Global)], + )], + }, + explicit_self: false, + nonself_args: vec![( + Ref(Box::new(Path(Path::new_local(typaram))), Mutability::Mut), + sym::d, + )], + ret_ty: Path(Path::new_( + pathvec_std!(result::Result), + vec![ + Box::new(Self_), + Box::new(Path(Path::new_(vec![typaram, sym::Error], vec![], PathKind::Local))), + ], + PathKind::Std, + )), + attributes: Vec::new(), + unify_fieldless_variants: false, + combine_substructure: combine_substructure(Box::new(|a, b, c| { + decodable_substructure(a, b, c, krate) + })), + }], + associated_types: Vec::new(), + }; + + trait_def.expand(cx, mitem, item, push) +} + +fn decodable_substructure( + cx: &mut ExtCtxt<'_>, + trait_span: Span, + substr: &Substructure<'_>, + krate: Symbol, +) -> BlockOrExpr { + let decoder = substr.nonselflike_args[0].clone(); + let recurse = vec![ + Ident::new(krate, trait_span), + Ident::new(sym::Decodable, trait_span), + Ident::new(sym::decode, trait_span), + ]; + let exprdecode = cx.expr_path(cx.path_global(trait_span, recurse)); + // throw an underscore in front to suppress unused variable warnings + let blkarg = Ident::new(sym::_d, trait_span); + let blkdecoder = cx.expr_ident(trait_span, blkarg); + + let expr = match *substr.fields { + StaticStruct(_, ref summary) => { + let nfields = match *summary { + Unnamed(ref fields, _) => fields.len(), + Named(ref fields) => fields.len(), + }; + let fn_read_struct_field_path: Vec<_> = + cx.def_site_path(&[sym::rustc_serialize, sym::Decoder, sym::read_struct_field]); + + let path = cx.path_ident(trait_span, substr.type_ident); + let result = + decode_static_fields(cx, trait_span, path, summary, |cx, span, name, field| { + cx.expr_try( + span, + cx.expr_call_global( + span, + fn_read_struct_field_path.clone(), + vec![ + blkdecoder.clone(), + cx.expr_str(span, name), + cx.expr_usize(span, field), + exprdecode.clone(), + ], + ), + ) + }); + let result = cx.expr_ok(trait_span, result); + let fn_read_struct_path: Vec<_> = + cx.def_site_path(&[sym::rustc_serialize, sym::Decoder, sym::read_struct]); + + cx.expr_call_global( + trait_span, + fn_read_struct_path, + vec![ + decoder, + cx.expr_str(trait_span, substr.type_ident.name), + cx.expr_usize(trait_span, nfields), + cx.lambda1(trait_span, result, blkarg), + ], + ) + } + StaticEnum(_, ref fields) => { + let variant = Ident::new(sym::i, trait_span); + + let mut arms = Vec::with_capacity(fields.len() + 1); + let mut variants = Vec::with_capacity(fields.len()); + + let fn_read_enum_variant_arg_path: Vec<_> = + cx.def_site_path(&[sym::rustc_serialize, sym::Decoder, sym::read_enum_variant_arg]); + + for (i, &(ident, v_span, ref parts)) in fields.iter().enumerate() { + variants.push(cx.expr_str(v_span, ident.name)); + + let path = cx.path(trait_span, vec![substr.type_ident, ident]); + let decoded = + decode_static_fields(cx, v_span, path, parts, |cx, span, _, field| { + let idx = cx.expr_usize(span, field); + cx.expr_try( + span, + cx.expr_call_global( + span, + fn_read_enum_variant_arg_path.clone(), + vec![blkdecoder.clone(), idx, exprdecode.clone()], + ), + ) + }); + + arms.push(cx.arm(v_span, cx.pat_lit(v_span, cx.expr_usize(v_span, i)), decoded)); + } + + arms.push(cx.arm_unreachable(trait_span)); + + let result = cx.expr_ok( + trait_span, + cx.expr_match(trait_span, cx.expr_ident(trait_span, variant), arms), + ); + let lambda = cx.lambda(trait_span, vec![blkarg, variant], result); + let variant_array_ref = cx.expr_array_ref(trait_span, variants); + let fn_read_enum_variant_path: Vec<_> = + cx.def_site_path(&[sym::rustc_serialize, sym::Decoder, sym::read_enum_variant]); + let result = cx.expr_call_global( + trait_span, + fn_read_enum_variant_path, + vec![blkdecoder, variant_array_ref, lambda], + ); + let fn_read_enum_path: Vec<_> = + cx.def_site_path(&[sym::rustc_serialize, sym::Decoder, sym::read_enum]); + + cx.expr_call_global( + trait_span, + fn_read_enum_path, + vec![ + decoder, + cx.expr_str(trait_span, substr.type_ident.name), + cx.lambda1(trait_span, result, blkarg), + ], + ) + } + _ => cx.bug("expected StaticEnum or StaticStruct in derive(Decodable)"), + }; + BlockOrExpr::new_expr(expr) +} + +/// Creates a decoder for a single enum variant/struct: +/// - `outer_pat_path` is the path to this enum variant/struct +/// - `getarg` should retrieve the `usize`-th field with name `@str`. +fn decode_static_fields<F>( + cx: &mut ExtCtxt<'_>, + trait_span: Span, + outer_pat_path: ast::Path, + fields: &StaticFields, + mut getarg: F, +) -> P<Expr> +where + F: FnMut(&mut ExtCtxt<'_>, Span, Symbol, usize) -> P<Expr>, +{ + match *fields { + Unnamed(ref fields, is_tuple) => { + let path_expr = cx.expr_path(outer_pat_path); + if !is_tuple { + path_expr + } else { + let fields = fields + .iter() + .enumerate() + .map(|(i, &span)| getarg(cx, span, Symbol::intern(&format!("_field{}", i)), i)) + .collect(); + + cx.expr_call(trait_span, path_expr, fields) + } + } + Named(ref fields) => { + // use the field's span to get nicer error messages. + let fields = fields + .iter() + .enumerate() + .map(|(i, &(ident, span))| { + let arg = getarg(cx, span, ident.name, i); + cx.field_imm(span, ident, arg) + }) + .collect(); + cx.expr_struct(trait_span, outer_pat_path, fields) + } + } +} diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs new file mode 100644 index 000000000..517769091 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -0,0 +1,267 @@ +use crate::deriving::generic::ty::*; +use crate::deriving::generic::*; + +use rustc_ast as ast; +use rustc_ast::walk_list; +use rustc_ast::EnumDef; +use rustc_ast::VariantData; +use rustc_errors::Applicability; +use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt}; +use rustc_span::symbol::Ident; +use rustc_span::symbol::{kw, sym}; +use rustc_span::Span; +use smallvec::SmallVec; + +pub fn expand_deriving_default( + cx: &mut ExtCtxt<'_>, + span: Span, + mitem: &ast::MetaItem, + item: &Annotatable, + push: &mut dyn FnMut(Annotatable), +) { + item.visit_with(&mut DetectNonVariantDefaultAttr { cx }); + + let inline = cx.meta_word(span, sym::inline); + let attrs = vec![cx.attribute(inline)]; + let trait_def = TraitDef { + span, + attributes: Vec::new(), + path: Path::new(vec![kw::Default, sym::Default]), + additional_bounds: Vec::new(), + generics: Bounds::empty(), + supports_unions: false, + methods: vec![MethodDef { + name: kw::Default, + generics: Bounds::empty(), + explicit_self: false, + nonself_args: Vec::new(), + ret_ty: Self_, + attributes: attrs, + unify_fieldless_variants: false, + combine_substructure: combine_substructure(Box::new(|cx, trait_span, substr| { + match substr.fields { + StaticStruct(_, fields) => { + default_struct_substructure(cx, trait_span, substr, fields) + } + StaticEnum(enum_def, _) => default_enum_substructure(cx, trait_span, enum_def), + _ => cx.span_bug(trait_span, "method in `derive(Default)`"), + } + })), + }], + associated_types: Vec::new(), + }; + trait_def.expand(cx, mitem, item, push) +} + +fn default_struct_substructure( + cx: &mut ExtCtxt<'_>, + trait_span: Span, + substr: &Substructure<'_>, + summary: &StaticFields, +) -> BlockOrExpr { + // Note that `kw::Default` is "default" and `sym::Default` is "Default"! + let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]); + let default_call = |span| cx.expr_call_global(span, default_ident.clone(), Vec::new()); + + let expr = match summary { + Unnamed(ref fields, is_tuple) => { + if !is_tuple { + cx.expr_ident(trait_span, substr.type_ident) + } else { + let exprs = fields.iter().map(|sp| default_call(*sp)).collect(); + cx.expr_call_ident(trait_span, substr.type_ident, exprs) + } + } + Named(ref fields) => { + let default_fields = fields + .iter() + .map(|&(ident, span)| cx.field_imm(span, ident, default_call(span))) + .collect(); + cx.expr_struct_ident(trait_span, substr.type_ident, default_fields) + } + }; + BlockOrExpr::new_expr(expr) +} + +fn default_enum_substructure( + cx: &mut ExtCtxt<'_>, + trait_span: Span, + enum_def: &EnumDef, +) -> BlockOrExpr { + let expr = if let Ok(default_variant) = extract_default_variant(cx, enum_def, trait_span) + && let Ok(_) = validate_default_attribute(cx, default_variant) + { + // We now know there is exactly one unit variant with exactly one `#[default]` attribute. + cx.expr_path(cx.path( + default_variant.span, + vec![Ident::new(kw::SelfUpper, default_variant.span), default_variant.ident], + )) + } else { + DummyResult::raw_expr(trait_span, true) + }; + BlockOrExpr::new_expr(expr) +} + +fn extract_default_variant<'a>( + cx: &mut ExtCtxt<'_>, + enum_def: &'a EnumDef, + trait_span: Span, +) -> Result<&'a rustc_ast::Variant, ()> { + let default_variants: SmallVec<[_; 1]> = enum_def + .variants + .iter() + .filter(|variant| cx.sess.contains_name(&variant.attrs, kw::Default)) + .collect(); + + let variant = match default_variants.as_slice() { + [variant] => variant, + [] => { + let possible_defaults = enum_def + .variants + .iter() + .filter(|variant| matches!(variant.data, VariantData::Unit(..))) + .filter(|variant| !cx.sess.contains_name(&variant.attrs, sym::non_exhaustive)); + + let mut diag = cx.struct_span_err(trait_span, "no default declared"); + diag.help("make a unit variant default by placing `#[default]` above it"); + for variant in possible_defaults { + // Suggest making each unit variant default. + diag.tool_only_span_suggestion( + variant.span, + &format!("make `{}` default", variant.ident), + format!("#[default] {}", variant.ident), + Applicability::MaybeIncorrect, + ); + } + diag.emit(); + + return Err(()); + } + [first, rest @ ..] => { + let mut diag = cx.struct_span_err(trait_span, "multiple declared defaults"); + diag.span_label(first.span, "first default"); + diag.span_labels(rest.iter().map(|variant| variant.span), "additional default"); + diag.note("only one variant can be default"); + for variant in &default_variants { + // Suggest making each variant already tagged default. + let suggestion = default_variants + .iter() + .filter_map(|v| { + if v.ident == variant.ident { + None + } else { + Some((cx.sess.find_by_name(&v.attrs, kw::Default)?.span, String::new())) + } + }) + .collect(); + + diag.tool_only_multipart_suggestion( + &format!("make `{}` default", variant.ident), + suggestion, + Applicability::MaybeIncorrect, + ); + } + diag.emit(); + + return Err(()); + } + }; + + if !matches!(variant.data, VariantData::Unit(..)) { + cx.struct_span_err( + variant.ident.span, + "the `#[default]` attribute may only be used on unit enum variants", + ) + .help("consider a manual implementation of `Default`") + .emit(); + + return Err(()); + } + + if let Some(non_exhaustive_attr) = cx.sess.find_by_name(&variant.attrs, sym::non_exhaustive) { + cx.struct_span_err(variant.ident.span, "default variant must be exhaustive") + .span_label(non_exhaustive_attr.span, "declared `#[non_exhaustive]` here") + .help("consider a manual implementation of `Default`") + .emit(); + + return Err(()); + } + + Ok(variant) +} + +fn validate_default_attribute( + cx: &mut ExtCtxt<'_>, + default_variant: &rustc_ast::Variant, +) -> Result<(), ()> { + let attrs: SmallVec<[_; 1]> = + cx.sess.filter_by_name(&default_variant.attrs, kw::Default).collect(); + + let attr = match attrs.as_slice() { + [attr] => attr, + [] => cx.bug( + "this method must only be called with a variant that has a `#[default]` attribute", + ), + [first, rest @ ..] => { + let suggestion_text = + if rest.len() == 1 { "try removing this" } else { "try removing these" }; + + cx.struct_span_err(default_variant.ident.span, "multiple `#[default]` attributes") + .note("only one `#[default]` attribute is needed") + .span_label(first.span, "`#[default]` used here") + .span_label(rest[0].span, "`#[default]` used again here") + .span_help(rest.iter().map(|attr| attr.span).collect::<Vec<_>>(), suggestion_text) + // This would otherwise display the empty replacement, hence the otherwise + // repetitive `.span_help` call above. + .tool_only_multipart_suggestion( + suggestion_text, + rest.iter().map(|attr| (attr.span, String::new())).collect(), + Applicability::MachineApplicable, + ) + .emit(); + + return Err(()); + } + }; + if !attr.is_word() { + cx.struct_span_err(attr.span, "`#[default]` attribute does not accept a value") + .span_suggestion_hidden( + attr.span, + "try using `#[default]`", + "#[default]", + Applicability::MaybeIncorrect, + ) + .emit(); + + return Err(()); + } + Ok(()) +} + +struct DetectNonVariantDefaultAttr<'a, 'b> { + cx: &'a ExtCtxt<'b>, +} + +impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, 'b> { + fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) { + if attr.has_name(kw::Default) { + self.cx + .struct_span_err( + attr.span, + "the `#[default]` attribute may only be used on unit enum variants", + ) + .emit(); + } + + rustc_ast::visit::walk_attribute(self, attr); + } + fn visit_variant(&mut self, v: &'a rustc_ast::Variant) { + self.visit_ident(v.ident); + self.visit_vis(&v.vis); + self.visit_variant_data(&v.data); + walk_list!(self, visit_anon_const, &v.disr_expr); + for attr in &v.attrs { + rustc_ast::visit::walk_attribute(self, attr); + } + } +} diff --git a/compiler/rustc_builtin_macros/src/deriving/encodable.rs b/compiler/rustc_builtin_macros/src/deriving/encodable.rs new file mode 100644 index 000000000..70167cac6 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/deriving/encodable.rs @@ -0,0 +1,295 @@ +//! The compiler code necessary to implement the `#[derive(RustcEncodable)]` +//! (and `RustcDecodable`, in `decodable.rs`) extension. The idea here is that +//! type-defining items may be tagged with +//! `#[derive(RustcEncodable, RustcDecodable)]`. +//! +//! For example, a type like: +//! +//! ```ignore (old code) +//! #[derive(RustcEncodable, RustcDecodable)] +//! struct Node { id: usize } +//! ``` +//! +//! would generate two implementations like: +//! +//! ```ignore (old code) +//! # struct Node { id: usize } +//! impl<S: Encoder<E>, E> Encodable<S, E> for Node { +//! fn encode(&self, s: &mut S) -> Result<(), E> { +//! s.emit_struct("Node", 1, |this| { +//! this.emit_struct_field("id", 0, |this| { +//! Encodable::encode(&self.id, this) +//! /* this.emit_usize(self.id) can also be used */ +//! }) +//! }) +//! } +//! } +//! +//! impl<D: Decoder<E>, E> Decodable<D, E> for Node { +//! fn decode(d: &mut D) -> Result<Node, E> { +//! d.read_struct("Node", 1, |this| { +//! match this.read_struct_field("id", 0, |this| Decodable::decode(this)) { +//! Ok(id) => Ok(Node { id: id }), +//! Err(e) => Err(e), +//! } +//! }) +//! } +//! } +//! ``` +//! +//! Other interesting scenarios are when the item has type parameters or +//! references other non-built-in types. A type definition like: +//! +//! ```ignore (old code) +//! # #[derive(RustcEncodable, RustcDecodable)] +//! # struct Span; +//! #[derive(RustcEncodable, RustcDecodable)] +//! struct Spanned<T> { node: T, span: Span } +//! ``` +//! +//! would yield functions like: +//! +//! ```ignore (old code) +//! # #[derive(RustcEncodable, RustcDecodable)] +//! # struct Span; +//! # struct Spanned<T> { node: T, span: Span } +//! impl< +//! S: Encoder<E>, +//! E, +//! T: Encodable<S, E> +//! > Encodable<S, E> for Spanned<T> { +//! fn encode(&self, s: &mut S) -> Result<(), E> { +//! s.emit_struct("Spanned", 2, |this| { +//! this.emit_struct_field("node", 0, |this| self.node.encode(this)) +//! .unwrap(); +//! this.emit_struct_field("span", 1, |this| self.span.encode(this)) +//! }) +//! } +//! } +//! +//! impl< +//! D: Decoder<E>, +//! E, +//! T: Decodable<D, E> +//! > Decodable<D, E> for Spanned<T> { +//! fn decode(d: &mut D) -> Result<Spanned<T>, E> { +//! d.read_struct("Spanned", 2, |this| { +//! Ok(Spanned { +//! node: this.read_struct_field("node", 0, |this| Decodable::decode(this)) +//! .unwrap(), +//! span: this.read_struct_field("span", 1, |this| Decodable::decode(this)) +//! .unwrap(), +//! }) +//! }) +//! } +//! } +//! ``` + +use crate::deriving::generic::ty::*; +use crate::deriving::generic::*; +use crate::deriving::pathvec_std; + +use rustc_ast::{ExprKind, MetaItem, Mutability}; +use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_span::symbol::{sym, Ident, Symbol}; +use rustc_span::Span; + +pub fn expand_deriving_rustc_encodable( + cx: &mut ExtCtxt<'_>, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut dyn FnMut(Annotatable), +) { + let krate = sym::rustc_serialize; + let typaram = sym::__S; + + let trait_def = TraitDef { + span, + attributes: Vec::new(), + path: Path::new_(vec![krate, sym::Encodable], vec![], PathKind::Global), + additional_bounds: Vec::new(), + generics: Bounds::empty(), + supports_unions: false, + methods: vec![MethodDef { + name: sym::encode, + generics: Bounds { + bounds: vec![( + typaram, + vec![Path::new_(vec![krate, sym::Encoder], vec![], PathKind::Global)], + )], + }, + explicit_self: true, + nonself_args: vec![( + Ref(Box::new(Path(Path::new_local(typaram))), Mutability::Mut), + sym::s, + )], + ret_ty: Path(Path::new_( + pathvec_std!(result::Result), + vec![ + Box::new(Unit), + Box::new(Path(Path::new_(vec![typaram, sym::Error], vec![], PathKind::Local))), + ], + PathKind::Std, + )), + attributes: Vec::new(), + unify_fieldless_variants: false, + combine_substructure: combine_substructure(Box::new(|a, b, c| { + encodable_substructure(a, b, c, krate) + })), + }], + associated_types: Vec::new(), + }; + + trait_def.expand(cx, mitem, item, push) +} + +fn encodable_substructure( + cx: &mut ExtCtxt<'_>, + trait_span: Span, + substr: &Substructure<'_>, + krate: Symbol, +) -> BlockOrExpr { + let encoder = substr.nonselflike_args[0].clone(); + // throw an underscore in front to suppress unused variable warnings + let blkarg = Ident::new(sym::_e, trait_span); + let blkencoder = cx.expr_ident(trait_span, blkarg); + let fn_path = cx.expr_path(cx.path_global( + trait_span, + vec![ + Ident::new(krate, trait_span), + Ident::new(sym::Encodable, trait_span), + Ident::new(sym::encode, trait_span), + ], + )); + + match *substr.fields { + Struct(_, ref fields) => { + let fn_emit_struct_field_path = + cx.def_site_path(&[sym::rustc_serialize, sym::Encoder, sym::emit_struct_field]); + let mut stmts = Vec::new(); + for (i, &FieldInfo { name, ref self_expr, span, .. }) in fields.iter().enumerate() { + let name = match name { + Some(id) => id.name, + None => Symbol::intern(&format!("_field{}", i)), + }; + let self_ref = cx.expr_addr_of(span, self_expr.clone()); + let enc = cx.expr_call(span, fn_path.clone(), vec![self_ref, blkencoder.clone()]); + let lambda = cx.lambda1(span, enc, blkarg); + let call = cx.expr_call_global( + span, + fn_emit_struct_field_path.clone(), + vec![ + blkencoder.clone(), + cx.expr_str(span, name), + cx.expr_usize(span, i), + lambda, + ], + ); + + // last call doesn't need a try! + let last = fields.len() - 1; + let call = if i != last { + cx.expr_try(span, call) + } else { + cx.expr(span, ExprKind::Ret(Some(call))) + }; + + let stmt = cx.stmt_expr(call); + stmts.push(stmt); + } + + // unit structs have no fields and need to return Ok() + let blk = if stmts.is_empty() { + let ok = cx.expr_ok(trait_span, cx.expr_tuple(trait_span, vec![])); + cx.lambda1(trait_span, ok, blkarg) + } else { + cx.lambda_stmts_1(trait_span, stmts, blkarg) + }; + + let fn_emit_struct_path = + cx.def_site_path(&[sym::rustc_serialize, sym::Encoder, sym::emit_struct]); + + let expr = cx.expr_call_global( + trait_span, + fn_emit_struct_path, + vec![ + encoder, + cx.expr_str(trait_span, substr.type_ident.name), + cx.expr_usize(trait_span, fields.len()), + blk, + ], + ); + BlockOrExpr::new_expr(expr) + } + + EnumMatching(idx, _, variant, ref fields) => { + // We're not generating an AST that the borrow checker is expecting, + // so we need to generate a unique local variable to take the + // mutable loan out on, otherwise we get conflicts which don't + // actually exist. + let me = cx.stmt_let(trait_span, false, blkarg, encoder); + let encoder = cx.expr_ident(trait_span, blkarg); + + let fn_emit_enum_variant_arg_path: Vec<_> = + cx.def_site_path(&[sym::rustc_serialize, sym::Encoder, sym::emit_enum_variant_arg]); + + let mut stmts = Vec::new(); + if !fields.is_empty() { + let last = fields.len() - 1; + for (i, &FieldInfo { ref self_expr, span, .. }) in fields.iter().enumerate() { + let self_ref = cx.expr_addr_of(span, self_expr.clone()); + let enc = + cx.expr_call(span, fn_path.clone(), vec![self_ref, blkencoder.clone()]); + let lambda = cx.lambda1(span, enc, blkarg); + + let call = cx.expr_call_global( + span, + fn_emit_enum_variant_arg_path.clone(), + vec![blkencoder.clone(), cx.expr_usize(span, i), lambda], + ); + let call = if i != last { + cx.expr_try(span, call) + } else { + cx.expr(span, ExprKind::Ret(Some(call))) + }; + stmts.push(cx.stmt_expr(call)); + } + } else { + let ok = cx.expr_ok(trait_span, cx.expr_tuple(trait_span, vec![])); + let ret_ok = cx.expr(trait_span, ExprKind::Ret(Some(ok))); + stmts.push(cx.stmt_expr(ret_ok)); + } + + let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg); + let name = cx.expr_str(trait_span, variant.ident.name); + + let fn_emit_enum_variant_path: Vec<_> = + cx.def_site_path(&[sym::rustc_serialize, sym::Encoder, sym::emit_enum_variant]); + + let call = cx.expr_call_global( + trait_span, + fn_emit_enum_variant_path, + vec![ + blkencoder, + name, + cx.expr_usize(trait_span, idx), + cx.expr_usize(trait_span, fields.len()), + blk, + ], + ); + + let blk = cx.lambda1(trait_span, call, blkarg); + let fn_emit_enum_path: Vec<_> = + cx.def_site_path(&[sym::rustc_serialize, sym::Encoder, sym::emit_enum]); + let expr = cx.expr_call_global( + trait_span, + fn_emit_enum_path, + vec![encoder, cx.expr_str(trait_span, substr.type_ident.name), blk], + ); + BlockOrExpr::new_mixed(vec![me], Some(expr)) + } + + _ => cx.bug("expected Struct or EnumMatching in derive(Encodable)"), + } +} 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) +} diff --git a/compiler/rustc_builtin_macros/src/deriving/hash.rs b/compiler/rustc_builtin_macros/src/deriving/hash.rs new file mode 100644 index 000000000..32ae3d344 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/deriving/hash.rs @@ -0,0 +1,80 @@ +use crate::deriving::generic::ty::*; +use crate::deriving::generic::*; +use crate::deriving::{path_std, pathvec_std}; + +use rustc_ast::{MetaItem, Mutability}; +use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_span::symbol::sym; +use rustc_span::Span; + +pub fn expand_deriving_hash( + cx: &mut ExtCtxt<'_>, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut dyn FnMut(Annotatable), +) { + let path = Path::new_(pathvec_std!(hash::Hash), vec![], PathKind::Std); + + let typaram = sym::__H; + + let arg = Path::new_local(typaram); + let hash_trait_def = TraitDef { + span, + attributes: Vec::new(), + path, + additional_bounds: Vec::new(), + generics: Bounds::empty(), + supports_unions: false, + methods: vec![MethodDef { + name: sym::hash, + generics: Bounds { bounds: vec![(typaram, vec![path_std!(hash::Hasher)])] }, + explicit_self: true, + nonself_args: vec![(Ref(Box::new(Path(arg)), Mutability::Mut), sym::state)], + ret_ty: Unit, + attributes: vec![], + unify_fieldless_variants: true, + combine_substructure: combine_substructure(Box::new(|a, b, c| { + hash_substructure(a, b, c) + })), + }], + associated_types: Vec::new(), + }; + + hash_trait_def.expand(cx, mitem, item, push); +} + +fn hash_substructure( + cx: &mut ExtCtxt<'_>, + trait_span: Span, + substr: &Substructure<'_>, +) -> BlockOrExpr { + let [state_expr] = substr.nonselflike_args else { + cx.span_bug(trait_span, "incorrect number of arguments in `derive(Hash)`"); + }; + let call_hash = |span, expr| { + let hash_path = { + let strs = cx.std_path(&[sym::hash, sym::Hash, sym::hash]); + + cx.expr_path(cx.path_global(span, strs)) + }; + let expr = cx.expr_call(span, hash_path, vec![expr, state_expr.clone()]); + cx.stmt_expr(expr) + }; + + let (stmts, match_expr) = match substr.fields { + Struct(_, fields) | EnumMatching(.., fields) => { + let stmts = + fields.iter().map(|field| call_hash(field.span, field.self_expr.clone())).collect(); + (stmts, None) + } + EnumTag(tag_field, match_expr) => { + assert!(tag_field.other_selflike_exprs.is_empty()); + let stmts = vec![call_hash(tag_field.span, tag_field.self_expr.clone())]; + (stmts, match_expr.clone()) + } + _ => cx.span_bug(trait_span, "impossible substructure in `derive(Hash)`"), + }; + + BlockOrExpr::new_mixed(stmts, match_expr) +} diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs new file mode 100644 index 000000000..c1ca089da --- /dev/null +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -0,0 +1,208 @@ +//! The compiler code necessary to implement the `#[derive]` extensions. + +use rustc_ast as ast; +use rustc_ast::ptr::P; +use rustc_ast::{GenericArg, Impl, ItemKind, MetaItem}; +use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier}; +use rustc_span::symbol::{sym, Ident, Symbol}; +use rustc_span::Span; + +macro path_local($x:ident) { + generic::ty::Path::new_local(sym::$x) +} + +macro pathvec_std($($rest:ident)::+) {{ + vec![ $( sym::$rest ),+ ] +}} + +macro path_std($($x:tt)*) { + generic::ty::Path::new( pathvec_std!( $($x)* ) ) +} + +pub mod bounds; +pub mod clone; +pub mod debug; +pub mod decodable; +pub mod default; +pub mod encodable; +pub mod hash; + +#[path = "cmp/eq.rs"] +pub mod eq; +#[path = "cmp/ord.rs"] +pub mod ord; +#[path = "cmp/partial_eq.rs"] +pub mod partial_eq; +#[path = "cmp/partial_ord.rs"] +pub mod partial_ord; + +pub mod generic; + +pub(crate) struct BuiltinDerive( + pub(crate) fn(&mut ExtCtxt<'_>, Span, &MetaItem, &Annotatable, &mut dyn FnMut(Annotatable)), +); + +impl MultiItemModifier for BuiltinDerive { + fn expand( + &self, + ecx: &mut ExtCtxt<'_>, + span: Span, + meta_item: &MetaItem, + item: Annotatable, + ) -> ExpandResult<Vec<Annotatable>, Annotatable> { + // FIXME: Built-in derives often forget to give spans contexts, + // so we are doing it here in a centralized way. + let span = ecx.with_def_site_ctxt(span); + let mut items = Vec::new(); + match item { + Annotatable::Stmt(stmt) => { + if let ast::StmtKind::Item(item) = stmt.into_inner().kind { + (self.0)(ecx, span, meta_item, &Annotatable::Item(item), &mut |a| { + // Cannot use 'ecx.stmt_item' here, because we need to pass 'ecx' + // to the function + items.push(Annotatable::Stmt(P(ast::Stmt { + id: ast::DUMMY_NODE_ID, + kind: ast::StmtKind::Item(a.expect_item()), + span, + }))); + }); + } else { + unreachable!("should have already errored on non-item statement") + } + } + _ => { + (self.0)(ecx, span, meta_item, &item, &mut |a| items.push(a)); + } + } + ExpandResult::Ready(items) + } +} + +/// Constructs an expression that calls an intrinsic +fn call_intrinsic( + cx: &ExtCtxt<'_>, + span: Span, + intrinsic: Symbol, + args: Vec<P<ast::Expr>>, +) -> P<ast::Expr> { + let span = cx.with_def_site_ctxt(span); + let path = cx.std_path(&[sym::intrinsics, intrinsic]); + cx.expr_call_global(span, path, args) +} + +/// Constructs an expression that calls the `unreachable` intrinsic. +fn call_unreachable(cx: &ExtCtxt<'_>, span: Span) -> P<ast::Expr> { + let span = cx.with_def_site_ctxt(span); + let path = cx.std_path(&[sym::intrinsics, sym::unreachable]); + let call = cx.expr_call_global(span, path, vec![]); + + cx.expr_block(P(ast::Block { + stmts: vec![cx.stmt_expr(call)], + id: ast::DUMMY_NODE_ID, + rules: ast::BlockCheckMode::Unsafe(ast::CompilerGenerated), + span, + tokens: None, + could_be_bare_literal: false, + })) +} + +// Injects `impl<...> Structural for ItemType<...> { }`. In particular, +// does *not* add `where T: Structural` for parameters `T` in `...`. +// (That's the main reason we cannot use TraitDef here.) +fn inject_impl_of_structural_trait( + cx: &mut ExtCtxt<'_>, + span: Span, + item: &Annotatable, + structural_path: generic::ty::Path, + push: &mut dyn FnMut(Annotatable), +) { + let Annotatable::Item(ref item) = *item else { + unreachable!(); + }; + + let generics = match item.kind { + ItemKind::Struct(_, ref generics) | ItemKind::Enum(_, ref generics) => generics, + // Do not inject `impl Structural for Union`. (`PartialEq` does not + // support unions, so we will see error downstream.) + ItemKind::Union(..) => return, + _ => unreachable!(), + }; + + // Create generics param list for where clauses and impl headers + let mut generics = generics.clone(); + + // Create the type of `self`. + // + // in addition, remove defaults from generic params (impls cannot have them). + let self_params: Vec<_> = generics + .params + .iter_mut() + .map(|param| match &mut param.kind { + ast::GenericParamKind::Lifetime => { + ast::GenericArg::Lifetime(cx.lifetime(span, param.ident)) + } + ast::GenericParamKind::Type { default } => { + *default = None; + ast::GenericArg::Type(cx.ty_ident(span, param.ident)) + } + ast::GenericParamKind::Const { ty: _, kw_span: _, default } => { + *default = None; + ast::GenericArg::Const(cx.const_ident(span, param.ident)) + } + }) + .collect(); + + let type_ident = item.ident; + + let trait_ref = cx.trait_ref(structural_path.to_path(cx, span, type_ident, &generics)); + let self_type = cx.ty_path(cx.path_all(span, false, vec![type_ident], self_params)); + + // It would be nice to also encode constraint `where Self: Eq` (by adding it + // onto `generics` cloned above). Unfortunately, that strategy runs afoul of + // rust-lang/rust#48214. So we perform that additional check in the compiler + // itself, instead of encoding it here. + + // Keep the lint and stability attributes of the original item, to control + // how the generated implementation is linted. + let mut attrs = Vec::new(); + 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(), + ); + + let newitem = cx.item( + span, + Ident::empty(), + attrs, + ItemKind::Impl(Box::new(Impl { + unsafety: ast::Unsafe::No, + polarity: ast::ImplPolarity::Positive, + defaultness: ast::Defaultness::Final, + constness: ast::Const::No, + generics, + of_trait: Some(trait_ref), + self_ty: self_type, + items: Vec::new(), + })), + ); + + push(Annotatable::Item(newitem)); +} + +fn assert_ty_bounds( + cx: &mut ExtCtxt<'_>, + stmts: &mut Vec<ast::Stmt>, + ty: P<ast::Ty>, + span: Span, + assert_path: &[Symbol], +) { + // Generate statement `let _: assert_path<ty>;`. + let span = cx.with_def_site_ctxt(span); + let assert_path = cx.path_all(span, true, cx.std_path(assert_path), vec![GenericArg::Type(ty)]); + stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path))); +} |