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