]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_ext/deriving/cmp/partial_eq.rs
Rollup merge of #65192 - estebank:restrict-bound, r=matthewjasper
[rust.git] / src / libsyntax_ext / deriving / cmp / partial_eq.rs
1 use crate::deriving::{path_local, path_std};
2 use crate::deriving::generic::*;
3 use crate::deriving::generic::ty::*;
4
5 use syntax::ast::{BinOpKind, Expr, MetaItem};
6 use syntax_expand::base::{Annotatable, ExtCtxt, SpecialDerives};
7 use syntax::ptr::P;
8 use syntax::symbol::sym;
9 use syntax_pos::Span;
10
11 pub fn expand_deriving_partial_eq(cx: &mut ExtCtxt<'_>,
12                                   span: Span,
13                                   mitem: &MetaItem,
14                                   item: &Annotatable,
15                                   push: &mut dyn FnMut(Annotatable)) {
16     cx.resolver.add_derives(cx.current_expansion.id.expn_data().parent, SpecialDerives::PARTIAL_EQ);
17
18     // structures are equal if all fields are equal, and non equal, if
19     // any fields are not equal or if the enum variants are different
20     fn cs_op(cx: &mut ExtCtxt<'_>,
21              span: Span,
22              substr: &Substructure<'_>,
23              op: BinOpKind,
24              combiner: BinOpKind,
25              base: bool)
26              -> P<Expr>
27     {
28         let op = |cx: &mut ExtCtxt<'_>, span: Span, self_f: P<Expr>, other_fs: &[P<Expr>]| {
29             let other_f = match other_fs {
30                 [o_f] => o_f,
31                 _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialEq)`"),
32             };
33
34             cx.expr_binary(span, op, self_f, other_f.clone())
35         };
36
37         cs_fold1(true, // use foldl
38             |cx, span, subexpr, self_f, other_fs| {
39                 let eq = op(cx, span, self_f, other_fs);
40                 cx.expr_binary(span, combiner, subexpr, eq)
41             },
42             |cx, args| {
43                 match args {
44                     Some((span, self_f, other_fs)) => {
45                         // Special-case the base case to generate cleaner code.
46                         op(cx, span, self_f, other_fs)
47                     }
48                     None => cx.expr_bool(span, base),
49                 }
50             },
51             Box::new(|cx, span, _, _| cx.expr_bool(span, !base)),
52             cx,
53             span,
54             substr)
55     }
56
57     fn cs_eq(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P<Expr> {
58         cs_op(cx, span, substr, BinOpKind::Eq, BinOpKind::And, true)
59     }
60     fn cs_ne(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P<Expr> {
61         cs_op(cx, span, substr, BinOpKind::Ne, BinOpKind::Or, false)
62     }
63
64     macro_rules! md {
65         ($name:expr, $f:ident) => { {
66             let inline = cx.meta_word(span, sym::inline);
67             let attrs = vec![cx.attribute(inline)];
68             MethodDef {
69                 name: $name,
70                 generics: LifetimeBounds::empty(),
71                 explicit_self: borrowed_explicit_self(),
72                 args: vec![(borrowed_self(), "other")],
73                 ret_ty: Literal(path_local!(bool)),
74                 attributes: attrs,
75                 is_unsafe: false,
76                 unify_fieldless_variants: true,
77                 combine_substructure: combine_substructure(Box::new(|a, b, c| {
78                     $f(a, b, c)
79                 }))
80             }
81         } }
82     }
83
84     // avoid defining `ne` if we can
85     // c-like enums, enums without any fields and structs without fields
86     // can safely define only `eq`.
87     let mut methods = vec![md!("eq", cs_eq)];
88     if !is_type_without_fields(item) {
89         methods.push(md!("ne", cs_ne));
90     }
91
92     let trait_def = TraitDef {
93         span,
94         attributes: Vec::new(),
95         path: path_std!(cx, cmp::PartialEq),
96         additional_bounds: Vec::new(),
97         generics: LifetimeBounds::empty(),
98         is_unsafe: false,
99         supports_unions: false,
100         methods,
101         associated_types: Vec::new(),
102     };
103     trait_def.expand(cx, mitem, item, push)
104 }