diff options
author | Arthur Cohen <arthur.cohen@embecosm.com> | 2025-01-30 14:41:11 +0100 |
---|---|---|
committer | Arthur Cohen <arthur.cohen@embecosm.com> | 2025-03-24 13:07:08 +0100 |
commit | e98f95a473dd02bd335d9ca1204e87c7be514d5b (patch) | |
tree | 7567a3a305deeb054b8bca258fdb7a73865d6354 /gcc/rust/expand | |
parent | 20580f75496c1071c52273e2dc6cc289304206fa (diff) | |
download | gcc-e98f95a473dd02bd335d9ca1204e87c7be514d5b.zip gcc-e98f95a473dd02bd335d9ca1204e87c7be514d5b.tar.gz gcc-e98f95a473dd02bd335d9ca1204e87c7be514d5b.tar.bz2 |
gccrs: derive(PartialEq): Add partial implementation
We are still missing some deriving for enums, as part of our codegen and nameres for rebinding struct
field patterns is missing.
gcc/rust/ChangeLog:
* expand/rust-derive-partial-eq.cc: New file.
* expand/rust-derive-partial-eq.h: New file.
* expand/rust-derive.cc (DeriveVisitor::derive): Call them.
* Make-lang.in: Compile them.
gcc/testsuite/ChangeLog:
* rust/compile/derive-eq-invalid.rs: Mark PartialEq def as a lang item.
* rust/compile/derive-partialeq1.rs: New test.
* rust/execute/torture/derive-partialeq1.rs: New test.
* rust/compile/nr2/exclude: Exclude all of them.
Diffstat (limited to 'gcc/rust/expand')
-rw-r--r-- | gcc/rust/expand/rust-derive-partial-eq.cc | 308 | ||||
-rw-r--r-- | gcc/rust/expand/rust-derive-partial-eq.h | 81 | ||||
-rw-r--r-- | gcc/rust/expand/rust-derive.cc | 2 |
3 files changed, 391 insertions, 0 deletions
diff --git a/gcc/rust/expand/rust-derive-partial-eq.cc b/gcc/rust/expand/rust-derive-partial-eq.cc new file mode 100644 index 0000000..6f7ef7d --- /dev/null +++ b/gcc/rust/expand/rust-derive-partial-eq.cc @@ -0,0 +1,308 @@ +// Copyright (C) 2020-2024 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +#include "rust-derive-partial-eq.h" +#include "rust-ast.h" +#include "rust-expr.h" +#include "rust-item.h" +#include "rust-operators.h" +#include "rust-path.h" +#include "rust-pattern.h" +#include "rust-system.h" + +namespace Rust { +namespace AST { +DerivePartialEq::DerivePartialEq (location_t loc) + : DeriveVisitor (loc), expanded (nullptr) +{} + +std::unique_ptr<AST::Item> +DerivePartialEq::go (Item &item) +{ + item.accept_vis (*this); + + rust_assert (expanded); + + return std::move (expanded); +} + +std::unique_ptr<Item> +DerivePartialEq::partial_eq_impl ( + std::unique_ptr<AssociatedItem> &&eq_fn, std::string name, + const std::vector<std::unique_ptr<GenericParam>> &type_generics) +{ + auto eq = builder.type_path (LangItem::Kind::EQ); + + auto trait_items = vec (std::move (eq_fn)); + + auto generics + = setup_impl_generics (name, type_generics, builder.trait_bound (eq)); + + return builder.trait_impl (eq, std::move (generics.self_type), + std::move (trait_items), + std::move (generics.impl)); +} + +std::unique_ptr<AssociatedItem> +DerivePartialEq::eq_fn (std::unique_ptr<Expr> &&cmp_expression, + std::string type_name) +{ + auto block = builder.block (tl::nullopt, std::move (cmp_expression)); + + auto self_type + = std::unique_ptr<TypeNoBounds> (new TypePath (builder.type_path ("Self"))); + + auto params + = vec (builder.self_ref_param (), + builder.function_param (builder.identifier_pattern ("other"), + builder.reference_type ( + std::move (self_type)))); + + return builder.function ("eq", std::move (params), + builder.single_type_path ("bool"), + std::move (block)); +} + +DerivePartialEq::SelfOther +DerivePartialEq::tuple_indexes (int idx) +{ + return SelfOther{ + builder.tuple_idx ("self", idx), + builder.tuple_idx ("other", idx), + }; +} + +DerivePartialEq::SelfOther +DerivePartialEq::field_acccesses (const std::string &field_name) +{ + return SelfOther{ + builder.field_access (builder.identifier ("self"), field_name), + builder.field_access (builder.identifier ("other"), field_name), + }; +} + +std::unique_ptr<Expr> +DerivePartialEq::build_eq_expression ( + std::vector<SelfOther> &&field_expressions) +{ + // for unit structs or empty tuples, this is always true + if (field_expressions.empty ()) + return builder.literal_bool (true); + + auto cmp_expression + = builder.comparison_expr (std::move (field_expressions.at (0).self_expr), + std::move (field_expressions.at (0).other_expr), + ComparisonOperator::EQUAL); + + for (size_t i = 1; i < field_expressions.size (); i++) + { + auto tmp = builder.comparison_expr ( + std::move (field_expressions.at (i).self_expr), + std::move (field_expressions.at (i).other_expr), + ComparisonOperator::EQUAL); + + cmp_expression + = builder.boolean_operation (std::move (cmp_expression), + std::move (tmp), + LazyBooleanOperator::LOGICAL_AND); + } + + return cmp_expression; +} + +void +DerivePartialEq::visit_tuple (TupleStruct &item) +{ + auto type_name = item.get_struct_name ().as_string (); + auto fields = std::vector<SelfOther> (); + + for (size_t idx = 0; idx < item.get_fields ().size (); idx++) + fields.emplace_back (tuple_indexes (idx)); + + auto fn = eq_fn (build_eq_expression (std::move (fields)), type_name); + + expanded + = partial_eq_impl (std::move (fn), type_name, item.get_generic_params ()); +} + +void +DerivePartialEq::visit_struct (StructStruct &item) +{ + auto type_name = item.get_struct_name ().as_string (); + auto fields = std::vector<SelfOther> (); + + for (auto &field : item.get_fields ()) + fields.emplace_back ( + field_acccesses (field.get_field_name ().as_string ())); + + auto fn = eq_fn (build_eq_expression (std::move (fields)), type_name); + + expanded + = partial_eq_impl (std::move (fn), type_name, item.get_generic_params ()); +} + +MatchCase +DerivePartialEq::match_enum_identifier ( + PathInExpression variant_path, const std::unique_ptr<EnumItem> &variant) +{ + auto inner_ref_patterns + = vec (builder.ref_pattern ( + std::unique_ptr<Pattern> (new PathInExpression (variant_path))), + builder.ref_pattern ( + std::unique_ptr<Pattern> (new PathInExpression (variant_path)))); + + auto tuple_items = std::make_unique<TuplePatternItemsMultiple> ( + std::move (inner_ref_patterns)); + + auto pattern = std::make_unique<TuplePattern> (std::move (tuple_items), loc); + + return builder.match_case (std::move (pattern), builder.literal_bool (true)); +} + +MatchCase +DerivePartialEq::match_enum_tuple (PathInExpression variant_path, + const EnumItemTuple &variant) +{ + auto self_patterns = std::vector<std::unique_ptr<Pattern>> (); + auto other_patterns = std::vector<std::unique_ptr<Pattern>> (); + + auto self_other_exprs = std::vector<SelfOther> (); + + for (size_t i = 0; i < variant.get_tuple_fields ().size (); i++) + { + // The patterns we're creating for each field are `self_<i>` and + // `other_<i>` where `i` is the index of the field. It doesn't actually + // matter what we use, as long as it's ordered, unique, and that we can + // reuse it in the match case's return expression to check that they are + // equal. + + auto self_pattern_str = "__self_" + std::to_string (i); + auto other_pattern_str = "__other_" + std::to_string (i); + + rust_debug ("]ARTHUR[ %s", self_pattern_str.c_str ()); + + self_patterns.emplace_back ( + builder.identifier_pattern (self_pattern_str)); + other_patterns.emplace_back ( + builder.identifier_pattern (other_pattern_str)); + + self_other_exprs.emplace_back (SelfOther{ + builder.identifier (self_pattern_str), + builder.identifier (other_pattern_str), + }); + } + + auto self_pattern_items = std::unique_ptr<TupleStructItems> ( + new TupleStructItemsNoRange (std::move (self_patterns))); + auto other_pattern_items = std::unique_ptr<TupleStructItems> ( + new TupleStructItemsNoRange (std::move (other_patterns))); + + auto self_pattern = std::unique_ptr<Pattern> ( + new ReferencePattern (std::unique_ptr<Pattern> (new TupleStructPattern ( + variant_path, std::move (self_pattern_items))), + false, false, loc)); + auto other_pattern = std::unique_ptr<Pattern> ( + new ReferencePattern (std::unique_ptr<Pattern> (new TupleStructPattern ( + variant_path, std::move (other_pattern_items))), + false, false, loc)); + + auto tuple_items = std::make_unique<TuplePatternItemsMultiple> ( + vec (std::move (self_pattern), std::move (other_pattern))); + + auto pattern = std::make_unique<TuplePattern> (std::move (tuple_items), loc); + + auto expr = build_eq_expression (std::move (self_other_exprs)); + + return builder.match_case (std::move (pattern), std::move (expr)); +} + +MatchCase +DerivePartialEq::match_enum_struct (PathInExpression variant_path, + const EnumItemStruct &variant) +{ + // NOTE: We currently do not support compiling struct patterns where an + // identifier is assigned a new pattern, e.g. Bloop { f0: x } + // This is what we should be using to compile PartialEq for enum struct + // variants, as we need to be comparing the field of each instance meaning we + // need to give two different names to two different instances of the same + // field. We cannot just use the field's name like we do when deriving + // `Clone`. + + rust_unreachable (); +} + +void +DerivePartialEq::visit_enum (Enum &item) +{ + auto cases = std::vector<MatchCase> (); + + for (auto &variant : item.get_variants ()) + { + auto variant_path + = builder.variant_path (item.get_identifier ().as_string (), + variant->get_identifier ().as_string ()); + + switch (variant->get_enum_item_kind ()) + { + case EnumItem::Kind::Identifier: + case EnumItem::Kind::Discriminant: + cases.emplace_back (match_enum_identifier (variant_path, variant)); + break; + case EnumItem::Kind::Tuple: + cases.emplace_back ( + match_enum_tuple (variant_path, + static_cast<EnumItemTuple &> (*variant))); + break; + case EnumItem::Kind::Struct: + rust_sorry_at ( + item.get_locus (), + "cannot derive(PartialEq) for enum struct variants yet"); + break; + } + } + + // NOTE: Mention using discriminant_value and skipping that last case, and + // instead skipping all identifiers/discriminant enum items and returning + // `true` in the wildcard case + + // In case the two instances of `Self` don't have the same discriminant, + // automatically return false. + cases.emplace_back ( + builder.match_case (builder.wildcard (), builder.literal_bool (false))); + + auto match + = builder.match (builder.tuple (vec (builder.identifier ("self"), + builder.identifier ("other"))), + std::move (cases)); + + auto fn = eq_fn (std::move (match), item.get_identifier ().as_string ()); + + expanded + = partial_eq_impl (std::move (fn), item.get_identifier ().as_string (), + item.get_generic_params ()); +} + +void +DerivePartialEq::visit_union (Union &item) +{ + rust_error_at (item.get_locus (), + "derive(PartialEq) cannot be used on unions"); +} + +} // namespace AST +} // namespace Rust diff --git a/gcc/rust/expand/rust-derive-partial-eq.h b/gcc/rust/expand/rust-derive-partial-eq.h new file mode 100644 index 0000000..2bc18d2 --- /dev/null +++ b/gcc/rust/expand/rust-derive-partial-eq.h @@ -0,0 +1,81 @@ +// Copyright (C) 2025 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +#ifndef RUST_DERIVE_PARTIAL_EQ_H +#define RUST_DERIVE_PARTIAL_EQ_H + +#include "rust-derive.h" +#include "rust-path.h" + +namespace Rust { +namespace AST { + +class DerivePartialEq : DeriveVisitor +{ +public: + DerivePartialEq (location_t loc); + + std::unique_ptr<AST::Item> go (Item &item); + +private: + std::unique_ptr<Item> expanded; + + std::unique_ptr<Item> partial_eq_impl ( + std::unique_ptr<AssociatedItem> &&eq_fn, std::string name, + const std::vector<std::unique_ptr<GenericParam>> &type_generics); + + std::unique_ptr<AssociatedItem> eq_fn (std::unique_ptr<Expr> &&cmp_expression, + std::string type_name); + + /** + * A pair of two expressions from each instance being compared. E.g. this + * could be `self.0` and `other.0`, or `self.field` and `other.field` + */ + struct SelfOther + { + std::unique_ptr<Expr> self_expr; + std::unique_ptr<Expr> other_expr; + }; + + SelfOther tuple_indexes (int idx); + SelfOther field_acccesses (const std::string &field_name); + + /** + * Build a suite of equality arithmetic expressions chained together by a + * boolean AND operator + */ + std::unique_ptr<Expr> + build_eq_expression (std::vector<SelfOther> &&field_expressions); + + MatchCase match_enum_identifier (PathInExpression variant_path, + const std::unique_ptr<EnumItem> &variant); + MatchCase match_enum_tuple (PathInExpression variant_path, + const EnumItemTuple &variant); + MatchCase match_enum_struct (PathInExpression variant_path, + const EnumItemStruct &variant); + + virtual void visit_struct (StructStruct &item); + virtual void visit_tuple (TupleStruct &item); + virtual void visit_enum (Enum &item); + virtual void visit_union (Union &item); +}; + +} // namespace AST +} // namespace Rust + +#endif // ! RUST_DERIVE_PARTIAL_EQ_H diff --git a/gcc/rust/expand/rust-derive.cc b/gcc/rust/expand/rust-derive.cc index 8289a12..1f89502 100644 --- a/gcc/rust/expand/rust-derive.cc +++ b/gcc/rust/expand/rust-derive.cc @@ -22,6 +22,7 @@ #include "rust-derive-debug.h" #include "rust-derive-default.h" #include "rust-derive-eq.h" +#include "rust-derive-partial-eq.h" namespace Rust { namespace AST { @@ -51,6 +52,7 @@ DeriveVisitor::derive (Item &item, const Attribute &attr, case BuiltinMacro::Eq: return DeriveEq (attr.get_locus ()).go (item); case BuiltinMacro::PartialEq: + return DerivePartialEq (attr.get_locus ()).go (item); case BuiltinMacro::Ord: case BuiltinMacro::PartialOrd: case BuiltinMacro::Hash: |