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(Clone, Copy, Debug, Hash)]
44pub enum UnstableFeatures {
45    /// Disallow use of unstable features, as on beta/stable channels.
46    Disallow,
47    /// Allow use of unstable features, as on nightly.
48    Allow,
49    /// Errors are bypassed for bootstrapping. This is required any time
50    /// during the build that feature-related lints are set to warn or above
51    /// because the build turns on warnings-as-errors and uses lots of unstable
52    /// features. As a result, this is always required for building Rust itself.
53    Cheat,
54}
55
56impl UnstableFeatures {
57    /// This takes into account `RUSTC_BOOTSTRAP`.
58    ///
59    /// If `krate` is [`Some`], then setting `RUSTC_BOOTSTRAP=krate` will enable the nightly
60    /// features. Otherwise, only `RUSTC_BOOTSTRAP=1` will work.
61    pub fn from_environment(krate: Option<&str>) -> Self {
62        Self::from_environment_value(krate, std::env::var("RUSTC_BOOTSTRAP"))
63    }
64
65    /// Avoid unsafe `std::env::set_var()` by allowing tests to inject
66    /// `std::env::var("RUSTC_BOOTSTRAP")` with the `env_var_rustc_bootstrap`
67    /// arg.
68    fn from_environment_value(
69        krate: Option<&str>,
70        env_var_rustc_bootstrap: Result<String, std::env::VarError>,
71    ) -> Self {
72        // `true` if this is a feature-staged build, i.e., on the beta or stable channel.
73        let disable_unstable_features =
74            option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some_and(|s| s != "0");
75        // Returns whether `krate` should be counted as unstable
76        let is_unstable_crate =
77            |var: &str| krate.is_some_and(|name| var.split(',').any(|new_krate| new_krate == name));
78
79        let bootstrap = env_var_rustc_bootstrap.ok();
80        if let Some(val) = bootstrap.as_deref() {
81            match val {
82                val if val == "1" || is_unstable_crate(val) => return UnstableFeatures::Cheat,
83                // Hypnotize ourselves so that we think we are a stable compiler and thus don't
84                // allow any unstable features.
85                "-1" => return UnstableFeatures::Disallow,
86                _ => {}
87            }
88        }
89
90        if disable_unstable_features { UnstableFeatures::Disallow } else { UnstableFeatures::Allow }
91    }
92
93    pub fn is_nightly_build(&self) -> bool {
94        match *self {
95            UnstableFeatures::Allow | UnstableFeatures::Cheat => true,
96            UnstableFeatures::Disallow => false,
97        }
98    }
99}
100
101fn find_lang_feature_issue(feature: Symbol) -> Option<NonZero<u32>> {
102    // Search in all the feature lists.
103    if let Some(f) = UNSTABLE_LANG_FEATURES.iter().find(|f| f.name == feature) {
104        return f.issue;
105    }
106    if let Some(f) = ACCEPTED_LANG_FEATURES.iter().find(|f| f.name == feature) {
107        return f.issue;
108    }
109    if let Some(f) = REMOVED_LANG_FEATURES.iter().find(|f| f.feature.name == feature) {
110        return f.feature.issue;
111    }
112    panic!("feature `{feature}` is not declared anywhere");
113}
114
115const fn to_nonzero(n: Option<u32>) -> Option<NonZero<u32>> {
116    // Can be replaced with `n.and_then(NonZero::new)` if that is ever usable
117    // in const context. Requires https://github.com/rust-lang/rfcs/pull/2632.
118    match n {
119        None => None,
120        Some(n) => NonZero::new(n),
121    }
122}
123
124pub enum GateIssue {
125    Language,
126    Library(Option<NonZero<u32>>),
127}
128
129pub fn find_feature_issue(feature: Symbol, issue: GateIssue) -> Option<NonZero<u32>> {
130    match issue {
131        GateIssue::Language => find_lang_feature_issue(feature),
132        GateIssue::Library(lib) => lib,
133    }
134}
135
136pub use accepted::ACCEPTED_LANG_FEATURES;
137pub use builtin_attrs::{
138    AttributeDuplicates, AttributeGate, AttributeSafety, AttributeTemplate, AttributeType,
139    BUILTIN_ATTRIBUTE_MAP, BUILTIN_ATTRIBUTES, BuiltinAttribute, GatedCfg, encode_cross_crate,
140    find_gated_cfg, is_builtin_attr_name, is_stable_diagnostic_attribute, is_valid_for_get_attr,
141};
142pub use removed::REMOVED_LANG_FEATURES;
143pub use unstable::{
144    EnabledLangFeature, EnabledLibFeature, Features, INCOMPATIBLE_FEATURES, UNSTABLE_LANG_FEATURES,
145};