]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
gccrs: Split feature collection from feature gating
authorPierre-Emmanuel Patry <pierre-emmanuel.patry@embecosm.com>
Fri, 30 Jan 2026 16:54:10 +0000 (17:54 +0100)
committerArthur Cohen <arthur.cohen@embecosm.com>
Fri, 20 Mar 2026 17:10:36 +0000 (18:10 +0100)
Feature collection should happen before expansion because some feature
gating happens with macros. This commit does no move the feature
collection before the expansion pass, it simply split the collection part
from the gating part.

gcc/rust/ChangeLog:

* Make-lang.in: Add new feature collector file.
* checks/errors/feature/rust-feature-gate.cc (FeatureGate::visit):
Remove feature collection from gating visitor.
(FeatureGate::gate): Use features from the crate feature entity instead
of old class members.
* checks/errors/feature/rust-feature-gate.h: Get features from a
separate CrateFeatures struct instead of keeping them in the class.
* rust-session-manager.cc (Session::compile_crate): Collect features
before gating them.
* util/rust-attribute-values.h: Add feature attribute value.
* checks/errors/feature/rust-feature-collector.cc: New file.
* checks/errors/feature/rust-feature-collector.h: New file.

Signed-off-by: Pierre-Emmanuel Patry <pierre-emmanuel.patry@embecosm.com>
gcc/rust/Make-lang.in
gcc/rust/checks/errors/feature/rust-feature-collector.cc [new file with mode: 0644]
gcc/rust/checks/errors/feature/rust-feature-collector.h [new file with mode: 0644]
gcc/rust/checks/errors/feature/rust-feature-gate.cc
gcc/rust/checks/errors/feature/rust-feature-gate.h
gcc/rust/rust-session-manager.cc
gcc/rust/util/rust-attribute-values.h

index 5c8f0d90207532af0541509889903feacf098395..b5d837f4685a518f715ab056ca62b72bdaf7571b 100644 (file)
@@ -229,6 +229,7 @@ GRS_OBJS = \
     rust/rust-builtins.o \
     rust/rust-feature.o \
     rust/rust-feature-gate.o \
+    rust/rust-feature-collector.o \
     rust/rust-ast-validation.o \
     rust/rust-dir-owner.o \
     rust/rust-unicode.o \
