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
2 changes: 1 addition & 1 deletion src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@
- [`#[test]` implementation](./test-implementation.md)
- [Panic implementation](./panic-implementation.md)
- [AST validation](./ast-validation.md)
- [Feature gate checking](./feature-gate-ck.md)
- [Feature gate checking](./feature-gate-check.md)
- [Lang Items](./lang-items.md)
- [The HIR (High-level IR)](./hir.md)
- [Lowering AST to HIR](./hir/lowering.md)
Expand Down
140 changes: 140 additions & 0 deletions src/feature-gate-check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# Feature Gate Checking

Feature gates prevent usage of unstable language and library features without a
nightly-only `#![feature(...)]` opt-in. This chapter documents the implementation
of feature gating: where gates are defined, how they are enabled, and how usage
is verified.

<!-- data-check: Feb 2026 -->

## Feature Definitions

All feature gate definitions are located in the `rustc_feature` crate:

- **Unstable features** are declared in [`rustc_feature/src/unstable.rs`] via
the `declare_features!` macro. This associates features with issue numbers and
tracking metadata.
- **Accepted features** (stabilized) are listed in [`rustc_feature/src/accepted.rs`].
- **Removed features** (explicitly disallowed) are listed in [`rustc_feature/src/removed.rs`].
- **Gated built-in attributes and cfgs** are declared in [`rustc_feature/src/builtin_attrs.rs`].

The [`rustc_feature::Features`] type represents the **active feature set** for a
crate. Helpers like `enabled`, `incomplete`, and `internal` are used during
compilation to check status.

## Collecting Features

Before AST validation or expansion, `rustc` collects crate-level
`#![feature(...)]` attributes to build the active `Features` set.

- The collection happens in [`rustc_expand/src/config.rs`] in [`features`].
- Each `#![feature]` entry is classified against the `unstable`, `accepted`, and
`removed` tables:
- **Removed** features cause an immediate error.
- **Accepted** features are recorded but do not require nightly. On
stable/beta, `maybe_stage_features` in
[`rustc_ast_passes/src/feature_gate.rs`] emits the non-nightly
diagnostic and lists stable features, which is where the "already
stabilized" messaging comes from.
- **Unstable** features are recorded as enabled.
- Unknown features are treated as **library features** and validated later.
- With `-Z allow-features=...`, any **unstable** or **unknown** feature
not in the allowlist is rejected.
- [`RUSTC_BOOTSTRAP`] feeds into `UnstableFeatures::from_environment`. This
variable controls whether the compiler is treated as "nightly", allowing
feature gates to be bypassed during bootstrapping or explicitly disabled (`-1`).

## Parser Gating

Some syntax is detected and gated during parsing. The parser records spans for
later checking to keep diagnostics consistent and deferred until after parsing.

- [`rustc_session/src/parse.rs`] defines [`GatedSpans`] and the `gate` method.
- The parser uses it in [`rustc_parse/src/parser/*`] when it encounters
syntax that requires a gate (e.g., `async for`, `yield`, experimental patterns).

## Checking Pass

The central logic lives in [`rustc_ast_passes/src/feature_gate.rs`], primarily
in `check_crate` and its AST visitor.

### `check_crate`

`check_crate` performs high-level validation:

- `maybe_stage_features`: Rejects `#![feature]` on stable/beta.
- `check_incompatible_features`: Ensures incompatible feature combinations
(declared in `rustc_feature::INCOMPATIBLE_FEATURES`) are not used together.
- `check_new_solver_banned_features`: Bans features incompatible with
compiler mode for the next trait solver.
- **Parser-gated spans**: Processes the `GatedSpans` recorded during parsing
(see [Checking `GatedSpans`](#checking-gatedspans)).

### Checking `GatedSpans`

`check_crate` iterates over `sess.psess.gated_spans`:
Copy link
Member

Choose a reason for hiding this comment

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

this make it seem like it's not a continuation of line 65... maybe line 65 could have a link to this, or that the content of this section should be added under line 65

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh, that's right, I changed the location of the section and added a link just in case


- The `gate_all!` macro emits diagnostics for each gated span if the feature is
not enabled.
- Some gates have extra logic (e.g., `yield` can be allowed by `coroutines` or
`gen_blocks`).
- Legacy gates (e.g., `box_patterns`, `try_blocks`) may use a separate path that
emits future-incompatibility warnings instead of hard errors.

### AST Visitor

A `PostExpansionVisitor` walks the expanded AST to check constructs that are
easier to validate after expansion.

- The visitor uses helper macros (`gate!`, `gate_alt!`, `gate_multi!`) to check:
1. Is the feature enabled?
2. Does `span.allows_unstable` permit it (for internal compiler macros)?
- Examples include `trait_alias`, `decl_macro`, `extern types`, and various
`impl Trait` forms.

## Attributes and `cfg`

Beyond syntax, rustc also gates attributes and `cfg` options.

### Built-in attributes

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

### `cfg` options

- [`rustc_attr_parsing/src/attributes/cfg.rs`] defines `gate_cfg` and uses
[`rustc_feature::find_gated_cfg`] to reject gated `cfg`s.
- `gate_cfg` respects `Span::allows_unstable`, allowing internal compiler
macros to bypass `cfg` gates when marked with `#[allow_internal_unstable]`.
- The gated cfg list is defined in [`rustc_feature/src/builtin_attrs.rs`].

## Diagnostics

Diagnostic helpers are located in [`rustc_session/src/parse.rs`].

- `feature_err` and `feature_warn` emit standardized diagnostics, attaching the
tracking issue number where possible.
- `Span::allows_unstable` in [`rustc_span/src/lib.rs`] checks if a span originates
from a macro marked with `#[allow_internal_unstable]`. This allows internal
macros to use unstable features on stable channels while enforcing gates for
user code.

[`rustc_feature/src/unstable.rs`]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_feature/src/unstable.rs
[`rustc_feature/src/removed.rs`]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_feature/src/removed.rs
[`rustc_feature/src/accepted.rs`]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_feature/src/accepted.rs
[`rustc_feature/src/builtin_attrs.rs`]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_feature/src/builtin_attrs.rs
[`rustc_feature::Features`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_feature/struct.Features.html
[`rustc_expand/src/config.rs`]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_expand/src/config.rs
[`features`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/config/fn.features.html
[`RUSTC_BOOTSTRAP`]: https://doc.rust-lang.org/beta/unstable-book/compiler-environment-variables/RUSTC_BOOTSTRAP.html
[`rustc_session/src/parse.rs`]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_session/src/parse.rs
[`GatedSpans`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/parse/struct.GatedSpans.html
[`rustc_ast_passes/src/feature_gate.rs`]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_ast_passes/src/feature_gate.rs
[`rustc_parse/src/parser/*`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/parser/index.html
[`rustc_ast_passes::check_attribute`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast_passes/feature_gate/fn.check_attribute.html
[`rustc_attr_parsing/src/attributes/cfg.rs`]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_attr_parsing/src/attributes/cfg.rs
[`rustc_feature::find_gated_cfg`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_feature/fn.find_gated_cfg.html
[`rustc_span/src/lib.rs`]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_span/src/lib.rs
3 changes: 0 additions & 3 deletions src/feature-gate-ck.md

This file was deleted.

2 changes: 1 addition & 1 deletion src/syntax-intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ And parsing requires macro expansion, which in turn may require parsing the outp

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