https://gcc.gnu.org/g:051e54e5fcf0b0b852fef0c7f0e53e62b19a4176

commit r15-8823-g051e54e5fcf0b0b852fef0c7f0e53e62b19a4176
Author: Arthur Cohen <arthur.co...@embecosm.com>
Date:   Thu Feb 20 17:01:28 2025 +0000

    gccrs: derive(Hash): Add implementation.
    
    gcc/rust/ChangeLog:
    
            * Make-lang.in: Compile it.
            * expand/rust-derive.cc (DeriveVisitor::derive): Call it.
            * expand/rust-derive-hash.cc: New file.
            * expand/rust-derive-hash.h: New file.
    
    gcc/testsuite/ChangeLog:
    
            * rust/compile/derive-hash1.rs: New test.
            * rust/compile/nr2/exclude: Add testcase to exclusion list.

Diff:
---
 gcc/rust/Make-lang.in                      |   1 +
 gcc/rust/expand/rust-derive-hash.cc        | 293 +++++++++++++++++++++++++++++
 gcc/rust/expand/rust-derive-hash.h         |  61 ++++++
 gcc/rust/expand/rust-derive.cc             |  22 ++-
 gcc/testsuite/rust/compile/derive-hash1.rs |  91 +++++++++
 gcc/testsuite/rust/compile/nr2/exclude     |   1 +
 6 files changed, 460 insertions(+), 9 deletions(-)

diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in
index 5e13c6cfc3ff..3e44b12c3d16 100644
--- a/gcc/rust/Make-lang.in
+++ b/gcc/rust/Make-lang.in
@@ -100,6 +100,7 @@ GRS_OBJS = \
     rust/rust-derive-default.o \
     rust/rust-derive-partial-eq.o \
     rust/rust-derive-eq.o \
