|
| 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 |
0 commit comments