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-partial-eq.h"
20 : #include "rust-ast.h"
21 : #include "rust-expr.h"
22 : #include "rust-item.h"
23 : #include "rust-operators.h"
24 : #include "rust-path.h"
25 : #include "rust-pattern.h"
26 : #include "rust-system.h"
27 :
28 : namespace Rust {
29 : namespace AST {
30 94 : DerivePartialEq::DerivePartialEq (location_t loc) : DeriveVisitor (loc) {}
31 :
32 : std::vector<std::unique_ptr<AST::Item>>
33 94 : DerivePartialEq::go (Item &item)
34 : {
35 94 : item.accept_vis (*this);
36 :
37 94 : return std::move (expanded);
38 : }
39 :
40 : std::vector<std::unique_ptr<Item>>
41 94 : DerivePartialEq::partialeq_impls (
42 : std::unique_ptr<AssociatedItem> &&eq_fn, std::string name,
43 : const std::vector<std::unique_ptr<GenericParam>> &type_generics)
44 : {
45 94 : auto eq = [this] () { return builder.type_path (LangItem::Kind::EQ); };
46 94 : auto speq = builder.type_path (LangItem::Kind::STRUCTURAL_PEQ);
47 :
48 94 : auto trait_items = vec (std::move (eq_fn));
49 :
50 : // no extra bound on StructuralPeq
51 188 : auto peq_generics = setup_impl_generics (name, type_generics, [&, this] () {
52 0 : return builder.trait_bound (eq ());
53 94 : });
54 94 : auto speq_generics = setup_impl_generics (name, type_generics);
55 :
56 188 : auto peq = builder.trait_impl (eq (), std::move (peq_generics.self_type),
57 : std::move (trait_items),
58 94 : std::move (peq_generics.impl));
59 :
60 94 : auto structural_peq
61 188 : = builder.trait_impl (speq, std::move (speq_generics.self_type), {},
62 94 : std::move (speq_generics.impl));
63 :
64 94 : return vec (std::move (peq), std::move (structural_peq));
65 282 : }
66 :
67 : std::unique_ptr<AssociatedItem>
68 94 : DerivePartialEq::eq_fn (std::unique_ptr<BlockExpr> &&block,
69 : std::string type_name)
70 : {
71 94 : auto self_type
72 94 : = std::unique_ptr<TypeNoBounds> (new TypePath (builder.type_path ("Self")));
73 :
74 94 : auto params
75 188 : = vec (builder.self_ref_param (),
76 188 : builder.function_param (builder.identifier_pattern ("other"),
77 188 : builder.reference_type (
78 94 : std::move (self_type))));
79 :
80 376 : return builder.function ("eq", std::move (params),
81 188 : builder.single_type_path ("bool"),
82 94 : std::move (block));
83 94 : }
84 :
85 : std::unique_ptr<Expr>
86 109 : DerivePartialEq::build_eq_expression (
87 : std::vector<SelfOther> &&field_expressions)
88 : {
89 : // for unit structs or empty tuples, this is always true
90 109 : if (field_expressions.empty ())
91 9 : return builder.literal_bool (true);
92 :
93 100 : auto cmp_expression
94 100 : = builder.comparison_expr (std::move (field_expressions.at (0).self_expr),
95 100 : std::move (field_expressions.at (0).other_expr),
96 100 : ComparisonOperator::EQUAL);
97 :
98 169 : for (size_t i = 1; i < field_expressions.size (); i++)
99 : {
100 69 : auto tmp = builder.comparison_expr (
101 69 : std::move (field_expressions.at (i).self_expr),
102 69 : std::move (field_expressions.at (i).other_expr),
103 138 : ComparisonOperator::EQUAL);
104 :
105 69 : cmp_expression
106 69 : = builder.boolean_operation (std::move (cmp_expression),
107 : std::move (tmp),
108 69 : LazyBooleanOperator::LOGICAL_AND);
109 69 : }
110 :
111 100 : return cmp_expression;
112 100 : }
113 :
114 : void
115 9 : DerivePartialEq::visit_tuple (TupleStruct &item)
116 : {
117 18 : auto type_name = item.get_struct_name ().as_string ();
118 9 : auto fields = SelfOther::indexes (builder, item.get_fields ());
119 :
120 27 : auto fn = eq_fn (builder.block (build_eq_expression (std::move (fields))),
121 9 : type_name);
122 :
123 9 : expanded
124 18 : = partialeq_impls (std::move (fn), type_name, item.get_generic_params ());
125 9 : }
126 :
127 : void
128 63 : DerivePartialEq::visit_struct (StructStruct &item)
129 : {
130 126 : auto type_name = item.get_struct_name ().as_string ();
131 63 : auto fields = SelfOther::fields (builder, item.get_fields ());
132 :
133 189 : auto fn = eq_fn (builder.block (build_eq_expression (std::move (fields))),
134 63 : type_name);
135 :
136 63 : expanded
137 126 : = partialeq_impls (std::move (fn), type_name, item.get_generic_params ());
138 63 : }
139 :
140 : MatchCase
141 0 : DerivePartialEq::match_enum_identifier (
142 : PathInExpression variant_path, const std::unique_ptr<EnumItem> &variant)
143 : {
144 0 : auto inner_ref_patterns
145 0 : = vec (builder.ref_pattern (
146 0 : std::unique_ptr<Pattern> (new PathInExpression (variant_path))),
147 0 : builder.ref_pattern (
148 0 : std::unique_ptr<Pattern> (new PathInExpression (variant_path))));
149 :
150 0 : auto tuple_items = std::make_unique<TuplePatternItemsNoRest> (
151 0 : std::move (inner_ref_patterns));
152 :
153 0 : auto pattern = std::make_unique<TuplePattern> (std::move (tuple_items), loc);
154 :
155 0 : return builder.match_case (std::move (pattern), builder.literal_bool (true));
156 0 : }
157 :
158 : MatchCase
159 0 : DerivePartialEq::match_enum_tuple (PathInExpression variant_path,
160 : const EnumItemTuple &variant)
161 : {
162 0 : auto self_patterns = std::vector<std::unique_ptr<Pattern>> ();
163 0 : auto other_patterns = std::vector<std::unique_ptr<Pattern>> ();
164 :
165 0 : auto self_other_exprs = std::vector<SelfOther> ();
166 :
167 0 : for (size_t i = 0; i < variant.get_tuple_fields ().size (); i++)
168 : {
169 : // The patterns we're creating for each field are `self_<i>` and
170 : // `other_<i>` where `i` is the index of the field. It doesn't actually
171 : // matter what we use, as long as it's ordered, unique, and that we can
172 : // reuse it in the match case's return expression to check that they are
173 : // equal.
174 :
175 0 : auto self_pattern_str = "__self_" + std::to_string (i);
176 0 : auto other_pattern_str = "__other_" + std::to_string (i);
177 :
178 0 : self_patterns.emplace_back (
179 0 : builder.identifier_pattern (self_pattern_str));
180 0 : other_patterns.emplace_back (
181 0 : builder.identifier_pattern (other_pattern_str));
182 :
183 0 : self_other_exprs.emplace_back (SelfOther{
184 0 : builder.identifier (self_pattern_str),
185 0 : builder.identifier (other_pattern_str),
186 : });
187 0 : }
188 :
189 0 : auto self_pattern_items = std::unique_ptr<TupleStructItems> (
190 0 : new TupleStructItemsNoRest (std::move (self_patterns)));
191 0 : auto other_pattern_items = std::unique_ptr<TupleStructItems> (
192 0 : new TupleStructItemsNoRest (std::move (other_patterns)));
193 :
194 0 : auto self_pattern = std::unique_ptr<Pattern> (
195 0 : new ReferencePattern (std::unique_ptr<Pattern> (new TupleStructPattern (
196 0 : variant_path, std::move (self_pattern_items))),
197 0 : false, false, loc));
198 0 : auto other_pattern = std::unique_ptr<Pattern> (
199 0 : new ReferencePattern (std::unique_ptr<Pattern> (new TupleStructPattern (
200 0 : variant_path, std::move (other_pattern_items))),
201 0 : false, false, loc));
202 :
203 0 : auto tuple_items = std::make_unique<TuplePatternItemsNoRest> (
204 0 : vec (std::move (self_pattern), std::move (other_pattern)));
205 :
206 0 : auto pattern = std::make_unique<TuplePattern> (std::move (tuple_items), loc);
207 :
208 0 : auto expr = build_eq_expression (std::move (self_other_exprs));
209 :
210 0 : return builder.match_case (std::move (pattern), std::move (expr));
211 0 : }
212 :
213 : MatchCase
214 0 : DerivePartialEq::match_enum_struct (PathInExpression variant_path,
215 : const EnumItemStruct &variant)
216 : {
217 0 : auto self_fields = std::vector<std::unique_ptr<StructPatternField>> ();
218 0 : auto other_fields = std::vector<std::unique_ptr<StructPatternField>> ();
219 :
220 0 : auto self_other_exprs = std::vector<SelfOther> ();
221 :
222 0 : for (auto &field : variant.get_struct_fields ())
223 : {
224 : // The patterns we're creating for each field are `self_<field>` and
225 : // `other_<field>` where `field` is the name of the field. It doesn't
226 : // actually matter what we use, as long as it's ordered, unique, and that
227 : // we can reuse it in the match case's return expression to check that
228 : // they are equal.
229 :
230 0 : auto field_name = field.get_field_name ().as_string ();
231 :
232 0 : auto self_pattern_str = "__self_" + field_name;
233 0 : auto other_pattern_str = "__other_" + field_name;
234 :
235 0 : self_fields.emplace_back (builder.struct_pattern_ident_pattern (
236 0 : field_name, builder.identifier_pattern (self_pattern_str)));
237 0 : other_fields.emplace_back (builder.struct_pattern_ident_pattern (
238 0 : field_name, builder.identifier_pattern (other_pattern_str)));
239 :
240 0 : self_other_exprs.emplace_back (SelfOther{
241 0 : builder.identifier (self_pattern_str),
242 0 : builder.identifier (other_pattern_str),
243 : });
244 0 : }
245 :
246 0 : auto self_elts = StructPatternElements (std::move (self_fields));
247 0 : auto other_elts = StructPatternElements (std::move (other_fields));
248 :
249 0 : auto self_pattern = std::unique_ptr<Pattern> (
250 0 : new ReferencePattern (std::unique_ptr<Pattern> (new StructPattern (
251 0 : variant_path, loc, std::move (self_elts))),
252 0 : false, false, loc));
253 0 : auto other_pattern = std::unique_ptr<Pattern> (
254 0 : new ReferencePattern (std::unique_ptr<Pattern> (new StructPattern (
255 0 : variant_path, loc, std::move (other_elts))),
256 0 : false, false, loc));
257 :
258 0 : auto tuple_items = std::make_unique<TuplePatternItemsNoRest> (
259 0 : vec (std::move (self_pattern), std::move (other_pattern)));
260 :
261 0 : auto pattern = std::make_unique<TuplePattern> (std::move (tuple_items), loc);
262 :
263 0 : auto expr = build_eq_expression (std::move (self_other_exprs));
264 :
265 0 : return builder.match_case (std::move (pattern), std::move (expr));
266 0 : }
267 :
268 : void
269 22 : DerivePartialEq::visit_enum (Enum &item)
270 : {
271 22 : auto cases = std::vector<MatchCase> ();
272 44 : auto type_name = item.get_identifier ().as_string ();
273 :
274 59 : auto eq_expr_fn = [this] (std::vector<SelfOther> &&fields) {
275 37 : return build_eq_expression (std::move (fields));
276 22 : };
277 :
278 22 : auto let_sd
279 22 : = builder.discriminant_value (DerivePartialEq::self_discr, "self");
280 22 : auto let_od
281 22 : = builder.discriminant_value (DerivePartialEq::other_discr, "other");
282 :
283 22 : auto discr_cmp
284 44 : = builder.comparison_expr (builder.identifier (DerivePartialEq::self_discr),
285 44 : builder.identifier (
286 22 : DerivePartialEq::other_discr),
287 22 : ComparisonOperator::EQUAL);
288 :
289 81 : for (auto &variant : item.get_variants ())
290 : {
291 59 : auto enum_builder
292 118 : = EnumMatchBuilder (type_name, variant->get_identifier ().as_string (),
293 59 : eq_expr_fn, builder);
294 :
295 59 : switch (variant->get_enum_item_kind ())
296 : {
297 22 : case EnumItem::Kind::Tuple:
298 22 : cases.emplace_back (enum_builder.tuple (*variant));
299 22 : break;
300 15 : case EnumItem::Kind::Struct:
301 15 : cases.emplace_back (enum_builder.strukt (*variant));
302 15 : break;
303 : case EnumItem::Kind::Identifier:
304 : case EnumItem::Kind::Discriminant:
305 : // We don't need to do anything for these, as they are handled by the
306 : // discriminant value comparison
307 : break;
308 : }
309 59 : }
310 :
311 : // In case the two instances of `Self` don't have the same discriminant,
312 : // automatically return false.
313 22 : cases.emplace_back (
314 44 : builder.match_case (builder.wildcard (), std::move (discr_cmp)));
315 :
316 22 : auto match
317 44 : = builder.match (builder.tuple (vec (builder.identifier ("self"),
318 44 : builder.identifier ("other"))),
319 22 : std::move (cases));
320 :
321 66 : auto fn = eq_fn (builder.block (vec (std::move (let_sd), std::move (let_od)),
322 : std::move (match)),
323 22 : type_name);
324 :
325 22 : expanded
326 44 : = partialeq_impls (std::move (fn), type_name, item.get_generic_params ());
327 22 : }
328 :
329 : void
330 0 : DerivePartialEq::visit_union (Union &item)
331 : {
332 0 : rust_error_at (item.get_locus (),
333 : "derive(PartialEq) cannot be used on unions");
334 0 : }
335 :
336 : } // namespace AST
337 : } // namespace Rust
|