]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
gccrs: Improve feature handling
authorOwen Avery <powerboat9.gamer@gmail.com>
Sat, 29 Nov 2025 23:20:01 +0000 (18:20 -0500)
committerArthur Cohen <arthur.cohen@embecosm.com>
Wed, 3 Dec 2025 12:26:05 +0000 (13:26 +0100)
This fixes a false positive with undefined features, improves the
implementation of Feature::as_name, and configures the testsuite to
check further stages of libcore compilation.

gcc/rust/ChangeLog:

* ast/rust-macro.h (MetaNameValueStr::get_name): New function.
(MetaNameValueStr::get_value): Likewise.
* checks/errors/feature/rust-feature-gate.cc
(FeatureGate::visit): Tweak unknown feature detection.
(FeatureGate::gate): Handle field rename.
(FeatureGate::note_stability_attribute): New function
definition.
* checks/errors/feature/rust-feature-gate.h
(FeatureGate::note_stability_attribute): New function
declaration.
(FeatureGate::Stability): New enum class.
(FeatureGate::valid_features): Rename field to...
(FeatureGate::valid_lang_features): ...here.
(FeatureGate::valid_lib_features): New field.
(FeatureGate::defined_lib_features): Likewise.
* checks/errors/feature/rust-feature.cc (Feature::as_name):
Improve implementation.

gcc/testsuite/ChangeLog:

* rust/core/core.exp: Change -frust-compile-until=astvalidation
to -frust-compile-until=nameresolution.

Signed-off-by: Owen Avery <powerboat9.gamer@gmail.com>
gcc/rust/ast/rust-macro.h
gcc/rust/checks/errors/feature/rust-feature-gate.cc
gcc/rust/checks/errors/feature/rust-feature-gate.h
gcc/rust/checks/errors/feature/rust-feature.cc
gcc/testsuite/rust/core/core.exp

index 416507501ac84b320c122598ffbb6fdeaf3858ba..71de8f022d227e95d3bb24878cb36a3ebcfb8b79 100644 (file)
@@ -993,6 +993,10 @@ public:
     return ident.as_string () + " = \"" + str + "\"";
   }
 
+  const Identifier &get_name () const { return ident; }
+
+  const std::string &get_value () const { return str; }
+
   void accept_vis (ASTVisitor &vis) override;
 
   // HACK: used to simplify parsing - creates a copy of this
index 448fa18f21d42ddc49fceae977c16d5cf25525cb..9df822b7e1c723467510867034fee2afd9b26600 100644 (file)
@@ -34,7 +34,10 @@ FeatureGate::check (AST::Crate &crate)
 void
 FeatureGate::visit (AST::Crate &crate)
 {
-  valid_features.clear ();
+  valid_lang_features.clear ();
+  valid_lib_features.clear ();
+
+  // avoid clearing defined features (?)
 
   for (const auto &attr : crate.inner_attrs)
     {
@@ -58,29 +61,45 @@ FeatureGate::visit (AST::Crate &crate)
              for (const auto &item : meta_item->get_items ())
                {
                  const auto &name_str = item->as_string ();
-                 auto tname = Feature::as_name (name_str);
-                 if (tname.has_value ())
-                   {
-                     auto name = tname.value ();
-                     valid_features.insert (name);
-                   }
 
+                 // TODO: detect duplicates
+                 if (auto tname = Feature::as_name (name_str))
+                   valid_lang_features.insert (tname.value ());
                  else
-                   rust_error_at (item->get_locus (), ErrorCode::E0635,
-                                  "unknown feature %qs", name_str.c_str ());
+                   valid_lib_features.emplace (name_str, item->get_locus ());
                }
            }
        }
     }
 
   AST::DefaultASTVisitor::visit (crate);
