From: Pierre-Emmanuel Patry <[email protected]>

Attribute have always been a bit weird, they were part of the AST but did
not have any visitor function, this create a lot of friction around them
as their input which may be an expression, a literal and even an
expression in the future didn't get visited properly during the different
passes of the compiler.

gcc/rust/ChangeLog:

        * ast/rust-ast-full-decls.h (struct Attribute): Change from here...
        (class Attribute): ...to a class here.
        * ast/rust-ast-visitor.cc (DefaultASTVisitor::visit): Add a default
        visit function for the attributes.
        * ast/rust-ast-visitor.h: Add a proper visit function declaration for
        attributes.
        * ast/rust-ast.cc (Attribute::accept_vis): Add a function to accept a
        visitor within the attributes.
        * ast/rust-ast.h (struct Attribute): Add the accept_vis function
        prototype.
        (class Attribute): Rename from struct to class.
        * expand/rust-derive.h: Add an empty implementation for visits to
        prevent the visitor from recursing into the attributes.
        * hir/rust-ast-lower-base.cc (ASTLoweringBase::visit): Add missing
        implementation for attribute visit function. Add a check for derive
        attributes that would have slipped through the passes.
        * hir/rust-ast-lower-base.h: Add function prototype.
        * resolve/rust-early-name-resolver-2.0.cc (Early::visit_attributes):
        Split the function into two halves.
        (Early::visit_derive_attribute): Specialized function from
        visit_attribute for derive attributes.
        (Early::visit_non_builtin_attribute): Likewise for non builtin
        attributes.
        (Early::visit): Rename visit_attributes to visit.
        * resolve/rust-early-name-resolver-2.0.h (class Early): Update function
        prototypes.

gcc/testsuite/ChangeLog:

        * rust/compile/issue-4212.rs: Update test with the new errors.

Signed-off-by: Pierre-Emmanuel Patry <[email protected]>
---
This change was merged into the gccrs repository and is posted here for
upstream visibility and potential drive-by review, as requested by GCC
release managers.
Each commit email contains a link to its details on github from where you can
find the Pull-Request and associated discussions.


Commit on github: 
https://github.com/Rust-GCC/gccrs/commit/b4d43073858164ff3d0585fb58650d2595f04355

The commit has been mentioned in the following pull-request(s):
 - https://github.com/Rust-GCC/gccrs/pull/4425

 gcc/rust/ast/rust-ast-full-decls.h            |   2 +-
 gcc/rust/ast/rust-ast-visitor.cc              |   8 ++
 gcc/rust/ast/rust-ast-visitor.h               |   3 +-
 gcc/rust/ast/rust-ast.cc                      |   6 +
 gcc/rust/ast/rust-ast.h                       |   5 +-
 gcc/rust/expand/rust-derive.h                 |   3 +-
 gcc/rust/hir/rust-ast-lower-base.cc           |  11 ++
 gcc/rust/hir/rust-ast-lower-base.h            |   1 +
 .../resolve/rust-early-name-resolver-2.0.cc   | 115 ++++++++++--------
 .../resolve/rust-early-name-resolver-2.0.h    |   6 +
 gcc/testsuite/rust/compile/issue-4212.rs      |   2 +
 11 files changed, 108 insertions(+), 54 deletions(-)

diff --git a/gcc/rust/ast/rust-ast-full-decls.h 
b/gcc/rust/ast/rust-ast-full-decls.h
index f839348c7..f90b93145 100644
--- a/gcc/rust/ast/rust-ast-full-decls.h
+++ b/gcc/rust/ast/rust-ast-full-decls.h
@@ -34,7 +34,7 @@ class DelimTokenTree;
 class PathSegment;
 class SimplePathSegment;
 class SimplePath;
-struct Attribute;
+class Attribute;
 class MetaItemInner;
 class AttrInputMetaItemContainer;
 class MetaItem;
diff --git a/gcc/rust/ast/rust-ast-visitor.cc b/gcc/rust/ast/rust-ast-visitor.cc
index 6973d65bc..e04ee4dff 100644
--- a/gcc/rust/ast/rust-ast-visitor.cc
+++ b/gcc/rust/ast/rust-ast-visitor.cc
@@ -194,6 +194,14 @@ DefaultASTVisitor::visit (AST::LiteralExpr &expr)
   visit_outer_attrs (expr);
 }
 
+void
+DefaultASTVisitor::visit (AST::Attribute &attribute)
+{
+  visit (attribute.get_path ());
+  if (attribute.has_attr_input ())
+    visit (attribute.get_attr_input ());
+}
+
 void
 DefaultASTVisitor::visit (AST::AttrInputLiteral &attr_input)
 {
diff --git a/gcc/rust/ast/rust-ast-visitor.h b/gcc/rust/ast/rust-ast-visitor.h
index 07ba1e185..fee25e463 100644
--- a/gcc/rust/ast/rust-ast-visitor.h
+++ b/gcc/rust/ast/rust-ast-visitor.h
@@ -39,6 +39,7 @@ public:
 
   // rust-ast.h
   // virtual void visit(AttrInput& attr_input) = 0;
+  virtual void visit (AST::Attribute &attribute) = 0;
   // virtual void visit(TokenTree& token_tree) = 0;
   // virtual void visit(MacroMatch& macro_match) = 0;
   virtual void visit (Token &tok) = 0;
@@ -444,7 +445,7 @@ public:
   virtual void visit (AST::StructPatternElements &spe);
   virtual void visit (AST::MaybeNamedParam &param);
 
-  void visit (AST::Attribute &attribute) {}
+  virtual void visit (AST::Attribute &attribute) override;
 
   template <typename T> void visit_outer_attrs (T &node)
   {
diff --git a/gcc/rust/ast/rust-ast.cc b/gcc/rust/ast/rust-ast.cc
index e571fc8ea..ec25ba268 100644
--- a/gcc/rust/ast/rust-ast.cc
+++ b/gcc/rust/ast/rust-ast.cc
@@ -253,6 +253,12 @@ Attribute::as_string () const
     return path_str + attr_input->as_string ();
 }
 
+void
+Attribute::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
 bool
 Attribute::is_derive () const
 {
diff --git a/gcc/rust/ast/rust-ast.h b/gcc/rust/ast/rust-ast.h
index 0f78f01e1..349318540 100644
--- a/gcc/rust/ast/rust-ast.h
+++ b/gcc/rust/ast/rust-ast.h
@@ -566,9 +566,8 @@ protected:
 
 // aka Attr
 // Attribute AST representation
-struct Attribute
+class Attribute : Visitable
 {
-private:
   SimplePath path;
 
   // bool has_attr_input;
@@ -686,7 +685,7 @@ public:
 
   bool is_inner_attribute () const { return inner_attribute; }
 
-  // no visitor pattern as not currently polymorphic
+  void accept_vis (ASTVisitor &vis) override;
 
   const SimplePath &get_path () const { return path; }
   SimplePath &get_path () { return path; }
diff --git a/gcc/rust/expand/rust-derive.h b/gcc/rust/expand/rust-derive.h
index 7a7019544..9cfd311f6 100644
--- a/gcc/rust/expand/rust-derive.h
+++ b/gcc/rust/expand/rust-derive.h
@@ -256,10 +256,11 @@ private:
   virtual void visit (FunctionParam &param) override final{};
   virtual void visit (VariadicParam &param) override final{};
   virtual void visit (FormatArgs &param) override final{};
+  virtual void visit (Attribute &attribute) override final{};
   virtual void visit (OffsetOf &param) override final{};
 };
 
 } // namespace AST
 } // namespace Rust
 
-#endif // DERIVE_VISITOR_H
\ No newline at end of file
+#endif // DERIVE_VISITOR_H
diff --git a/gcc/rust/hir/rust-ast-lower-base.cc 
b/gcc/rust/hir/rust-ast-lower-base.cc
index 639c95245..814b851ae 100644
--- a/gcc/rust/hir/rust-ast-lower-base.cc
+++ b/gcc/rust/hir/rust-ast-lower-base.cc
@@ -67,6 +67,17 @@ ASTLoweringBase::visit (AST::WhileLetLoopExpr &expr)
   rust_unreachable ();
 }
 
