aboutsummaryrefslogtreecommitdiff
path: root/gcc/rust/expand/rust-derive-hash.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/rust/expand/rust-derive-hash.cc')
-rw-r--r--gcc/rust/expand/rust-derive-hash.cc293
1 files changed, 293 insertions, 0 deletions
diff --git a/gcc/rust/expand/rust-derive-hash.cc b/gcc/rust/expand/rust-derive-hash.cc
new file mode 100644
index 0000000..0c9b0f7
--- /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