summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
blob: 0141b337726214877c66d5253825f3ed46a7780d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
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)
}