+void
+ASTLoweringBase::visit (AST::Attribute &attribute)
+{
+  auto &path = attribute.get_path ();
+  if (path.as_string () == "derive")
+    {
+      rust_fatal_error (attribute.get_locus (),
+                       "missing desugar for attribute");
+    }
+}
+
 void
 ASTLoweringBase::visit (AST::Token &)
 {}
diff --git a/gcc/rust/hir/rust-ast-lower-base.h 
b/gcc/rust/hir/rust-ast-lower-base.h
index 46b1b91f0..94cef480e 100644
--- a/gcc/rust/hir/rust-ast-lower-base.h
+++ b/gcc/rust/hir/rust-ast-lower-base.h
@@ -71,6 +71,7 @@ public:
 
   // visitor impl
   // rust-ast.h
+  virtual void visit (AST::Attribute &attribute) override;
   //  virtual void visit(AttrInput& attr_input);
   //  virtual void visit(TokenTree& token_tree);
   //  virtual void visit(MacroMatch& macro_match);
diff --git a/gcc/rust/resolve/rust-early-name-resolver-2.0.cc 
b/gcc/rust/resolve/rust-early-name-resolver-2.0.cc
index fe40bcd7f..1ee8e4ac1 100644
--- a/gcc/rust/resolve/rust-early-name-resolver-2.0.cc
+++ b/gcc/rust/resolve/rust-early-name-resolver-2.0.cc
@@ -314,65 +314,84 @@ Early::visit (AST::MacroInvocation &invoc)
 }
 
 void
-Early::visit_attributes (std::vector<AST::Attribute> &attrs)
+Early::visit_derive_attribute (AST::Attribute &attr,
+                              Analysis::Mappings &mappings)
 {
-  auto &mappings = Analysis::Mappings::get ();
+  auto traits = attr.get_traits_to_derive ();
+  for (auto &trait : traits)
+    {
+      auto definition = ctx.resolve_path (trait.get (), Namespace::Macros);
+      if (!definition.has_value ())
+       {
+         // FIXME: Change to proper error message
+         // FIXME: Change locus to trait locus instead of attribute locus
+         collect_error (Error (attr.get_locus (),
+                               "could not resolve trait %qs",
+                               trait.get ().as_string ().c_str ()));
+         continue;
+       }
 
-  for (auto &attr : attrs)
+      auto pm_def
+       = mappings.lookup_derive_proc_macro_def (definition->get_node_id ());
+
+      if (pm_def.has_value ())
+       mappings.insert_derive_proc_macro_invocation (trait, pm_def.value ());
+    }
+}
+
+void
+Early::visit_non_builtin_attribute (AST::Attribute &attr,
+                                   Analysis::Mappings &mappings,
+                                   std::string &name)
+{
+  auto definition = ctx.resolve_path (attr.get_path (), Namespace::Macros);
+  if (!definition.has_value ())
     {
-      auto name = attr.get_path ().get_segments ().at (0).get_segment_name ();
+      // FIXME: Change to proper error message
+      collect_error (Error (attr.get_locus (),
+                           "could not resolve attribute macro invocation %qs",
+                           name.c_str ()));
+      return;
+    }
+  auto pm_def
+    = mappings.lookup_attribute_proc_macro_def (definition->get_node_id ());
 
-      if (attr.is_derive ())
-       {
-         auto traits = attr.get_traits_to_derive ();
-         for (auto &trait : traits)
-           {
-             auto definition
-               = ctx.resolve_path (trait.get (), Namespace::Macros);
-             if (!definition.has_value ())
-               {
-                 // FIXME: Change to proper error message
-                 collect_error (Error (trait.get ().get_locus (),
-                                       "could not resolve trait %qs",
-                                       trait.get ().as_string ().c_str ()));
-                 continue;
-               }
+  if (!pm_def.has_value ())
+    return;
 
-             auto pm_def = mappings.lookup_derive_proc_macro_def (
-               definition->get_node_id ());
+  mappings.insert_attribute_proc_macro_invocation (attr.get_path (),
+                                                  pm_def.value ());
+}
 
-             if (pm_def.has_value ())
-               mappings.insert_derive_proc_macro_invocation (trait,
-                                                             pm_def.value ());
-           }
-       }
-      else if (Analysis::BuiltinAttributeMappings::get ()
-                ->lookup_builtin (name)
-                .is_error ()) // Do not resolve builtins
-       {
-         auto definition
-           = ctx.resolve_path (attr.get_path (), Namespace::Macros);
-         if (!definition.has_value ())
-           {
-             // FIXME: Change to proper error message
-             collect_error (
-               Error (attr.get_locus (),
-                      "could not resolve attribute macro invocation %qs",
-                      name.c_str ()));
-             return;
-           }
-         auto pm_def = mappings.lookup_attribute_proc_macro_def (
-           definition->get_node_id ());
+void
+Early::visit (AST::Attribute &attr)
+{
+  auto &mappings = Analysis::Mappings::get ();
 
-         if (!pm_def.has_value ())
-           return;
+  auto name = attr.get_path ().get_segments ().at (0).get_segment_name ();
+  auto is_not_builtin = [&name] (AST::Attribute &attr) {
+    return Analysis::BuiltinAttributeMappings::get ()
+      ->lookup_builtin (name)
+      .is_error ();
+  };
 
-         mappings.insert_attribute_proc_macro_invocation (attr.get_path (),
-                                                          pm_def.value ());
-       }
+  if (attr.is_derive ())
+    {
+      visit_derive_attribute (attr, mappings);
+    }
+  else if (is_not_builtin (attr)) // Do not resolve builtins
+    {
+      visit_non_builtin_attribute (attr, mappings, name);
     }
 }
 
