1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
|
# Implementing new language features
<!-- toc -->
When you want to implement a new significant feature in the compiler,
you need to go through this process to make sure everything goes
smoothly.
**NOTE: this section is for *language* features, not *library* features,
which use [a different process].**
[a different process]: ./stability.md
## The @rfcbot FCP process
When the change is small and uncontroversial, then it can be done
with just writing a PR and getting an r+ from someone who knows that
part of the code. However, if the change is potentially controversial,
it would be a bad idea to push it without consensus from the rest
of the team (both in the "distributed system" sense to make sure
you don't break anything you don't know about, and in the social
sense to avoid PR fights).
If such a change seems to be too small to require a full formal RFC process
(e.g., a small standard library addition, a big refactoring of the code, a
"technically-breaking" change, or a "big bugfix" that basically amounts to a
small feature) but is still too controversial or big to get by with a single r+,
you can propose a final comment period (FCP). Or, if you're not on the relevant
team (and thus don't have @rfcbot permissions), ask someone who is to start one;
unless they have a concern themselves, they should.
Again, the FCP process is only needed if you need consensus – if you
don't think anyone would have a problem with your change, it's OK to
get by with only an r+. For example, it is OK to add or modify
unstable command-line flags or attributes without an FCP for
compiler development or standard library use, as long as you don't
expect them to be in wide use in the nightly ecosystem.
Some teams have lighter weight processes that they use in scenarios
like this; for example, the compiler team recommends
filing a Major Change Proposal ([MCP][mcp]) as a lightweight way to
garner support and feedback without requiring full consensus.
[mcp]: https://forge.rust-lang.org/compiler/mcp.html#public-facing-changes-require-rfcbot-fcp
You don't need to have the implementation fully ready for r+ to propose an FCP,
but it is generally a good idea to have at least a proof
of concept so that people can see what you are talking about.
When an FCP is proposed, it requires all members of the team to sign off the
FCP. After they all do so, there's a 10-day-long "final comment period" (hence
the name) where everybody can comment, and if no concerns are raised, the
PR/issue gets FCP approval.
## The logistics of writing features
There are a few "logistic" hoops you might need to go through in
order to implement a feature in a working way.
### Warning Cycles
In some cases, a feature or bugfix might break some existing programs
in some edge cases. In that case, you might want to do a crater run
to assess the impact and possibly add a future-compatibility lint,
similar to those used for
[edition-gated lints](diagnostics.md#edition-gated-lints).
### Stability
We [value the stability of Rust]. Code that works and runs on stable
should (mostly) not break. Because of that, we don't want to release
a feature to the world with only team consensus and code review -
we want to gain real-world experience on using that feature on nightly,
and we might want to change the feature based on that experience.
To allow for that, we must make sure users don't accidentally depend
on that new feature - otherwise, especially if experimentation takes
time or is delayed and the feature takes the trains to stable,
it would end up de facto stable and we'll not be able to make changes
in it without breaking people's code.
The way we do that is that we make sure all new features are feature
gated - they can't be used without enabling a feature gate
(`#[feature(foo)]`), which can't be done in a stable/beta compiler.
See the [stability in code] section for the technical details.
Eventually, after we gain enough experience using the feature,
make the necessary changes, and are satisfied, we expose it to
the world using the stabilization process described [here].
Until then, the feature is not set in stone: every part of the
feature can be changed, or the feature might be completely
rewritten or removed. Features are not supposed to gain tenure
by being unstable and unchanged for a year.
### Tracking Issues
To keep track of the status of an unstable feature, the
experience we get while using it on nightly, and of the
concerns that block its stabilization, every feature-gate
needs a tracking issue. General discussions about the feature should be done on the tracking issue.
For features that have an RFC, you should use the RFC's
tracking issue for the feature.
For other features, you'll have to make a tracking issue
for that feature. The issue title should be "Tracking issue
for YOUR FEATURE". Use the ["Tracking Issue" issue template][template].
[template]: https://github.com/rust-lang/rust/issues/new?template=tracking_issue.md
## Stability in code
The below steps needs to be followed in order to implement
a new unstable feature:
1. Open a [tracking issue] -
if you have an RFC, you can use the tracking issue for the RFC.
The tracking issue should be labeled with at least `C-tracking-issue`.
For a language feature, a label `F-feature_name` should be added as well.
1. Pick a name for the feature gate (for RFCs, use the name
in the RFC).
1. Add the feature name to `rustc_span/src/symbol.rs` in the `Symbols {...}` block.
1. Add a feature gate declaration to `rustc_feature/src/active.rs` in the active
`declare_features` block.
```rust ignore
/// description of feature
(active, $feature_name, "CURRENT_RUSTC_VERSION", Some($tracking_issue_number), $edition)
```
where `$edition` has the type `Option<Edition>`, and is typically just `None`. If you haven't yet
opened a tracking issue (e.g. because you want initial feedback on whether the feature is likely
to be accepted), you can temporarily use `None` - but make sure to update it before the PR is
merged!
For example:
```rust ignore
/// Allows defining identifiers beyond ASCII.
(active, non_ascii_idents, "CURRENT_RUSTC_VERSION", Some(55467), None),
```
Features can be marked as incomplete, and trigger the warn-by-default [`incomplete_features`
lint]
by setting their type to `incomplete`:
[`incomplete_features` lint]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#incomplete-features
```rust ignore
/// Allows unsized rvalues at arguments and parameters.
(incomplete, unsized_locals, "CURRENT_RUSTC_VERSION", Some(48055), None),
```
To avoid [semantic merge conflicts], please use `CURRENT_RUSTC_VERSION` instead of `1.70` or
another explicit version number.
[semantic merge conflicts]: https://bors.tech/essay/2017/02/02/pitch/
1. Prevent usage of the new feature unless the feature gate is set.
You can check it in most places in the compiler using the
expression `tcx.features().$feature_name` (or
`sess.features_untracked().$feature_name` if the
tcx is unavailable)
If the feature gate is not set, you should either maintain
the pre-feature behavior or raise an error, depending on
what makes sense. Errors should generally use [`rustc_session::parse::feature_err`].
For an example of adding an error, see [#81015].
For features introducing new syntax, pre-expansion gating should be used instead.
To do so, extend the [`GatedSpans`] struct, add spans to it during parsing,
and then finally feature-gate all the spans in
[`rustc_ast_passes::feature_gate::check_crate`].
1. Add a test to ensure the feature cannot be used without
a feature gate, by creating `tests/ui/feature-gates/feature-gate-$feature_name.rs`.
You can generate the corresponding `.stderr` file by running `./x test
tests/ui/feature-gates/ --bless`.
1. Add a section to the unstable book, in
`src/doc/unstable-book/src/language-features/$feature_name.md`.
1. Write a lot of tests for the new feature, preferably in `tests/ui/$feature_name/`.
PRs without tests will not be accepted!
1. Get your PR reviewed and land it. You have now successfully
implemented a feature in Rust!
[`GatedSpans`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/parse/struct.GatedSpans.html
[#81015]: https://github.com/rust-lang/rust/pull/81015
[`rustc_session::parse::feature_err`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/parse/fn.feature_err.html
[`rustc_ast_passes::feature_gate::check_crate`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast_passes/feature_gate/fn.check_crate.html
[value the stability of Rust]: https://github.com/rust-lang/rfcs/blob/master/text/1122-language-semver.md
[stability in code]: #stability-in-code
[here]: ./stabilization_guide.md
[tracking issue]: #tracking-issues
[add-feature-gate]: ./feature-gates.md#adding-a-feature-gate
|