// 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 // . #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 DeriveHash::go (Item &item) { item.accept_vis (*this); return std::move (expanded); } std::unique_ptr DeriveHash::hash_call (std::unique_ptr &&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 DeriveHash::hash_fn (std::unique_ptr &&block) { auto hash_calls = std::vector> (); auto state_type = std::unique_ptr ( 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 DeriveHash::hash_impl ( std::unique_ptr &&hash_fn, std::string name, const std::vector> &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> (); 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> (); 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> (); auto hash_calls = std::vector> (); 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 ( new TupleStructItemsNoRange (std::move (self_patterns))); auto pattern = std::unique_ptr ( new ReferencePattern (std::unique_ptr (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> (); auto hash_calls = std::vector> (); 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 (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 ( new ReferencePattern (std::unique_ptr (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(&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 (); 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 (*variant))); break; case EnumItem::Kind::Struct: cases.emplace_back ( match_enum_struct (variant_path, static_cast (*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