// Copyright (C) 2020-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-clone.h" #include "rust-ast.h" #include "rust-ast-dump.h" #include "rust-expr.h" #include "rust-item.h" #include "rust-path.h" #include "rust-pattern.h" #include "rust-system.h" namespace Rust { namespace AST { std::unique_ptr DeriveClone::clone_call (std::unique_ptr &&to_clone) { // $crate::core::clone::Clone::clone for the fully qualified path - we don't // link with `core` yet so that might be an issue. Use `Clone::clone` for now? // TODO: Factor this function inside the DeriveAccumulator // Interestingly, later versions of Rust have a `clone_fn` lang item which // corresponds to this. But because we are first targeting 1.49, we cannot use // it yet. Once we target a new, more recent version of the language, we'll // have figured out how to compile and distribute `core`, meaning we'll be // able to directly call `::core::clone::Clone::clone()` // Not sure how to call it properly in the meantime... auto path = std::unique_ptr ( new PathInExpression (builder.path_in_expression ({"Clone", "clone"}))); auto args = std::vector> (); args.emplace_back (std::move (to_clone)); return builder.call (std::move (path), std::move (args)); } /** * Create the actual "clone" function of the implementation, so * * fn clone(&self) -> Self { } * */ std::unique_ptr DeriveClone::clone_fn (std::unique_ptr &&clone_expr) { auto block = std::unique_ptr ( new BlockExpr ({}, std::move (clone_expr), {}, {}, AST::LoopLabel::error (), loc, loc)); auto big_self_type = builder.single_type_path ("Self"); std::unique_ptr self (new SelfParam (Lifetime::error (), /* is_mut */ false, loc)); std::vector> params; params.push_back (std::move (self)); return std::unique_ptr ( new Function ({"clone"}, builder.fn_qualifiers (), /* generics */ {}, /* function params */ std::move (params), std::move (big_self_type), WhereClause::create_empty (), std::move (block), Visibility::create_private (), {}, loc)); } /** * Create the Clone trait implementation for a type * * impl Clone for { * * } * */ std::unique_ptr DeriveClone::clone_impl ( std::unique_ptr &&clone_fn, std::string name, const std::vector> &type_generics) { auto clone = builder.type_path (LangItem::Kind::CLONE); auto trait_items = std::vector> (); trait_items.emplace_back (std::move (clone_fn)); // we need to build up the generics for this impl block which will be just a // clone of the types specified ones // // for example: // // #[derive(Clone)] // struct Be { ... } // // we need to generate the impl block: // // impl Clone for Be std::vector lifetime_args; std::vector generic_args; std::vector> impl_generics; for (const auto &generic : type_generics) { switch (generic->get_kind ()) { case GenericParam::Kind::Lifetime: { LifetimeParam &lifetime_param = (LifetimeParam &) *generic.get (); Lifetime l = builder.new_lifetime (lifetime_param.get_lifetime ()); lifetime_args.push_back (std::move (l)); auto impl_lifetime_param = builder.new_lifetime_param (lifetime_param); impl_generics.push_back (std::move (impl_lifetime_param)); } break; case GenericParam::Kind::Type: { TypeParam &type_param = (TypeParam &) *generic.get (); std::unique_ptr associated_type = builder.single_type_path ( type_param.get_type_representation ().as_string ()); GenericArg type_arg = GenericArg::create_type (std::move (associated_type)); generic_args.push_back (std::move (type_arg)); auto impl_type_param = builder.new_type_param (type_param); impl_generics.push_back (std::move (impl_type_param)); } break; case GenericParam::Kind::Const: { rust_unreachable (); // TODO // const ConstGenericParam *const_param // = (const ConstGenericParam *) generic.get (); // std::unique_ptr const_expr = nullptr; // GenericArg type_arg // = GenericArg::create_const (std::move (const_expr)); // generic_args.push_back (std::move (type_arg)); } break; } } GenericArgs generic_args_for_self (lifetime_args, generic_args, {} /*binding args*/, loc); std::unique_ptr self_type_path = impl_generics.empty () ? builder.single_type_path (name) : builder.single_generic_type_path (name, generic_args_for_self); return std::unique_ptr ( new TraitImpl (clone, /* unsafe */ false, /* exclam */ false, std::move (trait_items), std::move (impl_generics), std::move (self_type_path), WhereClause::create_empty (), Visibility::create_private (), {}, {}, loc)); } // TODO: Create new `make_qualified_call` helper function DeriveClone::DeriveClone (location_t loc) : DeriveVisitor (loc), expanded (nullptr) {} std::unique_ptr DeriveClone::go (Item &item) { item.accept_vis (*this); rust_assert (expanded); return std::move (expanded); } void DeriveClone::visit_tuple (TupleStruct &item) { auto cloned_fields = std::vector> (); for (size_t idx = 0; idx < item.get_fields ().size (); idx++) cloned_fields.emplace_back ( clone_call (builder.ref (builder.tuple_idx ("self", idx)))); auto path = std::unique_ptr (new PathInExpression ( builder.path_in_expression ({item.get_identifier ().as_string ()}))); auto constructor = builder.call (std::move (path), std::move (cloned_fields)); expanded = clone_impl (clone_fn (std::move (constructor)), item.get_identifier ().as_string (), item.get_generic_params ()); } void DeriveClone::visit_struct (StructStruct &item) { if (item.is_unit_struct ()) { auto unit_ctor = builder.struct_expr_struct (item.get_struct_name ().as_string ()); expanded = clone_impl (clone_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 = clone_call ( builder.ref (builder.field_access (builder.identifier ("self"), name))); 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 = clone_impl (clone_fn (std::move (ctor)), item.get_struct_name ().as_string (), item.get_generic_params ()); } PathInExpression DeriveClone::variant_match_path (Enum &item, const Identifier &variant) { return PathInExpression ({builder.path_segment ( item.get_identifier ().as_string ()), builder.path_segment (variant.as_string ())}, {}, loc, false); } MatchCase DeriveClone::clone_enum_identifier (Enum &item, const std::unique_ptr &variant) { auto variant_path = variant_match_path (item, variant->get_identifier ()); auto pattern = std::unique_ptr (new ReferencePattern ( std::unique_ptr (new PathInExpression (variant_path)), false, false, loc)); auto expr = std::unique_ptr (new PathInExpression (variant_path)); return builder.match_case (std::move (pattern), std::move (expr)); } MatchCase DeriveClone::clone_enum_tuple (Enum &item, const EnumItemTuple &variant) { auto variant_path = variant_match_path (item, variant.get_identifier ()); auto patterns = std::vector> (); auto cloned_patterns = std::vector> (); for (size_t i = 0; i < variant.get_tuple_fields ().size (); i++) { // The pattern we're creating for each field is `self_` where `i` is // the index of the field. It doesn't actually matter what we use, as long // as it's ordered, unique, and that we can reuse it in the match case's // return expression to clone the field. auto pattern_str = "__self_" + std::to_string (i); patterns.emplace_back (builder.identifier_pattern (pattern_str)); // Now, for each tuple's element, we create a new expression calling // `clone` on it for the match case's return expression cloned_patterns.emplace_back ( clone_call (builder.ref (builder.identifier (pattern_str)))); } auto pattern_items = std::unique_ptr ( new TupleStructItemsNoRange (std::move (patterns))); auto pattern = std::unique_ptr ( new ReferencePattern (std::unique_ptr (new TupleStructPattern ( variant_path, std::move (pattern_items))), false, false, loc)); auto expr = builder.call (std::unique_ptr (new PathInExpression (variant_path)), std::move (cloned_patterns)); return builder.match_case (std::move (pattern), std::move (expr)); } MatchCase DeriveClone::clone_enum_struct (Enum &item, const EnumItemStruct &variant) { auto variant_path = variant_match_path (item, variant.get_identifier ()); auto field_patterns = std::vector> (); auto cloned_fields = std::vector> (); #if 0 // NOTE: We currently do not support compiling struct patterns where an // identifier is assigned a new pattern, e.g. Bloop { f0: x } // This is the code we should eventually produce as it mimics what rustc does // - which is probably here for a good reason. In the meantime, we can just // use the field's identifier as the pattern: Bloop { f0 } // We can then clone the field directly instead of calling `clone()` on the // new pattern. // TODO: Figure out if that is actually needed and why rustc does it? for (size_t i = 0; i < variant.get_struct_fields ().size (); i++) { auto &field = variant.get_struct_fields ()[i]; // Just like for tuples, the pattern we're creating for each field is // `self_` where `i` is the index of the field. It doesn't actually // matter what we use, as long as it's ordered, unique, and that we can // reuse it in the match case's return expression to clone the field. auto pattern_str = "__self_" + std::to_string (i); field_patterns.emplace_back ( std::unique_ptr (new StructPatternFieldIdentPat ( field.get_field_name (), builder.identifier_pattern (pattern_str), {}, loc))); cloned_fields.emplace_back ( std::unique_ptr (new StructExprFieldIdentifierValue ( field.get_field_name (), clone_call (builder.ref (builder.identifier (pattern_str))), {}, loc))); } #endif for (const auto &field : variant.get_struct_fields ()) { // We match on the struct's fields, and then recreate an instance of that // struct, cloning each field field_patterns.emplace_back ( std::unique_ptr (new StructPatternFieldIdent ( field.get_field_name (), false /* is_ref? true? */, false, {}, loc))); cloned_fields.emplace_back ( std::unique_ptr (new StructExprFieldIdentifierValue ( field.get_field_name (), clone_call (builder.ref ( builder.identifier (field.get_field_name ().as_string ()))), {}, loc))); } auto pattern_elts = StructPatternElements (std::move (field_patterns)); auto pattern = std::unique_ptr ( new ReferencePattern (std::unique_ptr (new StructPattern ( variant_path, loc, pattern_elts)), false, false, loc)); auto expr = std::unique_ptr ( new StructExprStructFields (variant_path, std::move (cloned_fields), loc)); return builder.match_case (std::move (pattern), std::move (expr)); } void DeriveClone::visit_enum (Enum &item) { // Create an arm for each variant of the enum: // - For enum item variants (simple identifiers), just create the same // variant. // - For struct and tuple variants, destructure the pattern and call clone for // each field. auto cases = std::vector (); for (const auto &variant : item.get_variants ()) { switch (variant->get_enum_item_kind ()) { // Identifiers and discriminated variants are the same for a clone - we // just return the same variant case EnumItem::Kind::Identifier: case EnumItem::Kind::Discriminant: cases.emplace_back (clone_enum_identifier (item, variant)); break; case EnumItem::Kind::Tuple: cases.emplace_back ( clone_enum_tuple (item, static_cast (*variant))); break; case EnumItem::Kind::Struct: cases.emplace_back ( clone_enum_struct (item, static_cast (*variant))); break; } } // match self { ... } auto match = builder.match (builder.identifier ("self"), std::move (cases)); expanded = clone_impl (clone_fn (std::move (match)), item.get_identifier ().as_string (), item.get_generic_params ()); } void DeriveClone::visit_union (Union &item) { // FIXME: Should be $crate::core::clone::AssertParamIsCopy (or similar) // (Rust-GCC#3329) auto copy_path = TypePath (vec (builder.type_path_segment ("Copy")), loc); auto sized_path = TypePath (vec (builder.type_path_segment ("Sized")), loc); auto copy_bound = std::unique_ptr ( new TraitBound (copy_path, item.get_locus ())); auto sized_bound = std::unique_ptr ( new TraitBound (sized_path, item.get_locus (), false, true)); auto bounds = std::vector> (); bounds.emplace_back (std::move (copy_bound)); bounds.emplace_back (std::move (sized_bound)); // struct AssertParamIsCopy { _t: PhantomData } auto assert_param_is_copy = "AssertParamIsCopy"; auto t = std::unique_ptr ( new TypeParam (Identifier ("T"), item.get_locus (), std::move (bounds))); auto assert_param_is_copy_struct = builder.struct_struct ( assert_param_is_copy, vec (std::move (t)), {StructField ( Identifier ("_t"), builder.single_generic_type_path ( LangItem::Kind::PHANTOM_DATA, GenericArgs ( {}, {GenericArg::create_type (builder.single_type_path ("T"))}, {})), Visibility::create_private (), item.get_locus ())}); // auto arg = GenericArg::create_type (builder.single_type_path ("Self")); // AssertParamIsCopy:: auto type = std::unique_ptr ( new TypePathSegmentGeneric (PathIdentSegment (assert_param_is_copy, loc), false, GenericArgs ({}, {arg}, {}, loc), loc)); auto type_paths = std::vector> (); type_paths.emplace_back (std::move (type)); auto full_path = std::unique_ptr (new TypePath ({std::move (type_paths)}, loc)); auto tail_expr = builder.deref (builder.identifier ("self")); auto stmts = vec (std::move (assert_param_is_copy_struct), builder.let (builder.wildcard (), std::move (full_path), nullptr)); auto block = builder.block (std::move (stmts), std::move (tail_expr)); expanded = clone_impl (clone_fn (std::move (block)), item.get_identifier ().as_string (), item.get_generic_params ()); } } // namespace AST } // namespace Rust