From e5079a74235e4729847fbbba6003f7b2be715bb3 Mon Sep 17 00:00:00 2001 From: Arthur Cohen Date: Thu, 25 May 2023 14:18:39 +0200 Subject: [PATCH] expansion: Add base for deriving builtin macros (Clone, Copy...) gcc/rust/ChangeLog: * Make-lang.in: Add new object files to Makefile. * expand/rust-expand-visitor.cc (is_derive): Move function. (is_builtin): Likewise. (get_traits_to_derive): New function. (derive_item): Likewise. (ExpandVisitor::visit): Visit item/statements containers. (ExpandVisitor::visit_outer_attrs): Take a reference on the attribute instead of a copy. (ExpandVisitor::is_derive): Deleted function. (ExpandVisitor::is_builtin): Likewise. * expand/rust-expand-visitor.h (RUST_EXPAND_VISITOR_H): Add missing #ifdef guards. (is_derive): Declare function. (is_builtin): Likewise. * expand/rust-macro-builtins.cc (builtin_macro_from_string): Use new MacroBuiltin::builtins map. (make_macro_path_str): Likewise. * expand/rust-macro-builtins.h (enum class): Add builtin derive macros. * expand/rust-derive-clone.cc: New file. * expand/rust-derive-clone.h: New file. * expand/rust-derive.cc: New file. * expand/rust-derive.h: New file. gcc/testsuite/ChangeLog: * rust/compile/macro43.rs: Fix test with new derive macros. * rust/compile/derive_macro1.rs: New test. * rust/compile/derive_macro3.rs: New test. * rust/execute/torture/derive_macro1.rs: New test. * rust/execute/torture/derive_macro3.rs: New test. --- gcc/rust/Make-lang.in | 3 + gcc/rust/expand/rust-derive-clone.cc | 153 ++++++++++++ gcc/rust/expand/rust-derive-clone.h | 77 ++++++ gcc/rust/expand/rust-derive.cc | 48 ++++ gcc/rust/expand/rust-derive.h | 222 ++++++++++++++++++ gcc/rust/expand/rust-expand-visitor.cc | 221 +++++++++++++---- gcc/rust/expand/rust-expand-visitor.h | 49 +++- gcc/rust/expand/rust-macro-builtins.cc | 24 +- gcc/rust/expand/rust-macro-builtins.h | 11 + gcc/testsuite/rust/compile/derive_macro1.rs | 12 + gcc/testsuite/rust/compile/derive_macro3.rs | 21 ++ gcc/testsuite/rust/compile/macro43.rs | 3 +- .../rust/execute/torture/derive_macro1.rs | 23 ++ .../rust/execute/torture/derive_macro3.rs | 19 ++ 14 files changed, 828 insertions(+), 58 deletions(-) create mode 100644 gcc/rust/expand/rust-derive-clone.cc create mode 100644 gcc/rust/expand/rust-derive-clone.h create mode 100644 gcc/rust/expand/rust-derive.cc create mode 100644 gcc/rust/expand/rust-derive.h create mode 100644 gcc/testsuite/rust/compile/derive_macro1.rs create mode 100644 gcc/testsuite/rust/compile/derive_macro3.rs create mode 100644 gcc/testsuite/rust/execute/torture/derive_macro1.rs create mode 100644 gcc/testsuite/rust/execute/torture/derive_macro3.rs diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index 14f1d08a6f72..209429c31618 100644 --- a/gcc/rust/Make-lang.in +++ b/gcc/rust/Make-lang.in @@ -87,6 +87,9 @@ GRS_OBJS = \ rust/rust-macro-expand.o \ rust/rust-cfg-strip.o \ rust/rust-expand-visitor.o \ + rust/rust-ast-builder.o \ + rust/rust-derive.o \ + rust/rust-derive-clone.o \ rust/rust-macro-invoc-lexer.o \ rust/rust-macro-substitute-ctx.o \ rust/rust-macro-builtins.o \ diff --git a/gcc/rust/expand/rust-derive-clone.cc b/gcc/rust/expand/rust-derive-clone.cc new file mode 100644 index 000000000000..73f1e663f864 --- /dev/null +++ b/gcc/rust/expand/rust-derive-clone.cc @@ -0,0 +1,153 @@ +// Copyright (C) 2020-2023 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" + +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: `has_opening_scope_resolution`? Does it have it? + // TODO: Factor this function inside the DeriveAccumulator + 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), {}, {}, loc, loc)); + auto big_self_type = builder.single_type_path ("Self"); + + return std::unique_ptr ( + new Method ("clone", builder.fn_qualifiers (), /* generics */ {}, + SelfParam (Lifetime::error (), /* is_mut */ false, loc), + /* function 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) +{ + // should that be `$crate::core::clone::Clone` instead? + auto segments = std::vector> (); + segments.emplace_back (builder.type_path_segment ("Clone")); + auto clone = TypePath (std::move (segments), loc); + + auto trait_items = std::vector> (); + trait_items.emplace_back (std::move (clone_fn)); + + return std::unique_ptr ( + new TraitImpl (clone, /* unsafe */ false, + /* exclam */ false, std::move (trait_items), + /* generics */ {}, builder.single_type_path (name), + WhereClause::create_empty (), Visibility::create_private (), + {}, {}, loc)); +} + +// TODO: Create new `make_qualified_call` helper function + +DeriveClone::DeriveClone (Location loc) + : loc (loc), expanded (nullptr), builder (AstBuilder (loc)) +{} + +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 ()}))); + auto constructor = builder.call (std::move (path), std::move (cloned_fields)); + + expanded + = clone_impl (clone_fn (std::move (constructor)), item.get_identifier ()); +} + +void +DeriveClone::visit_struct (StructStruct &item) +{ + if (item.is_unit_struct ()) + { + auto unit_ctor = builder.struct_expr_struct (item.get_struct_name ()); + expanded = clone_impl (clone_fn (std::move (unit_ctor)), + item.get_struct_name ()); + } + else + { + rust_sorry_at (item.get_locus (), "cannot derive %qs for these items yet", + "Clone"); + } +} + +void +DeriveClone::visit_enum (Enum &item) +{ + rust_sorry_at (item.get_locus (), "cannot derive %qs for these items yet", + "Clone"); +} + +void +DeriveClone::visit_union (Union &item) +{ + rust_sorry_at (item.get_locus (), "cannot derive %qs for these items yet", + "Clone"); +} + +} // namespace AST +} // namespace Rust diff --git a/gcc/rust/expand/rust-derive-clone.h b/gcc/rust/expand/rust-derive-clone.h new file mode 100644 index 000000000000..6c82a6478851 --- /dev/null +++ b/gcc/rust/expand/rust-derive-clone.h @@ -0,0 +1,77 @@ +// Copyright (C) 2020-2023 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 +// . + +#ifndef RUST_DERIVE_CLONE_H +#define RUST_DERIVE_CLONE_H + +#include "rust-derive.h" +#include "rust-ast-builder.h" + +namespace Rust { +namespace AST { + +class DeriveClone : DeriveVisitor +{ +public: + DeriveClone (Location loc); + + std::unique_ptr go (Item &item); + +private: + Location loc; + std::unique_ptr expanded; + AstBuilder builder; + + /** + * Create a call to "clone". For now, this creates a call to + * `Clone::clone`, but should ultimately call into + * `::core::clone::Clone::clone` + * + * Clone::clone() + */ + std::unique_ptr clone_call (std::unique_ptr &&to_clone); + + /** + * Create the actual "clone" function of the implementation, so + * + * fn clone(&self) -> Self { } + * + */ + std::unique_ptr clone_fn (std::unique_ptr &&clone_expr); + + /** + * Create the Clone trait implementation for a type + * + * impl Clone for { + * + * } + * + */ + std::unique_ptr clone_impl (std::unique_ptr &&clone_fn, + std::string name); + + virtual void visit_struct (StructStruct &item); + virtual void visit_tuple (TupleStruct &item); + virtual void visit_enum (Enum &item); + virtual void visit_union (Union &item); +}; + +} // namespace AST +} // namespace Rust + +#endif // ! RUST_DERIVE_CLONE_H diff --git a/gcc/rust/expand/rust-derive.cc b/gcc/rust/expand/rust-derive.cc new file mode 100644 index 000000000000..cf5126663f6a --- /dev/null +++ b/gcc/rust/expand/rust-derive.cc @@ -0,0 +1,48 @@ +// Copyright (C) 2020-2023 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.h" +#include "rust-derive-clone.h" + +namespace Rust { +namespace AST { + +std::unique_ptr +DeriveVisitor::derive (Item &item, const Attribute &attr, + BuiltinMacro to_derive) +{ + switch (to_derive) + { + case BuiltinMacro::Clone: + return DeriveClone (attr.get_locus ()).go (item); + case BuiltinMacro::Copy: + case BuiltinMacro::Debug: + case BuiltinMacro::Default: + case BuiltinMacro::Eq: + case BuiltinMacro::PartialEq: + case BuiltinMacro::Ord: + case BuiltinMacro::PartialOrd: + case BuiltinMacro::Hash: + default: + rust_sorry_at (attr.get_locus (), "uninmplemented builtin derive macro"); + return nullptr; + }; +} + +} // namespace AST +} // namespace Rust diff --git a/gcc/rust/expand/rust-derive.h b/gcc/rust/expand/rust-derive.h new file mode 100644 index 000000000000..9a62c29e58f3 --- /dev/null +++ b/gcc/rust/expand/rust-derive.h @@ -0,0 +1,222 @@ +// Copyright (C) 2020-2023 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 +// . + +#ifndef DERIVE_VISITOR_H +#define DERIVE_VISITOR_H + +#include "rust-ast-full.h" +#include "rust-ast-visitor.h" +#include "rust-macro-builtins.h" + +namespace Rust { +namespace AST { + +/** + * The goal of this class is to accumulate and create the required items from a + * builtin `#[derive]` macro applied on a struct, enum or union. + */ +class DeriveVisitor : public AST::ASTVisitor +{ +public: + static std::unique_ptr derive (Item &item, const Attribute &derive, + BuiltinMacro to_derive); + +private: + // the 4 "allowed" visitors, which a derive-visitor can specify and override + virtual void visit_struct (StructStruct &struct_item) = 0; + virtual void visit_tuple (TupleStruct &tuple_item) = 0; + virtual void visit_enum (Enum &enum_item) = 0; + virtual void visit_union (Union &enum_item) = 0; + + // all visitors are final, so no deriving class can implement `derive` for + // anything other than structs, tuples, enums and unions + + virtual void visit (StructStruct &struct_item) override final + { + visit_struct (struct_item); + } + + virtual void visit (TupleStruct &tuple_struct) override final + { + visit_tuple (tuple_struct); + } + + virtual void visit (Enum &enum_item) override final + { + visit_enum (enum_item); + } + + virtual void visit (Union &union_item) override final + { + visit_union (union_item); + } + + virtual void visit (Token &tok) override final{}; + virtual void visit (DelimTokenTree &delim_tok_tree) override final{}; + virtual void visit (AttrInputMetaItemContainer &input) override final{}; + virtual void visit (AttrInputMacro &expr) override final{}; + virtual void visit (IdentifierExpr &ident_expr) override final{}; + virtual void visit (Lifetime &lifetime) override final{}; + virtual void visit (LifetimeParam &lifetime_param) override final{}; + virtual void visit (ConstGenericParam &const_param) override final{}; + virtual void visit (PathInExpression &path) override final{}; + virtual void visit (TypePathSegment &segment) override final{}; + virtual void visit (TypePathSegmentGeneric &segment) override final{}; + virtual void visit (TypePathSegmentFunction &segment) override final{}; + virtual void visit (TypePath &path) override final{}; + virtual void visit (QualifiedPathInExpression &path) override final{}; + virtual void visit (QualifiedPathInType &path) override final{}; + virtual void visit (LiteralExpr &expr) override final{}; + virtual void visit (AttrInputLiteral &attr_input) override final{}; + virtual void visit (MetaItemLitExpr &meta_item) override final{}; + virtual void visit (MetaItemPathLit &meta_item) override final{}; + virtual void visit (BorrowExpr &expr) override final{}; + virtual void visit (DereferenceExpr &expr) override final{}; + virtual void visit (ErrorPropagationExpr &expr) override final{}; + virtual void visit (NegationExpr &expr) override final{}; + virtual void visit (ArithmeticOrLogicalExpr &expr) override final{}; + virtual void visit (ComparisonExpr &expr) override final{}; + virtual void visit (LazyBooleanExpr &expr) override final{}; + virtual void visit (TypeCastExpr &expr) override final{}; + virtual void visit (AssignmentExpr &expr) override final{}; + virtual void visit (CompoundAssignmentExpr &expr) override final{}; + virtual void visit (GroupedExpr &expr) override final{}; + virtual void visit (ArrayElemsValues &elems) override final{}; + virtual void visit (ArrayElemsCopied &elems) override final{}; + virtual void visit (ArrayExpr &expr) override final{}; + virtual void visit (ArrayIndexExpr &expr) override final{}; + virtual void visit (TupleExpr &expr) override final{}; + virtual void visit (TupleIndexExpr &expr) override final{}; + virtual void visit (StructExprStruct &expr) override final{}; + virtual void visit (StructExprFieldIdentifier &field) override final{}; + virtual void visit (StructExprFieldIdentifierValue &field) override final{}; + virtual void visit (StructExprFieldIndexValue &field) override final{}; + virtual void visit (StructExprStructFields &expr) override final{}; + virtual void visit (StructExprStructBase &expr) override final{}; + virtual void visit (CallExpr &expr) override final{}; + virtual void visit (MethodCallExpr &expr) override final{}; + virtual void visit (FieldAccessExpr &expr) override final{}; + virtual void visit (ClosureExprInner &expr) override final{}; + virtual void visit (BlockExpr &expr) override final{}; + virtual void visit (ClosureExprInnerTyped &expr) override final{}; + virtual void visit (ContinueExpr &expr) override final{}; + virtual void visit (BreakExpr &expr) override final{}; + virtual void visit (RangeFromToExpr &expr) override final{}; + virtual void visit (RangeFromExpr &expr) override final{}; + virtual void visit (RangeToExpr &expr) override final{}; + virtual void visit (RangeFullExpr &expr) override final{}; + virtual void visit (RangeFromToInclExpr &expr) override final{}; + virtual void visit (RangeToInclExpr &expr) override final{}; + virtual void visit (ReturnExpr &expr) override final{}; + virtual void visit (UnsafeBlockExpr &expr) override final{}; + virtual void visit (LoopExpr &expr) override final{}; + virtual void visit (WhileLoopExpr &expr) override final{}; + virtual void visit (WhileLetLoopExpr &expr) override final{}; + virtual void visit (ForLoopExpr &expr) override final{}; + virtual void visit (IfExpr &expr) override final{}; + virtual void visit (IfExprConseqElse &expr) override final{}; + virtual void visit (IfLetExpr &expr) override final{}; + virtual void visit (IfLetExprConseqElse &expr) override final{}; + virtual void visit (MatchExpr &expr) override final{}; + virtual void visit (AwaitExpr &expr) override final{}; + virtual void visit (AsyncBlockExpr &expr) override final{}; + virtual void visit (TypeParam ¶m) override final{}; + virtual void visit (LifetimeWhereClauseItem &item) override final{}; + virtual void visit (TypeBoundWhereClauseItem &item) override final{}; + virtual void visit (Method &method) override final{}; + virtual void visit (Module &module) override final{}; + virtual void visit (ExternCrate &crate) override final{}; + virtual void visit (UseTreeGlob &use_tree) override final{}; + virtual void visit (UseTreeList &use_tree) override final{}; + virtual void visit (UseTreeRebind &use_tree) override final{}; + virtual void visit (UseDeclaration &use_decl) override final{}; + virtual void visit (Function &function) override final{}; + virtual void visit (TypeAlias &type_alias) override final{}; + virtual void visit (EnumItem &item) override final{}; + virtual void visit (EnumItemTuple &item) override final{}; + virtual void visit (EnumItemStruct &item) override final{}; + virtual void visit (EnumItemDiscriminant &item) override final{}; + virtual void visit (ConstantItem &const_item) override final{}; + virtual void visit (StaticItem &static_item) override final{}; + virtual void visit (TraitItemFunc &item) override final{}; + virtual void visit (TraitItemMethod &item) override final{}; + virtual void visit (TraitItemConst &item) override final{}; + virtual void visit (TraitItemType &item) override final{}; + virtual void visit (Trait &trait) override final{}; + virtual void visit (InherentImpl &impl) override final{}; + virtual void visit (TraitImpl &impl) override final{}; + virtual void visit (ExternalTypeItem &type) override final{}; + virtual void visit (ExternalStaticItem &item) override final{}; + virtual void visit (ExternalFunctionItem &item) override final{}; + virtual void visit (ExternBlock &block) override final{}; + virtual void visit (MacroMatchFragment &match) override final{}; + virtual void visit (MacroMatchRepetition &match) override final{}; + virtual void visit (MacroMatcher &matcher) override final{}; + virtual void visit (MacroRulesDefinition &rules_def) override final{}; + virtual void visit (MacroInvocation ¯o_invoc) override final{}; + virtual void visit (MetaItemPath &meta_item) override final{}; + virtual void visit (MetaItemSeq &meta_item) override final{}; + virtual void visit (MetaWord &meta_item) override final{}; + virtual void visit (MetaNameValueStr &meta_item) override final{}; + virtual void visit (MetaListPaths &meta_item) override final{}; + virtual void visit (MetaListNameValueStr &meta_item) override final{}; + virtual void visit (LiteralPattern &pattern) override final{}; + virtual void visit (IdentifierPattern &pattern) override final{}; + virtual void visit (WildcardPattern &pattern) override final{}; + virtual void visit (RestPattern &pattern) override final{}; + virtual void visit (RangePatternBoundLiteral &bound) override final{}; + virtual void visit (RangePatternBoundPath &bound) override final{}; + virtual void visit (RangePatternBoundQualPath &bound) override final{}; + virtual void visit (RangePattern &pattern) override final{}; + virtual void visit (ReferencePattern &pattern) override final{}; + virtual void visit (StructPatternFieldTuplePat &field) override final{}; + virtual void visit (StructPatternFieldIdentPat &field) override final{}; + virtual void visit (StructPatternFieldIdent &field) override final{}; + virtual void visit (StructPattern &pattern) override final{}; + virtual void visit (TupleStructItemsNoRange &tuple_items) override final{}; + virtual void visit (TupleStructItemsRange &tuple_items) override final{}; + virtual void visit (TupleStructPattern &pattern) override final{}; + virtual void visit (TuplePatternItemsMultiple &tuple_items) override final{}; + virtual void visit (TuplePatternItemsRanged &tuple_items) override final{}; + virtual void visit (TuplePattern &pattern) override final{}; + virtual void visit (GroupedPattern &pattern) override final{}; + virtual void visit (SlicePattern &pattern) override final{}; + virtual void visit (AltPattern &pattern) override final{}; + virtual void visit (EmptyStmt &stmt) override final{}; + virtual void visit (LetStmt &stmt) override final{}; + virtual void visit (ExprStmt &stmt) override final{}; + virtual void visit (TraitBound &bound) override final{}; + virtual void visit (ImplTraitType &type) override final{}; + virtual void visit (TraitObjectType &type) override final{}; + virtual void visit (ParenthesisedType &type) override final{}; + virtual void visit (ImplTraitTypeOneBound &type) override final{}; + virtual void visit (TraitObjectTypeOneBound &type) override final{}; + virtual void visit (TupleType &type) override final{}; + virtual void visit (NeverType &type) override final{}; + virtual void visit (RawPointerType &type) override final{}; + virtual void visit (ReferenceType &type) override final{}; + virtual void visit (ArrayType &type) override final{}; + virtual void visit (SliceType &type) override final{}; + virtual void visit (InferredType &type) override final{}; + virtual void visit (BareFunctionType &type) override final{}; +}; + +} // namespace AST +} // namespace Rust + +#endif // DERIVE_VISITOR_H diff --git a/gcc/rust/expand/rust-expand-visitor.cc b/gcc/rust/expand/rust-expand-visitor.cc index be1a7a4225f9..b95649d5cd6f 100644 --- a/gcc/rust/expand/rust-expand-visitor.cc +++ b/gcc/rust/expand/rust-expand-visitor.cc @@ -18,38 +18,199 @@ #include "rust-expand-visitor.h" #include "rust-attributes.h" +#include "rust-ast.h" +#include "rust-type.h" +#include "rust-derive.h" namespace Rust { +bool +is_derive (AST::Attribute &attr) +{ + auto path = attr.get_path (); + return attr.has_attr_input () + && attr.get_attr_input ().get_attr_input_type () + == AST::AttrInput::TOKEN_TREE + && path == "derive"; +} + +bool +is_builtin (AST::Attribute &attr) +{ + auto &segments = attr.get_path ().get_segments (); + return !segments.empty () + && !Analysis::BuiltinAttributeMappings::get () + ->lookup_builtin (segments[0].get_segment_name ()) + .is_error (); +} + /* Expand all of the macro invocations currently contained in a crate */ void ExpandVisitor::go (AST::Crate &crate) +{ + expand_inner_items (crate.items); +} + +/** + * Returns a list of all the derive macros to apply, as well as the Attribute + * they are from. + * + * ```rust + * #[derive(Clone, Copy)] // attr1 + * struct S; + * + * // returns [{Clone, &attr1}, {Copy, &attr1}] + * + * #[derive(Clone)] // attr1 + * #[derive(Copy, PartialEq, Ord)] // attr2 + * struct S; + * + * // returns [{Clone, &attr1}, {Copy, &attr2}, {PartialEq, &attr2}, {Ord, + * &attr2}] + * ``` + * + * @param outer_attrs The list of attributes on the item to derive + */ +static std::vector< + std::pair>> +get_traits_to_derive (std::vector &outer_attrs) +{ + std::vector< + std::pair>> + to_derive; + for (auto it = outer_attrs.begin (); it != outer_attrs.end ();) + { + auto &attr = *it; + + if (is_derive (attr)) + { + auto &input = attr.get_attr_input (); + switch (input.get_attr_input_type ()) + { + // isn't there a better way to do this?? like parse it or + // something idk. some function I'm not thinking of? + case AST::AttrInput::TOKEN_TREE: { + auto &tokens = static_cast (input) + .get_token_trees (); + + // erase the delimiters + rust_assert (tokens.size () >= 3); + tokens.erase (tokens.begin ()); + tokens.pop_back (); + + for (auto &token : tokens) + { + // skip commas, as they are part of the token stream + if (token->as_string () == ",") + continue; + + to_derive.emplace_back (token->as_string (), attr); + } + break; + } + case AST::AttrInput::LITERAL: + case AST::AttrInput::META_ITEM: + case AST::AttrInput::MACRO: + gcc_unreachable (); + break; + } + + it = outer_attrs.erase (it); + } + else + { + it++; + } + } + + return to_derive; +} + +static std::unique_ptr +derive_item (std::unique_ptr &item, const AST::Attribute &derive, + BuiltinMacro to_derive) +{ + return AST::DeriveVisitor::derive (*item, derive, to_derive); +} + +void +ExpandVisitor::expand_inner_items ( + std::vector> &items) { expander.push_context (MacroExpander::ContextType::ITEM); - // expand attributes recursively and strip items if required - // AttrVisitor attr_visitor (*this); - auto &items = crate.items; - for (auto it = items.begin (); it != items.end ();) + for (auto it = items.begin (); it != items.end (); it++) { auto &item = *it; - item->accept_vis (*this); - - auto fragment = expander.take_expanded_fragment (); - if (fragment.should_expand ()) + if (item->has_outer_attrs ()) { - // Remove the current expanded invocation - it = items.erase (it); - for (auto &node : fragment.get_nodes ()) + auto traits_to_derive + = get_traits_to_derive (item->get_outer_attrs ()); + + for (auto &to_derive : traits_to_derive) { - it = items.insert (it, node.take_item ()); - it++; + auto &name = to_derive.first; + auto &attr = to_derive.second; + + auto maybe_builtin = MacroBuiltin::builtins.lookup (name); + if (MacroBuiltin::builtins.is_iter_ok (maybe_builtin)) + { + auto new_item + = derive_item (item, attr, maybe_builtin->second); + // this inserts the derive *before* the item - is it a + // problem? + it = items.insert (it, std::move (new_item)); + } } } - else - it++; } + std::function (AST::SingleASTNode)> extractor + = [] (AST::SingleASTNode node) { return node.take_item (); }; + + expand_macro_children (items, extractor); + + expander.pop_context (); +} + +void +ExpandVisitor::expand_inner_stmts ( + std::vector> &stmts) +{ + expander.push_context (MacroExpander::ContextType::BLOCK); + + for (auto it = stmts.begin (); it != stmts.end (); it++) + { + // TODO: Eventually we need to derive here as well + + // auto &stmt = *it; + + // if (stmt->has_outer_attrs ()) + // { + // auto traits_to_derive + // = get_traits_to_derive (stmt->get_outer_attrs ()); + + // // FIXME: This needs to be reworked absolutely + // static const std::set builtin_derives + // = {"Clone", "Copy", "Eq", "PartialEq", "Ord", "PartialOrd"}; + + // for (auto &to_derive : traits_to_derive) + // if (builtin_derives.find (to_derive) != builtin_derives.end ()) + // { + // auto new_item = derive_item ( + // item, item->get_outer_attrs ()[0] /* FIXME: This is wrong */, + // to_derive); + // // this inserts the derive *before* the item - is it a problem? + // it = items.insert (it, std::move (new_item)); + // } + // } + } + + std::function (AST::SingleASTNode)> extractor + = [] (AST::SingleASTNode node) { return node.take_stmt (); }; + + expand_macro_children (stmts, extractor); + expander.pop_context (); } @@ -544,12 +705,7 @@ ExpandVisitor::visit (AST::ClosureExprInner &expr) void ExpandVisitor::visit (AST::BlockExpr &expr) { - visit_outer_attrs (expr); - std::function (AST::SingleASTNode)> extractor - = [] (AST::SingleASTNode node) { return node.take_stmt (); }; - - expand_macro_children (MacroExpander::ContextType::BLOCK, - expr.get_statements (), extractor); + expand_inner_stmts (expr.get_statements ()); expander.push_context (MacroExpander::ContextType::BLOCK); @@ -1404,7 +1560,7 @@ ExpandVisitor::visit_outer_attrs (T &item, std::vector &attrs) { for (auto it = attrs.begin (); it != attrs.end (); /* erase => No increment*/) { - auto current = *it; + auto ¤t = *it; if (!is_builtin (current) && !is_derive (current)) { @@ -1509,25 +1665,4 @@ ExpandVisitor::visit_attrs_with_derive (T &item) } } } - -bool -ExpandVisitor::is_derive (AST::Attribute &attr) -{ - auto &segments = attr.get_path ().get_segments (); - return attr.has_attr_input () - && attr.get_attr_input ().get_attr_input_type () - == AST::AttrInput::TOKEN_TREE - && !segments.empty () && "derive" == segments[0].get_segment_name (); -} - -bool -ExpandVisitor::is_builtin (AST::Attribute &attr) -{ - auto &segments = attr.get_path ().get_segments (); - return !segments.empty () - && !Analysis::BuiltinAttributeMappings::get () - ->lookup_builtin (segments[0].get_segment_name ()) - .is_error (); -} - } // namespace Rust diff --git a/gcc/rust/expand/rust-expand-visitor.h b/gcc/rust/expand/rust-expand-visitor.h index 21677ec9002c..85893347defa 100644 --- a/gcc/rust/expand/rust-expand-visitor.h +++ b/gcc/rust/expand/rust-expand-visitor.h @@ -16,11 +16,26 @@ // along with GCC; see the file COPYING3. If not see // . +#ifndef RUST_EXPAND_VISITOR_H +#define RUST_EXPAND_VISITOR_H + #include "rust-ast-visitor.h" #include "rust-macro-expand.h" namespace Rust { +/** + * Whether or not an attribute is a derive attribute + */ +bool +is_derive (AST::Attribute &attr); + +/** + * Whether or not an attribute is builtin + */ +bool +is_builtin (AST::Attribute &attr); + class ExpandVisitor : public AST::ASTVisitor { public: @@ -64,6 +79,7 @@ class ExpandVisitor : public AST::ASTVisitor */ void expand_qualified_path_type (AST::QualifiedPathType &path_type); + // FIXME: Add documentation void expand_closure_params (std::vector ¶ms); void expand_self_param (AST::SelfParam &self_param); void expand_where_clause (AST::WhereClause &where_clause); @@ -88,6 +104,20 @@ class ExpandVisitor : public AST::ASTVisitor { expander.push_context (ctx); + expand_macro_children (values, extractor); + + expander.pop_context (); + } + + /** + * Same as `expand_macro_children`, but does not push a context. This is + * useful if you're already pushing the context manually anyway for proc macro + * expansion, like in `expand_inner_{items, stmts}` + */ + template + void expand_macro_children (T &values, + std::function extractor) + { for (auto it = values.begin (); it != values.end ();) { auto &value = *it; @@ -116,10 +146,19 @@ class ExpandVisitor : public AST::ASTVisitor ++it; } } - - expander.pop_context (); } + /** + * Perform in-place expansion of procedural macros and macro invocations for + * an item container or statement container, such as `AST::Crate`, + * `AST::Module` or `AST::BlockExpr`. This function will insert the expanded + * nodes in place, and replace macro invocations with their expanded nodes. + * + * @param values Vector of values to mutate in-place and append into + */ + void expand_inner_items (std::vector> &values); + void expand_inner_stmts (std::vector> &values); + // TODO: See if possible to make more specialization for Impl items, Block // stmts etc? This could allow us to remove expand_macro_children or at least // its extractor parameter @@ -325,8 +364,6 @@ class ExpandVisitor : public AST::ASTVisitor template void visit_inner_attrs (T &item); - bool is_derive (AST::Attribute &attr); - template void expand_derive (const T &item, std::unique_ptr &trait); @@ -335,10 +372,10 @@ class ExpandVisitor : public AST::ASTVisitor template void visit_attrs_with_derive (T &item); - bool is_builtin (AST::Attribute &attr); - private: MacroExpander &expander; }; } // namespace Rust + +#endif // RUST_EXPAND_VISITOR_H diff --git a/gcc/rust/expand/rust-macro-builtins.cc b/gcc/rust/expand/rust-macro-builtins.cc index 67ec82166435..8104c572b45a 100644 --- a/gcc/rust/expand/rust-macro-builtins.cc +++ b/gcc/rust/expand/rust-macro-builtins.cc @@ -29,10 +29,10 @@ #include "rust-macro.h" #include "rust-parse.h" #include "rust-session-manager.h" -#include "bi-map.h" namespace Rust { -static const BiMap builtins = {{ + +const BiMap MacroBuiltin::builtins = {{ {"assert", BuiltinMacro::Assert}, {"file", BuiltinMacro::File}, {"line", BuiltinMacro::Line}, @@ -62,6 +62,16 @@ static const BiMap builtins = {{ {"cfg_accessible", BuiltinMacro::CfgAccessible}, {"RustcEncodable", BuiltinMacro::RustcDecodable}, {"RustcDecodable", BuiltinMacro::RustcEncodable}, + {"Clone", BuiltinMacro::Clone}, + {"Copy", BuiltinMacro::Copy}, + {"Debug", BuiltinMacro::Debug}, + {"Default", BuiltinMacro::Default}, + {"Eq", BuiltinMacro::Eq}, + {"PartialEq", BuiltinMacro::PartialEq}, + {"Ord", BuiltinMacro::Ord}, + {"PartialOrd", BuiltinMacro::PartialOrd}, + {"Hash", BuiltinMacro::Hash}, + }}; std::unordered_map< @@ -95,16 +105,14 @@ std::unordered_map< {"test_case", MacroBuiltin::sorry}, {"global_allocator", MacroBuiltin::sorry}, {"cfg_accessible", MacroBuiltin::sorry}, - {"RustcEncodable", MacroBuiltin::sorry}, - {"RustcDecodable", MacroBuiltin::sorry}, }; // FIXME: This should return an Optional BuiltinMacro builtin_macro_from_string (const std::string &identifier) { - auto macro = builtins.lookup (identifier); - rust_assert (builtins.is_iter_ok (macro)); + auto macro = MacroBuiltin::builtins.lookup (identifier); + rust_assert (MacroBuiltin::builtins.is_iter_ok (macro)); return macro->second; } @@ -113,8 +121,8 @@ namespace { std::string make_macro_path_str (BuiltinMacro kind) { - auto str = builtins.lookup (kind); - rust_assert (builtins.is_iter_ok (str)); + auto str = MacroBuiltin::builtins.lookup (kind); + rust_assert (MacroBuiltin::builtins.is_iter_ok (str)); return str->second; } diff --git a/gcc/rust/expand/rust-macro-builtins.h b/gcc/rust/expand/rust-macro-builtins.h index 8a36ddfed6f8..cf70e49634e4 100644 --- a/gcc/rust/expand/rust-macro-builtins.h +++ b/gcc/rust/expand/rust-macro-builtins.h @@ -22,6 +22,7 @@ #include "rust-ast.h" #include "rust-ast-fragment.h" #include "rust-location.h" +#include "bi-map.h" namespace Rust { @@ -63,6 +64,15 @@ enum class BuiltinMacro CfgAccessible, RustcDecodable, RustcEncodable, + Clone, + Copy, + Debug, + Default, + Eq, + PartialEq, + Ord, + PartialOrd, + Hash, }; BuiltinMacro @@ -107,6 +117,7 @@ builtin_macro_from_string (const std::string &identifier); class MacroBuiltin { public: + static const BiMap builtins; static std::unordered_map< std::string, std::function> builtin_transcribers; diff --git a/gcc/testsuite/rust/compile/derive_macro1.rs b/gcc/testsuite/rust/compile/derive_macro1.rs new file mode 100644 index 000000000000..131822820b0e --- /dev/null +++ b/gcc/testsuite/rust/compile/derive_macro1.rs @@ -0,0 +1,12 @@ +pub trait Clone { + fn clone(&self) -> Self; +} + +// This warning can be removed once we properly handle implems with #[automatically_derived] +#[derive(Clone)] // { dg-warning "unused name .self." } +pub struct S; + +fn main() { + let s = S; + let _s_clone = s.clone(); +} diff --git a/gcc/testsuite/rust/compile/derive_macro3.rs b/gcc/testsuite/rust/compile/derive_macro3.rs new file mode 100644 index 000000000000..35284d8fa3b7 --- /dev/null +++ b/gcc/testsuite/rust/compile/derive_macro3.rs @@ -0,0 +1,21 @@ +pub trait Clone { + fn clone(&self) -> Self; +} + +pub trait Copy {} + +impl Copy for i32 {} + +impl Clone for T +where + T: Copy, +{ + fn clone(&self) -> Self { + *self + } +} + +fn main() { + let a = 15i32; + let _ = a.clone(); +} diff --git a/gcc/testsuite/rust/compile/macro43.rs b/gcc/testsuite/rust/compile/macro43.rs index c7bf50a030e6..992bc77cedf3 100644 --- a/gcc/testsuite/rust/compile/macro43.rs +++ b/gcc/testsuite/rust/compile/macro43.rs @@ -11,7 +11,8 @@ macro_rules! nonzero_integers { /// assert_eq!(size_of::>(), size_of::()); /// ``` #[stable(feature = "nonzero", since = "1.28.0")] - #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] + // not all derive macros are implemented yet, and this test does not test these anyways + // #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] #[repr(transparent)] pub struct $Ty(NonZero<$Int>); diff --git a/gcc/testsuite/rust/execute/torture/derive_macro1.rs b/gcc/testsuite/rust/execute/torture/derive_macro1.rs new file mode 100644 index 000000000000..6e0350de3de9 --- /dev/null +++ b/gcc/testsuite/rust/execute/torture/derive_macro1.rs @@ -0,0 +1,23 @@ +pub trait Clone { + fn clone(&self) -> Self; +} + +pub trait Copy {} + +impl Copy for i32 {} + +impl Clone for T +where + T: Copy, +{ + fn clone(&self) -> Self { + *self + } +} + +fn main() -> i32 { + let a = 15i32; + let b = a.clone(); + + a - b +} diff --git a/gcc/testsuite/rust/execute/torture/derive_macro3.rs b/gcc/testsuite/rust/execute/torture/derive_macro3.rs new file mode 100644 index 000000000000..716bd908f4e8 --- /dev/null +++ b/gcc/testsuite/rust/execute/torture/derive_macro3.rs @@ -0,0 +1,19 @@ +pub trait Clone { + fn clone(&self) -> Self; +} + +impl Clone for i32 { + fn clone(&self) -> i32 { + *self + } +} + +#[derive(Clone)] +struct S(i32, i32); + +fn main() -> i32 { + let a = S(15, 15); + let b = a.clone(); + + b.0 - b.1 +}