Skip to content

Commit 771b0c3

Browse files
committed
derive(PartialEq): Add partial implementation
We are still missing some deriving for enums, as part of our codegen and nameres for rebinding struct field patterns is missing. gcc/rust/ChangeLog: * expand/rust-derive-partial-eq.cc: New file. * expand/rust-derive-partial-eq.h: New file. * expand/rust-derive.cc (DeriveVisitor::derive): Call them. * Make-lang.in: Compile them. gcc/testsuite/ChangeLog: * rust/compile/derive-eq-invalid.rs: Mark PartialEq def as a lang item. * rust/compile/derive-partialeq1.rs: New test. * rust/execute/torture/derive-partialeq1.rs: New test. * rust/compile/nr2/exclude: Exclude all of them.
1 parent 2c0cf9f commit 771b0c3

File tree

8 files changed

+520
-5
lines changed

8 files changed

+520
-5
lines changed

gcc/rust/Make-lang.in

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

0 commit comments

Comments
 (0)