+void
+Early::visit_attributes (std::vector<AST::Attribute> &attrs)
+{
+  for (auto &attr : attrs)
+    visit (attr);
+}
+
 void
 Early::visit (AST::Function &fn)
 {
diff --git a/gcc/rust/resolve/rust-early-name-resolver-2.0.h 
b/gcc/rust/resolve/rust-early-name-resolver-2.0.h
index 02a395be2..e2415dd0c 100644
--- a/gcc/rust/resolve/rust-early-name-resolver-2.0.h
+++ b/gcc/rust/resolve/rust-early-name-resolver-2.0.h
@@ -37,6 +37,10 @@ class Early : public DefaultResolver
   TopLevel toplevel;
   bool dirty;
 
+  void visit_derive_attribute (AST::Attribute &, Analysis::Mappings &);
+  void visit_non_builtin_attribute (AST::Attribute &, Analysis::Mappings &,
+                                   std::string &name);
+
 public:
   Early (NameResolutionContext &ctx);
 
@@ -63,6 +67,8 @@ public:
   void visit (AST::UseDeclaration &) override;
   void visit (AST::UseTreeList &) override;
 
+  void visit (AST::Attribute &) override;
+
   struct ImportData
   {
     enum class Kind
diff --git a/gcc/testsuite/rust/compile/issue-4212.rs 
b/gcc/testsuite/rust/compile/issue-4212.rs
index a633b0d83..c33b66b56 100644
--- a/gcc/testsuite/rust/compile/issue-4212.rs
+++ b/gcc/testsuite/rust/compile/issue-4212.rs
@@ -3,6 +3,8 @@
 
 #![derive(PartialOrd, PartialEq)]
 // { dg-error "attribute cannot be used at crate level" "" { target *-*-* } 
.-1 }
+// { dg-error "could not resolve trait .PartialOrd." "" { target *-*-* } .-2 }
+// { dg-error "could not resolve trait .PartialEq." "" { target *-*-* } .-3 }
 pub fn check_ge(a: i32, b: i32) -> bool {
     a >= b
 }
-- 
2.53.0

Reply via email to