]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
gccrs: expansion: Add base for deriving builtin macros (Clone, Copy...)
authorArthur Cohen <arthur.cohen@embecosm.com>
Thu, 25 May 2023 12:18:39 +0000 (14:18 +0200)
committerArthur Cohen <arthur.cohen@embecosm.com>
Tue, 16 Jan 2024 17:37:23 +0000 (18:37 +0100)
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.
* util/rust-hir-map.cc (Mappings::insert_macro_def): Fix logic for
setting builtin macro transcribers.

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.

15 files changed:
gcc/rust/Make-lang.in
gcc/rust/expand/rust-derive-clone.cc [new file with mode: 0644]
gcc/rust/expand/rust-derive-clone.h [new file with mode: 0644]
gcc/rust/expand/rust-derive.cc [new file with mode: 0644]
gcc/rust/expand/rust-derive.h [new file with mode: 0644]
gcc/rust/expand/rust-expand-visitor.cc
gcc/rust/expand/rust-expand-visitor.h
gcc/rust/expand/rust-macro-builtins.cc
gcc/rust/expand/rust-macro-builtins.h
gcc/rust/util/rust-hir-map.cc
gcc/testsuite/rust/compile/derive_macro1.rs [new file with mode: 0644]
gcc/testsuite/rust/compile/derive_macro3.rs [new file with mode: 0644]
gcc/testsuite/rust/compile/macro43.rs
gcc/testsuite/rust/execute/torture/derive_macro1.rs [new file with mode: 0644]
gcc/testsuite/rust/execute/torture/derive_macro3.rs [new file with mode: 0644]

