diff options
Diffstat (limited to 'gcc/rust/expand/rust-derive-default.cc')
-rw-r--r-- | gcc/rust/expand/rust-derive-default.cc | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/gcc/rust/expand/rust-derive-default.cc b/gcc/rust/expand/rust-derive-default.cc new file mode 100644 index 0000000..c54f8c3 --- /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 |