From: Arthur Cohen <arthur.co...@embecosm.com> gcc/rust/ChangeLog:
* expand/rust-derive.cc (DeriveVisitor::derive): Call DeriveDefault. * expand/rust-derive-default.cc: New file. * expand/rust-derive-default.h: New file. * Make-lang.in: Compile them. gcc/testsuite/ChangeLog: * rust/compile/derive-default1.rs: New test. * rust/execute/torture/derive-default1.rs: New test. * rust/compile/nr2/exclude: Exclude them. --- gcc/rust/Make-lang.in | 1 + gcc/rust/expand/rust-derive-default.cc | 173 ++++++++++++++++++ gcc/rust/expand/rust-derive-default.h | 58 ++++++ gcc/rust/expand/rust-derive.cc | 2 + gcc/testsuite/rust/compile/derive-default1.rs | 29 +++ gcc/testsuite/rust/compile/nr2/exclude | 1 + .../rust/execute/torture/derive-default1.rs | 26 +++ 7 files changed, 290 insertions(+) create mode 100644 gcc/rust/expand/rust-derive-default.cc create mode 100644 gcc/rust/expand/rust-derive-default.h create mode 100644 gcc/testsuite/rust/compile/derive-default1.rs create mode 100644 gcc/testsuite/rust/execute/torture/derive-default1.rs diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index bc58a341131..24054531d9e 100644 --- a/gcc/rust/Make-lang.in +++ b/gcc/rust/Make-lang.in @@ -97,6 +97,7 @@ GRS_OBJS = \ rust/rust-derive-clone.o \ rust/rust-derive-copy.o \ rust/rust-derive-debug.o \ + rust/rust-derive-default.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-default.cc b/gcc/rust/expand/rust-derive-default.cc new file mode 100644 index 00000000000..c54f8c318dd --- /dev/null +++ b/gcc/rust/expand/rust-derive-default.cc @@ -0,0 +1,173 @@ +// 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-default.h" +#include "rust-ast.h" +#include "rust-diagnostics.h" +#include "rust-path.h" +#include "rust-system.h" + +namespace Rust { +namespace AST { + +DeriveDefault::DeriveDefault (location_t loc) + : DeriveVisitor (loc), expanded (nullptr) +{} + +std::unique_ptr<Item> +DeriveDefault::go (Item &item) +{ + item.accept_vis (*this); + + rust_assert (expanded); + + return std::move (expanded); +} + +std::unique_ptr<Expr> +DeriveDefault::default_call (std::unique_ptr<Type> &&type) +{ + auto default_trait = builder.type_path ({"core", "default", "Default"}, true); + + auto default_fn + = builder.qualified_path_in_expression (std::move (type), default_trait, + builder.path_segment ("default")); + + return builder.call (std::move (default_fn)); +} + +std::unique_ptr<AssociatedItem> +DeriveDefault::default_fn (std::unique_ptr<Expr> &&return_expr) +{ + auto self_ty + = std::unique_ptr<Type> (new TypePath (builder.type_path ("Self"))); + + auto block = std::unique_ptr<BlockExpr> ( + new BlockExpr ({}, std::move (return_expr), {}, {}, + AST::LoopLabel::error (), loc, loc)); + + return builder.function ("default", {}, std::move (self_ty), + std::move (block)); +} + +std::unique_ptr<Item> +DeriveDefault::default_impl ( + std::unique_ptr<AssociatedItem> &&default_fn, std::string name, + const std::vector<std::unique_ptr<GenericParam>> &type_generics) +{ + auto default_path = builder.type_path ({"core", "default", "Default"}, true); + + auto trait_items = vec (std::move (default_fn)); + + auto generics = setup_impl_generics (name, type_generics, + builder.trait_bound (default_path)); + + return builder.trait_impl (default_path, std::move (generics.self_type), + std::move (trait_items), + std::move (generics.impl)); +} + +void +DeriveDefault::visit_struct (StructStruct &item) +{ + if (item.is_unit_struct ()) + { + auto unit_ctor + = builder.struct_expr_struct (item.get_struct_name ().as_string ()); + expanded = default_impl (default_fn (std::move (unit_ctor)), + item.get_struct_name ().as_string (), + item.get_generic_params ()); + return; + } + + auto cloned_fields = std::vector<std::unique_ptr<StructExprField>> (); + for (auto &field : item.get_fields ()) + { + auto name = field.get_field_name ().as_string (); + auto expr = default_call (field.get_field_type ().clone_type ()); + + cloned_fields.emplace_back ( + builder.struct_expr_field (std::move (name), std::move (expr))); + } + + auto ctor = builder.struct_expr (item.get_struct_name ().as_string (), + std::move (cloned_fields)); + + expanded = default_impl (default_fn (std::move (ctor)), + item.get_struct_name ().as_string (), + item.get_generic_params ()); +} + +void +DeriveDefault::visit_tuple (TupleStruct &tuple_item) +{ + auto defaulted_fields = std::vector<std::unique_ptr<Expr>> (); + + for (auto &field : tuple_item.get_fields ()) + { + auto type = field.get_field_type ().clone_type (); + + defaulted_fields.emplace_back (default_call (std::move (type))); + } + + auto return_expr + = builder.call (builder.identifier ( + tuple_item.get_struct_name ().as_string ()), + std::move (defaulted_fields)); + + expanded = default_impl (default_fn (std::move (return_expr)), + tuple_item.get_struct_name ().as_string (), + tuple_item.get_generic_params ()); +} + +void +DeriveDefault::visit_enum (Enum &enum_item) +{ + // This is no longer the case in later Rust versions where you can choose a + // default variant to emit using the `#[default]` attribute: + // + // ```rust + // #[derive(Default)] + // enum Baz { + // #[default] + // A, + // B(i32), + // C { a: i32 } + // } + // ``` + // + // will emit the following impl + // + // ```rust + // impl ::core::default::Default for Baz { + // #[inline] + // fn default() -> Baz { Self::A } + // } + // ``` + rust_error_at (loc, ErrorCode::E0665, + "%<Default%> cannot be derived for enums, only structs"); +} + +void +DeriveDefault::visit_union (Union &enum_item) +{ + rust_error_at (loc, "derive(Default) cannot be used on unions"); +} + +} // namespace AST +} // namespace Rust diff --git a/gcc/rust/expand/rust-derive-default.h b/gcc/rust/expand/rust-derive-default.h new file mode 100644 index 00000000000..eae9e8511dc --- /dev/null +++ b/gcc/rust/expand/rust-derive-default.h @@ -0,0 +1,58 @@ +// 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_DEFAULT_H +#define RUST_DERIVE_DEFAULT_H + +#include "rust-derive.h" +#include "rust-ast.h" + +namespace Rust { +namespace AST { + +// This derive is currently incomplete and only generate a stub implementation +// which does not do any debug formatting +class DeriveDefault : DeriveVisitor +{ +public: + DeriveDefault (location_t loc); + + std::unique_ptr<Item> go (Item &); + +private: + std::unique_ptr<Item> expanded; + + std::unique_ptr<Expr> default_call (std::unique_ptr<Type> &&type); + + std::unique_ptr<AssociatedItem> + default_fn (std::unique_ptr<Expr> &&return_expr); + + std::unique_ptr<Item> default_impl ( + std::unique_ptr<AssociatedItem> &&default_fn, std::string name, + const std::vector<std::unique_ptr<GenericParam>> &type_generics); + + virtual void visit_struct (StructStruct &struct_item) override; + virtual void visit_tuple (TupleStruct &tuple_item) override; + virtual void visit_enum (Enum &enum_item) override; + virtual void visit_union (Union &enum_item) override; +}; + +} // namespace AST +} // namespace Rust + +#endif // ! RUST_DERIVE_DEFAULT_H diff --git a/gcc/rust/expand/rust-derive.cc b/gcc/rust/expand/rust-derive.cc index 2c5ccc3ce52..f8d1d015d9c 100644 --- a/gcc/rust/expand/rust-derive.cc +++ b/gcc/rust/expand/rust-derive.cc @@ -20,6 +20,7 @@ #include "rust-derive-clone.h" #include "rust-derive-copy.h" #include "rust-derive-debug.h" +#include "rust-derive-default.h" namespace Rust { namespace AST { @@ -45,6 +46,7 @@ DeriveVisitor::derive (Item &item, const Attribute &attr, "stub implementation will be generated"); return DeriveDebug (attr.get_locus ()).go (item); case BuiltinMacro::Default: + return DeriveDefault (attr.get_locus ()).go (item); case BuiltinMacro::Eq: case BuiltinMacro::PartialEq: case BuiltinMacro::Ord: diff --git a/gcc/testsuite/rust/compile/derive-default1.rs b/gcc/testsuite/rust/compile/derive-default1.rs new file mode 100644 index 00000000000..902c65eca58 --- /dev/null +++ b/gcc/testsuite/rust/compile/derive-default1.rs @@ -0,0 +1,29 @@ +#[derive(Default)] +struct Foo { _a: i32, _b: i64, _c: u8 } + +#[lang = "sized"] +trait Sized {} + +mod core { + mod default { + trait Default: Sized { + fn default() -> Self; + } + + impl Default for i32 { + fn default() -> Self { 0 } + } + + impl Default for i64 { + fn default() -> Self { 27 } + } + + impl Default for u8 { + fn default() -> Self { 18 } + } + } +} + +fn main() { + let _ = Foo::default(); +} diff --git a/gcc/testsuite/rust/compile/nr2/exclude b/gcc/testsuite/rust/compile/nr2/exclude index 8229b541bbc..f8e280e341c 100644 --- a/gcc/testsuite/rust/compile/nr2/exclude +++ b/gcc/testsuite/rust/compile/nr2/exclude @@ -124,4 +124,5 @@ traits12.rs try-trait.rs derive-debug1.rs issue-3382.rs +derive-default1.rs # please don't delete the trailing newline diff --git a/gcc/testsuite/rust/execute/torture/derive-default1.rs b/gcc/testsuite/rust/execute/torture/derive-default1.rs new file mode 100644 index 00000000000..4bcafa0fc44 --- /dev/null +++ b/gcc/testsuite/rust/execute/torture/derive-default1.rs @@ -0,0 +1,26 @@ +#[derive(Default)] +struct Foo { a: i32 } +#[derive(Default)] +struct Bar(i32); + +#[lang = "sized"] +trait Sized {} + +mod core { + mod default { + trait Default: Sized { + fn default() -> Self; + } + + impl Default for i32 { + fn default() -> Self { 1 } + } + } +} + +fn main() -> i32 { + let foo = Foo::default(); + let bar = Bar::default(); + + foo.a + bar.0 - 2 +} -- 2.45.2