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.9 % 140 137
Test Date: 2025-06-21 16:26:05 Functions: 90.9 % 11 10
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

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

Generated by: LCOV version 2.1-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.