Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4068,6 +4068,7 @@ name = "rustc_feature"
version = "0.0.0"
dependencies = [
"rustc_data_structures",
"rustc_macros",
"rustc_span",
"serde",
"serde_json",
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_attr_parsing/src/attributes/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ fn parse_cfg_attr_internal<'a>(
}

fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Features>) {
let gate = find_gated_cfg(|sym| sym == name);
let gate = find_gated_cfg(name);
if let (Some(feats), Some(gated_cfg)) = (features, gate) {
gate_cfg(gated_cfg, span, sess, feats);
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_attr_parsing/src/attributes/doc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ fn check_keyword(cx: &mut AcceptContext<'_, '_>, keyword: Symbol, span: Span) ->

fn check_attribute(cx: &mut AcceptContext<'_, '_>, attribute: Symbol, span: Span) -> bool {
// FIXME: This should support attributes with namespace like `diagnostic::do_not_recommend`.
if rustc_feature::BUILTIN_ATTRIBUTE_MAP.contains(&attribute) {
if rustc_feature::BUILTIN_ATTRIBUTE_SET.contains(&attribute) {
return true;
}
cx.emit_err(DocAttributeNotAttribute { span, attribute });
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_attr_parsing/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use rustc_attr_ir::target::Target;
use rustc_attr_ir::{AttrArgs, AttrItem, AttrPath, Attribute, AttributeKind, HashIgnoredAttrId};
use rustc_data_structures::sync::{DynSend, DynSync};
use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level, MultiSpan};
use rustc_feature::{BUILTIN_ATTRIBUTE_MAP, Features};
use rustc_feature::{BUILTIN_ATTRIBUTE_SET, Features};
use rustc_lint_defs::{LintId, RegisteredTools};
use rustc_session::Session;
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
Expand Down Expand Up @@ -376,7 +376,7 @@ impl<'sess> AttributeParser<'sess> {
);
self.check_attribute_stability(&attr_path, attr_span, accept.stability);
if let [part] = parts.as_slice() {
debug_assert!(BUILTIN_ATTRIBUTE_MAP.contains(part));
debug_assert!(BUILTIN_ATTRIBUTE_SET.contains(part));
}

let Some(args) = ArgParser::from_attr_args(
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_attr_parsing/src/validate_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use rustc_ast::{
};
use rustc_attr_ir::AttrPath;
use rustc_errors::{Applicability, Diagnostic, PResult};
use rustc_feature::BUILTIN_ATTRIBUTE_MAP;
use rustc_feature::BUILTIN_ATTRIBUTE_SET;
use rustc_lint_defs::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
use rustc_parse::parse_in;
use rustc_session::diagnostics::report_lit_error;
Expand All @@ -27,7 +27,7 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute) {
AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace(_)) | AttrKind::DocComment(..) => return,
}

let builtin_attr_info = attr.name().and_then(|name| BUILTIN_ATTRIBUTE_MAP.get(&name));
let builtin_attr_info = attr.name().and_then(|name| BUILTIN_ATTRIBUTE_SET.get(&name));

// Check input tokens for built-in and key-value attributes.
if let Some(name) = builtin_attr_info {
Expand Down
4 changes: 1 addition & 3 deletions compiler/rustc_driver_impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -741,9 +741,7 @@ fn print_crate_info(
.iter()
.filter_map(|&(name, value)| {
// On stable, exclude unstable flags.
if !sess.is_nightly_build()
&& find_gated_cfg(|cfg_sym| cfg_sym == name).is_some()
{
if !sess.is_nightly_build() && find_gated_cfg(name).is_some() {
return None;
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_feature/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ edition = "2024"
[dependencies]
# tidy-alphabetical-start
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_macros = { path = "../rustc_macros" }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new dependency already existed transitively through rustc_data_structures so is fine :)

rustc_span = { path = "../rustc_span" }
serde = { version = "1.0.125", features = ["derive"] }
serde_json = "1.0.59"
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_feature/src/accepted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ declare_features! (
/// Allows some increased flexibility in the name resolution rules,
/// especially around globs and shadowing (RFC 1560).
(accepted, item_like_imports, "1.15.0", Some(35120)),
// Allows using the `kl` and `widekl` target features and the associated intrinsics
/// Allows using the `kl` and `widekl` target features and the associated intrinsics
(accepted, keylocker_x86, "1.89.0", Some(134813)),
/// Allows `'a: { break 'a; }`.
(accepted, label_break_value, "1.65.0", Some(48594)),
Expand Down
26 changes: 14 additions & 12 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,20 +54,22 @@ const GATED_CFGS: &[GatedCfg] = &[
(sym::target_object_format, sym::cfg_target_object_format, Features::cfg_target_object_format),
];

/// Find a gated cfg determined by the `pred`icate which is given the cfg's name.
pub fn find_gated_cfg(pred: impl Fn(Symbol) -> bool) -> Option<&'static GatedCfg> {
GATED_CFGS.iter().find(|(cfg_sym, ..)| pred(*cfg_sym))
/// Find a gated cfg matching `name`.
pub fn find_gated_cfg(name: Symbol) -> Option<&'static GatedCfg> {
GATED_CFGS.iter().find(|(cfg_sym, ..)| name == *cfg_sym)
}

#[derive(Clone, Debug, Copy)]
pub enum AttributeStability {
/// An attribute that is unstable behind a specified feature fagte
/// An attribute that is unstable behind a specified feature gate.
Unstable {
/// The feature gate, for example `rustc_attrs` for rustc_* attributes.
gate_name: Symbol,
/// Check function to be called during the `PostExpansionVisitor` pass, which will be one of the `Features::*` functions
gate_check: fn(&Features) -> bool,
/// Notes to be displayed when an attempt is made to use the attribute without its feature gate.
/// Check function to be called during the `PostExpansionVisitor` pass, which will be one
/// of the `Features::*` functions
gate_check: GateFn,

@mejrs mejrs Aug 31, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW this field only really exists to statically check that the feature actually exists - Features::doesnt_exist can't be found because that's not a feature.

You could remove this field if you can do that check in another way.

View changes since the review

/// Notes to be displayed when an attempt is made to use the attribute without its feature
/// gate.
notes: &'static [&'static str],
},
/// A stable attribute, can be used on all release channels
Expand Down Expand Up @@ -417,15 +419,15 @@ pub static BUILTIN_ATTRIBUTES: &[Symbol] = &[
];

pub fn is_builtin_attr_name(name: Symbol) -> bool {
BUILTIN_ATTRIBUTE_MAP.get(&name).is_some()
BUILTIN_ATTRIBUTE_SET.contains(&name)
}

pub static BUILTIN_ATTRIBUTE_MAP: LazyLock<FxHashSet<Symbol>> = LazyLock::new(|| {
let mut map = FxHashSet::default();
pub static BUILTIN_ATTRIBUTE_SET: LazyLock<FxHashSet<Symbol>> = LazyLock::new(|| {
let mut set = FxHashSet::default();
for attr in BUILTIN_ATTRIBUTES.iter() {
if !map.insert(*attr) {
if !set.insert(*attr) {
panic!("duplicate builtin attribute `{}`", attr);
}
}
map
set
});
5 changes: 2 additions & 3 deletions compiler/rustc_feature/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@ impl UnstableFeatures {
let is_unstable_crate =
|var: &str| krate.is_some_and(|name| var.split(',').any(|new_krate| new_krate == name));

let bootstrap = env_var_rustc_bootstrap.ok();
if let Some(val) = bootstrap.as_deref() {
if let Ok(val) = env_var_rustc_bootstrap.as_deref() {
match val {
val if val == "1" || is_unstable_crate(val) => return UnstableFeatures::Cheat,
// Hypnotize ourselves so that we think we are a stable compiler and thus don't
Expand Down Expand Up @@ -129,7 +128,7 @@ pub fn find_feature_issue(feature: Symbol, issue: GateIssue) -> Option<NonZero<u

pub use accepted::ACCEPTED_LANG_FEATURES;
pub use builtin_attrs::{
AttributeStability, BUILTIN_ATTRIBUTE_MAP, BUILTIN_ATTRIBUTES, GatedCfg, find_gated_cfg,
AttributeStability, BUILTIN_ATTRIBUTE_SET, BUILTIN_ATTRIBUTES, GatedCfg, find_gated_cfg,
is_builtin_attr_name,
};
pub use removed::REMOVED_LANG_FEATURES;
Expand Down
10 changes: 5 additions & 5 deletions compiler/rustc_feature/src/removed.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! List of the removed feature gates.

use std::num::{NonZero, NonZeroU32};
use std::num::NonZero;

use rustc_span::sym;

Expand All @@ -17,7 +17,7 @@ macro_rules! opt_nonzero_u32 {
None
};
($val:expr) => {
Some(NonZeroU32::new($val).unwrap())
Some(<NonZero<u32>>::new($val).unwrap())
};
}

Expand All @@ -34,7 +34,7 @@ macro_rules! declare_features {
issue: to_nonzero($issue),
},
reason: $reason,
pull: opt_nonzero_u32!($($pull)?),
pull: opt_nonzero_u32!($($pull)?),
}),+
];
};
Expand Down Expand Up @@ -211,7 +211,7 @@ declare_features! (
(removed, no_coverage, "1.74.0", Some(84605), Some("renamed to `coverage_attribute`"), 114656),
/// Allows `#[no_debug]`.
(removed, no_debug, "1.43.0", Some(29721), Some("removed due to lack of demand"), 69667),
// Allows the use of `no_sanitize` attribute.
/// Allows the use of `no_sanitize` attribute.
/// The feature was renamed to `sanitize` and the attribute to `#[sanitize(xyz = "on|off")]`
(removed, no_sanitize, "1.91.0", Some(39699), Some(r#"renamed to sanitize(xyz = "on|off")"#), 142681),
/// Note: this feature was previously recorded in a separate
Expand Down Expand Up @@ -266,7 +266,7 @@ declare_features! (
(removed, pushpop_unsafe, "1.2.0", None, None),
(removed, quad_precision_float, "1.0.0", None, None),
(removed, quote, "1.33.0", Some(29601), None),
(removed, ref_pat_everywhere, "1.80.0", Some(123076), Some("superseded by `ref_pat_eat_one_layer_2024"), 125168),
(removed, ref_pat_everywhere, "1.80.0", Some(123076), Some("superseded by `ref_pat_eat_one_layer_2024`"), 125168),
(removed, reflect, "1.0.0", Some(27749), None),
/// Allows using the `#[register_attr]` attribute.
(removed, register_attr, "1.65.0", Some(66080),
Expand Down
53 changes: 16 additions & 37 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::time::{SystemTime, UNIX_EPOCH};

use rustc_data_structures::AtomicRef;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::stable_hash::{StableHash, StableHashCtxt, StableHasher};
use rustc_macros::StableHash;
use rustc_span::{Span, Symbol, sym};

use super::{Feature, to_nonzero};
Expand Down Expand Up @@ -43,18 +43,19 @@ macro_rules! status_to_enum {
///
/// The former is preferred. `enabled` should only be used when the feature symbol is not a
/// constant, e.g. a parameter, or when the feature is a library feature.
#[derive(Clone, Default, Debug)]
#[derive(Clone, Default, Debug, StableHash)]
pub struct Features {
/// `#![feature]` attrs for language features, for error reporting.
enabled_lang_features: Vec<EnabledLangFeature>,
/// `#![feature]` attrs for non-language (library) features.
enabled_lib_features: Vec<EnabledLibFeature>,
/// `enabled_lang_features` + `enabled_lib_features`.
#[stable_hash(ignore)] // Ignored because it's the sum of the other two fields
enabled_features: FxHashSet<Symbol>,
}

/// Information about an enabled language feature.
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Copy, Clone, StableHash)]
pub struct EnabledLangFeature {
/// Name of the feature gate guarding the language feature.
pub gate_name: Symbol,
Expand All @@ -65,7 +66,7 @@ pub struct EnabledLangFeature {
}

/// Information about an enabled library feature.
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Copy, Clone, StableHash)]
pub struct EnabledLibFeature {
pub gate_name: Symbol,
pub attr_sp: Span,
Expand All @@ -87,19 +88,19 @@ impl Features {
/// - Feature gate name.
/// - The span of the `#[feature]` attribute.
/// - For stable language features, version info for when it was stabilized.
pub fn enabled_lang_features(&self) -> &Vec<EnabledLangFeature> {
pub fn enabled_lang_features(&self) -> &[EnabledLangFeature] {
&self.enabled_lang_features
}

pub fn enabled_lib_features(&self) -> &Vec<EnabledLibFeature> {
pub fn enabled_lib_features(&self) -> &[EnabledLibFeature] {
&self.enabled_lib_features
}

pub fn enabled_features(&self) -> &FxHashSet<Symbol> {
&self.enabled_features
}

/// Returns a iterator of enabled features in stable order.
/// Returns an iterator of enabled features in stable order.
pub fn enabled_features_iter_stable_order(
&self,
) -> impl Iterator<Item = (Symbol, Span)> + Clone {
Expand All @@ -120,32 +121,6 @@ impl Features {
}
}

impl StableHash for Features {
fn stable_hash<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx, hasher: &mut StableHasher) {
// `enabled_features` is skipped because it's the sum of the lang and lib features.
let Features { enabled_lang_features, enabled_lib_features, enabled_features: _ } = self;
enabled_lang_features.stable_hash(hcx, hasher);
enabled_lib_features.stable_hash(hcx, hasher);
}
}

impl StableHash for EnabledLangFeature {
fn stable_hash<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx, hasher: &mut StableHasher) {
let EnabledLangFeature { gate_name, attr_sp, stable_since } = self;
gate_name.stable_hash(hcx, hasher);
attr_sp.stable_hash(hcx, hasher);
stable_since.stable_hash(hcx, hasher);
}
}

impl StableHash for EnabledLibFeature {
fn stable_hash<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx, hasher: &mut StableHasher) {
let EnabledLibFeature { gate_name, attr_sp } = self;
gate_name.stable_hash(hcx, hasher);
attr_sp.stable_hash(hcx, hasher);
}
}

macro_rules! declare_features {
($(
$(#[doc = $doc:tt])* ($status:ident, $feature:ident, $ver:expr, $issue:expr),
Expand Down Expand Up @@ -515,7 +490,7 @@ declare_features! (
(unstable, diagnostic_on_unknown, "1.96.0", Some(152900)),
/// Allows macros to customize macro argument matcher diagnostics.
(unstable, diagnostic_on_unmatched_args, "1.97.0", Some(155642)),
// Used by macros to not show their bodies in error messages. No-op with `-Z macro-backtrace`.
/// Used by macros to not show their bodies in error messages. No-op with `-Z macro-backtrace`.
(unstable, diagnostic_opaque, "1.99.0", Some(158813)),
/// Allows `#[doc(cfg(...))]`.
(unstable, doc_cfg, "1.21.0", Some(43781)),
Expand Down Expand Up @@ -578,7 +553,8 @@ declare_features! (
(incomplete, generic_const_parameter_types, "1.87.0", Some(137626)),
/// Allows any generic constants being used as pattern type range ends
(incomplete, generic_pattern_types, "1.86.0", Some(136574)),
/// Allows registering static items globally, possibly across crates, to iterate over at runtime.
/// Allows registering static items globally, possibly across crates, to iterate over at
/// runtime.
(unstable, global_registration, "1.80.0", Some(125119)),
/// Allows using guards in patterns.
(incomplete, guard_patterns, "1.85.0", Some(129967)),
Expand Down Expand Up @@ -679,7 +655,7 @@ declare_features! (
(unstable, non_exhaustive_omitted_patterns_lint, "1.57.0", Some(89554)),
/// Allows `for<T>` binders in where-clauses
(incomplete, non_lifetime_binders, "1.69.0", Some(108185)),
/// Target feaures on nvptx.
/// Target features on nvptx.
(unstable, nvptx_target_feature, "1.91.0", Some(150254)),
/// Allows using enums in offset_of!
(unstable, offset_of_enum, "1.75.0", Some(120141)),
Expand All @@ -701,10 +677,12 @@ declare_features! (
(unstable, proc_macro_hygiene, "1.30.0", Some(54727)),
/// Allows the use of raw-dylibs on ELF platforms
(incomplete, raw_dylib_elf, "1.87.0", Some(135694)),
/// Allows the `Reborrow` and `CoerceShared` traits.
(unstable, reborrow, "1.91.0", Some(145612)),
/// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024.
(incomplete, ref_pat_eat_one_layer_2024, "1.79.0", Some(123076)),
/// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024—structural variant
/// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024—structural
/// variant.
(incomplete, ref_pat_eat_one_layer_2024_structural, "1.81.0", Some(123076)),
/// Allows using the `#[register_tool]` attribute.
(unstable, register_tool, "1.41.0", Some(66079)),
Expand Down Expand Up @@ -791,6 +769,7 @@ declare_features! (
(unstable, xtensa_target_feature, "1.98.0", Some(157063)),
/// Allows `do yeet` expressions
(unstable, yeet_expr, "1.62.0", Some(96373)),
/// Allows the `yield` keyword for coroutines/generators.
(unstable, yield_expr, "1.87.0", Some(43122)),
// !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!!
// Features are listed in alphabetical order. Tidy will fail if you don't keep it this way.
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use rustc_ast::{AttrStyle, MetaItemKind, ast};
use rustc_attr_parsing::AttributeParser;
use rustc_data_structures::thin_vec::ThinVec;
use rustc_errors::{DiagCtxtHandle, IntoDiagArg, MultiSpan, msg};
use rustc_feature::BUILTIN_ATTRIBUTE_MAP;
use rustc_feature::BUILTIN_ATTRIBUTE_SET;
use rustc_hir::attrs::diagnostic::Directive;
use rustc_hir::attrs::lang_items::LangItem;
use rustc_hir::attrs::{
Expand Down Expand Up @@ -146,7 +146,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
[sym::allow | sym::expect | sym::warn | sym::deny | sym::forbid, ..] => {}

[name, rest @ ..] => {
if let Some(_) = BUILTIN_ATTRIBUTE_MAP.get(name) {
if BUILTIN_ATTRIBUTE_SET.contains(name) {
if rest.len() > 0
&& AttributeParser::is_parsed_attribute(slice::from_ref(name))
{
Expand Down
2 changes: 1 addition & 1 deletion src/doc/rustc-dev-guide/src/feature-gate-check.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ Beyond syntax, rustc also gates attributes and `cfg` options.

### Built-in attributes

- [`rustc_ast_passes::check_attribute`] inspects attributes against `BUILTIN_ATTRIBUTE_MAP`.
- [`rustc_ast_passes::check_attribute`] inspects attributes against `BUILTIN_ATTRIBUTE_SET`.
- If the attribute is `AttributeGate::Gated` and the feature isn’t enabled,
`feature_err` is emitted.

Expand Down
Loading