rustc_feature/
lib.rs

1//! # Feature gates
2//!
3//! This crate declares the set of past and present unstable features in the compiler.
4//! Feature gate checking itself is done in `rustc_ast_passes/src/feature_gate.rs`
5//! at the moment.
6//!
7//! Features are enabled in programs via the crate-level attributes of
8//! `#![feature(...)]` with a comma-separated list of features.
9//!
10//! For the purpose of future feature-tracking, once a feature gate is added,
11//! even if it is stabilized or removed, *do not remove it*. Instead, move the
12//! symbol to the `accepted` or `removed` modules respectively.
13
14// tidy-alphabetical-start
15#![allow(internal_features)]
16#![doc(rust_logo)]
17#![feature(rustdoc_internals)]
18// tidy-alphabetical-end
19
20mod accepted;
21mod builtin_attrs;
22mod removed;
23mod unstable;
24
25#[cfg(test)]
26mod tests;
27
28use std::num::NonZero;
29
30use rustc_span::Symbol;
31
32#[derive(Debug, Clone)]
33pub struct Feature {
34    pub name: Symbol,
35    /// For unstable features: the version the feature was added in.
36    /// For accepted features: the version the feature got stabilized in.
37    /// For removed features we are inconsistent; sometimes this is the
38    /// version it got added, sometimes the version it got removed.
39    pub since: &'static str,
40    issue: Option<NonZero<u32>>,
41}
42
43#[derive(Copy, Clone, Debug)]
44pub enum Stability {
45    Unstable,
46    // First argument is tracking issue link; second argument is an optional
47    // help message, which defaults to "remove this attribute".
48    Deprecated(&'static str, Option<&'static str>),
49}
50
51#[derive(Clone, Copy, Debug, Hash)]
52pub enum UnstableFeatures {
53    /// Disallow use of unstable features, as on beta/stable channels.
54    Disallow,
55    /// Allow use of unstable features, as on nightly.
56    Allow,
57    /// Errors are bypassed for bootstrapping. This is required any time
58    /// during the build that feature-related lints are set to warn or above
59    /// because the build turns on warnings-as-errors and uses lots of unstable
60    /// features. As a result, this is always required for building Rust itself.
61    Cheat,
62}
63
64impl UnstableFeatures {
65    /// This takes into account `RUSTC_BOOTSTRAP`.
66    ///
67    /// If `krate` is [`Some`], then setting `RUSTC_BOOTSTRAP=krate` will enable the nightly
68    /// features. Otherwise, only `RUSTC_BOOTSTRAP=1` will work.
69    pub fn from_environment(krate: Option<&str>) -> Self {
70        Self::from_environment_value(krate, std::env::var("RUSTC_BOOTSTRAP"))
71    }
72
73    /// Avoid unsafe `std::env::set_var()` by allowing tests to inject
74    /// `std::env::var("RUSTC_BOOTSTRAP")` with the `env_var_rustc_bootstrap`
75    /// arg.
76    fn from_environment_value(
77        krate: Option<&str>,
78        env_var_rustc_bootstrap: Result<String, std::env::VarError>,
79    ) -> Self {
80        // `true` if this is a feature-staged build, i.e., on the beta or stable channel.
81        let disable_unstable_features =
82            option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some_and(|s| s != "0");
83        // Returns whether `krate` should be counted as unstable
84        let is_unstable_crate =
85            |var: &str| krate.is_some_and(|name| var.split(',').any(|new_krate| new_krate == name));
86
87        let bootstrap = env_var_rustc_bootstrap.ok();
88        if let Some(val) = bootstrap.as_deref() {
89            match val {
90                val if val == "1" || is_unstable_crate(val) => return UnstableFeatures::Cheat,
91                // Hypnotize ourselves so that we think we are a stable compiler and thus don't
92                // allow any unstable features.
93                "-1" => return UnstableFeatures::Disallow,
94                _ => {}
95            }
96        }
97
98        if disable_unstable_features { UnstableFeatures::Disallow } else { UnstableFeatures::Allow }
99    }
100
101    pub fn is_nightly_build(&self) -> bool {
102        match *self {
103            UnstableFeatures::Allow | UnstableFeatures::Cheat => true,
104            UnstableFeatures::Disallow => false,
105        }
106    }
107}
108
109fn find_lang_feature_issue(feature: Symbol) -> Option<NonZero<u32>> {
110    // Search in all the feature lists.
111    if let Some(f) = UNSTABLE_LANG_FEATURES.iter().find(|f| f.name == feature) {
112        return f.issue;
113    }
114    if let Some(f) = ACCEPTED_LANG_FEATURES.iter().find(|f| f.name == feature) {
115        return f.issue;
116    }
117    if let Some(f) = REMOVED_LANG_FEATURES.iter().find(|f| f.feature.name == feature) {
118        return f.feature.issue;
119    }
120    panic!("feature `{feature}` is not declared anywhere");
121}
122
123const fn to_nonzero(n: Option<u32>) -> Option<NonZero<u32>> {
124    // Can be replaced with `n.and_then(NonZero::new)` if that is ever usable
125    // in const context. Requires https://github.com/rust-lang/rfcs/pull/2632.
126    match n {
127        None => None,
128        Some(n) => NonZero::new(n),
129    }
130}
131
132pub enum GateIssue {
133    Language,
134    Library(Option<NonZero<u32>>),
135}
136
137pub fn find_feature_issue(feature: Symbol, issue: GateIssue) -> Option<NonZero<u32>> {
138    match issue {
139        GateIssue::Language => find_lang_feature_issue(feature),
140        GateIssue::Library(lib) => lib,
141    }
142}
143
144pub use accepted::ACCEPTED_LANG_FEATURES;
145pub use builtin_attrs::{
146    AttributeDuplicates, AttributeGate, AttributeSafety, AttributeTemplate, AttributeType,
147    BUILTIN_ATTRIBUTE_MAP, BUILTIN_ATTRIBUTES, BuiltinAttribute, GatedCfg, deprecated_attributes,
148    encode_cross_crate, find_gated_cfg, is_builtin_attr_name, is_stable_diagnostic_attribute,
149    is_valid_for_get_attr,
150};
151pub use removed::REMOVED_LANG_FEATURES;
152pub use unstable::{
153    EnabledLangFeature, EnabledLibFeature, Features, INCOMPATIBLE_FEATURES, UNSTABLE_LANG_FEATURES,
154};