summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_builtin_macros/src/deriving/cmp
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_builtin_macros/src/deriving/cmp')
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs90
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs79
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs110
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs88
4 files changed, 367 insertions, 0 deletions
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
new file mode 100644
index 000000000..4e798bf6a
--- /dev/null
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
@@ -0,0 +1,90 @@
+use crate::deriving::generic::ty::*;
+use crate::deriving::generic::*;
+use crate::deriving::path_std;
+
+use rustc_ast::{self as ast, MetaItem};
+use rustc_data_structures::fx::FxHashSet;
+use rustc_expand::base::{Annotatable, ExtCtxt};
+use rustc_span::symbol::{sym, Ident};
+use rustc_span::Span;
+
+pub fn expand_deriving_eq(
+ cx: &mut ExtCtxt<'_>,
+ span: Span,
+ mitem: &MetaItem,
+ item: &Annotatable,
+ push: &mut dyn FnMut(Annotatable),
+) {
+ let span = cx.with_def_site_ctxt(span);
+ let inline = cx.meta_word(span, sym::inline);
+ let hidden = rustc_ast::attr::mk_nested_word_item(Ident::new(sym::hidden, span));
+ let doc = rustc_ast::attr::mk_list_item(Ident::new(sym::doc, span), vec![hidden]);
+ let no_coverage = cx.meta_word(span, sym::no_coverage);
+ let attrs = vec![cx.attribute(inline), cx.attribute(doc), cx.attribute(no_coverage)];
+ let trait_def = TraitDef {
+ span,
+ attributes: Vec::new(),
+ path: path_std!(cmp::Eq),
+ additional_bounds: Vec::new(),
+ generics: Bounds::empty(),
+ supports_unions: true,
+ methods: vec![MethodDef {
+ name: sym::assert_receiver_is_total_eq,
+ generics: Bounds::empty(),
+ explicit_self: true,
+ nonself_args: vec![],
+ ret_ty: Unit,
+ attributes: attrs,
+ unify_fieldless_variants: true,
+ combine_substructure: combine_substructure(Box::new(|a, b, c| {
+ cs_total_eq_assert(a, b, c)
+ })),
+ }],
+ associated_types: Vec::new(),
+ };
+
+ super::inject_impl_of_structural_trait(cx, span, item, path_std!(marker::StructuralEq), push);
+
+ trait_def.expand_ext(cx, mitem, item, push, true)
+}
+
+fn cs_total_eq_assert(
+ cx: &mut ExtCtxt<'_>,
+ trait_span: Span,
+ substr: &Substructure<'_>,
+) -> BlockOrExpr {
+ let mut stmts = Vec::new();
+ let mut seen_type_names = FxHashSet::default();
+ let mut process_variant = |variant: &ast::VariantData| {
+ for field in variant.fields() {
+ // This basic redundancy checking only prevents duplication of
+ // assertions like `AssertParamIsEq<Foo>` where the type is a
+ // simple name. That's enough to get a lot of cases, though.
+ if let Some(name) = field.ty.kind.is_simple_path() && !seen_type_names.insert(name) {
+ // Already produced an assertion for this type.
+ } else {
+ // let _: AssertParamIsEq<FieldTy>;
+ super::assert_ty_bounds(
+ cx,
+ &mut stmts,
+ field.ty.clone(),
+ field.span,
+ &[sym::cmp, sym::AssertParamIsEq],
+ );
+ }
+ }
+ };
+
+ match *substr.fields {
+ StaticStruct(vdata, ..) => {
+ process_variant(vdata);
+ }
+ StaticEnum(enum_def, ..) => {
+ for variant in &enum_def.variants {
+ process_variant(&variant.data);
+ }
+ }
+ _ => cx.span_bug(trait_span, "unexpected substructure in `derive(Eq)`"),
+ }
+ BlockOrExpr::new_stmts(stmts)
+}
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
new file mode 100644
index 000000000..1612be862
--- /dev/null
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
@@ -0,0 +1,79 @@
+use crate::deriving::generic::ty::*;
+use crate::deriving::generic::*;
+use crate::deriving::path_std;
+
+use rustc_ast::MetaItem;
+use rustc_expand::base::{Annotatable, ExtCtxt};
+use rustc_span::symbol::{sym, Ident};
+use rustc_span::Span;
+
+pub fn expand_deriving_ord(
+ cx: &mut ExtCtxt<'_>,
+ span: Span,
+ mitem: &MetaItem,
+ item: &Annotatable,
+ push: &mut dyn FnMut(Annotatable),
+) {
+ let inline = cx.meta_word(span, sym::inline);
+ let attrs = vec![cx.attribute(inline)];
+ let trait_def = TraitDef {
+ span,
+ attributes: Vec::new(),
+ path: path_std!(cmp::Ord),
+ additional_bounds: Vec::new(),
+ generics: Bounds::empty(),
+ supports_unions: false,
+ methods: vec![MethodDef {
+ name: sym::cmp,
+ generics: Bounds::empty(),
+ explicit_self: true,
+ nonself_args: vec![(self_ref(), sym::other)],
+ ret_ty: Path(path_std!(cmp::Ordering)),
+ attributes: attrs,
+ unify_fieldless_variants: true,
+ combine_substructure: combine_substructure(Box::new(|a, b, c| cs_cmp(a, b, c))),
+ }],
+ associated_types: Vec::new(),
+ };
+
+ trait_def.expand(cx, mitem, item, push)
+}
+
+pub fn cs_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr {
+ let test_id = Ident::new(sym::cmp, span);
+ let equal_path = cx.path_global(span, cx.std_path(&[sym::cmp, sym::Ordering, sym::Equal]));
+ let cmp_path = cx.std_path(&[sym::cmp, sym::Ord, sym::cmp]);
+
+ // Builds:
+ //
+ // match ::core::cmp::Ord::cmp(&self.x, &other.x) {
+ // ::std::cmp::Ordering::Equal =>
+ // ::core::cmp::Ord::cmp(&self.y, &other.y),
+ // cmp => cmp,
+ // }
+ let expr = cs_fold(
+ // foldr nests the if-elses correctly, leaving the first field
+ // as the outermost one, and the last as the innermost.
+ false,
+ 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(Ord)`");
+ };
+ let args = vec![field.self_expr.clone(), other_expr.clone()];
+ cx.expr_call_global(field.span, cmp_path.clone(), args)
+ }
+ CsFold::Combine(span, expr1, expr2) => {
+ let eq_arm = cx.arm(span, cx.pat_path(span, equal_path.clone()), expr1);
+ let neq_arm =
+ cx.arm(span, cx.pat_ident(span, test_id), cx.expr_ident(span, test_id));
+ cx.expr_match(span, expr2, vec![eq_arm, neq_arm])
+ }
+ CsFold::Fieldless => cx.expr_path(equal_path.clone()),
+ },
+ );
+ BlockOrExpr::new_expr(expr)
+}
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
new file mode 100644
index 000000000..0141b3377
--- /dev/null
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
@@ -0,0 +1,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)
+}
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
new file mode 100644
index 000000000..2ebb01cc8
--- /dev/null
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
@@ -0,0 +1,88 @@
+use crate::deriving::generic::ty::*;
+use crate::deriving::generic::*;
+use crate::deriving::{path_std, pathvec_std};
+
+use rustc_ast::MetaItem;
+use rustc_expand::base::{Annotatable, ExtCtxt};
+use rustc_span::symbol::{sym, Ident};
+use rustc_span::Span;
+
+pub fn expand_deriving_partial_ord(
+ cx: &mut ExtCtxt<'_>,
+ span: Span,
+ mitem: &MetaItem,
+ item: &Annotatable,
+ push: &mut dyn FnMut(Annotatable),
+) {
+ let ordering_ty = Path(path_std!(cmp::Ordering));
+ let ret_ty =
+ Path(Path::new_(pathvec_std!(option::Option), vec![Box::new(ordering_ty)], PathKind::Std));
+
+ let inline = cx.meta_word(span, sym::inline);
+ let attrs = vec![cx.attribute(inline)];
+
+ let partial_cmp_def = MethodDef {
+ name: sym::partial_cmp,
+ generics: Bounds::empty(),
+ explicit_self: true,
+ nonself_args: vec![(self_ref(), sym::other)],
+ ret_ty,
+ attributes: attrs,
+ unify_fieldless_variants: true,
+ combine_substructure: combine_substructure(Box::new(|cx, span, substr| {
+ cs_partial_cmp(cx, span, substr)
+ })),
+ };
+
+ let trait_def = TraitDef {
+ span,
+ attributes: vec![],
+ path: path_std!(cmp::PartialOrd),
+ additional_bounds: vec![],
+ generics: Bounds::empty(),
+ supports_unions: false,
+ methods: vec![partial_cmp_def],
+ associated_types: Vec::new(),
+ };
+ trait_def.expand(cx, mitem, item, push)
+}
+
+pub fn cs_partial_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr {
+ let test_id = Ident::new(sym::cmp, span);
+ let equal_path = cx.path_global(span, cx.std_path(&[sym::cmp, sym::Ordering, sym::Equal]));
+ let partial_cmp_path = cx.std_path(&[sym::cmp, sym::PartialOrd, sym::partial_cmp]);
+
+ // Builds:
+ //
+ // match ::core::cmp::PartialOrd::partial_cmp(&self.x, &other.x) {
+ // ::core::option::Option::Some(::core::cmp::Ordering::Equal) =>
+ // ::core::cmp::PartialOrd::partial_cmp(&self.y, &other.y),
+ // cmp => cmp,
+ // }
+ let expr = cs_fold(
+ // foldr nests the if-elses correctly, leaving the first field
+ // as the outermost one, and the last as the innermost.
+ false,
+ 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(Ord)`");
+ };
+ let args = vec![field.self_expr.clone(), other_expr.clone()];
+ cx.expr_call_global(field.span, partial_cmp_path.clone(), args)
+ }
+ CsFold::Combine(span, expr1, expr2) => {
+ let eq_arm =
+ cx.arm(span, cx.pat_some(span, cx.pat_path(span, equal_path.clone())), expr1);
+ let neq_arm =
+ cx.arm(span, cx.pat_ident(span, test_id), cx.expr_ident(span, test_id));
+ cx.expr_match(span, expr2, vec![eq_arm, neq_arm])
+ }
+ CsFold::Fieldless => cx.expr_some(span, cx.expr_path(equal_path.clone())),
+ },
+ );
+ BlockOrExpr::new_expr(expr)
+}