From: Arthur Cohen <arthur.co...@embecosm.com>

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.
---
 gcc/rust/Make-lang.in                         |   1 +
 gcc/rust/expand/rust-derive-partial-eq.cc     | 308 ++++++++++++++++++
 gcc/rust/expand/rust-derive-partial-eq.h      |  81 +++++
 gcc/rust/expand/rust-derive.cc                |   2 +
 .../rust/compile/derive-eq-invalid.rs         |   9 +-
 .../rust/compile/derive-partialeq1.rs         |  59 ++++
 gcc/testsuite/rust/compile/nr2/exclude        |   2 +
 .../rust/execute/torture/derive-partialeq1.rs |  61 ++++
 8 files changed, 519 insertions(+), 4 deletions(-)
 create mode 100644 gcc/rust/expand/rust-derive-partial-eq.cc
 create mode 100644 gcc/rust/expand/rust-derive-partial-eq.h
 create mode 100644 gcc/testsuite/rust/compile/derive-partialeq1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/derive-partialeq1.rs

diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in
index 83ffc47ce28..5e13c6cfc3f 100644
--- a/gcc/rust/Make-lang.in
+++ b/gcc/rust/Make-lang.in
@@ -98,6 +98,7 @@ GRS_OBJS = \
     rust/rust-derive-copy.o \
     rust/rust-derive-debug.o \
     rust/rust-derive-default.o \
+    rust/rust-derive-partial-eq.o \
     rust/rust-derive-eq.o \
     rust/rust-proc-macro.o \
     rust/rust-macro-invoc-lexer.o \
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 00000000000..6f7ef7d8780
--- /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 00000000000..2bc18d2b98a
--- /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 8289a120f04..1f89502f639 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:
diff --git a/gcc/testsuite/rust/compile/derive-eq-invalid.rs 
b/gcc/testsuite/rust/compile/derive-eq-invalid.rs
index 017241db86d..0c4d48ef6ea 100644
--- a/gcc/testsuite/rust/compile/derive-eq-invalid.rs
+++ b/gcc/testsuite/rust/compile/derive-eq-invalid.rs
@@ -1,5 +1,6 @@
 mod core {
     mod cmp {
+        #[lang = "eq"]
         pub trait PartialEq<Rhs: ?Sized = Self> {
             fn eq(&self, other: &Rhs) -> bool;
 
@@ -14,11 +15,11 @@ mod core {
     }
 }
 
-// #[lang = "phantom_data"]
-// struct PhantomData<T>;
+#[lang = "phantom_data"]
+struct PhantomData<T>;
 
-// #[lang = "sized"]
-// trait Sized {}
+#[lang = "sized"]
+trait Sized {}
 
 #[derive(PartialEq)]
 struct NotEq;
diff --git a/gcc/testsuite/rust/compile/derive-partialeq1.rs 
b/gcc/testsuite/rust/compile/derive-partialeq1.rs
new file mode 100644
index 00000000000..71513241929
--- /dev/null
+++ b/gcc/testsuite/rust/compile/derive-partialeq1.rs
@@ -0,0 +1,59 @@
+#![feature(intrinsics)]
+
+#[lang = "sized"]
+trait Sized {}
+
+#[lang = "copy"]
+trait Copy {}
+
+#[lang = "eq"]
+pub trait PartialEq<Rhs: ?Sized = Self> {
+    /// This method tests for `self` and `other` values to be equal, and is 
used
+    /// by `==`.
+    #[must_use]
+    #[stable(feature = "rust1", since = "1.0.0")]
+    fn eq(&self, other: &Rhs) -> bool;
+
+    /// This method tests for `!=`.
+    #[inline]
+    #[must_use]
+    #[stable(feature = "rust1", since = "1.0.0")]
+    fn ne(&self, other: &Rhs) -> bool {
+        !self.eq(other)
+    }
+}
+
+#[derive(PartialEq, Copy)] // { dg-warning "unused name" }
+struct Foo;
+
+#[derive(PartialEq)]
+struct Bar(Foo);
+
+#[derive(PartialEq)]
+struct Baz { _inner: Foo }
+
+extern "C" {
+    fn puts(s: *const i8);
+}
+
+fn print(b: bool) {
+    if b {
+        unsafe { puts("true" as *const str as *const i8) }
+    } else {
+        unsafe { puts("false" as *const str as *const i8) }
+    }
+}
+
+fn main() -> i32 {
+    let x = Foo;
+
+    let b1 = x == Foo;
+    let b2 = Bar(x) != Bar(Foo);
+    let b3 = Baz { _inner: Foo } != Baz { _inner: x };
+
+    print(b1);
+    print(b2);
+    print(b3);
+
+    0
+}
diff --git a/gcc/testsuite/rust/compile/nr2/exclude 
b/gcc/testsuite/rust/compile/nr2/exclude
index 6eff293f0aa..26b830eab7e 100644
--- a/gcc/testsuite/rust/compile/nr2/exclude
+++ b/gcc/testsuite/rust/compile/nr2/exclude
@@ -75,4 +75,6 @@ issue-3402-1.rs
 for-loop1.rs
 for-loop2.rs
 issue-3403.rs
+derive-eq-invalid.rs
+derive-partialeq1.rs
 # please don't delete the trailing newline
diff --git a/gcc/testsuite/rust/execute/torture/derive-partialeq1.rs 
b/gcc/testsuite/rust/execute/torture/derive-partialeq1.rs
new file mode 100644
index 00000000000..4d5124e85cf
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/derive-partialeq1.rs
@@ -0,0 +1,61 @@
+// { dg-output "true\r*\nfalse\r*\nfalse\r*\n" }
+
+#![feature(intrinsics)]
+
+#[lang = "sized"]
+trait Sized {}
+
+#[lang = "copy"]
+trait Copy {}
+
+#[lang = "eq"]
+pub trait PartialEq<Rhs: ?Sized = Self> {
+    /// This method tests for `self` and `other` values to be equal, and is 
used
+    /// by `==`.
+    #[must_use]
+    #[stable(feature = "rust1", since = "1.0.0")]
+    fn eq(&self, other: &Rhs) -> bool;
+
+    /// This method tests for `!=`.
+    #[inline]
+    #[must_use]
+    #[stable(feature = "rust1", since = "1.0.0")]
+    fn ne(&self, other: &Rhs) -> bool {
+        !self.eq(other)
+    }
+}
+
+#[derive(PartialEq, Copy)] // { dg-warning "unused name" }
+struct Foo;
+
+#[derive(PartialEq)]
+struct Bar(Foo);
+
+#[derive(PartialEq)]
+struct Baz { _inner: Foo }
+
+extern "C" {
+    fn puts(s: *const i8);
+}
+
+fn print(b: bool) {
+    if b {
+        unsafe { puts("true" as *const str as *const i8) }
+    } else {
+        unsafe { puts("false" as *const str as *const i8) }
+    }
+}
+
+fn main() -> i32 {
+    let x = Foo;
+
+    let b1 = x == Foo;
+    let b2 = Bar(x) != Bar(Foo);
+    let b3 = Baz { _inner: Foo } != Baz { _inner: x };
+
+    print(b1);
+    print(b2);
+    print(b3);
+
+    0
+}
-- 
2.45.2

Reply via email to