summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_builtin_macros/src/deriving
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /compiler/rustc_builtin_macros/src/deriving
parentInitial commit. (diff)
downloadrustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz
rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_builtin_macros/src/deriving')
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/bounds.rs28
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/clone.rs212
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs90
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs79
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs110
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs88
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/debug.rs181
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/decodable.rs224
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/default.rs267
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/encodable.rs295
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/mod.rs1655
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/ty.rs203
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/hash.rs80
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/mod.rs208
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 &param.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)));
+}