-
Notifications
You must be signed in to change notification settings - Fork 170
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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.
- Loading branch information
1 parent
ddd37fb
commit 5b6bdff
Showing
8 changed files
with
520 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,308 @@ | ||
// Copyright (C) 2020-2024 Free Software Foundation, Inc. | ||
|
||
// This file is part of GCC. | ||
|
||
// GCC is free software; you can redistribute it and/or modify it under | ||
// the terms of the GNU General Public License as published by the Free | ||
// Software Foundation; either version 3, or (at your option) any later | ||
// version. | ||
|
||
// GCC is distributed in the hope that it will be useful, but WITHOUT ANY | ||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
// for more details. | ||
|
||
// You should have received a copy of the GNU General Public License | ||
// along with GCC; see the file COPYING3. If not see | ||
// <http://www.gnu.org/licenses/>. | ||
|
||
#include "rust-derive-partial-eq.h" | ||
#include "rust-ast.h" | ||
#include "rust-expr.h" | ||
#include "rust-item.h" | ||
#include "rust-operators.h" | ||
#include "rust-path.h" | ||
#include "rust-pattern.h" | ||
#include "rust-system.h" | ||
|
||
namespace Rust { | ||
namespace AST { | ||
DerivePartialEq::DerivePartialEq (location_t loc) | ||
: DeriveVisitor (loc), expanded (nullptr) | ||
{} | ||
|
||
std::unique_ptr<AST::Item> | ||
DerivePartialEq::go (Item &item) | ||
{ | ||
item.accept_vis (*this); | ||
|
||
rust_assert (expanded); | ||
|
||
return std::move (expanded); | ||
} | ||
|
||
std::unique_ptr<Item> | ||
DerivePartialEq::partial_eq_impl ( | ||
std::unique_ptr<AssociatedItem> &&eq_fn, std::string name, | ||
const std::vector<std::unique_ptr<GenericParam>> &type_generics) | ||
{ | ||
auto eq = builder.type_path (LangItem::Kind::EQ); | ||
|
||
auto trait_items = vec (std::move (eq_fn)); | ||
|
||
auto generics | ||
= setup_impl_generics (name, type_generics, builder.trait_bound (eq)); | ||
|
||
return builder.trait_impl (eq, std::move (generics.self_type), | ||
std::move (trait_items), | ||
std::move (generics.impl)); | ||
} | ||
|
||
std::unique_ptr<AssociatedItem> | ||
DerivePartialEq::eq_fn (std::unique_ptr<Expr> &&cmp_expression, | ||
std::string type_name) | ||
{ | ||
auto block = builder.block (tl::nullopt, std::move (cmp_expression)); | ||
|
||
auto self_type | ||
= std::unique_ptr<TypeNoBounds> (new TypePath (builder.type_path ("Self"))); | ||
|
||
auto params | ||
= vec (builder.self_ref_param (), | ||
builder.function_param (builder.identifier_pattern ("other"), | ||
builder.reference_type ( | ||
std::move (self_type)))); | ||
|
||
return builder.function ("eq", std::move (params), | ||
builder.single_type_path ("bool"), | ||
std::move (block)); | ||
} | ||
|
||
DerivePartialEq::SelfOther | ||
DerivePartialEq::tuple_indexes (int idx) | ||
{ | ||
return SelfOther{ | ||
builder.tuple_idx ("self", idx), | ||
builder.tuple_idx ("other", idx), | ||
}; | ||
} | ||
|
||
DerivePartialEq::SelfOther | ||
DerivePartialEq::field_acccesses (const std::string &field_name) | ||
{ | ||
return SelfOther{ | ||
builder.field_access (builder.identifier ("self"), field_name), | ||
builder.field_access (builder.identifier ("other"), field_name), | ||
}; | ||
} | ||
|
||
std::unique_ptr<Expr> | ||
DerivePartialEq::build_eq_expression ( | ||
std::vector<SelfOther> &&field_expressions) | ||
{ | ||
// for unit structs or empty tuples, this is always true | ||
if (field_expressions.empty ()) | ||
return builder.literal_bool (true); | ||
|
||
auto cmp_expression | ||
= builder.comparison_expr (std::move (field_expressions.at (0).self_expr), | ||
std::move (field_expressions.at (0).other_expr), | ||
ComparisonOperator::EQUAL); | ||
|
||
for (size_t i = 1; i < field_expressions.size (); i++) | ||
{ | ||
auto tmp = builder.comparison_expr ( | ||
std::move (field_expressions.at (i).self_expr), | ||
std::move (field_expressions.at (i).other_expr), | ||
ComparisonOperator::EQUAL); | ||
|
||
cmp_expression | ||
= builder.boolean_operation (std::move (cmp_expression), | ||
std::move (tmp), | ||
LazyBooleanOperator::LOGICAL_AND); | ||
} | ||
|
||
return cmp_expression; | ||
} | ||
|
||
void | ||
DerivePartialEq::visit_tuple (TupleStruct &item) | ||
{ | ||
auto type_name = item.get_struct_name ().as_string (); | ||
auto fields = std::vector<SelfOther> (); | ||
|
||
for (size_t idx = 0; idx < item.get_fields ().size (); idx++) | ||
fields.emplace_back (tuple_indexes (idx)); | ||
|
||
auto fn = eq_fn (build_eq_expression (std::move (fields)), type_name); | ||
|
||
expanded | ||
= partial_eq_impl (std::move (fn), type_name, item.get_generic_params ()); | ||
} | ||
|
||
void | ||
DerivePartialEq::visit_struct (StructStruct &item) | ||
{ | ||
auto type_name = item.get_struct_name ().as_string (); | ||
auto fields = std::vector<SelfOther> (); | ||
|
||
for (auto &field : item.get_fields ()) | ||
fields.emplace_back ( | ||
field_acccesses (field.get_field_name ().as_string ())); | ||
|
||
auto fn = eq_fn (build_eq_expression (std::move (fields)), type_name); | ||
|
||
expanded | ||
= partial_eq_impl (std::move (fn), type_name, item.get_generic_params ()); | ||
} | ||
|
||
MatchCase | ||
DerivePartialEq::match_enum_identifier ( | ||
PathInExpression variant_path, const std::unique_ptr<EnumItem> &variant) | ||
{ | ||
auto inner_ref_patterns | ||
= vec (builder.ref_pattern ( | ||
std::unique_ptr<Pattern> (new PathInExpression (variant_path))), | ||
builder.ref_pattern ( | ||
std::unique_ptr<Pattern> (new PathInExpression (variant_path)))); | ||
|
||
auto tuple_items = std::make_unique<TuplePatternItemsMultiple> ( | ||
std::move (inner_ref_patterns)); | ||
|
||
auto pattern = std::make_unique<TuplePattern> (std::move (tuple_items), loc); | ||
|
||
return builder.match_case (std::move (pattern), builder.literal_bool (true)); | ||
} | ||
|
||
MatchCase | ||
DerivePartialEq::match_enum_tuple (PathInExpression variant_path, | ||
const EnumItemTuple &variant) | ||
{ | ||
auto self_patterns = std::vector<std::unique_ptr<Pattern>> (); | ||
auto other_patterns = std::vector<std::unique_ptr<Pattern>> (); | ||
|
||
auto self_other_exprs = std::vector<SelfOther> (); | ||
|
||
for (size_t i = 0; i < variant.get_tuple_fields ().size (); i++) | ||
{ | ||
// The patterns we're creating for each field are `self_<i>` and | ||
// `other_<i>` where `i` is the index of the field. It doesn't actually | ||
// matter what we use, as long as it's ordered, unique, and that we can | ||
// reuse it in the match case's return expression to check that they are | ||
// equal. | ||
|
||
auto self_pattern_str = "__self_" + std::to_string (i); | ||
auto other_pattern_str = "__other_" + std::to_string (i); | ||
|
||
rust_debug ("]ARTHUR[ %s", self_pattern_str.c_str ()); | ||
|
||
self_patterns.emplace_back ( | ||
builder.identifier_pattern (self_pattern_str)); | ||
other_patterns.emplace_back ( | ||
builder.identifier_pattern (other_pattern_str)); | ||
|
||
self_other_exprs.emplace_back (SelfOther{ | ||
builder.identifier (self_pattern_str), | ||
builder.identifier (other_pattern_str), | ||
}); | ||
} | ||
|
||
auto self_pattern_items = std::unique_ptr<TupleStructItems> ( | ||
new TupleStructItemsNoRange (std::move (self_patterns))); | ||
auto other_pattern_items = std::unique_ptr<TupleStructItems> ( | ||
new TupleStructItemsNoRange (std::move (other_patterns))); | ||
|
||
auto self_pattern = std::unique_ptr<Pattern> ( | ||
new ReferencePattern (std::unique_ptr<Pattern> (new TupleStructPattern ( | ||
variant_path, std::move (self_pattern_items))), | ||
false, false, loc)); | ||
auto other_pattern = std::unique_ptr<Pattern> ( | ||
new ReferencePattern (std::unique_ptr<Pattern> (new TupleStructPattern ( | ||
variant_path, std::move (other_pattern_items))), | ||
false, false, loc)); | ||
|
||
auto tuple_items = std::make_unique<TuplePatternItemsMultiple> ( | ||
vec (std::move (self_pattern), std::move (other_pattern))); | ||
|
||
auto pattern = std::make_unique<TuplePattern> (std::move (tuple_items), loc); | ||
|
||
auto expr = build_eq_expression (std::move (self_other_exprs)); | ||
|
||
return builder.match_case (std::move (pattern), std::move (expr)); | ||
} | ||
|
||
MatchCase | ||
DerivePartialEq::match_enum_struct (PathInExpression variant_path, | ||
const EnumItemStruct &variant) | ||
{ | ||
// NOTE: We currently do not support compiling struct patterns where an | ||
// identifier is assigned a new pattern, e.g. Bloop { f0: x } | ||
// This is what we should be using to compile PartialEq for enum struct | ||
// variants, as we need to be comparing the field of each instance meaning we | ||
// need to give two different names to two different instances of the same | ||
// field. We cannot just use the field's name like we do when deriving | ||
// `Clone`. | ||
|
||
rust_unreachable (); | ||
} | ||
|
||
void | ||
DerivePartialEq::visit_enum (Enum &item) | ||
{ | ||
auto cases = std::vector<MatchCase> (); | ||
|
||
for (auto &variant : item.get_variants ()) | ||
{ | ||
auto variant_path | ||
= builder.variant_path (item.get_identifier ().as_string (), | ||
variant->get_identifier ().as_string ()); | ||
|
||
switch (variant->get_enum_item_kind ()) | ||
{ | ||
case EnumItem::Kind::Identifier: | ||
case EnumItem::Kind::Discriminant: | ||
cases.emplace_back (match_enum_identifier (variant_path, variant)); | ||
break; | ||
case EnumItem::Kind::Tuple: | ||
cases.emplace_back ( | ||
match_enum_tuple (variant_path, | ||
static_cast<EnumItemTuple &> (*variant))); | ||
break; | ||
case EnumItem::Kind::Struct: | ||
rust_sorry_at ( | ||
item.get_locus (), | ||
"cannot derive(PartialEq) for enum struct variants yet"); | ||
break; | ||
} | ||
} | ||
|
||
// NOTE: Mention using discriminant_value and skipping that last case, and | ||
// instead skipping all identifiers/discriminant enum items and returning | ||
// `true` in the wildcard case | ||
|
||
// In case the two instances of `Self` don't have the same discriminant, | ||
// automatically return false. | ||
cases.emplace_back ( | ||
builder.match_case (builder.wildcard (), builder.literal_bool (false))); | ||
|
||
auto match | ||
= builder.match (builder.tuple (vec (builder.identifier ("self"), | ||
builder.identifier ("other"))), | ||
std::move (cases)); | ||
|
||
auto fn = eq_fn (std::move (match), item.get_identifier ().as_string ()); | ||
|
||
expanded | ||
= partial_eq_impl (std::move (fn), item.get_identifier ().as_string (), | ||
item.get_generic_params ()); | ||
} | ||
|
||
void | ||
DerivePartialEq::visit_union (Union &item) | ||
{ | ||
rust_error_at (item.get_locus (), | ||
"derive(PartialEq) cannot be used on unions"); | ||
} | ||
|
||
} // namespace AST | ||
} // namespace Rust |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
// Copyright (C) 2025 Free Software Foundation, Inc. | ||
|
||
// This file is part of GCC. | ||
|
||
// GCC is free software; you can redistribute it and/or modify it under | ||
// the terms of the GNU General Public License as published by the Free | ||
// Software Foundation; either version 3, or (at your option) any later | ||
// version. | ||
|
||
// GCC is distributed in the hope that it will be useful, but WITHOUT ANY | ||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
// for more details. | ||
|
||
// You should have received a copy of the GNU General Public License | ||
// along with GCC; see the file COPYING3. If not see | ||
// <http://www.gnu.org/licenses/>. | ||
|
||
#ifndef RUST_DERIVE_PARTIAL_EQ_H | ||
#define RUST_DERIVE_PARTIAL_EQ_H | ||
|
||
#include "rust-derive.h" | ||
#include "rust-path.h" | ||
|
||
namespace Rust { | ||
namespace AST { | ||
|
||
class DerivePartialEq : DeriveVisitor | ||
{ | ||
public: | ||
DerivePartialEq (location_t loc); | ||
|
||
std::unique_ptr<AST::Item> go (Item &item); | ||
|
||
private: | ||
std::unique_ptr<Item> expanded; | ||
|
||
std::unique_ptr<Item> partial_eq_impl ( | ||
std::unique_ptr<AssociatedItem> &&eq_fn, std::string name, | ||
const std::vector<std::unique_ptr<GenericParam>> &type_generics); | ||
|
||
std::unique_ptr<AssociatedItem> eq_fn (std::unique_ptr<Expr> &&cmp_expression, | ||
std::string type_name); | ||
|
||
/** | ||
* A pair of two expressions from each instance being compared. E.g. this | ||
* could be `self.0` and `other.0`, or `self.field` and `other.field` | ||
*/ | ||
struct SelfOther | ||
{ | ||
std::unique_ptr<Expr> self_expr; | ||
std::unique_ptr<Expr> other_expr; | ||
}; | ||
|
||
SelfOther tuple_indexes (int idx); | ||
SelfOther field_acccesses (const std::string &field_name); | ||
|
||
/** | ||
* Build a suite of equality arithmetic expressions chained together by a | ||
* boolean AND operator | ||
*/ | ||
std::unique_ptr<Expr> | ||
build_eq_expression (std::vector<SelfOther> &&field_expressions); | ||
|
||
MatchCase match_enum_identifier (PathInExpression variant_path, | ||
const std::unique_ptr<EnumItem> &variant); | ||
MatchCase match_enum_tuple (PathInExpression variant_path, | ||
const EnumItemTuple &variant); | ||
MatchCase match_enum_struct (PathInExpression variant_path, | ||
const EnumItemStruct &variant); | ||
|
||
virtual void visit_struct (StructStruct &item); | ||
virtual void visit_tuple (TupleStruct &item); | ||
virtual void visit_enum (Enum &item); | ||
virtual void visit_union (Union &item); | ||
}; | ||
|
||
} // namespace AST | ||
} // namespace Rust | ||
|
||
#endif // ! RUST_DERIVE_PARTIAL_EQ_H |
Oops, something went wrong.