From 34b876dbea735410cd2274715630a212a08543d1 Mon Sep 17 00:00:00 2001 From: Arthur Cohen Date: Thu, 30 Jan 2025 14:04:17 +0100 Subject: [PATCH 1/8] derive(Eq): Add implementation. gcc/rust/ChangeLog: * expand/rust-derive.cc (DeriveVisitor::derive): Call into DeriveEq. * expand/rust-derive-eq.cc: New file. * expand/rust-derive-eq.h: New file. * Make-lang.in: Compile them. gcc/testsuite/ChangeLog: * rust/compile/derive-eq-invalid.rs: New test. --- gcc/rust/Make-lang.in | 1 + gcc/rust/expand/rust-derive-eq.cc | 207 ++++++++++++++++++ gcc/rust/expand/rust-derive-eq.h | 82 +++++++ gcc/rust/expand/rust-derive.cc | 2 + .../rust/compile/derive-eq-invalid.rs | 45 ++++ 5 files changed, 337 insertions(+) create mode 100644 gcc/rust/expand/rust-derive-eq.cc create mode 100644 gcc/rust/expand/rust-derive-eq.h create mode 100644 gcc/testsuite/rust/compile/derive-eq-invalid.rs diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index bb36063d1a7..287c18d1a8e 100644 --- a/gcc/rust/Make-lang.in +++ b/gcc/rust/Make-lang.in @@ -98,6 +98,7 @@ GRS_OBJS = \ rust/rust-derive-copy.o \ rust/rust-derive-debug.o \ rust/rust-derive-default.o \ + rust/rust-derive-eq.o \ rust/rust-proc-macro.o \ rust/rust-macro-invoc-lexer.o \ rust/rust-proc-macro-invoc-lexer.o \ diff --git a/gcc/rust/expand/rust-derive-eq.cc b/gcc/rust/expand/rust-derive-eq.cc new file mode 100644 index 00000000000..a2a7a769065 --- /dev/null +++ b/gcc/rust/expand/rust-derive-eq.cc @@ -0,0 +1,207 @@ +// 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 +// . + +#include "rust-derive-eq.h" +#include "rust-ast.h" +#include "rust-expr.h" +#include "rust-item.h" +#include "rust-path.h" +#include "rust-pattern.h" +#include "rust-system.h" + +namespace Rust { +namespace AST { + +std::unique_ptr +DeriveEq::assert_receiver_is_total_eq_fn ( + std::vector> &&types) +{ + auto stmts = std::vector> (); + + stmts.emplace_back (assert_param_is_eq ()); + + for (auto &&type : types) + stmts.emplace_back (assert_type_is_eq (std::move (type))); + + auto block = std::unique_ptr ( + new BlockExpr (std::move (stmts), nullptr, {}, {}, AST::LoopLabel::error (), + loc, loc)); + + auto self = builder.self_ref_param (); + + return builder.function ("assert_receiver_is_total_eq", + vec (std::move (self)), {}, std::move (block)); +} + +std::unique_ptr +DeriveEq::assert_param_is_eq () +{ + auto eq_bound = std::unique_ptr ( + new TraitBound (builder.type_path ({"core", "cmp", "Eq"}, true), loc)); + + auto sized_bound = std::unique_ptr ( + new TraitBound (builder.type_path (LangItem::Kind::SIZED), loc, false, + true /* opening_question_mark */)); + + auto bounds = vec (std::move (eq_bound), std::move (sized_bound)); + + auto assert_param_is_eq = "AssertParamIsEq"; + + auto t = std::unique_ptr ( + new TypeParam (Identifier ("T"), loc, std::move (bounds))); + + return builder.struct_struct ( + assert_param_is_eq, vec (std::move (t)), + {StructField ( + Identifier ("_t"), + builder.single_generic_type_path ( + LangItem::Kind::PHANTOM_DATA, + GenericArgs ( + {}, {GenericArg::create_type (builder.single_type_path ("T"))}, {})), + Visibility::create_private (), loc)}); +} + +std::unique_ptr +DeriveEq::assert_type_is_eq (std::unique_ptr &&type) +{ + auto assert_param_is_eq = "AssertParamIsEq"; + + // AssertParamIsCopy:: + auto assert_param_is_eq_ty + = std::unique_ptr (new TypePathSegmentGeneric ( + PathIdentSegment (assert_param_is_eq, loc), false, + GenericArgs ({}, {GenericArg::create_type (std::move (type))}, {}, loc), + loc)); + + // TODO: Improve this, it's really ugly + auto type_paths = std::vector> (); + type_paths.emplace_back (std::move (assert_param_is_eq_ty)); + + auto full_path + = std::unique_ptr (new TypePath ({std::move (type_paths)}, loc)); + + return builder.let (builder.wildcard (), std::move (full_path)); +} + +std::unique_ptr +DeriveEq::eq_impl ( + std::unique_ptr &&fn, std::string name, + const std::vector> &type_generics) +{ + auto eq = builder.type_path ({"core", "cmp", "Eq"}, true); + + auto trait_items = vec (std::move (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)); +} + +DeriveEq::DeriveEq (location_t loc) : DeriveVisitor (loc), expanded (nullptr) {} + +std::unique_ptr +DeriveEq::go (Item &item) +{ + item.accept_vis (*this); + + rust_assert (expanded); + + return std::move (expanded); +} + +void +DeriveEq::visit_tuple (TupleStruct &item) +{ + auto types = std::vector> (); + + for (auto &field : item.get_fields ()) + types.emplace_back (field.get_field_type ().clone_type ()); + + expanded + = eq_impl (assert_receiver_is_total_eq_fn (std::move (types)), + item.get_identifier ().as_string (), item.get_generic_params ()); +} + +void +DeriveEq::visit_struct (StructStruct &item) +{ + auto types = std::vector> (); + + for (auto &field : item.get_fields ()) + types.emplace_back (field.get_field_type ().clone_type ()); + + expanded + = eq_impl (assert_receiver_is_total_eq_fn (std::move (types)), + item.get_identifier ().as_string (), item.get_generic_params ()); +} + +void +DeriveEq::visit_enum (Enum &item) +{ + auto types = std::vector> (); + + for (auto &variant : item.get_variants ()) + { + switch (variant->get_enum_item_kind ()) + { + case EnumItem::Kind::Identifier: + case EnumItem::Kind::Discriminant: + // nothing to do as they contain no inner types + continue; + case EnumItem::Kind::Tuple: { + auto tuple = static_cast (*variant); + + for (auto &field : tuple.get_tuple_fields ()) + types.emplace_back (field.get_field_type ().clone_type ()); + + break; + } + case EnumItem::Kind::Struct: { + auto tuple = static_cast (*variant); + + for (auto &field : tuple.get_struct_fields ()) + types.emplace_back (field.get_field_type ().clone_type ()); + + break; + } + } + } + + expanded + = eq_impl (assert_receiver_is_total_eq_fn (std::move (types)), + item.get_identifier ().as_string (), item.get_generic_params ()); +} + +void +DeriveEq::visit_union (Union &item) +{ + auto types = std::vector> (); + + for (auto &field : item.get_variants ()) + types.emplace_back (field.get_field_type ().clone_type ()); + + expanded + = eq_impl (assert_receiver_is_total_eq_fn (std::move (types)), + item.get_identifier ().as_string (), item.get_generic_params ()); +} + +} // namespace AST +} // namespace Rust diff --git a/gcc/rust/expand/rust-derive-eq.h b/gcc/rust/expand/rust-derive-eq.h new file mode 100644 index 00000000000..655f1e82e02 --- /dev/null +++ b/gcc/rust/expand/rust-derive-eq.h @@ -0,0 +1,82 @@ +// 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 +// . + +#ifndef RUST_DERIVE_EQ_H +#define RUST_DERIVE_EQ_H + +#include "rust-derive.h" + +namespace Rust { +namespace AST { + +// FIXME: Need to figure out structuraleq marker trait + +class DeriveEq : DeriveVisitor +{ +public: + DeriveEq (location_t loc); + + std::unique_ptr go (Item &item); + +private: + std::unique_ptr expanded; + + /** + * Create the actual `assert_receiver_is_total_eq` function of the + * implementation, which asserts that every type contained within our targeted + * type also implements `Eq`. + */ + std::unique_ptr + assert_receiver_is_total_eq_fn (std::vector> &&types); + + /** + * Create the Eq trait implementation for a type + * + * impl Eq for { + * + * } + * + */ + std::unique_ptr + eq_impl (std::unique_ptr &&fn, std::string name, + const std::vector> &type_generics); + + /** + * Generate the following structure definition + * + * struct AssertParamIsEq { _t: PhantomData } + */ + std::unique_ptr assert_param_is_eq (); + + /** + * Generate a let statement to assert a type implements `Eq` + * + * let _: AssertParamIsEq; + */ + std::unique_ptr assert_type_is_eq (std::unique_ptr &&type); + + 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_EQ_H diff --git a/gcc/rust/expand/rust-derive.cc b/gcc/rust/expand/rust-derive.cc index d0ff06710f0..8226a61a787 100644 --- a/gcc/rust/expand/rust-derive.cc +++ b/gcc/rust/expand/rust-derive.cc @@ -21,6 +21,7 @@ #include "rust-derive-copy.h" #include "rust-derive-debug.h" #include "rust-derive-default.h" +#include "rust-derive-eq.h" namespace Rust { namespace AST { @@ -48,6 +49,7 @@ DeriveVisitor::derive (Item &item, const Attribute &attr, case BuiltinMacro::Default: return DeriveDefault (attr.get_locus ()).go (item); case BuiltinMacro::Eq: + return DeriveEq (attr.get_locus ()).go (item); case BuiltinMacro::PartialEq: case BuiltinMacro::Ord: case BuiltinMacro::PartialOrd: diff --git a/gcc/testsuite/rust/compile/derive-eq-invalid.rs b/gcc/testsuite/rust/compile/derive-eq-invalid.rs new file mode 100644 index 00000000000..017241db86d --- /dev/null +++ b/gcc/testsuite/rust/compile/derive-eq-invalid.rs @@ -0,0 +1,45 @@ +mod core { + mod cmp { + pub trait PartialEq { + fn eq(&self, other: &Rhs) -> bool; + + fn ne(&self, other: &Rhs) -> bool { + !self.eq(other) + } + } + + pub trait Eq: PartialEq { + fn assert_receiver_is_total_eq(&self) {} + } + } +} + +// #[lang = "phantom_data"] +// struct PhantomData; + +// #[lang = "sized"] +// trait Sized {} + +#[derive(PartialEq)] +struct NotEq; + +#[derive(Eq, PartialEq)] // { dg-error "bounds not satisfied for NotEq .Eq." } +struct Container(NotEq); + +// #[derive(Eq)] +// struct Foo { a: i32 } +// #[derive(Eq)] +// struct Bar(i32); + +// #[derive(Eq)] +// enum Baz { +// A, +// B(i32), +// C { a: i32 } +// } + +// #[derive(Eq)] +// union Qux { +// a: i32, +// b: i64, +// } From 4ef6702c832629d1d82d96014972fd6a63f6eb72 Mon Sep 17 00:00:00 2001 From: Arthur Cohen Date: Thu, 30 Jan 2025 14:56:37 +0100 Subject: [PATCH 2/8] ast-builder: Improve function generation. gcc/rust/ChangeLog: * ast/rust-ast-builder.cc (Builder::block): Change return type. (Builder::loop): Use new APIs. * ast/rust-ast-builder.h: Change return type of block functions. --- gcc/rust/ast/rust-ast-builder.cc | 22 +++++++++++----------- gcc/rust/ast/rust-ast-builder.h | 12 ++++++------ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/gcc/rust/ast/rust-ast-builder.cc b/gcc/rust/ast/rust-ast-builder.cc index e06b81c86d7..88acb7a3e01 100644 --- a/gcc/rust/ast/rust-ast-builder.cc +++ b/gcc/rust/ast/rust-ast-builder.cc @@ -279,23 +279,25 @@ Builder::path_in_expression (LangItem::Kind lang_item) const return PathInExpression (lang_item, {}, loc); } -std::unique_ptr -Builder::block (std::unique_ptr &&stmt, +std::unique_ptr +Builder::block (tl::optional> &&stmt, std::unique_ptr &&tail_expr) const { auto stmts = std::vector> (); - stmts.emplace_back (std::move (stmt)); + + if (stmt) + stmts.emplace_back (std::move (*stmt)); return block (std::move (stmts), std::move (tail_expr)); } -std::unique_ptr +std::unique_ptr Builder::block (std::vector> &&stmts, std::unique_ptr &&tail_expr) const { - return std::unique_ptr (new BlockExpr (std::move (stmts), - std::move (tail_expr), {}, {}, - LoopLabel::error (), loc, loc)); + return std::unique_ptr ( + new BlockExpr (std::move (stmts), std::move (tail_expr), {}, {}, + LoopLabel::error (), loc, loc)); } std::unique_ptr @@ -421,11 +423,9 @@ Builder::match_case (std::unique_ptr &&pattern, std::unique_ptr Builder::loop (std::vector> &&stmts) { - auto block = std::unique_ptr ( - new BlockExpr (std::move (stmts), nullptr, {}, {}, LoopLabel::error (), loc, - loc)); + auto block_expr = block (std::move (stmts), nullptr); - return std::unique_ptr (new LoopExpr (std::move (block), loc)); + return std::unique_ptr (new LoopExpr (std::move (block_expr), loc)); } std::unique_ptr diff --git a/gcc/rust/ast/rust-ast-builder.h b/gcc/rust/ast/rust-ast-builder.h index 6e4dfb8112f..3436244cb81 100644 --- a/gcc/rust/ast/rust-ast-builder.h +++ b/gcc/rust/ast/rust-ast-builder.h @@ -82,12 +82,12 @@ class Builder std::unique_ptr deref (std::unique_ptr &&of) const; /* Create a block with an optional tail expression */ - std::unique_ptr block (std::vector> &&stmts, - std::unique_ptr &&tail_expr - = nullptr) const; - std::unique_ptr block (std::unique_ptr &&stmt, - std::unique_ptr &&tail_expr - = nullptr) const; + std::unique_ptr block (std::vector> &&stmts, + std::unique_ptr &&tail_expr + = nullptr) const; + std::unique_ptr block (tl::optional> &&stmt, + std::unique_ptr &&tail_expr + = nullptr) const; /* Create an early return expression with an optional expression */ std::unique_ptr return_expr (std::unique_ptr &&to_return From b1a8720cf1bc1a0b0264283ea4b54065d2a8878e Mon Sep 17 00:00:00 2001 From: Arthur Cohen Date: Fri, 31 Jan 2025 13:24:02 +0000 Subject: [PATCH 3/8] ast-builder: Add new methods for creating operator expressions gcc/rust/ChangeLog: * ast/rust-ast-builder.cc (Builder::literal_bool): New method. (Builder::comparison_expr): Likewise. (Builder::boolean_operation): Likewise. * ast/rust-ast-builder.h: Declare them. --- gcc/rust/ast/rust-ast-builder.cc | 44 ++++++++++++++++++++++++++++++++ gcc/rust/ast/rust-ast-builder.h | 20 +++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/gcc/rust/ast/rust-ast-builder.cc b/gcc/rust/ast/rust-ast-builder.cc index 88acb7a3e01..4024e2d30eb 100644 --- a/gcc/rust/ast/rust-ast-builder.cc +++ b/gcc/rust/ast/rust-ast-builder.cc @@ -24,6 +24,7 @@ #include "rust-path.h" #include "rust-item.h" #include "rust-path.h" +#include "rust-pattern.h" #include "rust-system.h" #include "rust-token.h" #include @@ -39,6 +40,16 @@ Builder::literal_string (std::string &&content) const PrimitiveCoreType::CORETYPE_STR, {}, loc)); } +std::unique_ptr +Builder::literal_bool (bool b) const +{ + auto str = b ? "true" : "false"; + + return std::unique_ptr ( + new AST::LiteralExpr (std::move (str), Literal::LitType::BOOL, + PrimitiveCoreType::CORETYPE_BOOL, {}, loc)); +} + std::unique_ptr Builder::call (std::unique_ptr &&path, std::vector> &&args) const @@ -279,6 +290,14 @@ Builder::path_in_expression (LangItem::Kind lang_item) const return PathInExpression (lang_item, {}, loc); } +PathInExpression +Builder::variant_path (const std::string &enum_path, + const std::string &variant) const +{ + return PathInExpression ({path_segment (enum_path), path_segment (variant)}, + {}, loc, false); +} + std::unique_ptr Builder::block (tl::optional> &&stmt, std::unique_ptr &&tail_expr) const @@ -331,6 +350,24 @@ Builder::deref (std::unique_ptr &&of) const return std::unique_ptr (new DereferenceExpr (std::move (of), {}, loc)); } +std::unique_ptr +Builder::comparison_expr (std::unique_ptr &&lhs, + std::unique_ptr &&rhs, + ComparisonOperator op) const +{ + return std::make_unique (std::move (lhs), std::move (rhs), op, + loc); +} + +std::unique_ptr +Builder::boolean_operation (std::unique_ptr &&lhs, + std::unique_ptr &&rhs, + LazyBooleanOperator op) const +{ + return std::make_unique (std::move (lhs), std::move (rhs), + op, loc); +} + std::unique_ptr Builder::struct_struct (std::string struct_name, std::vector> &&generics, @@ -390,6 +427,13 @@ Builder::wildcard () const return std::unique_ptr (new WildcardPattern (loc)); } +std::unique_ptr +Builder::ref_pattern (std::unique_ptr &&inner) const +{ + return std::make_unique (std::move (inner), false, false, + loc); +} + std::unique_ptr Builder::lang_item_path (LangItem::Kind kind) const { diff --git a/gcc/rust/ast/rust-ast-builder.h b/gcc/rust/ast/rust-ast-builder.h index 3436244cb81..a1c56213449 100644 --- a/gcc/rust/ast/rust-ast-builder.h +++ b/gcc/rust/ast/rust-ast-builder.h @@ -23,6 +23,7 @@ #include "rust-expr.h" #include "rust-ast.h" #include "rust-item.h" +#include "rust-operators.h" namespace Rust { namespace AST { @@ -62,6 +63,9 @@ class Builder /* Create a string literal expression ("content") */ std::unique_ptr literal_string (std::string &&content) const; + /* Create a boolean literal expression (true) */ + std::unique_ptr literal_bool (bool b) const; + /* Create an identifier expression (`variable`) */ std::unique_ptr identifier (std::string name) const; std::unique_ptr identifier_pattern (std::string name, @@ -81,6 +85,16 @@ class Builder /* Create a dereference of an expression (`*of`) */ std::unique_ptr deref (std::unique_ptr &&of) const; + /* Build a comparison expression (`lhs == rhs`) */ + std::unique_ptr comparison_expr (std::unique_ptr &&lhs, + std::unique_ptr &&rhs, + ComparisonOperator op) const; + + /* Build a lazy boolean operator expression (`lhs && rhs`) */ + std::unique_ptr boolean_operation (std::unique_ptr &&lhs, + std::unique_ptr &&rhs, + LazyBooleanOperator op) const; + /* Create a block with an optional tail expression */ std::unique_ptr block (std::vector> &&stmts, std::unique_ptr &&tail_expr @@ -191,6 +205,10 @@ class Builder */ PathInExpression path_in_expression (LangItem::Kind lang_item) const; + /* Create the path to an enum's variant (`Result::Ok`) */ + PathInExpression variant_path (const std::string &enum_path, + const std::string &variant) const; + /* Create a new struct */ std::unique_ptr struct_struct (std::string struct_name, @@ -223,6 +241,8 @@ class Builder /* Create a wildcard pattern (`_`) */ std::unique_ptr wildcard () const; + /* Create a reference pattern (`&pattern`) */ + std::unique_ptr ref_pattern (std::unique_ptr &&inner) const; /* Create a lang item path usable as a general path */ std::unique_ptr lang_item_path (LangItem::Kind) const; From 2c0cf9f476d779f664f85f04c26b36dcd6c3c62f Mon Sep 17 00:00:00 2001 From: Arthur Cohen Date: Fri, 31 Jan 2025 17:58:24 +0000 Subject: [PATCH 4/8] derive(Clone): chore: Cleanup gcc/rust/ChangeLog: * expand/rust-derive-clone.cc: Cleanup implementation, avoid repetitions. * expand/rust-derive-clone.h: Likewise. --- gcc/rust/expand/rust-derive-clone.cc | 34 ++++++++++------------------ gcc/rust/expand/rust-derive-clone.h | 8 ++++--- 2 files changed, 17 insertions(+), 25 deletions(-) diff --git a/gcc/rust/expand/rust-derive-clone.cc b/gcc/rust/expand/rust-derive-clone.cc index 8a784acf783..3c4c8894631 100644 --- a/gcc/rust/expand/rust-derive-clone.cc +++ b/gcc/rust/expand/rust-derive-clone.cc @@ -18,7 +18,6 @@ #include "rust-derive-clone.h" #include "rust-ast.h" -#include "rust-ast-dump.h" #include "rust-expr.h" #include "rust-item.h" #include "rust-path.h" @@ -169,21 +168,10 @@ DeriveClone::visit_struct (StructStruct &item) item.get_generic_params ()); } -PathInExpression -DeriveClone::variant_match_path (Enum &item, const Identifier &variant) -{ - return PathInExpression ({builder.path_segment ( - item.get_identifier ().as_string ()), - builder.path_segment (variant.as_string ())}, - {}, loc, false); -} - MatchCase -DeriveClone::clone_enum_identifier (Enum &item, +DeriveClone::clone_enum_identifier (PathInExpression variant_path, const std::unique_ptr &variant) { - auto variant_path = variant_match_path (item, variant->get_identifier ()); - auto pattern = std::unique_ptr (new ReferencePattern ( std::unique_ptr (new PathInExpression (variant_path)), false, false, loc)); @@ -193,10 +181,9 @@ DeriveClone::clone_enum_identifier (Enum &item, } MatchCase -DeriveClone::clone_enum_tuple (Enum &item, const EnumItemTuple &variant) +DeriveClone::clone_enum_tuple (PathInExpression variant_path, + const EnumItemTuple &variant) { - auto variant_path = variant_match_path (item, variant.get_identifier ()); - auto patterns = std::vector> (); auto cloned_patterns = std::vector> (); @@ -232,10 +219,9 @@ DeriveClone::clone_enum_tuple (Enum &item, const EnumItemTuple &variant) } MatchCase -DeriveClone::clone_enum_struct (Enum &item, const EnumItemStruct &variant) +DeriveClone::clone_enum_struct (PathInExpression variant_path, + const EnumItemStruct &variant) { - auto variant_path = variant_match_path (item, variant.get_identifier ()); - auto field_patterns = std::vector> (); auto cloned_fields = std::vector> (); @@ -314,21 +300,25 @@ DeriveClone::visit_enum (Enum &item) for (const auto &variant : item.get_variants ()) { + auto path + = builder.variant_path (item.get_identifier ().as_string (), + variant->get_identifier ().as_string ()); + switch (variant->get_enum_item_kind ()) { // Identifiers and discriminated variants are the same for a clone - we // just return the same variant case EnumItem::Kind::Identifier: case EnumItem::Kind::Discriminant: - cases.emplace_back (clone_enum_identifier (item, variant)); + cases.emplace_back (clone_enum_identifier (path, variant)); break; case EnumItem::Kind::Tuple: cases.emplace_back ( - clone_enum_tuple (item, static_cast (*variant))); + clone_enum_tuple (path, static_cast (*variant))); break; case EnumItem::Kind::Struct: cases.emplace_back ( - clone_enum_struct (item, static_cast (*variant))); + clone_enum_struct (path, static_cast (*variant))); break; } } diff --git a/gcc/rust/expand/rust-derive-clone.h b/gcc/rust/expand/rust-derive-clone.h index 844b53f0472..e8613f59d17 100644 --- a/gcc/rust/expand/rust-derive-clone.h +++ b/gcc/rust/expand/rust-derive-clone.h @@ -73,10 +73,12 @@ class DeriveClone : DeriveVisitor /** * Implementation of clone for all possible variants of an enum */ - MatchCase clone_enum_identifier (Enum &item, + MatchCase clone_enum_identifier (PathInExpression variant_path, const std::unique_ptr &variant); - MatchCase clone_enum_tuple (Enum &item, const EnumItemTuple &variant); - MatchCase clone_enum_struct (Enum &item, const EnumItemStruct &variant); + MatchCase clone_enum_tuple (PathInExpression variant_path, + const EnumItemTuple &variant); + MatchCase clone_enum_struct (PathInExpression variant_path, + const EnumItemStruct &variant); virtual void visit_struct (StructStruct &item); virtual void visit_tuple (TupleStruct &item); From 771b0c37a506bf83bcdc02c812ebb56bd3fcada9 Mon Sep 17 00:00:00 2001 From: Arthur Cohen Date: Thu, 30 Jan 2025 14:41:11 +0100 Subject: [PATCH 5/8] 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. --- gcc/rust/Make-lang.in | 1 + gcc/rust/expand/rust-derive-partial-eq.cc | 308 ++++++++++++++++++ gcc/rust/expand/rust-derive-partial-eq.h | 81 +++++ gcc/rust/expand/rust-derive.cc | 2 + .../rust/compile/derive-eq-invalid.rs | 9 +- .../rust/compile/derive-partialeq1.rs | 59 ++++ gcc/testsuite/rust/compile/nr2/exclude | 4 +- .../rust/execute/torture/derive-partialeq1.rs | 61 ++++ 8 files changed, 520 insertions(+), 5 deletions(-) create mode 100644 gcc/rust/expand/rust-derive-partial-eq.cc create mode 100644 gcc/rust/expand/rust-derive-partial-eq.h create mode 100644 gcc/testsuite/rust/compile/derive-partialeq1.rs create mode 100644 gcc/testsuite/rust/execute/torture/derive-partialeq1.rs diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index 287c18d1a8e..c2df71021e2 100644 --- a/gcc/rust/Make-lang.in +++ b/gcc/rust/Make-lang.in @@ -98,6 +98,7 @@ GRS_OBJS = \ rust/rust-derive-copy.o \ rust/rust-derive-debug.o \ rust/rust-derive-default.o \ + rust/rust-derive-partial-eq.o \ rust/rust-derive-eq.o \ rust/rust-proc-macro.o \ rust/rust-macro-invoc-lexer.o \ diff --git a/gcc/rust/expand/rust-derive-partial-eq.cc b/gcc/rust/expand/rust-derive-partial-eq.cc new file mode 100644 index 00000000000..6f7ef7d8780 --- /dev/null +++ b/gcc/rust/expand/rust-derive-partial-eq.cc @@ -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 +// . + +#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 +DerivePartialEq::go (Item &item) +{ + item.accept_vis (*this); + + rust_assert (expanded); + + return std::move (expanded); +} + +std::unique_ptr +DerivePartialEq::partial_eq_impl ( + std::unique_ptr &&eq_fn, std::string name, + const std::vector> &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 +DerivePartialEq::eq_fn (std::unique_ptr &&cmp_expression, + std::string type_name) +{ + auto block = builder.block (tl::nullopt, std::move (cmp_expression)); + + auto self_type + = std::unique_ptr (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 +DerivePartialEq::build_eq_expression ( + std::vector &&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 (); + + 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 (); + + 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 &variant) +{ + auto inner_ref_patterns + = vec (builder.ref_pattern ( + std::unique_ptr (new PathInExpression (variant_path))), + builder.ref_pattern ( + std::unique_ptr (new PathInExpression (variant_path)))); + + auto tuple_items = std::make_unique ( + std::move (inner_ref_patterns)); + + auto pattern = std::make_unique (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> (); + auto other_patterns = std::vector> (); + + auto self_other_exprs = std::vector (); + + for (size_t i = 0; i < variant.get_tuple_fields ().size (); i++) + { + // The patterns we're creating for each field are `self_` and + // `other_` 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 ( + new TupleStructItemsNoRange (std::move (self_patterns))); + auto other_pattern_items = std::unique_ptr ( + new TupleStructItemsNoRange (std::move (other_patterns))); + + auto self_pattern = std::unique_ptr ( + new ReferencePattern (std::unique_ptr (new TupleStructPattern ( + variant_path, std::move (self_pattern_items))), + false, false, loc)); + auto other_pattern = std::unique_ptr ( + new ReferencePattern (std::unique_ptr (new TupleStructPattern ( + variant_path, std::move (other_pattern_items))), + false, false, loc)); + + auto tuple_items = std::make_unique ( + vec (std::move (self_pattern), std::move (other_pattern))); + + auto pattern = std::make_unique (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 (); + + 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 (*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 diff --git a/gcc/rust/expand/rust-derive-partial-eq.h b/gcc/rust/expand/rust-derive-partial-eq.h new file mode 100644 index 00000000000..2bc18d2b98a --- /dev/null +++ b/gcc/rust/expand/rust-derive-partial-eq.h @@ -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 +// . + +#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 go (Item &item); + +private: + std::unique_ptr expanded; + + std::unique_ptr partial_eq_impl ( + std::unique_ptr &&eq_fn, std::string name, + const std::vector> &type_generics); + + std::unique_ptr eq_fn (std::unique_ptr &&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 self_expr; + std::unique_ptr 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 + build_eq_expression (std::vector &&field_expressions); + + MatchCase match_enum_identifier (PathInExpression variant_path, + const std::unique_ptr &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 diff --git a/gcc/rust/expand/rust-derive.cc b/gcc/rust/expand/rust-derive.cc index 8226a61a787..6f026f3da64 100644 --- a/gcc/rust/expand/rust-derive.cc +++ b/gcc/rust/expand/rust-derive.cc @@ -22,6 +22,7 @@ #include "rust-derive-debug.h" #include "rust-derive-default.h" #include "rust-derive-eq.h" +#include "rust-derive-partial-eq.h" namespace Rust { namespace AST { @@ -51,6 +52,7 @@ DeriveVisitor::derive (Item &item, const Attribute &attr, case BuiltinMacro::Eq: return DeriveEq (attr.get_locus ()).go (item); case BuiltinMacro::PartialEq: + return DerivePartialEq (attr.get_locus ()).go (item); case BuiltinMacro::Ord: case BuiltinMacro::PartialOrd: case BuiltinMacro::Hash: diff --git a/gcc/testsuite/rust/compile/derive-eq-invalid.rs b/gcc/testsuite/rust/compile/derive-eq-invalid.rs index 017241db86d..0c4d48ef6ea 100644 --- a/gcc/testsuite/rust/compile/derive-eq-invalid.rs +++ b/gcc/testsuite/rust/compile/derive-eq-invalid.rs @@ -1,5 +1,6 @@ mod core { mod cmp { + #[lang = "eq"] pub trait PartialEq { fn eq(&self, other: &Rhs) -> bool; @@ -14,11 +15,11 @@ mod core { } } -// #[lang = "phantom_data"] -// struct PhantomData; +#[lang = "phantom_data"] +struct PhantomData; -// #[lang = "sized"] -// trait Sized {} +#[lang = "sized"] +trait Sized {} #[derive(PartialEq)] struct NotEq; diff --git a/gcc/testsuite/rust/compile/derive-partialeq1.rs b/gcc/testsuite/rust/compile/derive-partialeq1.rs new file mode 100644 index 00000000000..71513241929 --- /dev/null +++ b/gcc/testsuite/rust/compile/derive-partialeq1.rs @@ -0,0 +1,59 @@ +#![feature(intrinsics)] + +#[lang = "sized"] +trait Sized {} + +#[lang = "copy"] +trait Copy {} + +#[lang = "eq"] +pub trait PartialEq { + /// This method tests for `self` and `other` values to be equal, and is used + /// by `==`. + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + fn eq(&self, other: &Rhs) -> bool; + + /// This method tests for `!=`. + #[inline] + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + fn ne(&self, other: &Rhs) -> bool { + !self.eq(other) + } +} + +#[derive(PartialEq, Copy)] // { dg-warning "unused name" } +struct Foo; + +#[derive(PartialEq)] +struct Bar(Foo); + +#[derive(PartialEq)] +struct Baz { _inner: Foo } + +extern "C" { + fn puts(s: *const i8); +} + +fn print(b: bool) { + if b { + unsafe { puts("true" as *const str as *const i8) } + } else { + unsafe { puts("false" as *const str as *const i8) } + } +} + +fn main() -> i32 { + let x = Foo; + + let b1 = x == Foo; + let b2 = Bar(x) != Bar(Foo); + let b3 = Baz { _inner: Foo } != Baz { _inner: x }; + + print(b1); + print(b2); + print(b3); + + 0 +} diff --git a/gcc/testsuite/rust/compile/nr2/exclude b/gcc/testsuite/rust/compile/nr2/exclude index 6f6280d3b66..532504999a0 100644 --- a/gcc/testsuite/rust/compile/nr2/exclude +++ b/gcc/testsuite/rust/compile/nr2/exclude @@ -119,9 +119,11 @@ traits12.rs try-trait.rs derive-debug1.rs issue-3382.rs -derive-default1.rs issue-3402-1.rs for-loop1.rs for-loop2.rs issue-3403.rs +derive-default1.rs +derive-eq-invalid.rs +derive-partialeq1.rs # please don't delete the trailing newline diff --git a/gcc/testsuite/rust/execute/torture/derive-partialeq1.rs b/gcc/testsuite/rust/execute/torture/derive-partialeq1.rs new file mode 100644 index 00000000000..4d5124e85cf --- /dev/null +++ b/gcc/testsuite/rust/execute/torture/derive-partialeq1.rs @@ -0,0 +1,61 @@ +// { dg-output "true\r*\nfalse\r*\nfalse\r*\n" } + +#![feature(intrinsics)] + +#[lang = "sized"] +trait Sized {} + +#[lang = "copy"] +trait Copy {} + +#[lang = "eq"] +pub trait PartialEq { + /// This method tests for `self` and `other` values to be equal, and is used + /// by `==`. + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + fn eq(&self, other: &Rhs) -> bool; + + /// This method tests for `!=`. + #[inline] + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + fn ne(&self, other: &Rhs) -> bool { + !self.eq(other) + } +} + +#[derive(PartialEq, Copy)] // { dg-warning "unused name" } +struct Foo; + +#[derive(PartialEq)] +struct Bar(Foo); + +#[derive(PartialEq)] +struct Baz { _inner: Foo } + +extern "C" { + fn puts(s: *const i8); +} + +fn print(b: bool) { + if b { + unsafe { puts("true" as *const str as *const i8) } + } else { + unsafe { puts("false" as *const str as *const i8) } + } +} + +fn main() -> i32 { + let x = Foo; + + let b1 = x == Foo; + let b2 = Bar(x) != Bar(Foo); + let b3 = Baz { _inner: Foo } != Baz { _inner: x }; + + print(b1); + print(b2); + print(b3); + + 0 +} From 4d8b1bcc713c180e742c5c7b5f1943932ae8f784 Mon Sep 17 00:00:00 2001 From: Arthur Cohen Date: Tue, 4 Feb 2025 09:58:14 +0100 Subject: [PATCH 6/8] expand: Allow built-in derives to generate multiple items gcc/rust/ChangeLog: * expand/rust-derive.cc (DeriveVisitor::derive): Return a vector of items. * expand/rust-derive.h: Change return type. * expand/rust-expand-visitor.cc: Insert all generated items into the AST. --- gcc/rust/expand/rust-derive.cc | 16 ++++++++-------- gcc/rust/expand/rust-derive.h | 8 ++++++-- gcc/rust/expand/rust-expand-visitor.cc | 13 ++++++++----- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/gcc/rust/expand/rust-derive.cc b/gcc/rust/expand/rust-derive.cc index 6f026f3da64..bfcfd8fc861 100644 --- a/gcc/rust/expand/rust-derive.cc +++ b/gcc/rust/expand/rust-derive.cc @@ -31,34 +31,34 @@ DeriveVisitor::DeriveVisitor (location_t loc) : loc (loc), builder (Builder (loc)) {} -std::unique_ptr +std::vector> DeriveVisitor::derive (Item &item, const Attribute &attr, BuiltinMacro to_derive) { switch (to_derive) { case BuiltinMacro::Clone: - return DeriveClone (attr.get_locus ()).go (item); + return vec (DeriveClone (attr.get_locus ()).go (item)); case BuiltinMacro::Copy: - return DeriveCopy (attr.get_locus ()).go (item); + return vec (DeriveCopy (attr.get_locus ()).go (item)); case BuiltinMacro::Debug: rust_warning_at ( attr.get_locus (), 0, "derive(Debug) is not fully implemented yet and has no effect - only a " "stub implementation will be generated"); - return DeriveDebug (attr.get_locus ()).go (item); + return vec (DeriveDebug (attr.get_locus ()).go (item)); case BuiltinMacro::Default: - return DeriveDefault (attr.get_locus ()).go (item); + return vec (DeriveDefault (attr.get_locus ()).go (item)); case BuiltinMacro::Eq: - return DeriveEq (attr.get_locus ()).go (item); + return vec (DeriveEq (attr.get_locus ()).go (item)); case BuiltinMacro::PartialEq: - return DerivePartialEq (attr.get_locus ()).go (item); + return vec (DerivePartialEq (attr.get_locus ()).go (item)); case BuiltinMacro::Ord: case BuiltinMacro::PartialOrd: case BuiltinMacro::Hash: default: rust_sorry_at (attr.get_locus (), "unimplemented builtin derive macro"); - return nullptr; + return {}; }; } diff --git a/gcc/rust/expand/rust-derive.h b/gcc/rust/expand/rust-derive.h index 93025f1cb6d..71897ed49b5 100644 --- a/gcc/rust/expand/rust-derive.h +++ b/gcc/rust/expand/rust-derive.h @@ -34,8 +34,12 @@ namespace AST { class DeriveVisitor : public AST::ASTVisitor { public: - static std::unique_ptr derive (Item &item, const Attribute &derive, - BuiltinMacro to_derive); + /** + * Expand a built-in derive macro on an item. This may generate multiple items + * which all need to be integrated to the existing AST + */ + static std::vector> + derive (Item &item, const Attribute &derive, BuiltinMacro to_derive); protected: DeriveVisitor (location_t loc); diff --git a/gcc/rust/expand/rust-expand-visitor.cc b/gcc/rust/expand/rust-expand-visitor.cc index 1e85ed72efa..d1886cab3ba 100644 --- a/gcc/rust/expand/rust-expand-visitor.cc +++ b/gcc/rust/expand/rust-expand-visitor.cc @@ -43,7 +43,7 @@ ExpandVisitor::go (AST::Crate &crate) visit (crate); } -static std::unique_ptr +static std::vector> builtin_derive_item (AST::Item &item, const AST::Attribute &derive, BuiltinMacro to_derive) { @@ -189,11 +189,12 @@ ExpandVisitor::expand_inner_items ( to_derive.get ().as_string ()); if (maybe_builtin.has_value ()) { - auto new_item + auto new_items = builtin_derive_item (item, current, maybe_builtin.value ()); - it = items.insert (it, std::move (new_item)); + for (auto &&new_item : new_items) + it = items.insert (it, std::move (new_item)); } else { @@ -276,12 +277,14 @@ ExpandVisitor::expand_inner_stmts (AST::BlockExpr &expr) to_derive.get ().as_string ()); if (maybe_builtin.has_value ()) { - auto new_item + auto new_items = builtin_derive_item (item, current, maybe_builtin.value ()); + // this inserts the derive *before* the item - is it a // problem? - it = stmts.insert (it, std::move (new_item)); + for (auto &&new_item : new_items) + it = stmts.insert (it, std::move (new_item)); } else { From 87c6ce598de5e82af0b810f93c6f2e68a563ff40 Mon Sep 17 00:00:00 2001 From: Arthur Cohen Date: Tue, 4 Feb 2025 16:06:33 +0100 Subject: [PATCH 7/8] derive(PartialEq): Also derive StructuralPartialEq gcc/rust/ChangeLog: * expand/rust-derive-partial-eq.cc: Adapt signatures to generate two impls. * expand/rust-derive-partial-eq.h: Likewise. * expand/rust-derive.cc (DeriveVisitor::derive): Adapt to multiple item generation. gcc/testsuite/ChangeLog: * rust/compile/derive-eq-invalid.rs: Declare StructuralPartialEq. * rust/compile/derive-partialeq1.rs: Likewise. * rust/execute/torture/derive-partialeq1.rs: Likewise. --- gcc/rust/expand/rust-derive-partial-eq.cc | 41 +++++++++++-------- gcc/rust/expand/rust-derive-partial-eq.h | 10 +++-- gcc/rust/expand/rust-derive.cc | 2 +- .../rust/compile/derive-eq-invalid.rs | 6 +++ .../rust/compile/derive-partialeq1.rs | 3 ++ .../rust/execute/torture/derive-partialeq1.rs | 3 ++ 6 files changed, 43 insertions(+), 22 deletions(-) diff --git a/gcc/rust/expand/rust-derive-partial-eq.cc b/gcc/rust/expand/rust-derive-partial-eq.cc index 6f7ef7d8780..b1b5f29a863 100644 --- a/gcc/rust/expand/rust-derive-partial-eq.cc +++ b/gcc/rust/expand/rust-derive-partial-eq.cc @@ -27,35 +27,40 @@ namespace Rust { namespace AST { -DerivePartialEq::DerivePartialEq (location_t loc) - : DeriveVisitor (loc), expanded (nullptr) -{} +DerivePartialEq::DerivePartialEq (location_t loc) : DeriveVisitor (loc) {} -std::unique_ptr +std::vector> DerivePartialEq::go (Item &item) { item.accept_vis (*this); - rust_assert (expanded); - return std::move (expanded); } -std::unique_ptr -DerivePartialEq::partial_eq_impl ( +std::vector> +DerivePartialEq::partialeq_impls ( std::unique_ptr &&eq_fn, std::string name, const std::vector> &type_generics) { auto eq = builder.type_path (LangItem::Kind::EQ); + auto speq = builder.type_path (LangItem::Kind::STRUCTURAL_PEQ); auto trait_items = vec (std::move (eq_fn)); - auto generics + // no extra bound on StructuralPeq + auto peq_generics = setup_impl_generics (name, type_generics, builder.trait_bound (eq)); + auto speq_generics = setup_impl_generics (name, type_generics); + + auto peq = builder.trait_impl (eq, std::move (peq_generics.self_type), + std::move (trait_items), + std::move (peq_generics.impl)); + + auto structural_peq + = builder.trait_impl (speq, std::move (speq_generics.self_type), {}, + std::move (speq_generics.impl)); - return builder.trait_impl (eq, std::move (generics.self_type), - std::move (trait_items), - std::move (generics.impl)); + return vec (std::move (peq), std::move (structural_peq)); } std::unique_ptr @@ -137,7 +142,7 @@ DerivePartialEq::visit_tuple (TupleStruct &item) 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 ()); + = partialeq_impls (std::move (fn), type_name, item.get_generic_params ()); } void @@ -153,7 +158,7 @@ DerivePartialEq::visit_struct (StructStruct &item) 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 ()); + = partialeq_impls (std::move (fn), type_name, item.get_generic_params ()); } MatchCase @@ -250,11 +255,12 @@ void DerivePartialEq::visit_enum (Enum &item) { auto cases = std::vector (); + auto type_name = item.get_identifier ().as_string (); for (auto &variant : item.get_variants ()) { auto variant_path - = builder.variant_path (item.get_identifier ().as_string (), + = builder.variant_path (type_name, variant->get_identifier ().as_string ()); switch (variant->get_enum_item_kind ()) @@ -290,11 +296,10 @@ DerivePartialEq::visit_enum (Enum &item) builder.identifier ("other"))), std::move (cases)); - auto fn = eq_fn (std::move (match), item.get_identifier ().as_string ()); + auto fn = eq_fn (std::move (match), type_name); expanded - = partial_eq_impl (std::move (fn), item.get_identifier ().as_string (), - item.get_generic_params ()); + = partialeq_impls (std::move (fn), type_name, item.get_generic_params ()); } void diff --git a/gcc/rust/expand/rust-derive-partial-eq.h b/gcc/rust/expand/rust-derive-partial-eq.h index 2bc18d2b98a..ac963a63c8a 100644 --- a/gcc/rust/expand/rust-derive-partial-eq.h +++ b/gcc/rust/expand/rust-derive-partial-eq.h @@ -30,12 +30,16 @@ class DerivePartialEq : DeriveVisitor public: DerivePartialEq (location_t loc); - std::unique_ptr go (Item &item); + std::vector> go (Item &item); private: - std::unique_ptr expanded; + std::vector> expanded; - std::unique_ptr partial_eq_impl ( + /** + * Generate both an implementation of `PartialEq` and `StructuralPartialEq` + * for the given type + */ + std::vector> partialeq_impls ( std::unique_ptr &&eq_fn, std::string name, const std::vector> &type_generics); diff --git a/gcc/rust/expand/rust-derive.cc b/gcc/rust/expand/rust-derive.cc index bfcfd8fc861..56e0e9410fb 100644 --- a/gcc/rust/expand/rust-derive.cc +++ b/gcc/rust/expand/rust-derive.cc @@ -52,7 +52,7 @@ DeriveVisitor::derive (Item &item, const Attribute &attr, case BuiltinMacro::Eq: return vec (DeriveEq (attr.get_locus ()).go (item)); case BuiltinMacro::PartialEq: - return vec (DerivePartialEq (attr.get_locus ()).go (item)); + return DerivePartialEq (attr.get_locus ()).go (item); case BuiltinMacro::Ord: case BuiltinMacro::PartialOrd: case BuiltinMacro::Hash: diff --git a/gcc/testsuite/rust/compile/derive-eq-invalid.rs b/gcc/testsuite/rust/compile/derive-eq-invalid.rs index 0c4d48ef6ea..b0bf856486d 100644 --- a/gcc/testsuite/rust/compile/derive-eq-invalid.rs +++ b/gcc/testsuite/rust/compile/derive-eq-invalid.rs @@ -21,6 +21,12 @@ struct PhantomData; #[lang = "sized"] trait Sized {} +#[lang = "structural_peq"] +trait StructuralPartialEq {} + +#[lang = "structural_teq"] +trait StructuralEq {} + #[derive(PartialEq)] struct NotEq; diff --git a/gcc/testsuite/rust/compile/derive-partialeq1.rs b/gcc/testsuite/rust/compile/derive-partialeq1.rs index 71513241929..35e33fbc25c 100644 --- a/gcc/testsuite/rust/compile/derive-partialeq1.rs +++ b/gcc/testsuite/rust/compile/derive-partialeq1.rs @@ -6,6 +6,9 @@ trait Sized {} #[lang = "copy"] trait Copy {} +#[lang = "structural_peq"] +trait StructuralPartialEq {} + #[lang = "eq"] pub trait PartialEq { /// This method tests for `self` and `other` values to be equal, and is used diff --git a/gcc/testsuite/rust/execute/torture/derive-partialeq1.rs b/gcc/testsuite/rust/execute/torture/derive-partialeq1.rs index 4d5124e85cf..67b2773e498 100644 --- a/gcc/testsuite/rust/execute/torture/derive-partialeq1.rs +++ b/gcc/testsuite/rust/execute/torture/derive-partialeq1.rs @@ -25,6 +25,9 @@ pub trait PartialEq { } } +#[lang = "structural_peq"] +trait StructuralPartialEq {} + #[derive(PartialEq, Copy)] // { dg-warning "unused name" } struct Foo; From 741e633ac5ecf380d13a72191e4880d96e3a7a5b Mon Sep 17 00:00:00 2001 From: Arthur Cohen Date: Tue, 4 Feb 2025 16:12:25 +0100 Subject: [PATCH 8/8] derive(Eq): Also derive StructuralEq gcc/rust/ChangeLog: * expand/rust-derive-eq.cc: Adapt functions to return two generated impls. * expand/rust-derive-eq.h: Likewise. * expand/rust-derive.cc (DeriveVisitor::derive): Likewise. --- gcc/rust/expand/rust-derive-eq.cc | 64 +++++++++++++++++-------------- gcc/rust/expand/rust-derive-eq.h | 10 ++--- gcc/rust/expand/rust-derive.cc | 2 +- 3 files changed, 41 insertions(+), 35 deletions(-) diff --git a/gcc/rust/expand/rust-derive-eq.cc b/gcc/rust/expand/rust-derive-eq.cc index a2a7a769065..153e35693ea 100644 --- a/gcc/rust/expand/rust-derive-eq.cc +++ b/gcc/rust/expand/rust-derive-eq.cc @@ -27,6 +27,16 @@ namespace Rust { namespace AST { +DeriveEq::DeriveEq (location_t loc) : DeriveVisitor (loc) {} + +std::vector> +DeriveEq::go (Item &item) +{ + item.accept_vis (*this); + + return std::move (expanded); +} + std::unique_ptr DeriveEq::assert_receiver_is_total_eq_fn ( std::vector> &&types) @@ -98,33 +108,29 @@ DeriveEq::assert_type_is_eq (std::unique_ptr &&type) return builder.let (builder.wildcard (), std::move (full_path)); } -std::unique_ptr -DeriveEq::eq_impl ( +std::vector> +DeriveEq::eq_impls ( std::unique_ptr &&fn, std::string name, const std::vector> &type_generics) { auto eq = builder.type_path ({"core", "cmp", "Eq"}, true); + auto steq = builder.type_path (LangItem::Kind::STRUCTURAL_TEQ); auto trait_items = vec (std::move (fn)); - auto generics + auto eq_generics = setup_impl_generics (name, type_generics, builder.trait_bound (eq)); + auto steq_generics = setup_impl_generics (name, type_generics); - return builder.trait_impl (eq, std::move (generics.self_type), - std::move (trait_items), - std::move (generics.impl)); -} - -DeriveEq::DeriveEq (location_t loc) : DeriveVisitor (loc), expanded (nullptr) {} - -std::unique_ptr -DeriveEq::go (Item &item) -{ - item.accept_vis (*this); + auto eq_impl = builder.trait_impl (eq, std::move (eq_generics.self_type), + std::move (trait_items), + std::move (eq_generics.impl)); + auto steq_impl + = builder.trait_impl (steq, std::move (steq_generics.self_type), + std::move (trait_items), + std::move (steq_generics.impl)); - rust_assert (expanded); - - return std::move (expanded); + return vec (std::move (eq_impl), std::move (steq_impl)); } void @@ -135,9 +141,9 @@ DeriveEq::visit_tuple (TupleStruct &item) for (auto &field : item.get_fields ()) types.emplace_back (field.get_field_type ().clone_type ()); - expanded - = eq_impl (assert_receiver_is_total_eq_fn (std::move (types)), - item.get_identifier ().as_string (), item.get_generic_params ()); + expanded = eq_impls (assert_receiver_is_total_eq_fn (std::move (types)), + item.get_identifier ().as_string (), + item.get_generic_params ()); } void @@ -148,9 +154,9 @@ DeriveEq::visit_struct (StructStruct &item) for (auto &field : item.get_fields ()) types.emplace_back (field.get_field_type ().clone_type ()); - expanded - = eq_impl (assert_receiver_is_total_eq_fn (std::move (types)), - item.get_identifier ().as_string (), item.get_generic_params ()); + expanded = eq_impls (assert_receiver_is_total_eq_fn (std::move (types)), + item.get_identifier ().as_string (), + item.get_generic_params ()); } void @@ -185,9 +191,9 @@ DeriveEq::visit_enum (Enum &item) } } - expanded - = eq_impl (assert_receiver_is_total_eq_fn (std::move (types)), - item.get_identifier ().as_string (), item.get_generic_params ()); + expanded = eq_impls (assert_receiver_is_total_eq_fn (std::move (types)), + item.get_identifier ().as_string (), + item.get_generic_params ()); } void @@ -198,9 +204,9 @@ DeriveEq::visit_union (Union &item) for (auto &field : item.get_variants ()) types.emplace_back (field.get_field_type ().clone_type ()); - expanded - = eq_impl (assert_receiver_is_total_eq_fn (std::move (types)), - item.get_identifier ().as_string (), item.get_generic_params ()); + expanded = eq_impls (assert_receiver_is_total_eq_fn (std::move (types)), + item.get_identifier ().as_string (), + item.get_generic_params ()); } } // namespace AST diff --git a/gcc/rust/expand/rust-derive-eq.h b/gcc/rust/expand/rust-derive-eq.h index 655f1e82e02..17af52653de 100644 --- a/gcc/rust/expand/rust-derive-eq.h +++ b/gcc/rust/expand/rust-derive-eq.h @@ -31,10 +31,10 @@ class DeriveEq : DeriveVisitor public: DeriveEq (location_t loc); - std::unique_ptr go (Item &item); + std::vector> go (Item &item); private: - std::unique_ptr expanded; + std::vector> expanded; /** * Create the actual `assert_receiver_is_total_eq` function of the @@ -52,9 +52,9 @@ class DeriveEq : DeriveVisitor * } * */ - std::unique_ptr - eq_impl (std::unique_ptr &&fn, std::string name, - const std::vector> &type_generics); + std::vector> + eq_impls (std::unique_ptr &&fn, std::string name, + const std::vector> &type_generics); /** * Generate the following structure definition diff --git a/gcc/rust/expand/rust-derive.cc b/gcc/rust/expand/rust-derive.cc index 56e0e9410fb..8223281ed18 100644 --- a/gcc/rust/expand/rust-derive.cc +++ b/gcc/rust/expand/rust-derive.cc @@ -50,7 +50,7 @@ DeriveVisitor::derive (Item &item, const Attribute &attr, case BuiltinMacro::Default: return vec (DeriveDefault (attr.get_locus ()).go (item)); case BuiltinMacro::Eq: - return vec (DeriveEq (attr.get_locus ()).go (item)); + return DeriveEq (attr.get_locus ()).go (item); case BuiltinMacro::PartialEq: return DerivePartialEq (attr.get_locus ()).go (item); case BuiltinMacro::Ord: