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
|