Skip to content

Commit 3079be0

Browse files
authored
Merge pull request #2750 from reddevilmidzy/fgc
Document feature gate checking
2 parents ccb31b7 + 5c783ad commit 3079be0

4 files changed

Lines changed: 142 additions & 5 deletions

File tree

src/SUMMARY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@
124124
- [`#[test]` implementation](./test-implementation.md)
125125
- [Panic implementation](./panic-implementation.md)
126126
- [AST validation](./ast-validation.md)
127-
- [Feature gate checking](./feature-gate-ck.md)
127+
- [Feature gate checking](./feature-gate-check.md)
128128
- [Lang Items](./lang-items.md)
129129
- [The HIR (High-level IR)](./hir.md)
130130
- [Lowering AST to HIR](./hir/lowering.md)

src/feature-gate-check.md

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# Feature Gate Checking
2+
3+
Feature gates prevent usage of unstable language and library features without a
4+
nightly-only `#![feature(...)]` opt-in. This chapter documents the implementation
5+
of feature gating: where gates are defined, how they are enabled, and how usage
6+
is verified.
7+
8+
<!-- data-check: Feb 2026 -->
9+
10+
## Feature Definitions
11+
12+
All feature gate definitions are located in the `rustc_feature` crate:
13+
14+
- **Unstable features** are declared in [`rustc_feature/src/unstable.rs`] via
15+
the `declare_features!` macro. This associates features with issue numbers and
16+
tracking metadata.
17+
- **Accepted features** (stabilized) are listed in [`rustc_feature/src/accepted.rs`].
18+
- **Removed features** (explicitly disallowed) are listed in [`rustc_feature/src/removed.rs`].
19+
- **Gated built-in attributes and cfgs** are declared in [`rustc_feature/src/builtin_attrs.rs`].
20+
21+
The [`rustc_feature::Features`] type represents the **active feature set** for a
22+
crate. Helpers like `enabled`, `incomplete`, and `internal` are used during
23+
compilation to check status.
24+
25+
## Collecting Features
26+
27+
Before AST validation or expansion, `rustc` collects crate-level
28+
`#![feature(...)]` attributes to build the active `Features` set.
29+
30+
- The collection happens in [`rustc_expand/src/config.rs`] in [`features`].
31+
- Each `#![feature]` entry is classified against the `unstable`, `accepted`, and
32+
`removed` tables:
33+
- **Removed** features cause an immediate error.
34+
- **Accepted** features are recorded but do not require nightly. On
35+
stable/beta, `maybe_stage_features` in
36+
[`rustc_ast_passes/src/feature_gate.rs`] emits the non-nightly
37+
diagnostic and lists stable features, which is where the "already
38+
stabilized" messaging comes from.
39+
- **Unstable** features are recorded as enabled.
40+
- Unknown features are treated as **library features** and validated later.
41+
- With `-Z allow-features=...`, any **unstable** or **unknown** feature
42+
not in the allowlist is rejected.
43+
- [`RUSTC_BOOTSTRAP`] feeds into `UnstableFeatures::from_environment`. This
44+
variable controls whether the compiler is treated as "nightly", allowing
45+
feature gates to be bypassed during bootstrapping or explicitly disabled (`-1`).
46+
47+
## Parser Gating
48+
49+
Some syntax is detected and gated during parsing. The parser records spans for
50+
later checking to keep diagnostics consistent and deferred until after parsing.
51+
52+
- [`rustc_session/src/parse.rs`] defines [`GatedSpans`] and the `gate` method.
53+
- The parser uses it in [`rustc_parse/src/parser/*`] when it encounters
54+
syntax that requires a gate (e.g., `async for`, `yield`, experimental patterns).
55+
56+
## Checking Pass
57+
58+
The central logic lives in [`rustc_ast_passes/src/feature_gate.rs`], primarily
59+
in `check_crate` and its AST visitor.
60+
61+
### `check_crate`
62+
63+
`check_crate` performs high-level validation:
64+
65+
- `maybe_stage_features`: Rejects `#![feature]` on stable/beta.
66+
- `check_incompatible_features`: Ensures incompatible feature combinations
67+
(declared in `rustc_feature::INCOMPATIBLE_FEATURES`) are not used together.
68+
- `check_new_solver_banned_features`: Bans features incompatible with
69+
compiler mode for the next trait solver.
70+
- **Parser-gated spans**: Processes the `GatedSpans` recorded during parsing
71+
(see [Checking `GatedSpans`](#checking-gatedspans)).
72+
73+
### Checking `GatedSpans`
74+
75+
`check_crate` iterates over `sess.psess.gated_spans`:
76+
77+
- The `gate_all!` macro emits diagnostics for each gated span if the feature is
78+
not enabled.
79+
- Some gates have extra logic (e.g., `yield` can be allowed by `coroutines` or
80+
`gen_blocks`).
81+
- Legacy gates (e.g., `box_patterns`, `try_blocks`) may use a separate path that
82+
emits future-incompatibility warnings instead of hard errors.
83+
84+
### AST Visitor
85+
86+
A `PostExpansionVisitor` walks the expanded AST to check constructs that are
87+
easier to validate after expansion.
88+
89+
- The visitor uses helper macros (`gate!`, `gate_alt!`, `gate_multi!`) to check:
90+
1. Is the feature enabled?
91+
2. Does `span.allows_unstable` permit it (for internal compiler macros)?
92+
- Examples include `trait_alias`, `decl_macro`, `extern types`, and various
93+
`impl Trait` forms.
94+
95+
## Attributes and `cfg`
96+
97+
Beyond syntax, rustc also gates attributes and `cfg` options.
98+
99+
### Built-in attributes
100+
101+
- [`rustc_ast_passes::check_attribute`] inspects attributes against
102+
`BUILTIN_ATTRIBUTE_MAP`.
103+
- If the attribute is `AttributeGate::Gated` and the feature isn’t enabled,
104+
`feature_err` is emitted.
105+
106+
### `cfg` options
107+
108+
- [`rustc_attr_parsing/src/attributes/cfg.rs`] defines `gate_cfg` and uses
109+
[`rustc_feature::find_gated_cfg`] to reject gated `cfg`s.
110+
- `gate_cfg` respects `Span::allows_unstable`, allowing internal compiler
111+
macros to bypass `cfg` gates when marked with `#[allow_internal_unstable]`.
112+
- The gated cfg list is defined in [`rustc_feature/src/builtin_attrs.rs`].
113+
114+
## Diagnostics
115+
116+
Diagnostic helpers are located in [`rustc_session/src/parse.rs`].
117+
118+
- `feature_err` and `feature_warn` emit standardized diagnostics, attaching the
119+
tracking issue number where possible.
120+
- `Span::allows_unstable` in [`rustc_span/src/lib.rs`] checks if a span originates
121+
from a macro marked with `#[allow_internal_unstable]`. This allows internal
122+
macros to use unstable features on stable channels while enforcing gates for
123+
user code.
124+
125+
[`rustc_feature/src/unstable.rs`]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_feature/src/unstable.rs
126+
[`rustc_feature/src/removed.rs`]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_feature/src/removed.rs
127+
[`rustc_feature/src/accepted.rs`]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_feature/src/accepted.rs
128+
[`rustc_feature/src/builtin_attrs.rs`]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_feature/src/builtin_attrs.rs
129+
[`rustc_feature::Features`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_feature/struct.Features.html
130+
[`rustc_expand/src/config.rs`]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_expand/src/config.rs
131+
[`features`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/config/fn.features.html
132+
[`RUSTC_BOOTSTRAP`]: https://doc.rust-lang.org/beta/unstable-book/compiler-environment-variables/RUSTC_BOOTSTRAP.html
133+
[`rustc_session/src/parse.rs`]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_session/src/parse.rs
134+
[`GatedSpans`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/parse/struct.GatedSpans.html
135+
[`rustc_ast_passes/src/feature_gate.rs`]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_ast_passes/src/feature_gate.rs
136+
[`rustc_parse/src/parser/*`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/parser/index.html
137+
[`rustc_ast_passes::check_attribute`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast_passes/feature_gate/fn.check_attribute.html
138+
[`rustc_attr_parsing/src/attributes/cfg.rs`]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_attr_parsing/src/attributes/cfg.rs
139+
[`rustc_feature::find_gated_cfg`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_feature/fn.find_gated_cfg.html
140+
[`rustc_span/src/lib.rs`]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_span/src/lib.rs

src/feature-gate-ck.md

Lines changed: 0 additions & 3 deletions
This file was deleted.

src/syntax-intro.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ And parsing requires macro expansion, which in turn may require parsing the outp
1313

1414
[AST]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/index.html
1515
[macro expansion]: ./macro-expansion.md
16-
[feature-gate checking]: ./feature-gate-ck.md
16+
[feature-gate checking]: ./feature-gate-check.md
1717
[lexing, parsing]: ./the-parser.md
1818
[name resolution]: ./name-resolution.md
1919
[validation]: ./ast-validation.md

0 commit comments

Comments
 (0)