+
+  for (auto &ent : valid_lib_features)
+    {
+      const std::string &feature = ent.first;
+      location_t locus = ent.second;
+
+      // rustc treats these as valid,
+      // but apparently has special handling for them
+      if (feature == "libc" || feature == "test")
+       continue;
+
+      if (defined_lib_features.find (feature) != defined_lib_features.end ())
+       {
+         // TODO: emit warning if stable
+         continue;
+       }
+
+      rust_error_at (locus, ErrorCode::E0635, "unknown feature %qs",
+                    feature.c_str ());
+    }
 }
 
 void
 FeatureGate::gate (Feature::Name name, location_t loc,
                   const std::string &error_msg)
 {
-  if (!valid_features.count (name))
+  if (!valid_lang_features.count (name))
     {
       auto &feature = Feature::lookup (name);
       if (auto issue = feature.issue ())
@@ -164,10 +183,60 @@ FeatureGate::check_lang_item_attribute (
     }
 }
 
+void
+FeatureGate::note_stability_attribute (
+  const std::vector<AST::Attribute> &attributes)
+{
+  for (const AST::Attribute &attr : attributes)
+    {
+      std::string attr_name = attr.get_path ().as_string ();
+
+      Stability stability;
+
+      if (attr_name == Values::Attributes::STABLE)
+       stability = Stability::STABLE;
+      else if (attr_name == Values::Attributes::UNSTABLE)
+       stability = Stability::UNSTABLE;
+      else if (attr_name == Values::Attributes::RUSTC_CONST_STABLE)
+       stability = Stability::STABLE;
+      else if (attr_name == Values::Attributes::RUSTC_CONST_UNSTABLE)
+       stability = Stability::UNSTABLE;
+      else
+       continue;
+
+      if (attr.empty_input ())
+       // TODO: error?
+       continue;
+
+      auto &attr_input = attr.get_attr_input ();
+      if (attr_input.get_attr_input_type ()
+         != AST::AttrInput::AttrInputType::TOKEN_TREE)
+       // TODO: error?
+       continue;
+
+      std::unique_ptr<AST::AttrInputMetaItemContainer> meta_item (
+       static_cast<const AST::DelimTokenTree &> (attr_input)
+         .parse_to_meta_item ());
+
+      for (auto &item : meta_item->get_items ())
+       {
+         // TODO: more thorough error checking?
+         // ~only the standard libraries should ever exercise this
+         if (item->is_key_value_pair ())
+           {
+             auto &pair = static_cast<const AST::MetaNameValueStr &> (*item);
+             if (pair.get_name ().as_string () == "feature")
+               defined_lib_features.emplace (pair.get_value (), stability);
+           }
+       }
+    }
+}
+
 void
 FeatureGate::visit (AST::MacroRulesDefinition &rules_def)
 {
   check_rustc_attri (rules_def.get_outer_attrs ());
+  note_stability_attribute (rules_def.get_outer_attrs ());
 }
 
 void
@@ -178,6 +247,8 @@ FeatureGate::visit (AST::Function &function)
 
   check_lang_item_attribute (function.get_outer_attrs ());
 
+  note_stability_attribute (function.get_outer_attrs ());
+
   AST::DefaultASTVisitor::visit (function);
 }
 
index c8ab66b753db7387c800f93b3aaa07fd81814f1b..8ff491c108eaa0657ba8044181b84490b29c0368 100644 (file)
@@ -59,7 +59,18 @@ private:
   check_may_dangle_attribute (const std::vector<AST::Attribute> &attributes);
   void
   check_lang_item_attribute (const std::vector<AST::Attribute> &attributes);
-  std::set<Feature::Name> valid_features;
+  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;
+
+  enum class Stability
+  {
+    STABLE,
+    UNSTABLE,
+  };
+
+  std::map<std::string, Stability> defined_lib_features;
 };
 } // namespace Rust
 #endif
index 7fc5cb047975aca9815ec8b901f99e2976ca5495..1a967aa70a99212335ead6d27cd55f2fb55a8626 100644 (file)
@@ -77,10 +77,11 @@ const std::map<std::string, Feature::Name> Feature::name_hash_map = {
 tl::optional<Feature::Name>
 Feature::as_name (const std::string &name)
 {
-  if (Feature::name_hash_map.count (name))
-    return Feature::name_hash_map.at (name);
-
-  return tl::nullopt;
+  auto it = Feature::name_hash_map.find (name);
+  if (it == Feature::name_hash_map.end ())
+    return tl::nullopt;
+  else
+    return it->second;
 }
 
 tl::optional<std::reference_wrapper<const Feature>>
index 330c6d5ba5bd41e70d59cb306ae1eaf996fe0d5b..45bdbb599591cd02e75011ae385f374bcdac5cf1 100644 (file)
@@ -30,7 +30,7 @@ set saved-dg-do-what-default ${dg-do-what-default}
 set dg-do-what-default "compile"
 set individual_timeout 600
 dg-additional-files [lsort [glob -nocomplain $srcdir/../../libgrust/rustc-lib/*]]
-dg-runtest $srcdir/../../libgrust/rustc-lib/core/src/lib.rs "-frust-edition=2018 -frust-crate=core -frust-compile-until=astvalidation -w" ""
+dg-runtest $srcdir/../../libgrust/rustc-lib/core/src/lib.rs "-frust-edition=2018 -frust-crate=core -frust-compile-until=nameresolution -w" ""
 set dg-do-what-default ${saved-dg-do-what-default}
 
 # All done.