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

gcc/rust/ChangeLog:

        * expand/rust-derive.cc (DeriveVisitor::derive): Call into DeriveEq.
        * expand/rust-derive-eq.cc: New file.
        * expand/rust-derive-eq.h: New file.
        * Make-lang.in: Compile them.

gcc/testsuite/ChangeLog:

        * rust/compile/derive-eq-invalid.rs: New test.
---
 gcc/rust/Make-lang.in                         |   1 +
 gcc/rust/expand/rust-derive-eq.cc             | 207 ++++++++++++++++++
 gcc/rust/expand/rust-derive-eq.h              |  82 +++++++
 gcc/rust/expand/rust-derive.cc                |   2 +
 .../rust/compile/derive-eq-invalid.rs         |  45 ++++
 5 files changed, 337 insertions(+)
 create mode 100644 gcc/rust/expand/rust-derive-eq.cc
 create mode 100644 gcc/rust/expand/rust-derive-eq.h
 create mode 100644 gcc/testsuite/rust/compile/derive-eq-invalid.rs

diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in
index 5ddad257805..83ffc47ce28 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-eq.o \
     rust/rust-proc-macro.o \
     rust/rust-macro-invoc-lexer.o \
     rust/rust-proc-macro-invoc-lexer.o \
diff --git a/gcc/rust/expand/rust-derive-eq.cc 
b/gcc/rust/expand/rust-derive-eq.cc
new file mode 100644
index 00000000000..a2a7a769065
--- /dev/null
+++ b/gcc/rust/expand/rust-derive-eq.cc
@@ -0,0 +1,207 @@
+// 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-eq.h"
+#include "rust-ast.h"
+#include "rust-expr.h"
+#include "rust-item.h"
+#include "rust-path.h"
+#include "rust-pattern.h"
+#include "rust-system.h"
+
+namespace Rust {
+namespace AST {
+
+std::unique_ptr<AssociatedItem>
+DeriveEq::assert_receiver_is_total_eq_fn (
+  std::vector<std::unique_ptr<Type>> &&types)
+{
+  auto stmts = std::vector<std::unique_ptr<Stmt>> ();
+
+  stmts.emplace_back (assert_param_is_eq ());
+
+  for (auto &&type : types)
+    stmts.emplace_back (assert_type_is_eq (std::move (type)));
+
+  auto block = std::unique_ptr<BlockExpr> (
+    new BlockExpr (std::move (stmts), nullptr, {}, {}, AST::LoopLabel::error 
(),
+                  loc, loc));
+
+  auto self = builder.self_ref_param ();
+
+  return builder.function ("assert_receiver_is_total_eq",
+                          vec (std::move (self)), {}, std::move (block));
+}
+
+std::unique_ptr<Stmt>
+DeriveEq::assert_param_is_eq ()
+{
+  auto eq_bound = std::unique_ptr<TypeParamBound> (
+    new TraitBound (builder.type_path ({"core", "cmp", "Eq"}, true), loc));
+
+  auto sized_bound = std::unique_ptr<TypeParamBound> (
+    new TraitBound (builder.type_path (LangItem::Kind::SIZED), loc, false,
+                   true /* opening_question_mark */));
+
+  auto bounds = vec (std::move (eq_bound), std::move (sized_bound));
+
+  auto assert_param_is_eq = "AssertParamIsEq";
+
+  auto t = std::unique_ptr<GenericParam> (
+    new TypeParam (Identifier ("T"), loc, std::move (bounds)));
+
+  return builder.struct_struct (
+    assert_param_is_eq, vec (std::move (t)),
+    {StructField (
+      Identifier ("_t"),
+      builder.single_generic_type_path (
+       LangItem::Kind::PHANTOM_DATA,
+       GenericArgs (
+         {}, {GenericArg::create_type (builder.single_type_path ("T"))}, {})),
+      Visibility::create_private (), loc)});
+}
+
+std::unique_ptr<Stmt>
+DeriveEq::assert_type_is_eq (std::unique_ptr<Type> &&type)
+{
+  auto assert_param_is_eq = "AssertParamIsEq";
+
+  // AssertParamIsCopy::<Self>
+  auto assert_param_is_eq_ty
+    = std::unique_ptr<TypePathSegment> (new TypePathSegmentGeneric (
+      PathIdentSegment (assert_param_is_eq, loc), false,
+      GenericArgs ({}, {GenericArg::create_type (std::move (type))}, {}, loc),
+      loc));
+
+  // TODO: Improve this, it's really ugly
+  auto type_paths = std::vector<std::unique_ptr<TypePathSegment>> ();
+  type_paths.emplace_back (std::move (assert_param_is_eq_ty));
+
+  auto full_path
+    = std::unique_ptr<Type> (new TypePath ({std::move (type_paths)}, loc));
+
+  return builder.let (builder.wildcard (), std::move (full_path));
+}
+
+std::unique_ptr<Item>
+DeriveEq::eq_impl (
+  std::unique_ptr<AssociatedItem> &&fn, std::string name,
+  const std::vector<std::unique_ptr<GenericParam>> &type_generics)
+{
+  auto eq = builder.type_path ({"core", "cmp", "Eq"}, true);
+
+  auto trait_items = vec (std::move (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));
+}
+
+DeriveEq::DeriveEq (location_t loc) : DeriveVisitor (loc), expanded (nullptr) 
{}
+
+std::unique_ptr<AST::Item>
+DeriveEq::go (Item &item)
+{
+  item.accept_vis (*this);
+
+  rust_assert (expanded);
+
+  return std::move (expanded);
+}
+
+void
+DeriveEq::visit_tuple (TupleStruct &item)
+{
+  auto types = std::vector<std::unique_ptr<Type>> ();
+
+  for (auto &field : item.get_fields ())
+    types.emplace_back (field.get_field_type ().clone_type ());
+
+  expanded
+    = eq_impl (assert_receiver_is_total_eq_fn (std::move (types)),
+              item.get_identifier ().as_string (), item.get_generic_params ());
+}
+
+void
+DeriveEq::visit_struct (StructStruct &item)
+{
+  auto types = std::vector<std::unique_ptr<Type>> ();
+
+  for (auto &field : item.get_fields ())
+    types.emplace_back (field.get_field_type ().clone_type ());
+
+  expanded
+    = eq_impl (assert_receiver_is_total_eq_fn (std::move (types)),
+              item.get_identifier ().as_string (), item.get_generic_params ());
+}
+
+void
+DeriveEq::visit_enum (Enum &item)
+{
+  auto types = std::vector<std::unique_ptr<Type>> ();
+
+  for (auto &variant : item.get_variants ())
+    {
+      switch (variant->get_enum_item_kind ())
+       {
+       case EnumItem::Kind::Identifier:
+       case EnumItem::Kind::Discriminant:
+         // nothing to do as they contain no inner types
+         continue;
+         case EnumItem::Kind::Tuple: {
+           auto tuple = static_cast<EnumItemTuple &> (*variant);
+
+           for (auto &field : tuple.get_tuple_fields ())
+             types.emplace_back (field.get_field_type ().clone_type ());
+
+           break;
+         }
+         case EnumItem::Kind::Struct: {
+           auto tuple = static_cast<EnumItemStruct &> (*variant);
+
+           for (auto &field : tuple.get_struct_fields ())
+             types.emplace_back (field.get_field_type ().clone_type ());
+
+           break;
+         }
+       }
+    }
+
+  expanded
+    = eq_impl (assert_receiver_is_total_eq_fn (std::move (types)),
+              item.get_identifier ().as_string (), item.get_generic_params ());
+}
+
+void
+DeriveEq::visit_union (Union &item)
+{
+  auto types = std::vector<std::unique_ptr<Type>> ();
+
+  for (auto &field : item.get_variants ())
+    types.emplace_back (field.get_field_type ().clone_type ());
+
+  expanded
+    = eq_impl (assert_receiver_is_total_eq_fn (std::move (types)),
+              item.get_identifier ().as_string (), item.get_generic_params ());
+}
+
+} // namespace AST
+} // namespace Rust
diff --git a/gcc/rust/expand/rust-derive-eq.h b/gcc/rust/expand/rust-derive-eq.h
new file mode 100644
index 00000000000..655f1e82e02
--- /dev/null
+++ b/gcc/rust/expand/rust-derive-eq.h
@@ -0,0 +1,82 @@
+// 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_EQ_H
+#define RUST_DERIVE_EQ_H
+
+#include "rust-derive.h"
+
+namespace Rust {
+namespace AST {
+
+// FIXME: Need to figure out structuraleq marker trait
+
+class DeriveEq : DeriveVisitor
+{
+public:
+  DeriveEq (location_t loc);
+
+  std::unique_ptr<AST::Item> go (Item &item);
+
+private:
+  std::unique_ptr<Item> expanded;
+
+  /**
+   * Create the actual `assert_receiver_is_total_eq` function of the
+   * implementation, which asserts that every type contained within our 
targeted
+   * type also implements `Eq`.
+   */
+  std::unique_ptr<AssociatedItem>
+  assert_receiver_is_total_eq_fn (std::vector<std::unique_ptr<Type>> &&types);
+
+  /**
+   * Create the Eq trait implementation for a type
+   *
+   * impl Eq for <type> {
+   *     <assert_receiver_is_total_eq>
+   * }
+   *
+   */
+  std::unique_ptr<Item>
+  eq_impl (std::unique_ptr<AssociatedItem> &&fn, std::string name,
+          const std::vector<std::unique_ptr<GenericParam>> &type_generics);
+
+  /**
+   * Generate the following structure definition
+   *
+   * struct AssertParamIsEq<T: Eq + ?Sized> { _t: PhantomData<T> }
+   */
+  std::unique_ptr<Stmt> assert_param_is_eq ();
+
+  /**
+   * Generate a let statement to assert a type implements `Eq`
+   *
+   * let _: AssertParamIsEq<type>;
+   */
+  std::unique_ptr<Stmt> assert_type_is_eq (std::unique_ptr<Type> &&type);
+
+  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_EQ_H
diff --git a/gcc/rust/expand/rust-derive.cc b/gcc/rust/expand/rust-derive.cc
index f8d1d015d9c..8289a120f04 100644
--- a/gcc/rust/expand/rust-derive.cc
+++ b/gcc/rust/expand/rust-derive.cc
@@ -21,6 +21,7 @@
 #include "rust-derive-copy.h"
 #include "rust-derive-debug.h"
 #include "rust-derive-default.h"
+#include "rust-derive-eq.h"
 
 namespace Rust {
 namespace AST {
@@ -48,6 +49,7 @@ DeriveVisitor::derive (Item &item, const Attribute &attr,
     case BuiltinMacro::Default:
       return DeriveDefault (attr.get_locus ()).go (item);
     case BuiltinMacro::Eq:
+      return DeriveEq (attr.get_locus ()).go (item);
     case BuiltinMacro::PartialEq:
     case BuiltinMacro::Ord:
     case BuiltinMacro::PartialOrd:
diff --git a/gcc/testsuite/rust/compile/derive-eq-invalid.rs 
b/gcc/testsuite/rust/compile/derive-eq-invalid.rs
new file mode 100644
index 00000000000..017241db86d
--- /dev/null
+++ b/gcc/testsuite/rust/compile/derive-eq-invalid.rs
@@ -0,0 +1,45 @@
+mod core {
+    mod cmp {
+        pub trait PartialEq<Rhs: ?Sized = Self> {
+            fn eq(&self, other: &Rhs) -> bool;
+
+            fn ne(&self, other: &Rhs) -> bool {
+                !self.eq(other)
+            }
+        }
+
+        pub trait Eq: PartialEq<Self> {
+            fn assert_receiver_is_total_eq(&self) {}
+        }
+    }
+}
+
+// #[lang = "phantom_data"]
+// struct PhantomData<T>;
+
+// #[lang = "sized"]
+// trait Sized {}
+
+#[derive(PartialEq)]
+struct NotEq;
+
+#[derive(Eq, PartialEq)] // { dg-error "bounds not satisfied for NotEq .Eq." }
+struct Container(NotEq);
+
+// #[derive(Eq)]
+// struct Foo { a: i32 }
+// #[derive(Eq)]
+// struct Bar(i32);
+
+// #[derive(Eq)]
+// enum Baz {
+//     A,
+//     B(i32),
+//     C { a: i32 }
+// }
+
+// #[derive(Eq)]
+// union Qux {
+//     a: i32,
+//     b: i64,
+// }
-- 
2.45.2

Reply via email to