// 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/>. #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) {} std::vector<std::unique_ptr<AST::Item>> DerivePartialEq::go (Item &item) { item.accept_vis (*this); return std::move (expanded); } std::vector<std::unique_ptr<Item>> DerivePartialEq::partialeq_impls ( 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 speq = builder.type_path (LangItem::Kind::STRUCTURAL_PEQ); auto trait_items = vec (std::move (eq_fn)); // no extra bound on StructuralPeq auto peq_generics = setup_impl_generics (name, type_generics, builder.trait_bound (eq)); auto speq_generics = setup_impl_generics (name, type_generics); auto peq = builder.trait_impl (eq, std::move (peq_generics.self_type), std::move (trait_items), std::move (peq_generics.impl)); auto structural_peq = builder.trait_impl (speq, std::move (speq_generics.self_type), {}, std::move (speq_generics.impl)); return vec (std::move (peq), std::move (structural_peq)); } 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 = partialeq_impls (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 = partialeq_impls (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> (); auto type_name = item.get_identifier ().as_string (); for (auto &variant : item.get_variants ()) { auto variant_path = builder.variant_path (type_name, 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), type_name); expanded = partialeq_impls (std::move (fn), type_name, 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