diff --git a/gcc/rust/checks/errors/feature/rust-feature-collector.cc b/gcc/rust/checks/errors/feature/rust-feature-collector.cc
new file mode 100644 (file)
index 0000000..6a3a147
--- /dev/null
@@ -0,0 +1,117 @@
+// Copyright (C) 2026 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-feature-collector.h"
+#include "rust-attribute-values.h"
+
+namespace Rust {
+namespace Features {
+
+FeatureCollector::FeatureCollector () : features (CrateFeatures{UNKNOWN_NODEID})
+{}
+
+CrateFeatures
+FeatureCollector::collect (AST::Crate &crate)
+{
+  features.valid_lang_features.clear ();
+  features.valid_lib_features.clear ();
+  features.crate_id = crate.get_node_id ();
+
+  visit (crate);
+
+  return features;
+}
+
+namespace {
+bool
+is_feature_attribute (const AST::Attribute &attribute)
+{
+  return Values::Attributes::FEATURE == attribute.get_path ().as_string ();
+}
+
+// check for empty feature, such as `#![feature], this is an error
+bool
+is_valid_feature_attribute (const AST::Attribute &attribute)
+{
+  return !attribute.empty_input ();
+}
+} // namespace
+
+void
+FeatureCollector::add_features_from_token_tree (
+  const AST::DelimTokenTree &delim_ttree)
+{
+  std::unique_ptr<AST::AttrInputMetaItemContainer> meta_item (
+    delim_ttree.parse_to_meta_item ());
+  add_features_from_meta_item_container (*meta_item);
+}
+
+void
+FeatureCollector::add_features_from_meta_item_container (
+  const AST::AttrInputMetaItemContainer &meta_item_container)
+{
+  for (const auto &item : meta_item_container.get_items ())
+    {
+      const auto &name_str = item->as_string ();
+
+      // TODO: detect duplicates
+      if (auto tname = Feature::as_name (name_str))
+       features.valid_lang_features.insert (*tname);
+      else
+       features.valid_lib_features.emplace (name_str, item->get_locus ());
+    }
+}
+
+void
+FeatureCollector::identify_feature (const AST::Attribute &attribute)
+{
+  if (is_feature_attribute (attribute))
+    {
+      if (!is_valid_feature_attribute (attribute))
+       {
+         rust_error_at (attribute.get_locus (), ErrorCode::E0556,
+                        "malformed %<feature%> attribute input");
+         return;
+       }
+      const auto &attr_input = attribute.get_attr_input ();
+      auto type = attr_input.get_attr_input_type ();
+      if (type == AST::AttrInput::AttrInputType::TOKEN_TREE)
+       {
+         const auto &delim_ttree = static_cast<const AST::DelimTokenTree &> (
+           attribute.get_attr_input ());
+         add_features_from_token_tree (delim_ttree);
+       }
+      else if (type == AST::AttrInput::AttrInputType::META_ITEM)
+       {
+         // We can find a meta item in #[cfg(toto),feature(xxxx)]
+         const auto &meta_item_container
+           = static_cast<const AST::AttrInputMetaItemContainer &> (attr_input);
+         add_features_from_meta_item_container (meta_item_container);
+       }
+    }
+}
+
+void
+FeatureCollector::visit (AST::Crate &crate)
+{
+  for (const auto &attribute : crate.inner_attrs)
+    identify_feature (attribute);
+}
+
+} // namespace Features
+} // namespace Rust
diff --git a/gcc/rust/checks/errors/feature/rust-feature-collector.h b/gcc/rust/checks/errors/feature/rust-feature-collector.h
new file mode 100644 (file)
index 0000000..79fa34d
--- /dev/null
@@ -0,0 +1,62 @@
+// Copyright (C) 2026 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_FEATURE_COLLECTOR_H
+#define RUST_FEATURE_COLLECTOR_H
+
+#include "rust-feature.h"
+#include "rust-ast-visitor.h"
+
+namespace Rust {
+namespace Features {
+
+/** Helper structure gathering all features enabled within a given crate
+ * using the #![feature(XXXXX)] syntax.
+ **/
+struct CrateFeatures
+{
+  // The node id identifying the crate those features belong to.
+  NodeId crate_id;
+  // Language features that have been declared within the crate.
+  std::set<Feature::Name> valid_lang_features;
+  // Library features that have been declared within the crate.
+  std::map<std::string, location_t> valid_lib_features;
+
+  CrateFeatures (NodeId crate_id) : crate_id (crate_id) {}
+};
+
+class FeatureCollector : public AST::DefaultASTVisitor
+{
+public:
+  FeatureCollector ();
+
+  CrateFeatures collect (AST::Crate &crate);
+
+private:
+  CrateFeatures features;
+
+  using AST::DefaultASTVisitor::visit;
+  void visit (AST::Crate &crate) override;
+  void identify_feature (const AST::Attribute &attribute);
+  void add_features_from_token_tree (const AST::DelimTokenTree &delim_ttree);
+  void add_features_from_meta_item_container (
+    const AST::AttrInputMetaItemContainer &meta_item_container);
+};
+} // namespace Features
+} // namespace Rust
+
+#endif /* ! RUST_FEATURE_COLLECTOR_H */
index a346e1a3df1c30fe836dbb795de1a16a9b23974c..515b753a43bd7ddb0af4b4ffb3d5a453356a718f 100644 (file)
@@ -19,6 +19,7 @@
 #include "rust-feature-gate.h"
 #include "rust-abi.h"
 #include "rust-attribute-values.h"
+#include "rust-attributes.h"
 #include "rust-ast-visitor.h"
 #include "rust-feature.h"
 #include "rust-ast-full.h"
