diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index 5c8f0d90207..5a1551c09d0 100644 --- a/gcc/rust/Make-lang.in +++ b/gcc/rust/Make-lang.in @@ -94,6 +94,7 @@ GRS_OBJS = \ rust/rust-compile-resolve-path.o \ rust/rust-macro-expand.o \ rust/rust-cfg-strip.o \ + rust/rust-early-cfg-strip.o \ rust/rust-expand-visitor.o \ rust/rust-ast-builder.o \ rust/rust-derive.o \ @@ -229,6 +230,8 @@ GRS_OBJS = \ rust/rust-builtins.o \ rust/rust-feature.o \ rust/rust-feature-gate.o \ + rust/rust-feature-store.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/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 @@ class ASTVisitor // 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 @@ class DefaultASTVisitor : public ASTVisitor virtual void visit (AST::StructPatternElements &spe); virtual void visit (AST::MaybeNamedParam ¶m); - void visit (AST::Attribute &attribute) {} + virtual void visit (AST::Attribute &attribute) override; template void visit_outer_attrs (T &node) { diff --git a/gcc/rust/ast/rust-ast.cc b/gcc/rust/ast/rust-ast.cc index 30fb5f4d5d8..4472593766a 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 2e7eaa81e93..4e0f109c76f 100644 --- a/gcc/rust/ast/rust-ast.h +++ b/gcc/rust/ast/rust-ast.h @@ -565,9 +565,8 @@ struct Visibility // aka Attr // Attribute AST representation -struct Attribute +class Attribute : Visitable { -private: SimplePath path; // bool has_attr_input; @@ -577,6 +576,8 @@ struct Attribute bool inner_attribute; + NodeId node_id; + // TODO: maybe a variable storing whether attr input is parsed or not public: @@ -587,7 +588,8 @@ struct Attribute Attribute (SimplePath path, std::unique_ptr input, location_t locus = UNDEF_LOCATION, bool inner_attribute = false) : path (std::move (path)), attr_input (std::move (input)), locus (locus), - inner_attribute (inner_attribute) + inner_attribute (inner_attribute), + node_id (Analysis::Mappings::get ().get_next_node_id ()) {} bool is_derive () const; @@ -685,11 +687,13 @@ struct Attribute 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; } + NodeId get_node_id () { return node_id; } + // Call to parse attribute body to meta item syntax. void parse_attr_to_meta_item (); diff --git a/gcc/rust/checks/errors/feature/contrib/fetch b/gcc/rust/checks/errors/feature/contrib/fetch index c37688b1124..a9565e97961 100755 --- a/gcc/rust/checks/errors/feature/contrib/fetch +++ b/gcc/rust/checks/errors/feature/contrib/fetch @@ -18,7 +18,7 @@ # along with GCC; see the file COPYING3. If not see # . -RUST_VERSION="1.49.0" +RUST_VERSION="1.50.0" [ $# = 1 ] || exit 1 @@ -27,4 +27,4 @@ RUST_VERSION="1.49.0" URL_PREFIX='https://raw.githubusercontent.com/rust-lang/rust/refs/tags' URL_TEMPLATE="$URL_PREFIX/$RUST_VERSION/compiler/rustc_feature/src" -wget -O $1 "$URL_TEMPLATE"/{accepted,active,removed}.rs +wget -O $1 "$URL_TEMPLATE/accepted.rs" "$URL_TEMPLATE/active.rs" "$URL_TEMPLATE/removed.rs" 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 index 00000000000..6a3a1479227 --- /dev/null +++ b/gcc/rust/checks/errors/feature/rust-feature-collector.cc @@ -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 +// . + +#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 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 % 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 ( + 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 (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 index 00000000000..79fa34d2718 --- /dev/null +++ b/gcc/rust/checks/errors/feature/rust-feature-collector.h @@ -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 +// . +#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 valid_lang_features; + // Library features that have been declared within the crate. + std::map 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 */ diff --git a/gcc/rust/checks/errors/feature/rust-feature-defs.h b/gcc/rust/checks/errors/feature/rust-feature-defs.h index 864390565e7..852208ffe17 100644 --- a/gcc/rust/checks/errors/feature/rust-feature-defs.h +++ b/gcc/rust/checks/errors/feature/rust-feature-defs.h @@ -223,8 +223,6 @@ FEATURE_ACTIVE ("fundamental", FUNDAMENTAL, "1.0.0", ISSUE_SOME (29635), FEATURE_ACTIVE ("unboxed_closures", UNBOXED_CLOSURES, "1.0.0", ISSUE_SOME (29625), EDITION_NONE) FEATURE_ACTIVE ("linkage", LINKAGE, "1.0.0", ISSUE_SOME (29603), EDITION_NONE) -FEATURE_ACTIVE ("optin_builtin_traits", OPTIN_BUILTIN_TRAITS, "1.0.0", - ISSUE_SOME (13231), EDITION_NONE) FEATURE_ACTIVE ("box_patterns", BOX_PATTERNS, "1.0.0", ISSUE_SOME (29641), EDITION_NONE) FEATURE_ACTIVE ("prelude_import", PRELUDE_IMPORT, "1.2.0", ISSUE_NONE, @@ -257,6 +255,8 @@ FEATURE_ACTIVE ("test_2018_feature", TEST_2018_FEATURE, "1.31.0", ISSUE_NONE, FEATURE_ACTIVE ("no_niche", NO_NICHE, "1.42.0", ISSUE_NONE, EDITION_NONE) FEATURE_ACTIVE ("rustc_allow_const_fn_unstable", RUSTC_ALLOW_CONST_FN_UNSTABLE, "1.49.0", ISSUE_SOME (69399), EDITION_NONE) +FEATURE_ACTIVE ("auto_traits", AUTO_TRAITS, "1.50.0", ISSUE_SOME (13231), + EDITION_NONE) FEATURE_ACTIVE ("arm_target_feature", ARM_TARGET_FEATURE, "1.27.0", ISSUE_SOME (44839), EDITION_NONE) FEATURE_ACTIVE ("aarch64_target_feature", AARCH64_TARGET_FEATURE, "1.27.0", @@ -519,6 +519,10 @@ FEATURE_ACTIVE ("destructuring_assignment", DESTRUCTURING_ASSIGNMENT, "1.49.0", ISSUE_SOME (71126), EDITION_NONE) FEATURE_ACTIVE ("cfg_panic", CFG_PANIC, "1.49.0", ISSUE_SOME (77443), EDITION_NONE) +FEATURE_ACTIVE ("capture_disjoint_fields", CAPTURE_DISJOINT_FIELDS, "1.49.0", + ISSUE_SOME (53488), EDITION_NONE) +FEATURE_ACTIVE ("extended_key_value_attributes", EXTENDED_KEY_VALUE_ATTRIBUTES, + "1.50.0", ISSUE_SOME (78835), EDITION_NONE) FEATURE_REMOVED ("import_shadowing", IMPORT_SHADOWING, "1.0.0", ISSUE_NONE, REASON_NONE) FEATURE_REMOVED ("managed_boxes", MANAGED_BOXES, "1.0.0", ISSUE_NONE, @@ -550,6 +554,8 @@ FEATURE_REMOVED ( "custom_attribute", CUSTOM_ATTRIBUTE, "1.0.0", ISSUE_SOME (29642), REASON_SOME ( "removed in favor of `#![register_tool]` and `#![register_attr]`")) +FEATURE_REMOVED ("optin_builtin_traits", OPTIN_BUILTIN_TRAITS, "1.0.0", + ISSUE_SOME (13231), REASON_SOME ("renamed to `auto_traits`")) FEATURE_REMOVED ("pushpop_unsafe", PUSHPOP_UNSAFE, "1.2.0", ISSUE_NONE, REASON_NONE) FEATURE_REMOVED ("needs_allocator", NEEDS_ALLOCATOR, "1.4.0", diff --git a/gcc/rust/checks/errors/feature/rust-feature-gate.cc b/gcc/rust/checks/errors/feature/rust-feature-gate.cc index a346e1a3df1..14471c4f7f0 100644 --- a/gcc/rust/checks/errors/feature/rust-feature-gate.cc +++ b/gcc/rust/checks/errors/feature/rust-feature-gate.cc @@ -19,78 +19,32 @@ #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" +#include "rust-feature-store.h" namespace Rust { void FeatureGate::check (AST::Crate &crate) { + auto &store = Features::EarlyFeatureGateStore::get (); + while (store.has_error ()) + { + auto pair = store.get_error (); + gate (pair.first, pair.second.locus, pair.second.message); + } visit (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 % 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 ( - attr.get_attr_input ()); - std::unique_ptr 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 ( - 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 +69,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 ()) diff --git a/gcc/rust/checks/errors/feature/rust-feature-gate.h b/gcc/rust/checks/errors/feature/rust-feature-gate.h index 60b8645f87e..21997bcae0e 100644 --- a/gcc/rust/checks/errors/feature/rust-feature-gate.h +++ b/gcc/rust/checks/errors/feature/rust-feature-gate.h @@ -20,14 +20,14 @@ #define RUST_FEATURE_GATE_H #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 +61,7 @@ class FeatureGate : public AST::DefaultASTVisitor check_lang_item_attribute (const std::vector &attributes); void note_stability_attribute (const std::vector &attributes); - std::set valid_lang_features; - std::map valid_lib_features; + Features::CrateFeatures &features; enum class Stability { diff --git a/gcc/rust/checks/errors/feature/rust-feature-store.cc b/gcc/rust/checks/errors/feature/rust-feature-store.cc new file mode 100644 index 00000000000..da8a864cbb5 --- /dev/null +++ b/gcc/rust/checks/errors/feature/rust-feature-store.cc @@ -0,0 +1,47 @@ + +// 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 +// . + +#include "rust-feature-store.h" + +namespace Rust { +namespace Features { + +EarlyFeatureGateStore & +EarlyFeatureGateStore::get () +{ + static EarlyFeatureGateStore instance{}; + return instance; +} + +void +EarlyFeatureGateStore::add (Feature::Name name, Error error) +{ + potential_errors.emplace (name, error); +} + +std::pair +EarlyFeatureGateStore::get_error () +{ + auto ret = potential_errors.front (); + potential_errors.pop (); + return ret; +} + +} // namespace Features +} // namespace Rust diff --git a/gcc/rust/checks/errors/feature/rust-feature-store.h b/gcc/rust/checks/errors/feature/rust-feature-store.h new file mode 100644 index 00000000000..0d119038cc5 --- /dev/null +++ b/gcc/rust/checks/errors/feature/rust-feature-store.h @@ -0,0 +1,51 @@ +// 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 +// . + +#ifndef RUST_FEATURE_STORE_H +#define RUST_FEATURE_STORE_H + +#include "rust-system.h" +#include "rust-diagnostics.h" +#include "rust-feature.h" + +namespace Rust { + +namespace Features { + +/** + * We don't know the whole set of valid features until a crate has been parsed. + * We're collecting in this store all the potential feature errors and check + * them later. + */ +class EarlyFeatureGateStore +{ + std::queue> potential_errors; + +public: + static EarlyFeatureGateStore &get (); + void add (Feature::Name name, Error error); + + bool has_error () { return !potential_errors.empty (); } + + std::pair get_error (); +}; +} // namespace Features + +} // namespace Rust + +#endif // ! RUST_FEATURE_STORE_H diff --git a/gcc/rust/expand/rust-cfg-strip.cc b/gcc/rust/expand/rust-cfg-strip.cc index 071104036f7..59cec98af6c 100644 --- a/gcc/rust/expand/rust-cfg-strip.cc +++ b/gcc/rust/expand/rust-cfg-strip.cc @@ -26,27 +26,6 @@ namespace Rust { -/** - * Determines whether any cfg predicate is false and hence item with attributes - * should be stripped. Note that attributes must be expanded before calling. - */ -bool -CfgStrip::fails_cfg (const AST::AttrVec &attrs) const -{ - auto &session = Session::get_instance (); - - for (const auto &attr : attrs) - { - if (attr.get_path () == Values::Attributes::CFG - && !attr.check_cfg_predicate (session)) - return true; - else if (!expansion_cfg.should_test - && attr.get_path () == Values::Attributes::TEST) - return true; - } - return false; -} - /** * Determines whether any cfg predicate is false and hence item with attributes * should be stripped. Will expand attributes as well. diff --git a/gcc/rust/expand/rust-cfg-strip.h b/gcc/rust/expand/rust-cfg-strip.h index ace70af2b0b..e372744f62d 100644 --- a/gcc/rust/expand/rust-cfg-strip.h +++ b/gcc/rust/expand/rust-cfg-strip.h @@ -24,6 +24,8 @@ namespace Rust { +void expand_cfg_attrs (AST::AttrVec &attrs); + // forward declare struct ExpansionCfg; @@ -31,8 +33,6 @@ struct ExpansionCfg; class CfgStrip : public AST::DefaultASTVisitor { private: - bool fails_cfg (const AST::AttrVec &attrs) const; - bool fails_cfg_with_expand (AST::AttrVec &attrs) const; public: 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 @@ class DeriveVisitor : public AST::ASTVisitor virtual void visit (FunctionParam ¶m) override final{}; virtual void visit (VariadicParam ¶m) override final{}; virtual void visit (FormatArgs ¶m) override final{}; + virtual void visit (Attribute &attribute) override final{}; virtual void visit (OffsetOf ¶m) 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/expand/rust-early-cfg-strip.cc b/gcc/rust/expand/rust-early-cfg-strip.cc new file mode 100644 index 00000000000..bc0e4b5b0d4 --- /dev/null +++ b/gcc/rust/expand/rust-early-cfg-strip.cc @@ -0,0 +1,38 @@ +// 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 +// . + +#include "rust-early-cfg-strip.h" +#include "rust-cfg-strip.h" + +namespace Rust { + +void +EarlyCfgStrip::go (AST::Crate &crate) +{ + visit (crate); +} + +void +EarlyCfgStrip::visit (AST::Crate &crate) +{ + expand_cfg_attrs (crate.inner_attrs); + + AST::DefaultASTVisitor::visit (crate); +} + +} // namespace Rust diff --git a/gcc/rust/expand/rust-early-cfg-strip.h b/gcc/rust/expand/rust-early-cfg-strip.h new file mode 100644 index 00000000000..b342aee90c4 --- /dev/null +++ b/gcc/rust/expand/rust-early-cfg-strip.h @@ -0,0 +1,41 @@ +// 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 +// . + +#ifndef RUST_EARLY_CFG_STRIP_H +#define RUST_EARLY_CFG_STRIP_H + +#include "rust-ast-visitor.h" + +namespace Rust { + +/** + * Some parts cannot be stripped during the expansion passes + */ +class EarlyCfgStrip : AST::DefaultASTVisitor +{ +public: + using DefaultASTVisitor::visit; + + void go (AST::Crate &crate); + + void visit (AST::Crate &crate) override; +}; + +} // namespace Rust + +#endif 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 @@ class ASTLoweringBase : public AST::ASTVisitor // 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/parse/rust-parse-impl-attribute.hxx b/gcc/rust/parse/rust-parse-impl-attribute.hxx index 85fab52c997..5c7bd99bf3e 100644 --- a/gcc/rust/parse/rust-parse-impl-attribute.hxx +++ b/gcc/rust/parse/rust-parse-impl-attribute.hxx @@ -293,6 +293,16 @@ Parser::parse_attr_input () t = lexer.peek_token (); + /* Ensure token is a "literal expression" (literally only a literal + * token of any type) */ + if (!t->is_literal ()) + { + Error error ( + t->get_locus (), + "arbitrary expressions in key-value attributes are unstable"); + collect_potential_gating_error ( + Feature::Name::EXTENDED_KEY_VALUE_ATTRIBUTES, std::move (error)); + } // attempt to parse macro // TODO: macros may/may not be allowed in attributes // this is needed for "#[doc = include_str!(...)]" @@ -308,20 +318,6 @@ Parser::parse_attr_input () new AST::AttrInputMacro (std::move (invoke))); } - /* Ensure token is a "literal expression" (literally only a literal - * token of any type) */ - if (!t->is_literal ()) - { - Error error ( - t->get_locus (), - "unknown token %qs in attribute body - literal expected", - t->get_token_description ()); - add_error (std::move (error)); - - skip_after_end_attribute (); - return Parse::Error::AttrInput::make_malformed (); - } - AST::Literal::LitType lit_type = AST::Literal::STRING; // Crappy mapping of token type to literal type switch (t->get_id ()) @@ -345,9 +341,14 @@ Parser::parse_attr_input () lit_type = AST::Literal::RAW_STRING; break; case STRING_LITERAL: - default: lit_type = AST::Literal::STRING; break; // TODO: raw string? don't eliminate it from lexer? + default: + rust_sorry_at (t->get_locus (), + "Unsupported attribute input, only literals and " + "macros are supported for now"); + skip_after_end_attribute (); + return Parse::Error::AttrInput::make_malformed (); } // create actual LiteralExpr diff --git a/gcc/rust/parse/rust-parse.h b/gcc/rust/parse/rust-parse.h index 7d632391c7a..289018e32a5 100644 --- a/gcc/rust/parse/rust-parse.h +++ b/gcc/rust/parse/rust-parse.h @@ -24,6 +24,8 @@ along with GCC; see the file COPYING3. If not see #include "rust-diagnostics.h" #include "rust-parse-error.h" #include "rust-parse-utils.h" +#include "rust-feature.h" +#include "rust-feature-store.h" #include "expected.h" @@ -852,6 +854,11 @@ template class Parser void add_error (Error error) { error_table.push_back (std::move (error)); } + void collect_potential_gating_error (Feature::Name feature, Error error) + { + Features::EarlyFeatureGateStore::get ().add (feature, error); + } + public: // Construct parser with specified "managed" token source. Parser (ManagedTokenSource &tokenSource) : lexer (tokenSource) {} @@ -874,6 +881,12 @@ template class Parser // Get a reference to the list of errors encountered std::vector &get_errors () { return error_table; } + std::vector> & + get_potential_feature_gate_errors () + { + return gating_errors; + } + const ManagedTokenSource &get_token_source () const { return lexer; } const_TokenPtr peek_current_token () { return lexer.peek_token (0); } @@ -884,6 +897,8 @@ template class Parser ManagedTokenSource &lexer; // The error list. std::vector error_table; + + std::vector> gating_errors; // The names of inline modules while parsing. std::vector inline_module_stack; 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 &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 &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 @@ class Early : public DefaultResolver void visit (AST::UseDeclaration &) override; void visit (AST::UseTreeList &) override; + void visit (AST::Attribute &) override; + struct ImportData { enum class Kind diff --git a/gcc/rust/resolve/rust-late-name-resolver-2.0.cc b/gcc/rust/resolve/rust-late-name-resolver-2.0.cc index 05b2b56b0df..c804d3526e0 100644 --- a/gcc/rust/resolve/rust-late-name-resolver-2.0.cc +++ b/gcc/rust/resolve/rust-late-name-resolver-2.0.cc @@ -328,6 +328,12 @@ Late::visit (AST::ContinueExpr &expr) DefaultResolver::visit (expr); } +void +Late::visit (AST::Attribute &attribute) +{ + // Empty +} + void Late::visit (AST::IdentifierExpr &expr) { diff --git a/gcc/rust/resolve/rust-late-name-resolver-2.0.h b/gcc/rust/resolve/rust-late-name-resolver-2.0.h index 38785633448..41f9c14574e 100644 --- a/gcc/rust/resolve/rust-late-name-resolver-2.0.h +++ b/gcc/rust/resolve/rust-late-name-resolver-2.0.h @@ -55,6 +55,7 @@ class Late : public DefaultResolver // resolutions void visit (AST::IdentifierExpr &) override; + void visit (AST::Attribute &) override; void visit (AST::StructExprFieldIdentifier &) override; void visit (AST::BreakExpr &) override; void visit (AST::ContinueExpr &) override; diff --git a/gcc/rust/rust-session-manager.cc b/gcc/rust/rust-session-manager.cc index 4a2f5a5ad70..a74b99754b8 100644 --- a/gcc/rust/rust-session-manager.cc +++ b/gcc/rust/rust-session-manager.cc @@ -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" @@ -51,6 +52,7 @@ #include "rust-early-name-resolver-2.0.h" #include "rust-late-name-resolver-2.0.h" #include "rust-resolve-builtins.h" +#include "rust-early-cfg-strip.h" #include "rust-cfg-strip.h" #include "rust-expand-visitor.h" #include "rust-unicode.h" @@ -474,7 +476,7 @@ Session::handle_crate_name (const char *filename, for (const auto &attr : parsed_crate.inner_attrs) { - if (attr.get_path () != "crate_name") + if (attr.get_path () != Values::Attributes::CRATE_NAME) continue; auto msg_str = Analysis::Attributes::extract_string_literal (attr); @@ -668,7 +670,15 @@ Session::compile_crate (const char *filename) Analysis::AttributeChecker ().go (parsed_crate); - if (!has_attribute (parsed_crate, std::string (Values::Attributes::NO_CORE))) + EarlyCfgStrip ().go (parsed_crate); + + auto parsed_crate_features + = Features::FeatureCollector{}.collect (parsed_crate); + + // Do not inject core if some errors were emitted + if (!saw_errors () + && !has_attribute (parsed_crate, + std::string (Values::Attributes::NO_CORE))) { parsed_crate.inject_extern_crate ("core"); } @@ -701,7 +711,8 @@ Session::compile_crate (const char *filename) // feature gating if (last_step == CompileOptions::CompileStep::FeatureGating) return; - FeatureGate ().check (parsed_crate); + + FeatureGate (parsed_crate_features).check (parsed_crate); if (last_step == CompileOptions::CompileStep::NameResolution) return; diff --git a/gcc/rust/rust-system.h b/gcc/rust/rust-system.h index 6d3fe2e2a6a..b47fa265cf8 100644 --- a/gcc/rust/rust-system.h +++ b/gcc/rust/rust-system.h @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include diff --git a/gcc/rust/util/rust-attribute-values.h b/gcc/rust/util/rust-attribute-values.h index 85d3bac9242..f4146b8802a 100644 --- a/gcc/rust/util/rust-attribute-values.h +++ b/gcc/rust/util/rust-attribute-values.h @@ -50,8 +50,11 @@ class Attributes static constexpr auto &PROC_MACRO = "proc_macro"; static constexpr auto &PROC_MACRO_DERIVE = "proc_macro_derive"; static constexpr auto &PROC_MACRO_ATTRIBUTE = "proc_macro_attribute"; + static constexpr auto &CRATE_NAME = "crate_name"; + static constexpr auto &CRATE_TYPE = "crate_type"; 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"; diff --git a/gcc/rust/util/rust-attributes.cc b/gcc/rust/util/rust-attributes.cc index 8da23c5d1b2..719a972c9d1 100644 --- a/gcc/rust/util/rust-attributes.cc +++ b/gcc/rust/util/rust-attributes.cc @@ -96,6 +96,12 @@ static const BuiltinAttrDefinition __definitions[] {Attrs::TARGET_FEATURE, CODE_GENERATION}, // From now on, these are reserved by the compiler and gated through // #![feature(rustc_attrs)] + {Attrs::FEATURE, STATIC_ANALYSIS}, + {Attrs::NO_CORE, CODE_GENERATION}, + {Attrs::DOC, EXTERNAL}, + {Attrs::CRATE_NAME, CODE_GENERATION}, + {Attrs::CRATE_TYPE, CODE_GENERATION}, + {Attrs::MAY_DANGLE, STATIC_ANALYSIS}, {Attrs::RUSTC_DEPRECATED, STATIC_ANALYSIS}, {Attrs::RUSTC_INHERIT_OVERFLOW_CHECKS, CODE_GENERATION}, {Attrs::STABLE, STATIC_ANALYSIS}, @@ -193,8 +199,8 @@ AttributeChecker::visit (AST::Crate &crate) item->accept_vis (*this); } -static bool -is_builtin (const AST::Attribute &attribute, BuiltinAttrDefinition &builtin) +tl::optional +identify_builtin (const AST::Attribute &attribute) { auto &segments = attribute.get_path ().get_segments (); @@ -202,12 +208,10 @@ is_builtin (const AST::Attribute &attribute, BuiltinAttrDefinition &builtin) // strings all over the place and performing a linear search in the builtins // map if (segments.size () != 1) - return false; + return tl::nullopt; - builtin = BuiltinAttributeMappings::get ()->lookup_builtin ( + return BuiltinAttributeMappings::get ()->lookup_builtin ( segments.at (0).get_segment_name ()); - - return !builtin.is_error (); } /** @@ -427,9 +431,10 @@ check_valid_attribute_for_item (const AST::Attribute &attr, static bool is_proc_macro_type (const AST::Attribute &attribute) { - BuiltinAttrDefinition result; - if (!is_builtin (attribute, result)) + auto result_opt = identify_builtin (attribute); + if (!result_opt.has_value ()) return false; + auto result = result_opt.value (); auto name = result.name; return name == Attrs::PROC_MACRO || name == Attrs::PROC_MACRO_DERIVE @@ -465,10 +470,10 @@ check_proc_macro_non_root (const AST::Attribute &attr, location_t loc) void AttributeChecker::check_inner_attribute (const AST::Attribute &attribute) { - BuiltinAttrDefinition result; - - if (!is_builtin (attribute, result)) + auto result_opt = identify_builtin (attribute); + if (!result_opt.has_value ()) return; + auto result = result_opt.value (); if (__outer_attributes.find (result.name) != __outer_attributes.end ()) rust_error_at (attribute.get_locus (), @@ -499,11 +504,38 @@ check_export_name_attribute (const AST::Attribute &attribute) return; } - if (!Attributes::extract_string_literal (attribute)) + // We don't support the whole extended_key_value_attributes feature, we only + // support a subset for macros. We need to emit an error message when the + // attribute does not contain a macro or a string literal. + if (attribute.has_attr_input ()) { - rust_error_at (attribute.get_locus (), - "attribute must be a string literal"); + auto &attr_input = attribute.get_attr_input (); + switch (attr_input.get_attr_input_type ()) + { + case AST::AttrInput::AttrInputType::LITERAL: + { + auto &literal_expr + = static_cast (attr_input) + .get_literal (); + auto lit_type = literal_expr.get_lit_type (); + switch (lit_type) + { + case AST::Literal::LitType::STRING: + case AST::Literal::LitType::RAW_STRING: + case AST::Literal::LitType::BYTE_STRING: + return; + default: + break; + } + break; + } + case AST::AttrInput::AttrInputType::MACRO: + return; + default: + break; + } } + rust_error_at (attribute.get_locus (), "attribute must be a string literal"); } static void @@ -535,11 +567,12 @@ AttributeChecker::check_attribute (const AST::Attribute &attribute) } } - BuiltinAttrDefinition result; + auto result_opt = identify_builtin (attribute); // This checker does not check non-builtin attributes - if (!is_builtin (attribute, result)) + if (!result_opt.has_value ()) return; + auto result = result_opt.value (); // TODO: Add checks here for each builtin attribute // TODO: Have an enum of builtins as well, switching on strings is annoying @@ -935,12 +968,13 @@ AttributeChecker::visit (AST::Function &fun) BuiltinAttrDefinition result; for (auto &attribute : fun.get_outer_attrs ()) { - if (!is_builtin (attribute, result)) + auto result = identify_builtin (attribute); + if (!result) return; - auto name = result.name.c_str (); + auto name = result->name.c_str (); - if (result.name == Attrs::PROC_MACRO_DERIVE) + if (result->name == Attrs::PROC_MACRO_DERIVE) { if (!attribute.has_attr_input ()) { @@ -953,12 +987,12 @@ AttributeChecker::visit (AST::Function &fun) } check_crate_type (name, attribute); } - else if (result.name == Attrs::PROC_MACRO - || result.name == Attrs::PROC_MACRO_ATTRIBUTE) + else if (result->name == Attrs::PROC_MACRO + || result->name == Attrs::PROC_MACRO_ATTRIBUTE) { check_crate_type (name, attribute); } - else if (result.name == Attrs::TARGET_FEATURE) + else if (result->name == Attrs::TARGET_FEATURE) { if (!attribute.has_attr_input ()) { @@ -976,7 +1010,7 @@ AttributeChecker::visit (AST::Function &fun) "to % functions"); } } - else if (result.name == Attrs::NO_MANGLE) + else if (result->name == Attrs::NO_MANGLE) { if (attribute.has_attr_input ()) { @@ -988,16 +1022,16 @@ AttributeChecker::visit (AST::Function &fun) else check_no_mangle_function (attribute, fun); } - else if (result.name == Attrs::EXPORT_NAME) + else if (result->name == Attrs::EXPORT_NAME) { check_export_name_attribute (attribute); } - else if (result.name == Attrs::ALLOW || result.name == "deny" - || result.name == "warn" || result.name == "forbid") + else if (result->name == Attrs::ALLOW || result->name == "deny" + || result->name == "warn" || result->name == "forbid") { check_lint_attribute (attribute, name); } - else if (result.name == Attrs::LINK_NAME) + else if (result->name == Attrs::LINK_NAME) { if (!attribute.has_attr_input ()) { @@ -1007,7 +1041,7 @@ AttributeChecker::visit (AST::Function &fun) "must be of the form: %<#[link_name = \"name\"]%>"); } } - else if (result.name == Attrs::LINK_SECTION) + else if (result->name == Attrs::LINK_SECTION) { check_link_section_attribute (attribute); } @@ -1097,21 +1131,17 @@ AttributeChecker::visit (AST::ConstantItem &item) void AttributeChecker::visit (AST::StaticItem &item) { - BuiltinAttrDefinition result; for (auto &attr : item.get_outer_attrs ()) { check_valid_attribute_for_item (attr, item); check_proc_macro_non_function (attr); - if (is_builtin (attr, result)) + + if (auto result = identify_builtin (attr)) { - if (result.name == Attrs::LINK_SECTION) - { - check_link_section_attribute (attr); - } - else if (result.name == Attrs::EXPORT_NAME) - { - check_export_name_attribute (attr); - } + if (result->name == Attrs::LINK_SECTION) + check_link_section_attribute (attr); + else if (result->name == Attrs::EXPORT_NAME) + check_export_name_attribute (attr); } } } diff --git a/gcc/rust/util/rust-attributes.h b/gcc/rust/util/rust-attributes.h index 2a9f44da113..bae160167e6 100644 --- a/gcc/rust/util/rust-attributes.h +++ b/gcc/rust/util/rust-attributes.h @@ -277,6 +277,9 @@ class AttributeChecker : public AST::DefaultASTVisitor void visit (AST::SelfParam ¶m) override; }; +tl::optional +identify_builtin (const AST::Attribute &attribute); + } // namespace Analysis } // namespace Rust diff --git a/gcc/testsuite/rust/compile/doc_macro.rs b/gcc/testsuite/rust/compile/doc_macro.rs index f71bd7ca04c..179455ddf95 100644 --- a/gcc/testsuite/rust/compile/doc_macro.rs +++ b/gcc/testsuite/rust/compile/doc_macro.rs @@ -1,4 +1,4 @@ #![feature(no_core)] #![no_core] - +#![feature(extended_key_value_attributes)] #![doc = concat!("AB")] diff --git a/gcc/testsuite/rust/compile/early_feature_gate_in_macro.rs b/gcc/testsuite/rust/compile/early_feature_gate_in_macro.rs new file mode 100644 index 00000000000..f7657736261 --- /dev/null +++ b/gcc/testsuite/rust/compile/early_feature_gate_in_macro.rs @@ -0,0 +1,23 @@ +#![feature(rustc_attrs)] +#![feature(lang_items)] +#![feature(no_core)] +#![no_core] + +#[rustc_builtin_macro] +macro_rules! concat { + () => {{}}; +} + +macro_rules! prefix_symbol( + ($prefix : tt, { $($ident: ident, )* }) => { + $( + // { dg-error "arbitrary expressions in key-value attributes are unstable" "" { target *-*-* } .+1 } + #[export_name = concat!(stringify!($prefix), stringify!($ident))] + pub extern "C" fn $ident() { + } + )* +}); + +prefix_symbol!(a,{ + __func0, +}); diff --git a/gcc/testsuite/rust/compile/issue-3661.rs b/gcc/testsuite/rust/compile/issue-3661.rs index 6c1a1a52fe9..7cb7c6add7a 100644 --- a/gcc/testsuite/rust/compile/issue-3661.rs +++ b/gcc/testsuite/rust/compile/issue-3661.rs @@ -1,5 +1,6 @@ #![feature(no_core)] #![no_core] +#![feature(extended_key_value_attributes)] pub macro m($inner_str:expr) { #[m = $inner_str] 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 } diff --git a/gcc/testsuite/rust/compile/parse_time_feature_gate.rs b/gcc/testsuite/rust/compile/parse_time_feature_gate.rs new file mode 100644 index 00000000000..238b2cfd5c7 --- /dev/null +++ b/gcc/testsuite/rust/compile/parse_time_feature_gate.rs @@ -0,0 +1,6 @@ +#![feature(no_core)] +#![no_core] + +// { dg-error "arbitrary expressions in key-value attributes are unstable" "" { target *-*-* } .+1 } +#[export_name = concat!(stringify!(non), stringify!(literal))] +pub extern "C" fn attribute_test_function() {}