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