index 31c11b82e9cf735a04f60c5db37bb0acbe099193..1d72c86c1687798652eb77fc2484d3a3e3633566 100644 (file)
@@ -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 (file)
index 0000000..b641484
--- /dev/null
@@ -0,0 +1,152 @@
+// 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
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-derive-clone.h"
+
+namespace Rust {
+namespace AST {
+
+std::unique_ptr<Expr>
+DeriveClone::clone_call (std::unique_ptr<Expr> &&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
+  auto path = std::unique_ptr<Expr> (
+    new PathInExpression (builder.path_in_expression ({"Clone", "clone"})));
+
+  auto args = std::vector<std::unique_ptr<Expr>> ();
+  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 { <clone_expr> }
+ *
+ */
+std::unique_ptr<TraitImplItem>
+DeriveClone::clone_fn (std::unique_ptr<Expr> &&clone_expr)
+{
+  auto block = std::unique_ptr<BlockExpr> (
+    new BlockExpr ({}, std::move (clone_expr), {}, {}, loc, loc));
+  auto big_self_type = builder.single_type_path ("Self");
+
+  return std::unique_ptr<TraitImplItem> (
+    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 <type> {
+ *     <clone_fn>
+ * }
+ *
+ */
+std::unique_ptr<Item>
+DeriveClone::clone_impl (std::unique_ptr<TraitImplItem> &&clone_fn,
+                        std::string name)
+{
+  // should that be `$crate::core::clone::Clone` instead?
+  auto segments = std::vector<std::unique_ptr<TypePathSegment>> ();
+  segments.emplace_back (builder.type_path_segment ("Clone"));
+  auto clone = TypePath (std::move (segments), loc);
+
+  auto trait_items = std::vector<std::unique_ptr<TraitImplItem>> ();
+  trait_items.emplace_back (std::move (clone_fn));
+
+  return std::unique_ptr<Item> (
+    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<AST::Item>
+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<std::unique_ptr<Expr>> ();
+
+  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<Expr> (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 (file)
index 0000000..6c82a64
--- /dev/null
@@ -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
+// <http://www.gnu.org/licenses/>.
+
+#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<AST::Item> go (Item &item);
+
+private:
+  Location loc;
+  std::unique_ptr<AST::Item> 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(<to_clone>)
+   */
+  std::unique_ptr<Expr> clone_call (std::unique_ptr<Expr> &&to_clone);
+
+  /**
+   * Create the actual "clone" function of the implementation, so
+   *
+   * fn clone(&self) -> Self { <clone_expr> }
+   *
+   */
+  std::unique_ptr<TraitImplItem> clone_fn (std::unique_ptr<Expr> &&clone_expr);
+
+  /**
+   * Create the Clone trait implementation for a type
+   *
+   * impl Clone for <type> {
+   *     <clone_fn>
+   * }
+   *
+   */
+  std::unique_ptr<Item> clone_impl (std::unique_ptr<TraitImplItem> &&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 (file)
index 0000000..cf51266
--- /dev/null
@@ -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
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-derive.h"
+#include "rust-derive-clone.h"
+
+namespace Rust {
+namespace AST {
+
+std::unique_ptr<Item>
+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 (file)
index 0000000..9a62c29
--- /dev/null
@@ -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
+// <http://www.gnu.org/licenses/>.
+
+#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<Item> 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 &param) 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 &macro_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
index be1a7a4225f9c32a9173a742a310b806fd736e7b..b95649d5cd6fb5af7f993f4f0946741bd0e109b7 100644 (file)
 
 #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<std::string, std::reference_wrapper<const AST::Attribute>>>
+get_traits_to_derive (std::vector<AST::Attribute> &outer_attrs)
+{
+  std::vector<
+    std::pair<std::string, std::reference_wrapper<const AST::Attribute>>>
+    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<AST::DelimTokenTree &> (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<AST::Item>
+derive_item (std::unique_ptr<AST::Item> &item, const AST::Attribute &derive,
+            BuiltinMacro to_derive)
+{
+  return AST::DeriveVisitor::derive (*item, derive, to_derive);
+}
+
+void
+ExpandVisitor::expand_inner_items (
+  std::vector<std::unique_ptr<AST::Item>> &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<std::unique_ptr<AST::Item> (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<std::unique_ptr<AST::Stmt>> &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<std::string> 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<std::unique_ptr<AST::Stmt> (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<std::unique_ptr<AST::Stmt> (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<AST::Attribute> &attrs)
 {
   for (auto it = attrs.begin (); it != attrs.end (); /* erase => No increment*/)
     {
-      auto current = *it;
+      auto &current = *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
index 21677ec9002cc3ab9d087642429b00ba65dcc08e..85893347defa10053efad807612e7f6ffb00360d 100644 (file)
 // along with GCC; see the file COPYING3.  If not see
 // <http://www.gnu.org/licenses/>.
 
+#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 @@ public:
    */
   void expand_qualified_path_type (AST::QualifiedPathType &path_type);
 
+  // FIXME: Add documentation
   void expand_closure_params (std::vector<AST::ClosureParam> &params);
   void expand_self_param (AST::SelfParam &self_param);
   void expand_where_clause (AST::WhereClause &where_clause);
@@ -88,6 +104,20 @@ public:
   {
     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 <typename T, typename U>
+  void expand_macro_children (T &values,
+                             std::function<U (AST::SingleASTNode)> extractor)
+  {
     for (auto it = values.begin (); it != values.end ();)
       {
        auto &value = *it;
@@ -116,10 +146,19 @@ public:
            ++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<std::unique_ptr<AST::Item>> &values);
+  void expand_inner_stmts (std::vector<std::unique_ptr<AST::Stmt>> &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 @@ public:
 
   template <typename T> void visit_inner_attrs (T &item);
 
-  bool is_derive (AST::Attribute &attr);
-
   template <typename T>
   void expand_derive (const T &item, std::unique_ptr<AST::TokenTree> &trait);
 
@@ -335,10 +372,10 @@ public:
 
   template <typename T> void visit_attrs_with_derive (T &item);
 
-  bool is_builtin (AST::Attribute &attr);
-
 private:
   MacroExpander &expander;
 };
 
 } // namespace Rust
+
+#endif // RUST_EXPAND_VISITOR_H
index f0f1f8c9230f0dce67553a591da3697ccd9999ad..f4577503c25edca15f50d1d985aba03f52e12361 100644 (file)
 #include "rust-macro.h"
 #include "rust-parse.h"
 #include "rust-session-manager.h"
-#include "bi-map.h"
 
 namespace Rust {
-static const BiMap<std::string, BuiltinMacro> builtins = {{
+
+const BiMap<std::string, BuiltinMacro> MacroBuiltin::builtins = {{
   {"assert", BuiltinMacro::Assert},
   {"file", BuiltinMacro::File},
   {"line", BuiltinMacro::Line},
@@ -62,6 +62,16 @@ static const BiMap<std::string, BuiltinMacro> 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;
 }
index 10a38d7b93f03ce5bd1e68aa706ddbc6c4ad279f..0d8c4dba2dc164ac8f4b1fc6bdb4075a88945433 100644 (file)
@@ -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<std::string, BuiltinMacro> builtins;
   static std::unordered_map<
     std::string, std::function<AST::Fragment (Location, AST::MacroInvocData &)>>
     builtin_transcribers;
index 5c711ca6ee393a69092fbfa904d1a33a854eb9de..93066f52d9536497556067429683f0e2a20a873c 100644 (file)
@@ -876,14 +876,18 @@ Mappings::insert_macro_def (AST::MacroRulesDefinition *macro)
                   });
   if (should_be_builtin)
     {
-      auto builtin
+      auto builtin = MacroBuiltin::builtins.lookup (macro->get_rule_name ());
+      if (!MacroBuiltin::builtins.is_iter_ok (builtin))
+       {
+         rust_error_at (macro->get_locus (),
+                        "cannot find a built-in macro with name %qs",
+                        macro->get_rule_name ().c_str ());
+         return;
+       }
+
+      auto transcriber
        = MacroBuiltin::builtin_transcribers.find (macro->get_rule_name ());
-      if (builtin != MacroBuiltin::builtin_transcribers.end ())
-       macro->set_builtin_transcriber (builtin->second);
-      else
-       rust_error_at (macro->get_locus (),
-                      "cannot find a built-in macro with name %qs",
-                      macro->get_rule_name ().c_str ());
+      macro->set_builtin_transcriber (transcriber->second);
     }
 
   auto it = macroMappings.find (macro->get_node_id ());
diff --git a/gcc/testsuite/rust/compile/derive_macro1.rs b/gcc/testsuite/rust/compile/derive_macro1.rs
new file mode 100644 (file)
index 0000000..1318228
--- /dev/null
@@ -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 (file)
index 0000000..35284d8
--- /dev/null
@@ -0,0 +1,21 @@
+pub trait Clone {
+    fn clone(&self) -> Self;
+}
+
+pub trait Copy {}
+
+impl Copy for i32 {}
+
+impl<T> Clone for T
+where
+    T: Copy,
+{
+    fn clone(&self) -> Self {
+        *self
+    }
+}
+
+fn main() {
+    let a = 15i32;
+    let _ = a.clone();
+}
index c7bf50a030e62b533fbdb0585577f2bc1081bc47..992bc77cedf3239523622515b358f3cf6165417e 100644 (file)
@@ -11,7 +11,8 @@ macro_rules! nonzero_integers {
             /// assert_eq!(size_of::<Option<std::num::NonZeroU32>>(), size_of::<u32>());
             /// ```
             #[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 (file)
index 0000000..6e0350d
--- /dev/null
@@ -0,0 +1,23 @@
+pub trait Clone {
+    fn clone(&self) -> Self;
+}
+
+pub trait Copy {}
+
+impl Copy for i32 {}
+
+impl<T> 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 (file)
index 0000000..716bd90
--- /dev/null
@@ -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
+}