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
|