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

Reply via email to