@@ -34,63 +35,9 @@ FeatureGate::check (AST::Crate &crate)
 void
 FeatureGate::visit (AST::Crate &crate)
 {
-  valid_lang_features.clear ();
-  valid_lib_features.clear ();
-
-  // avoid clearing defined features (?)
-
-  for (const auto &attr : crate.inner_attrs)
-    {
-      if (attr.get_path ().as_string () == "feature")
-       {
-         // check for empty feature, such as `#![feature], this is an error
-         if (attr.empty_input ())
-           {
-             rust_error_at (attr.get_locus (), ErrorCode::E0556,
-                            "malformed %<feature%> attribute input");
-             continue;
-           }
-         const auto &attr_input = attr.get_attr_input ();
-         auto type = attr_input.get_attr_input_type ();
-         if (type == AST::AttrInput::AttrInputType::TOKEN_TREE)
-           {
-             const auto &option = static_cast<const AST::DelimTokenTree &> (
-               attr.get_attr_input ());
-             std::unique_ptr<AST::AttrInputMetaItemContainer> meta_item (
-               option.parse_to_meta_item ());
-             for (const auto &item : meta_item->get_items ())
-               {
-                 const auto &name_str = item->as_string ();
-
-                 // TODO: detect duplicates
-                 if (auto tname = Feature::as_name (name_str))
-                   valid_lang_features.insert (tname.value ());
-                 else
-                   valid_lib_features.emplace (name_str, item->get_locus ());
-               }
-           }
-         else if (type == AST::AttrInput::AttrInputType::META_ITEM)
-           {
-             const auto &meta_item
-               = static_cast<const AST::AttrInputMetaItemContainer &> (
-                 attr_input);
-             for (const auto &item : meta_item.get_items ())
-               {
-                 const auto &name_str = item->as_string ();
-
-                 // TODO: detect duplicates
-                 if (auto tname = Feature::as_name (name_str))
-                   valid_lang_features.insert (tname.value ());
-                 else
-                   valid_lib_features.emplace (name_str, item->get_locus ());
-               }
-           }
-       }
-    }
-
   AST::DefaultASTVisitor::visit (crate);
 
-  for (auto &ent : valid_lib_features)
+  for (auto &ent : features.valid_lib_features)
     {
       const std::string &feature = ent.first;
       location_t locus = ent.second;
@@ -115,7 +62,7 @@ void
 FeatureGate::gate (Feature::Name name, location_t loc,
                   const std::string &error_msg)
 {
-  if (!valid_lang_features.count (name))
+  if (!features.valid_lang_features.count (name))
     {
       auto &feature = Feature::lookup (name);
       if (auto issue = feature.issue ())
index 60b8645f87e07f69a65443d16c940a680462a9e9..120808339f0ee2b3b64e8a7e3e897dc11448a97e 100644 (file)
 
 #include "rust-ast-visitor.h"
 #include "rust-feature.h"
+#include "rust-feature-collector.h"
 
 namespace Rust {
 
 class FeatureGate : public AST::DefaultASTVisitor
 {
 public:
-  FeatureGate () {}
+  FeatureGate (Features::CrateFeatures &features) : features (features) {}
 
   using AST::DefaultASTVisitor::visit;
 
@@ -61,8 +62,7 @@ private:
   check_lang_item_attribute (const std::vector<AST::Attribute> &attributes);
   void note_stability_attribute (const std::vector<AST::Attribute> &attributes);
 
-  std::set<Feature::Name> valid_lang_features;
-  std::map<std::string, location_t> valid_lib_features;
+  Features::CrateFeatures &features;
 
   enum class Stability
   {
index 4a2f5a5ad706c6c63f7423ac41b3be9a7ecd55a0..67adb103cee082cd96986983f1d30829428ab375 100644 (file)
@@ -34,6 +34,7 @@
 #include "rust-hir-type-check.h"
 #include "rust-privacy-check.h"
 #include "rust-const-checker.h"
+#include "rust-feature-collector.h"
 #include "rust-feature-gate.h"
 #include "rust-compile.h"
 #include "rust-cfg-parser.h"
@@ -701,7 +702,9 @@ Session::compile_crate (const char *filename)
   // feature gating
   if (last_step == CompileOptions::CompileStep::FeatureGating)
     return;
-  FeatureGate ().check (parsed_crate);
+  auto parsed_crate_features
+    = Features::FeatureCollector{}.collect (parsed_crate);
+  FeatureGate (parsed_crate_features).check (parsed_crate);
 
   if (last_step == CompileOptions::CompileStep::NameResolution)
     return;
index 85d3bac924299cc6d96427a366fe66d5bd35fed6..34e938025b8fab9e9802b33c575a2125ec9c0022 100644 (file)
@@ -52,6 +52,7 @@ public:
   static constexpr auto &PROC_MACRO_ATTRIBUTE = "proc_macro_attribute";
 
   static constexpr auto &TARGET_FEATURE = "target_feature";
+  static constexpr auto &FEATURE = "feature";
   // From now on, these are reserved by the compiler and gated through
   // #![feature(rustc_attrs)]
   static constexpr auto &RUSTC_DEPRECATED = "rustc_deprecated";