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| { 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) }