rustc_builtin_macros/deriving/cmp/
partial_eq.rs

1use rustc_ast::ptr::P;
2use rustc_ast::{BinOpKind, BorrowKind, Expr, ExprKind, MetaItem, Mutability};
3use rustc_expand::base::{Annotatable, ExtCtxt};
4use rustc_span::{Span, sym};
5use thin_vec::thin_vec;
6
7use crate::deriving::generic::ty::*;
8use crate::deriving::generic::*;
9use crate::deriving::{path_local, path_std};
10
11pub(crate) fn expand_deriving_partial_eq(
12    cx: &ExtCtxt<'_>,
13    span: Span,
14    mitem: &MetaItem,
15    item: &Annotatable,
16    push: &mut dyn FnMut(Annotatable),
17    is_const: bool,
18) {
19    fn cs_eq(cx: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr {
20        let base = true;
21        let expr = cs_fold(
22            true, // use foldl
23            cx,
24            span,
25            substr,
26            |cx, fold| match fold {
27                CsFold::Single(field) => {
28                    let [other_expr] = &field.other_selflike_exprs[..] else {
29                        cx.dcx()
30                            .span_bug(field.span, "not exactly 2 arguments in `derive(PartialEq)`");
31                    };
32
33                    // We received arguments of type `&T`. Convert them to type `T` by stripping
34                    // any leading `&`. This isn't necessary for type checking, but
35                    // it results in better error messages if something goes wrong.
36                    //
37                    // Note: for arguments that look like `&{ x }`, which occur with packed
38                    // structs, this would cause expressions like `{ self.x } == { other.x }`,
39                    // which isn't valid Rust syntax. This wouldn't break compilation because these
40                    // AST nodes are constructed within the compiler. But it would mean that code
41                    // printed by `-Zunpretty=expanded` (or `cargo expand`) would have invalid
42                    // syntax, which would be suboptimal. So we wrap these in parens, giving
43                    // `({ self.x }) == ({ other.x })`, which is valid syntax.
44                    let convert = |expr: &P<Expr>| {
45                        if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) =
46                            &expr.kind
47                        {
48                            if let ExprKind::Block(..) = &inner.kind {
49                                // `&{ x }` form: remove the `&`, add parens.
50                                cx.expr_paren(field.span, inner.clone())
51                            } else {
52                                // `&x` form: remove the `&`.
53                                inner.clone()
54                            }
55                        } else {
56                            expr.clone()
57                        }
58                    };
59                    cx.expr_binary(
60                        field.span,
61                        BinOpKind::Eq,
62                        convert(&field.self_expr),
63                        convert(other_expr),
64                    )
65                }
66                CsFold::Combine(span, expr1, expr2) => {
67                    cx.expr_binary(span, BinOpKind::And, expr1, expr2)
68                }
69                CsFold::Fieldless => cx.expr_bool(span, base),
70            },
71        );
72        BlockOrExpr::new_expr(expr)
73    }
74
75    let structural_trait_def = TraitDef {
76        span,
77        path: path_std!(marker::StructuralPartialEq),
78        skip_path_as_bound: true, // crucial!
79        needs_copy_as_bound_if_packed: false,
80        additional_bounds: Vec::new(),
81        // We really don't support unions, but that's already checked by the impl generated below;
82        // a second check here would lead to redundant error messages.
83        supports_unions: true,
84        methods: Vec::new(),
85        associated_types: Vec::new(),
86        is_const: false,
87    };
88    structural_trait_def.expand(cx, mitem, item, push);
89
90    // No need to generate `ne`, the default suffices, and not generating it is
91    // faster.
92    let methods = vec![MethodDef {
93        name: sym::eq,
94        generics: Bounds::empty(),
95        explicit_self: true,
96        nonself_args: vec![(self_ref(), sym::other)],
97        ret_ty: Path(path_local!(bool)),
98        attributes: thin_vec![cx.attr_word(sym::inline, span)],
99        fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
100        combine_substructure: combine_substructure(Box::new(|a, b, c| cs_eq(a, b, c))),
101    }];
102
103    let trait_def = TraitDef {
104        span,
105        path: path_std!(cmp::PartialEq),
106        skip_path_as_bound: false,
107        needs_copy_as_bound_if_packed: true,
108        additional_bounds: Vec::new(),
109        supports_unions: false,
110        methods,
111        associated_types: Vec::new(),
112        is_const,
113    };
114    trait_def.expand(cx, mitem, item, push)
115}