+    rust/rust-derive-hash.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-hash.cc 
b/gcc/rust/expand/rust-derive-hash.cc
new file mode 100644
index 000000000000..0c9b0f7b1057
--- /dev/null
+++ b/gcc/rust/expand/rust-derive-hash.cc
@@ -0,0 +1,293 @@
+// 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-hash.h"
+#include "rust-ast.h"
+#include "rust-expr.h"
+#include "rust-item.h"
+#include "rust-path.h"
+#include "rust-pattern.h"
+#include "rust-stmt.h"
+#include "rust-system.h"
+
+namespace Rust {
+namespace AST {
+
+DeriveHash::DeriveHash (location_t loc) : DeriveVisitor (loc) {}
+
+std::unique_ptr<AST::Item>
+DeriveHash::go (Item &item)
+{
+  item.accept_vis (*this);
+
+  return std::move (expanded);
+}
+
+std::unique_ptr<Expr>
+DeriveHash::hash_call (std::unique_ptr<Expr> &&value)
+{
+  auto hash
+    = builder.path_in_expression ({"core", "hash", "Hash", "hash"}, true);
+
+  return builder.call (ptrify (hash),
+                      vec (std::move (value),
+                           builder.identifier (DeriveHash::state)));
+}
+
+std::unique_ptr<AssociatedItem>
+DeriveHash::hash_fn (std::unique_ptr<BlockExpr> &&block)
+{
+  auto hash_calls = std::vector<std::unique_ptr<Stmt>> ();
+
+  auto state_type = std::unique_ptr<TypeNoBounds> (
+    new TypePath (builder.type_path (DeriveHash::state_type)));
+  auto state_param =
+
+    builder.function_param (builder.identifier_pattern (DeriveHash::state),
+                           builder.reference_type (std::move (state_type),
+                                                   true));
+
+  auto params = vec (builder.self_ref_param (), std::move (state_param));
+  auto bounds = vec (
+    builder.trait_bound (builder.type_path ({"core", "hash", "Hasher"}, 
true)));
+  auto generics = vec (
+    builder.generic_type_param (DeriveHash::state_type, std::move (bounds)));
+
+  return builder.function ("hash", std::move (params), nullptr,
+                          std::move (block), std::move (generics));
+}
+
+std::unique_ptr<Item>
+DeriveHash::hash_impl (
+  std::unique_ptr<AssociatedItem> &&hash_fn, std::string name,
+  const std::vector<std::unique_ptr<GenericParam>> &type_generics)
+{
+  auto hash_path = builder.type_path ({"core", "hash", "Hash"}, true);
+
+  auto trait_items = vec (std::move (hash_fn));
+
+  auto generics = setup_impl_generics (name, type_generics,
+                                      builder.trait_bound (hash_path));
+
+  return builder.trait_impl (hash_path, std::move (generics.self_type),
+                            std::move (trait_items),
+                            std::move (generics.impl));
+}
+
+void
+DeriveHash::visit_struct (StructStruct &item)
+{
+  auto hash_calls = std::vector<std::unique_ptr<Stmt>> ();
+
+  for (auto &field : item.get_fields ())
+    {
+      auto value = builder.ref (
+       builder.field_access (builder.identifier ("self"),
+                             field.get_field_name ().as_string ()));
+
+      auto stmt = builder.statementify (hash_call (std::move (value)));
+
+      hash_calls.emplace_back (std::move (stmt));
+    }
+
+  auto block = builder.block (std::move (hash_calls));
+
+  expanded = hash_impl (hash_fn (std::move (block)),
+                       item.get_identifier ().as_string (),
+                       item.get_generic_params ());
+}
+
+void
+DeriveHash::visit_tuple (TupleStruct &item)
+{
+  auto hash_calls = std::vector<std::unique_ptr<Stmt>> ();
+
+  for (size_t idx = 0; idx < item.get_fields ().size (); idx++)
+    {
+      auto value = builder.ref (builder.tuple_idx ("self", idx));
+
+      auto stmt = builder.statementify (hash_call (std::move (value)));
+
+      hash_calls.emplace_back (std::move (stmt));
+    }
+
+  auto block = builder.block (std::move (hash_calls));
+
+  expanded = hash_impl (hash_fn (std::move (block)),
+                       item.get_identifier ().as_string (),
+                       item.get_generic_params ());
+}
+
+MatchCase
+DeriveHash::match_enum_tuple (PathInExpression variant_path,
+                             const EnumItemTuple &variant)
+{
+  auto self_patterns = std::vector<std::unique_ptr<Pattern>> ();
+  auto hash_calls = std::vector<std::unique_ptr<Stmt>> ();
+
+  for (size_t i = 0; i < variant.get_tuple_fields ().size (); i++)
+    {
+      auto pattern = "__self_" + std::to_string (i);
+
+      auto call = hash_call (builder.ref (builder.identifier (pattern)));
+
+      self_patterns.emplace_back (builder.identifier_pattern (pattern));
+      hash_calls.emplace_back (builder.statementify (std::move (call)));
+    }
+
+  auto patterns_elts = std::unique_ptr<TupleStructItems> (
+    new TupleStructItemsNoRange (std::move (self_patterns)));
+  auto pattern = std::unique_ptr<Pattern> (
+    new ReferencePattern (std::unique_ptr<Pattern> (new TupleStructPattern (
+                           variant_path, std::move (patterns_elts))),
+                         false, false, loc));
+
+  auto block = builder.block (std::move (hash_calls));
+
+  return builder.match_case (std::move (pattern), std::move (block));
+}
+
+MatchCase
+DeriveHash::match_enum_struct (PathInExpression variant_path,
+                              const EnumItemStruct &variant)
+{
+  auto field_patterns = std::vector<std::unique_ptr<StructPatternField>> ();
+  auto hash_calls = std::vector<std::unique_ptr<Stmt>> ();
+
+  for (const auto &field : variant.get_struct_fields ())
+    {
+      auto call = hash_call (builder.ref (
+       builder.identifier (field.get_field_name ().as_string ())));
+
+      field_patterns.emplace_back (
+       std::unique_ptr<StructPatternField> (new StructPatternFieldIdent (
+         field.get_field_name (), false /* is_ref? true? */, false, {}, loc)));
+
+      hash_calls.emplace_back (builder.statementify (std::move (call)));
+    }
+
+  auto pattern_elts = StructPatternElements (std::move (field_patterns));
+  auto pattern = std::unique_ptr<Pattern> (
+    new ReferencePattern (std::unique_ptr<Pattern> (new StructPattern (
+                           variant_path, loc, pattern_elts)),
+                         false, false, loc));
+
+  auto block = builder.block (std::move (hash_calls));
+  return builder.match_case (std::move (pattern), std::move (block));
+}
+
+void
+DeriveHash::visit_enum (Enum &item)
+{
+  // Enums are a bit different: We start by hashing the discriminant value of
+  // the enum instance, and then hash all of the data contained in each of the
+  // enum's variants. For data-less variants, we don't have any data to hash, 
so
+  // hashing the discriminant value is enough. To access the rest of the
+  // variants' data, we create a match and destructure each internal field and
+  // hash it.
+  //
+  // So for example with the following enum:
+  //
+  // ```rust
+  // enum Foo {
+  //     A,
+  //     B(i32),
+  //     C { a: i32 },
+  // }
+  // ```
+  //
+  // we create the following implementation:
+  //
+  // ```rust
+  // fn hash<H: Hasher>(&self, state: &mut H) {
+  //     let discriminant = intrinsics::discriminant_value(&self);
+  //     Hash::hash(&discriminant, state);
+  //
+  //     match self {
+  //         B(self_0) => { Hash::hash(self_0, state); },
+  //         C { a } => { Hash::hash(a, state); },
+  //         _ => {},
+  //     }
+  // }
+  // ```
+  //
+  // Note the extra wildcard pattern to satisfy the exhaust checker.
+
+  auto cases = std::vector<MatchCase> ();
+  auto type_name = item.get_identifier ().as_string ();
+
+  auto intrinsic = ptrify (
+    builder.path_in_expression ({"core", "intrinsics", "discriminant_value"},
+                               true));
+
+  auto let_discr
+    = builder.let (builder.identifier_pattern (DeriveHash::discr), nullptr,
+                  builder.call (std::move (intrinsic),
+                                builder.identifier ("self")));
+
+  auto discr_hash = builder.statementify (
+    hash_call (builder.ref (builder.identifier (DeriveHash::discr))));
+
+  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:
+         // nothing to do in these cases, as we just need to hash the
+         // discriminant value
+         continue;
+       case EnumItem::Kind::Tuple:
+         cases.emplace_back (
+           match_enum_tuple (variant_path,
+                             static_cast<EnumItemTuple &> (*variant)));
+         break;
+       case EnumItem::Kind::Struct:
+         cases.emplace_back (
+           match_enum_struct (variant_path,
+                              static_cast<EnumItemStruct &> (*variant)));
+         break;
+       }
+    }
+
+  // The extra empty wildcard case
+  cases.emplace_back (
+    builder.match_case (builder.wildcard (), builder.block ()));
+
+  auto match = builder.match (builder.identifier ("self"), std::move (cases));
+
+  auto block
+    = builder.block (vec (std::move (let_discr), std::move (discr_hash)),
+                    std::move (match));
+
+  expanded = hash_impl (hash_fn (std::move (block)), type_name,
+                       item.get_generic_params ());
+}
+
+void
+DeriveHash::visit_union (Union &item)
+{
+  rust_error_at (item.get_locus (), "derive(Hash) cannot be used on unions");
+}
+
+} // namespace AST
+} // namespace Rust
diff --git a/gcc/rust/expand/rust-derive-hash.h 
b/gcc/rust/expand/rust-derive-hash.h
new file mode 100644
index 000000000000..02b0bee10a5d
--- /dev/null
+++ b/gcc/rust/expand/rust-derive-hash.h
@@ -0,0 +1,61 @@
+// 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_HASH_H
+#define RUST_DERIVE_HASH_H
+
+#include "rust-derive.h"
+
+namespace Rust {
+namespace AST {
+
+class DeriveHash : DeriveVisitor
+{
+public:
+  DeriveHash (location_t loc);
+
+  std::unique_ptr<AST::Item> go (Item &item);
+
+private:
+  std::unique_ptr<Item> expanded;
+
+  constexpr static const char *state = "#state";
+  constexpr static const char *state_type = "#__H";
+  constexpr static const char *discr = "#discr";
+
+  std::unique_ptr<Expr> hash_call (std::unique_ptr<Expr> &&value);
+  std::unique_ptr<AssociatedItem> hash_fn (std::unique_ptr<BlockExpr> &&block);
+  std::unique_ptr<Item>
+  hash_impl (std::unique_ptr<AssociatedItem> &&hash_fn, std::string name,
+            const std::vector<std::unique_ptr<GenericParam>> &type_generics);
+
+  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_HASH_H
diff --git a/gcc/rust/expand/rust-derive.cc b/gcc/rust/expand/rust-derive.cc
index 39e03a67cd4a..015b81e90df2 100644
--- a/gcc/rust/expand/rust-derive.cc
+++ b/gcc/rust/expand/rust-derive.cc
@@ -23,6 +23,7 @@
 #include "rust-derive-default.h"
 #include "rust-derive-eq.h"
 #include "rust-derive-partial-eq.h"
+#include "rust-derive-hash.h"
 
 namespace Rust {
 namespace AST {
@@ -35,29 +36,32 @@ std::vector<std::unique_ptr<Item>>
 DeriveVisitor::derive (Item &item, const Attribute &attr,
                       BuiltinMacro to_derive)
 {
+  auto loc = attr.get_locus ();
+
   switch (to_derive)
     {
     case BuiltinMacro::Clone:
-      return vec (DeriveClone (attr.get_locus ()).go (item));
+      return vec (DeriveClone (loc).go (item));
     case BuiltinMacro::Copy:
-      return vec (DeriveCopy (attr.get_locus ()).go (item));
+      return vec (DeriveCopy (loc).go (item));
     case BuiltinMacro::Debug:
       rust_warning_at (
-       attr.get_locus (), 0,
+       loc, 0,
        "derive(Debug) is not fully implemented yet and has no effect - only a "
        "stub implementation will be generated");
-      return vec (DeriveDebug (attr.get_locus ()).go (item));
+      return vec (DeriveDebug (loc).go (item));
     case BuiltinMacro::Default:
-      return vec (DeriveDefault (attr.get_locus ()).go (item));
+      return vec (DeriveDefault (loc).go (item));
     case BuiltinMacro::Eq:
-      return DeriveEq (attr.get_locus ()).go (item);
+      return DeriveEq (loc).go (item);
     case BuiltinMacro::PartialEq:
-      return DerivePartialEq (attr.get_locus ()).go (item);
+      return DerivePartialEq (loc).go (item);
+    case BuiltinMacro::Hash:
+      return vec (DeriveHash (loc).go (item));
     case BuiltinMacro::Ord:
     case BuiltinMacro::PartialOrd:
-    case BuiltinMacro::Hash:
     default:
-      rust_sorry_at (attr.get_locus (), "unimplemented builtin derive macro");
+      rust_sorry_at (loc, "unimplemented builtin derive macro");
       return {};
     };
 }
diff --git a/gcc/testsuite/rust/compile/derive-hash1.rs 
b/gcc/testsuite/rust/compile/derive-hash1.rs
new file mode 100644
index 000000000000..80e1e2d2bda5
--- /dev/null
+++ b/gcc/testsuite/rust/compile/derive-hash1.rs
@@ -0,0 +1,91 @@
+#![feature(intrinsics)]
+
+#[lang = "sized"]
+trait Sized {}
+
+pub mod core {
+    pub mod intrinsics {
+        #[lang = "discriminant_kind"]
+        pub trait DiscriminantKind {
+            #[lang = "discriminant_type"]
+            type Discriminant;
+        }
+
+        extern "rust-intrinsic" {
+            pub fn discriminant_value<T>(v: &T) -> <T as 
DiscriminantKind>::Discriminant;
+        }
+    }
+
+    pub mod hash {
+        pub trait Hasher {}
+
+        pub trait Hash {
+            /// Feeds this value into the given [`Hasher`].
+            ///
+            /// # Examples
+            ///
+            /// ```
+            /// use std::collections::hash_map::DefaultHasher;
+            /// use std::hash::{Hash, Hasher};
+            ///
+            /// let mut hasher = DefaultHasher::new();
+            /// 7920.hash(&mut hasher);
+            /// println!("Hash is {:x}!", hasher.finish());
+            /// ```
+            #[stable(feature = "rust1", since = "1.0.0")]
+            fn hash<H: Hasher>(&self, state: &mut H);
+
+            /// Feeds a slice of this type into the given [`Hasher`].
+            ///
+            /// # Examples
+            ///
+            /// ```
+            /// use std::collections::hash_map::DefaultHasher;
+            /// use std::hash::{Hash, Hasher};
+            ///
+            /// let mut hasher = DefaultHasher::new();
+            /// let numbers = [6, 28, 496, 8128];
+            /// Hash::hash_slice(&numbers, &mut hasher);
+            /// println!("Hash is {:x}!", hasher.finish());
+            /// ```
+            #[stable(feature = "hash_slice", since = "1.3.0")]
+            fn hash_slice<H: Hasher>(data: &[Self], state: &mut H)
+            where
+                Self: Sized,
+            {
+                // for piece in data {
+                //     piece.hash(state);
+                // }
+            }
+        }
+    }
+}
+
+impl core::hash::Hash for i32 {
+    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {}
+}
+
+impl core::hash::Hash for i64 {
+    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {}
+}
+
+// for the discriminant value
+impl core::hash::Hash for isize {
+    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {}
+}
+
+#[derive(Hash)]
+struct Foo { // { dg-warning "never constructed" }
+    a: i32,
+    b: i32,
+}
+
+#[derive(Hash)]
+struct Bar(i32, i64); // { dg-warning "never constructed" }
+
+#[derive(Hash)]
+enum Baz {
+    A,
+    B(i32),
+    C { a: i64 }
+}
diff --git a/gcc/testsuite/rust/compile/nr2/exclude 
b/gcc/testsuite/rust/compile/nr2/exclude
index 26b830eab7e9..345b1d516ef4 100644
--- a/gcc/testsuite/rust/compile/nr2/exclude
+++ b/gcc/testsuite/rust/compile/nr2/exclude
@@ -77,4 +77,5 @@ for-loop2.rs
 issue-3403.rs
 derive-eq-invalid.rs
 derive-partialeq1.rs
+derive-hash1.rs
 # please don't delete the trailing newline

Reply via email to