From: Arthur Cohen <arthur.co...@embecosm.com> 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 5ddad257805..83ffc47ce28 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 +// <http://www.gnu.org/licenses/>. + +#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<AssociatedItem> +DeriveEq::assert_receiver_is_total_eq_fn ( + std::vector<std::unique_ptr<Type>> &&types) +{ + auto stmts = std::vector<std::unique_ptr<Stmt>> (); + + 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<BlockExpr> ( + 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<Stmt> +DeriveEq::assert_param_is_eq () +{ + auto eq_bound = std::unique_ptr<TypeParamBound> ( + new TraitBound (builder.type_path ({"core", "cmp", "Eq"}, true), loc)); + + auto sized_bound = std::unique_ptr<TypeParamBound> ( + 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<GenericParam> ( + 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<Stmt> +DeriveEq::assert_type_is_eq (std::unique_ptr<Type> &&type) +{ + auto assert_param_is_eq = "AssertParamIsEq"; + + // AssertParamIsCopy::<Self> + auto assert_param_is_eq_ty + = std::unique_ptr<TypePathSegment> (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<std::unique_ptr<TypePathSegment>> (); + type_paths.emplace_back (std::move (assert_param_is_eq_ty)); + + auto full_path + = std::unique_ptr<Type> (new TypePath ({std::move (type_paths)}, loc)); + + return builder.let (builder.wildcard (), std::move (full_path)); +} + +std::unique_ptr<Item> +DeriveEq::eq_impl ( + std::unique_ptr<AssociatedItem> &&fn, std::string name, + const std::vector<std::unique_ptr<GenericParam>> &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<AST::Item> +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<std::unique_ptr<Type>> (); + + 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<std::unique_ptr<Type>> (); + + 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<std::unique_ptr<Type>> (); + + 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<EnumItemTuple &> (*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<EnumItemStruct &> (*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<std::unique_ptr<Type>> (); + + 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 +// <http://www.gnu.org/licenses/>. + +#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<AST::Item> go (Item &item); + +private: + std::unique_ptr<Item> 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<AssociatedItem> + assert_receiver_is_total_eq_fn (std::vector<std::unique_ptr<Type>> &&types); + + /** + * Create the Eq trait implementation for a type + * + * impl Eq for <type> { + * <assert_receiver_is_total_eq> + * } + * + */ + std::unique_ptr<Item> + eq_impl (std::unique_ptr<AssociatedItem> &&fn, std::string name, + const std::vector<std::unique_ptr<GenericParam>> &type_generics); + + /** + * Generate the following structure definition + * + * struct AssertParamIsEq<T: Eq + ?Sized> { _t: PhantomData<T> } + */ + std::unique_ptr<Stmt> assert_param_is_eq (); + + /** + * Generate a let statement to assert a type implements `Eq` + * + * let _: AssertParamIsEq<type>; + */ + std::unique_ptr<Stmt> assert_type_is_eq (std::unique_ptr<Type> &&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 f8d1d015d9c..8289a120f04 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<Rhs: ?Sized = Self> { + fn eq(&self, other: &Rhs) -> bool; + + fn ne(&self, other: &Rhs) -> bool { + !self.eq(other) + } + } + + pub trait Eq: PartialEq<Self> { + fn assert_receiver_is_total_eq(&self) {} + } + } +} + +// #[lang = "phantom_data"] +// struct PhantomData<T>; + +// #[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, +// } -- 2.45.2