]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
Allow more deriving on packed structs.
[rust.git] / compiler / rustc_builtin_macros / src / deriving / cmp / partial_eq.rs
1 use crate::deriving::generic::ty::*;
2 use crate::deriving::generic::*;
3 use crate::deriving::{path_local, path_std};
4 use rustc_ast::ptr::P;
5 use rustc_ast::{BinOpKind, BorrowKind, Expr, ExprKind, MetaItem, Mutability};
6 use rustc_expand::base::{Annotatable, ExtCtxt};
7 use rustc_span::symbol::sym;
8 use rustc_span::Span;
9 use thin_vec::thin_vec;
10
11 pub fn expand_deriving_partial_eq(
12     cx: &mut 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: &mut 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.span_bug(field.span, "not exactly 2 arguments in `derive(PartialEq)`");
30                     };
31
32                     // We received `&T` arguments. Convert them to `T` by
33                     // stripping `&` or adding `*`. This isn't necessary for
34                     // type checking, but it results in much better error
35                     // messages if something goes wrong.
36                     let convert = |expr: &P<Expr>| {
37                         if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) =
38                             &expr.kind
39                         {
40                             inner.clone()
41                         } else {
42                             cx.expr_deref(field.span, expr.clone())
43                         }
44                     };
45                     cx.expr_binary(
46                         field.span,
47                         BinOpKind::Eq,
48                         convert(&field.self_expr),
49                         convert(other_expr),
50                     )
51                 }
52                 CsFold::Combine(span, expr1, expr2) => {
53                     cx.expr_binary(span, BinOpKind::And, expr1, expr2)
54                 }
55                 CsFold::Fieldless => cx.expr_bool(span, base),
56             },
57         );
58         BlockOrExpr::new_expr(expr)
59     }
60
61     super::inject_impl_of_structural_trait(
62         cx,
63         span,
64         item,
65         path_std!(marker::StructuralPartialEq),
66         push,
67     );
68
69     // No need to generate `ne`, the default suffices, and not generating it is
70     // faster.
71     let attrs = thin_vec![cx.attr_word(sym::inline, span)];
72     let methods = vec![MethodDef {
73         name: sym::eq,
74         generics: Bounds::empty(),
75         explicit_self: true,
76         nonself_args: vec![(self_ref(), sym::other)],
77         ret_ty: Path(path_local!(bool)),
78         attributes: attrs,
79         fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
80         combine_substructure: combine_substructure(Box::new(|a, b, c| cs_eq(a, b, c))),
81     }];
82
83     let trait_def = TraitDef {
84         span,
85         path: path_std!(cmp::PartialEq),
86         skip_path_as_bound: false,
87         needs_copy_as_bound_if_packed: true,
88         additional_bounds: Vec::new(),
89         supports_unions: false,
90         methods,
91         associated_types: Vec::new(),
92         is_const,
93     };
94     trait_def.expand(cx, mitem, item, push)
95 }