[COMMITTED 14/41] gccrs: Make attribute a proper AST node

arthur.cohen@embecosm.com arthur.cohen@embecosm.com
Fri Mar 20 17:30:03 GMT 2026


From: Pierre-Emmanuel Patry <pierre-emmanuel.patry@embecosm.com>

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 <pierre-emmanuel.patry@embecosm.com>
---
 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 f839348c7f5..f90b93145e8 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 6973d65bcff..e04ee4dffe2 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 07ba1e1851e..fee25e4635d 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 e571fc8ea0c..ec25ba2688a 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 0f78f01e120..3493185408b 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 7a7019544f9..9cfd311f67d 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 639c952459f..814b851ae40 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 46b1b91f039..94cef480e94 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 fe40bcd7f6e..1ee8e4ac19c 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 02a395be282..e2415dd0c6d 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 a633b0d838c..c33b66b56c0 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.50.1



More information about the Gcc-rust mailing list