diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /compiler/rustc_builtin_macros/src/deriving/cmp | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
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) +} |