// 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-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-
DeriveDefault::go (Item &item)
{
item.accept_vis (*this);
rust_assert (expanded);
return std::move (expanded);
}
std::unique_ptr
DeriveDefault::default_call (std::unique_ptr &&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
DeriveDefault::default_fn (std::unique_ptr &&return_expr)
{
auto self_ty
= std::unique_ptr (new TypePath (builder.type_path ("Self")));
auto block = std::unique_ptr (
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
-
DeriveDefault::default_impl (
std::unique_ptr &&default_fn, std::string name,
const std::vector> &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> ();
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> ();
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,
"% 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