Skip to content

Commit

Permalink
derive(Default): Add implementation
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
CohenArthur committed Feb 4, 2025
1 parent 32efb51 commit 0394968
Show file tree
Hide file tree
Showing 7 changed files with 290 additions and 0 deletions.
1 change: 1 addition & 0 deletions gcc/rust/Make-lang.in
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
173 changes: 173 additions & 0 deletions gcc/rust/expand/rust-derive-default.cc
Original file line number Diff line number Diff line change
@@ -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
58 changes: 58 additions & 0 deletions gcc/rust/expand/rust-derive-default.h
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions gcc/rust/expand/rust-derive.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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:
Expand Down
29 changes: 29 additions & 0 deletions gcc/testsuite/rust/compile/derive-default1.rs
Original file line number Diff line number Diff line change
@@ -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();
}
1 change: 1 addition & 0 deletions gcc/testsuite/rust/compile/nr2/exclude
Original file line number Diff line number Diff line change
Expand Up @@ -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
26 changes: 26 additions & 0 deletions gcc/testsuite/rust/execute/torture/derive-default1.rs
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit 0394968

Please sign in to comment.