LCOV - code coverage report
Current view: top level - gcc/rust/expand - rust-derive-hash.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 97.1 % 137 133
Test Date: 2026-02-28 14:20:25 Functions: 92.3 % 13 12
Legend: Lines:     hit not hit

            Line data    Source code
       1              : // Copyright (C) 2025-2026 Free Software Foundation, Inc.
       2              : 
       3              : // This file is part of GCC.
       4              : 
       5              : // GCC is free software; you can redistribute it and/or modify it under
       6              : // the terms of the GNU General Public License as published by the Free
       7              : // Software Foundation; either version 3, or (at your option) any later
       8              : // version.
       9              : 
      10              : // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      11              : // WARRANTY; without even the implied warranty of MERCHANTABILITY or
      12              : // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      13              : // for more details.
      14              : 
      15              : // You should have received a copy of the GNU General Public License
      16              : // along with GCC; see the file COPYING3.  If not see
      17              : // <http://www.gnu.org/licenses/>.
      18              : 
      19              : #include "rust-derive-hash.h"
      20              : #include "rust-ast.h"
      21              : #include "rust-expr.h"
      22              : #include "rust-item.h"
      23              : #include "rust-path.h"
      24              : #include "rust-pattern.h"
      25              : #include "rust-stmt.h"
      26              : #include "rust-system.h"
      27              : 
      28              : namespace Rust {
      29              : namespace AST {
      30              : 
      31            3 : DeriveHash::DeriveHash (location_t loc) : DeriveVisitor (loc) {}
      32              : 
      33              : std::unique_ptr<AST::Item>
      34            3 : DeriveHash::go (Item &item)
      35              : {
      36            3 :   item.accept_vis (*this);
      37              : 
      38            3 :   return std::move (expanded);
      39              : }
      40              : 
      41              : std::unique_ptr<Expr>
      42            7 : DeriveHash::hash_call (std::unique_ptr<Expr> &&value)
      43              : {
      44            7 :   auto hash
      45            7 :     = builder.path_in_expression ({"core", "hash", "Hash", "hash"}, true);
      46              : 
      47           14 :   return builder.call (ptrify (hash),
      48           14 :                        vec (std::move (value),
      49           21 :                             builder.identifier (DeriveHash::state)));
      50            7 : }
      51              : 
      52              : std::unique_ptr<AssociatedItem>
      53            3 : DeriveHash::hash_fn (std::unique_ptr<BlockExpr> &&block)
      54              : {
      55            3 :   auto hash_calls = std::vector<std::unique_ptr<Stmt>> ();
      56              : 
      57            3 :   auto state_type = std::unique_ptr<TypeNoBounds> (
      58            3 :     new TypePath (builder.type_path (DeriveHash::state_type)));
      59            3 :   auto state_param =
      60              : 
      61            6 :     builder.function_param (builder.identifier_pattern (DeriveHash::state),
      62            6 :                             builder.reference_type (std::move (state_type),
      63            3 :                                                     true));
      64              : 
      65            3 :   auto params = vec (builder.self_ref_param (), std::move (state_param));
      66            3 :   auto bounds = vec (
      67            3 :     builder.trait_bound (builder.type_path ({"core", "hash", "Hasher"}, true)));
      68            3 :   auto generics = vec (
      69            3 :     builder.generic_type_param (DeriveHash::state_type, std::move (bounds)));
      70              : 
      71            6 :   return builder.function ("hash", std::move (params), nullptr,
      72            3 :                            std::move (block), std::move (generics));
      73            3 : }
      74              : 
      75              : std::unique_ptr<Item>
      76            3 : DeriveHash::hash_impl (
      77              :   std::unique_ptr<AssociatedItem> &&hash_fn, std::string name,
      78              :   const std::vector<std::unique_ptr<GenericParam>> &type_generics)
      79              : {
      80            6 :   auto hash_path = [this] () {
      81            3 :     return builder.type_path ({"core", "hash", "Hash"}, true);
      82            3 :   };
      83              : 
      84            3 :   auto trait_items = vec (std::move (hash_fn));
      85              : 
      86            6 :   auto generics = setup_impl_generics (name, type_generics, [&, this] () {
      87            0 :     return builder.trait_bound (hash_path ());
      88            3 :   });
      89              : 
      90            6 :   return builder.trait_impl (hash_path (), std::move (generics.self_type),
      91              :                              std::move (trait_items),
      92            6 :                              std::move (generics.impl));
      93            3 : }
      94              : 
      95              : void
      96            1 : DeriveHash::visit_struct (StructStruct &item)
      97              : {
      98            1 :   auto hash_calls = std::vector<std::unique_ptr<Stmt>> ();
      99              : 
     100            3 :   for (auto &field : item.get_fields ())
     101              :     {
     102            2 :       auto value = builder.ref (
     103            4 :         builder.field_access (builder.identifier ("self"),
     104            4 :                               field.get_field_name ().as_string ()));
     105              : 
     106            2 :       auto stmt = builder.statementify (hash_call (std::move (value)));
     107              : 
     108            2 :       hash_calls.emplace_back (std::move (stmt));
     109            2 :     }
     110              : 
     111            1 :   auto block = builder.block (std::move (hash_calls));
     112              : 
     113            2 :   expanded = hash_impl (hash_fn (std::move (block)),
     114            1 :                         item.get_identifier ().as_string (),
     115            2 :                         item.get_generic_params ());
     116            1 : }
     117              : 
     118              : void
     119            1 : DeriveHash::visit_tuple (TupleStruct &item)
     120              : {
     121            1 :   auto hash_calls = std::vector<std::unique_ptr<Stmt>> ();
     122              : 
     123            3 :   for (size_t idx = 0; idx < item.get_fields ().size (); idx++)
     124              :     {
     125            2 :       auto value = builder.ref (builder.tuple_idx ("self", idx));
     126              : 
     127            2 :       auto stmt = builder.statementify (hash_call (std::move (value)));
     128              : 
     129            2 :       hash_calls.emplace_back (std::move (stmt));
     130            2 :     }
     131              : 
     132            1 :   auto block = builder.block (std::move (hash_calls));
     133              : 
     134            2 :   expanded = hash_impl (hash_fn (std::move (block)),
     135            1 :                         item.get_identifier ().as_string (),
     136            2 :                         item.get_generic_params ());
     137            1 : }
     138              : 
     139              : MatchCase
     140            1 : DeriveHash::match_enum_tuple (PathInExpression variant_path,
     141              :                               const EnumItemTuple &variant)
     142              : {
     143            1 :   auto self_patterns = std::vector<std::unique_ptr<Pattern>> ();
     144            1 :   auto hash_calls = std::vector<std::unique_ptr<Stmt>> ();
     145              : 
     146            2 :   for (size_t i = 0; i < variant.get_tuple_fields ().size (); i++)
     147              :     {
     148            1 :       auto pattern = "__self_" + std::to_string (i);
     149              : 
     150            2 :       auto call = hash_call (builder.ref (builder.identifier (pattern)));
     151              : 
     152            2 :       self_patterns.emplace_back (builder.identifier_pattern (pattern));
     153            1 :       hash_calls.emplace_back (builder.statementify (std::move (call)));
     154            1 :     }
     155              : 
     156            1 :   auto patterns_elts = std::unique_ptr<TupleStructItems> (
     157            1 :     new TupleStructItemsNoRest (std::move (self_patterns)));
     158            1 :   auto pattern = std::unique_ptr<Pattern> (
     159            1 :     new ReferencePattern (std::unique_ptr<Pattern> (new TupleStructPattern (
     160            2 :                             variant_path, std::move (patterns_elts))),
     161            2 :                           false, false, loc));
     162              : 
     163            1 :   auto block = builder.block (std::move (hash_calls));
     164              : 
     165            1 :   return builder.match_case (std::move (pattern), std::move (block));
     166            1 : }
     167              : 
     168              : MatchCase
     169            1 : DeriveHash::match_enum_struct (PathInExpression variant_path,
     170              :                                const EnumItemStruct &variant)
     171              : {
     172            1 :   auto field_patterns = std::vector<std::unique_ptr<StructPatternField>> ();
     173            1 :   auto hash_calls = std::vector<std::unique_ptr<Stmt>> ();
     174              : 
     175            2 :   for (const auto &field : variant.get_struct_fields ())
     176              :     {
     177            2 :       auto call = hash_call (builder.ref (
     178            4 :         builder.identifier (field.get_field_name ().as_string ())));
     179              : 
     180            1 :       field_patterns.emplace_back (
     181            1 :         std::unique_ptr<StructPatternField> (new StructPatternFieldIdent (
     182            2 :           field.get_field_name (), false /* is_ref? true? */, false, {}, loc)));
     183              : 
     184            1 :       hash_calls.emplace_back (builder.statementify (std::move (call)));
     185            1 :     }
     186              : 
     187            1 :   auto pattern_elts = StructPatternElements (std::move (field_patterns));
     188            1 :   auto pattern = std::unique_ptr<Pattern> (
     189            1 :     new ReferencePattern (std::unique_ptr<Pattern> (new StructPattern (
     190            2 :                             variant_path, loc, pattern_elts)),
     191            2 :                           false, false, loc));
     192              : 
     193            1 :   auto block = builder.block (std::move (hash_calls));
     194            1 :   return builder.match_case (std::move (pattern), std::move (block));
     195            2 : }
     196              : 
     197              : void
     198            1 : DeriveHash::visit_enum (Enum &item)
     199              : {
     200              :   // Enums are a bit different: We start by hashing the discriminant value of
     201              :   // the enum instance, and then hash all of the data contained in each of the
     202              :   // enum's variants. For data-less variants, we don't have any data to hash, so
     203              :   // hashing the discriminant value is enough. To access the rest of the
     204              :   // variants' data, we create a match and destructure each internal field and
     205              :   // hash it.
     206              :   //
     207              :   // So for example with the following enum:
     208              :   //
     209              :   // ```rust
     210              :   // enum Foo {
     211              :   //     A,
     212              :   //     B(i32),
     213              :   //     C { a: i32 },
     214              :   // }
     215              :   // ```
     216              :   //
     217              :   // we create the following implementation:
     218              :   //
     219              :   // ```rust
     220              :   // fn hash<H: Hasher>(&self, state: &mut H) {
     221              :   //     let discriminant = intrinsics::discriminant_value(&self);
     222              :   //     Hash::hash(&discriminant, state);
     223              :   //
     224              :   //     match self {
     225              :   //         B(self_0) => { Hash::hash(self_0, state); },
     226              :   //         C { a } => { Hash::hash(a, state); },
     227              :   //         _ => {},
     228              :   //     }
     229              :   // }
     230              :   // ```
     231              :   //
     232              :   // Note the extra wildcard pattern to satisfy the exhaust checker.
     233              : 
     234            1 :   auto cases = std::vector<MatchCase> ();
     235            2 :   auto type_name = item.get_identifier ().as_string ();
     236              : 
     237            1 :   auto let_discr = builder.discriminant_value (DeriveHash::discr);
     238              : 
     239            1 :   auto discr_hash = builder.statementify (
     240            1 :     hash_call (builder.ref (builder.identifier (DeriveHash::discr))));
     241              : 
     242            4 :   for (auto &variant : item.get_variants ())
     243              :     {
     244            3 :       auto variant_path
     245              :         = builder.variant_path (type_name,
     246            3 :                                 variant->get_identifier ().as_string ());
     247              : 
     248            3 :       switch (variant->get_enum_item_kind ())
     249              :         {
     250            1 :         case EnumItem::Kind::Identifier:
     251            1 :         case EnumItem::Kind::Discriminant:
     252              :           // nothing to do in these cases, as we just need to hash the
     253              :           // discriminant value
     254            1 :           continue;
     255            1 :         case EnumItem::Kind::Tuple:
     256            2 :           cases.emplace_back (
     257            2 :             match_enum_tuple (variant_path,
     258            1 :                               static_cast<EnumItemTuple &> (*variant)));
     259            1 :           break;
     260            1 :         case EnumItem::Kind::Struct:
     261            2 :           cases.emplace_back (
     262            2 :             match_enum_struct (variant_path,
     263            1 :                                static_cast<EnumItemStruct &> (*variant)));
     264            1 :           break;
     265              :         }
     266            3 :     }
     267              : 
     268              :   // The extra empty wildcard case
     269            1 :   cases.emplace_back (
     270            2 :     builder.match_case (builder.wildcard (), builder.block ()));
     271              : 
     272            1 :   auto match = builder.match (builder.identifier ("self"), std::move (cases));
     273              : 
     274            1 :   auto block
     275            2 :     = builder.block (vec (std::move (let_discr), std::move (discr_hash)),
     276            1 :                      std::move (match));
     277              : 
     278            3 :   expanded = hash_impl (hash_fn (std::move (block)), type_name,
     279            2 :                         item.get_generic_params ());
     280            1 : }
     281              : 
     282              : void
     283            0 : DeriveHash::visit_union (Union &item)
     284              : {
     285            0 :   rust_error_at (item.get_locus (), "derive(Hash) cannot be used on unions");
     286            0 : }
     287              : 
     288              : } // namespace AST
     289              : } // namespace Rust
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.