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