From d8dc928bad2854129eb7ae28cd6dc694f98fb47f Mon Sep 17 00:00:00 2001 From: Arthur Cohen Date: Thu, 30 Jan 2025 13:10:51 +0100 Subject: gccrs: derive(Default): Add implementation 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/expand/rust-derive-default.cc | 173 +++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 gcc/rust/expand/rust-derive-default.cc (limited to 'gcc/rust/expand/rust-derive-default.cc') 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 +// . + +#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 -- cgit v1.1