]> git.proxmox.com Git - rustc.git/blob - src/doc/rustc-dev-guide/src/bug-fix-procedure.md
e69ce48f95370989c581723bea5446b3b4b5d6e3
[rustc.git] / src / doc / rustc-dev-guide / src / bug-fix-procedure.md
1 # Procedures for Breaking Changes
2
3 <!-- toc -->
4
5 This page defines the best practices procedure for making bug fixes or soundness
6 corrections in the compiler that can cause existing code to stop compiling. This
7 text is based on
8 [RFC 1589](https://github.com/rust-lang/rfcs/blob/master/text/1589-rustc-bug-fix-procedure.md).
9
10 # Motivation
11
12 [motivation]: #motivation
13
14 From time to time, we encounter the need to make a bug fix, soundness
15 correction, or other change in the compiler which will cause existing code to
16 stop compiling. When this happens, it is important that we handle the change in
17 a way that gives users of Rust a smooth transition. What we want to avoid is
18 that existing programs suddenly stop compiling with opaque error messages: we
19 would prefer to have a gradual period of warnings, with clear guidance as to
20 what the problem is, how to fix it, and why the change was made. This RFC
21 describes the procedure that we have been developing for handling breaking
22 changes that aims to achieve that kind of smooth transition.
23
24 One of the key points of this policy is that (a) warnings should be issued
25 initially rather than hard errors if at all possible and (b) every change that
26 causes existing code to stop compiling will have an associated tracking issue.
27 This issue provides a point to collect feedback on the results of that change.
28 Sometimes changes have unexpectedly large consequences or there may be a way to
29 avoid the change that was not considered. In those cases, we may decide to
30 change course and roll back the change, or find another solution (if warnings
31 are being used, this is particularly easy to do).
32
33 ### What qualifies as a bug fix?
34
35 Note that this RFC does not try to define when a breaking change is permitted.
36 That is already covered under [RFC 1122][]. This document assumes that the
37 change being made is in accordance with those policies. Here is a summary of the
38 conditions from RFC 1122:
39
40 - **Soundness changes:** Fixes to holes uncovered in the type system.
41 - **Compiler bugs:** Places where the compiler is not implementing the specified
42 semantics found in an RFC or lang-team decision.
43 - **Underspecified language semantics:** Clarifications to grey areas where the
44 compiler behaves inconsistently and no formal behavior had been previously
45 decided.
46
47 Please see [the RFC][rfc 1122] for full details!
48
49 # Detailed design
50
51 [design]: #detailed-design
52
53 The procedure for making a breaking change is as follows (each of these steps is
54 described in more detail below):
55
56 1. Do a **crater run** to assess the impact of the change.
57 2. Make a **special tracking issue** dedicated to the change.
58 3. Do not report an error right away. Instead, **issue forwards-compatibility
59 lint warnings**.
60 - Sometimes this is not straightforward. See the text below for suggestions
61 on different techniques we have employed in the past.
62 - For cases where warnings are infeasible:
63 - Report errors, but make every effort to give a targeted error message
64 that directs users to the tracking issue
65 - Submit PRs to all known affected crates that fix the issue
66 - or, at minimum, alert the owners of those crates to the problem and
67 direct them to the tracking issue
68 4. Once the change has been in the wild for at least one cycle, we can
69 **stabilize the change**, converting those warnings into errors.
70
71 Finally, for changes to `rustc_ast` that will affect plugins, the general policy
72 is to batch these changes. That is discussed below in more detail.
73
74 ### Tracking issue
75
76 Every breaking change should be accompanied by a **dedicated tracking issue**
77 for that change. The main text of this issue should describe the change being
78 made, with a focus on what users must do to fix their code. The issue should be
79 approachable and practical; it may make sense to direct users to an RFC or some
80 other issue for the full details. The issue also serves as a place where users
81 can comment with questions or other concerns.
82
83 A template for these breaking-change tracking issues can be found below. An
84 example of how such an issue should look can be [found
85 here][breaking-change-issue].
86
87 The issue should be tagged with (at least) `B-unstable` and `T-compiler`.
88
89 ### Tracking issue template
90
91 This is a template to use for tracking issues:
92
93 ```
94 This is the **summary issue** for the `YOUR_LINT_NAME_HERE`
95 future-compatibility warning and other related errors. The goal of
96 this page is describe why this change was made and how you can fix
97 code that is affected by it. It also provides a place to ask questions
98 or register a complaint if you feel the change should not be made. For
99 more information on the policy around future-compatibility warnings,
100 see our [breaking change policy guidelines][guidelines].
101
102 [guidelines]: LINK_TO_THIS_RFC
103
104 #### What is the warning for?
105
106 *Describe the conditions that trigger the warning and how they can be
107 fixed. Also explain why the change was made.**
108
109 #### When will this warning become a hard error?
110
111 At the beginning of each 6-week release cycle, the Rust compiler team
112 will review the set of outstanding future compatibility warnings and
113 nominate some of them for **Final Comment Period**. Toward the end of
114 the cycle, we will review any comments and make a final determination
115 whether to convert the warning into a hard error or remove it
116 entirely.
117 ```
118
119 ### Issuing future compatibility warnings
120
121 The best way to handle a breaking change is to begin by issuing
122 future-compatibility warnings. These are a special category of lint warning.
123 Adding a new future-compatibility warning can be done as follows.
124
125 ```rust
126 // 1. Define the lint in `compiler/rustc_middle/src/lint/builtin.rs`:
127 declare_lint! {
128 pub YOUR_ERROR_HERE,
129 Warn,
130 "illegal use of foo bar baz"
131 }
132
133 // 2. Add to the list of HardwiredLints in the same file:
134 impl LintPass for HardwiredLints {
135 fn get_lints(&self) -> LintArray {
136 lint_array!(
137 ..,
138 YOUR_ERROR_HERE
139 )
140 }
141 }
142
143 // 3. Register the lint in `compiler/rustc_lint/src/lib.rs`:
144 store.register_future_incompatible(sess, vec![
145 ...,
146 FutureIncompatibleInfo {
147 id: LintId::of(YOUR_ERROR_HERE),
148 reference: "issue #1234", // your tracking issue here!
149 },
150 ]);
151
152 // 4. Report the lint:
153 tcx.lint_node(
154 lint::builtin::YOUR_ERROR_HERE,
155 path_id,
156 binding.span,
157 format!("some helper message here"));
158 ```
159
160 #### Helpful techniques
161
162 It can often be challenging to filter out new warnings from older, pre-existing
163 errors. One technique that has been used in the past is to run the older code
164 unchanged and collect the errors it would have reported. You can then issue
165 warnings for any errors you would give which do not appear in that original set.
166 Another option is to abort compilation after the original code completes if
167 errors are reported: then you know that your new code will only execute when
168 there were no errors before.
169
170 #### Crater and crates.io
171
172 [Crater] is a bot that will compile all crates.io crates and many
173 public github repos with the compiler with your changes. A report will then be
174 generated with crates that ceased to compile with or began to compile with your
175 changes. Crater runs can take a few days to complete.
176
177 [Crater]: ./tests/crater.md
178
179 We should always do a crater run to assess impact. It is polite and considerate
180 to at least notify the authors of affected crates the breaking change. If we can
181 submit PRs to fix the problem, so much the better.
182
183 #### Is it ever acceptable to go directly to issuing errors?
184
185 Changes that are believed to have negligible impact can go directly to issuing
186 an error. One rule of thumb would be to check against `crates.io`: if fewer than
187 10 **total** affected projects are found (**not** root errors), we can move
188 straight to an error. In such cases, we should still make the "breaking change"
189 page as before, and we should ensure that the error directs users to this page.
190 In other words, everything should be the same except that users are getting an
191 error, and not a warning. Moreover, we should submit PRs to the affected
192 projects (ideally before the PR implementing the change lands in rustc).
193
194 If the impact is not believed to be negligible (e.g., more than 10 crates are
195 affected), then warnings are required (unless the compiler team agrees to grant
196 a special exemption in some particular case). If implementing warnings is not
197 feasible, then we should make an aggressive strategy of migrating crates before
198 we land the change so as to lower the number of affected crates. Here are some
199 techniques for approaching this scenario:
200
201 1. Issue warnings for subparts of the problem, and reserve the new errors for
202 the smallest set of cases you can.
203 2. Try to give a very precise error message that suggests how to fix the problem
204 and directs users to the tracking issue.
205 3. It may also make sense to layer the fix:
206 - First, add warnings where possible and let those land before proceeding to
207 issue errors.
208 - Work with authors of affected crates to ensure that corrected versions are
209 available _before_ the fix lands, so that downstream users can use them.
210
211 ### Stabilization
212
213 After a change is made, we will **stabilize** the change using the same process
214 that we use for unstable features:
215
216 - After a new release is made, we will go through the outstanding tracking
217 issues corresponding to breaking changes and nominate some of them for **final
218 comment period** (FCP).
219 - The FCP for such issues lasts for one cycle. In the final week or two of the
220 cycle, we will review comments and make a final determination:
221
222 - Convert to error: the change should be made into a hard error.
223 - Revert: we should remove the warning and continue to allow the older code to
224 compile.
225 - Defer: can't decide yet, wait longer, or try other strategies.
226
227 Ideally, breaking changes should have landed on the **stable branch** of the
228 compiler before they are finalized.
229
230 <a name="guide">
231
232 ### Removing a lint
233
234 Once we have decided to make a "future warning" into a hard error, we need a PR
235 that removes the custom lint. As an example, here are the steps required to
236 remove the `overlapping_inherent_impls` compatibility lint. First, convert the
237 name of the lint to uppercase (`OVERLAPPING_INHERENT_IMPLS`) ripgrep through the
238 source for that string. We will basically by converting each place where this
239 lint name is mentioned (in the compiler, we use the upper-case name, and a macro
240 automatically generates the lower-case string; so searching for
241 `overlapping_inherent_impls` would not find much).
242
243 > NOTE: these exact files don't exist anymore, but the procedure is still the same.
244
245 #### Remove the lint.
246
247 The first reference you will likely find is the lint definition [in
248 `rustc_session/src/lint/builtin.rs` that resembles this][defsource]:
249
250 [defsource]: https://github.com/rust-lang/rust/blob/085d71c3efe453863739c1fb68fd9bd1beff214f/src/librustc/lint/builtin.rs#L171-L175
251
252 ```rust
253 declare_lint! {
254 pub OVERLAPPING_INHERENT_IMPLS,
255 Deny, // this may also say Warning
256 "two overlapping inherent impls define an item with the same name were erroneously allowed"
257 }
258 ```
259
260 This `declare_lint!` macro creates the relevant data structures. Remove it. You
261 will also find that there is a mention of `OVERLAPPING_INHERENT_IMPLS` later in
262 the file as [part of a `lint_array!`][lintarraysource]; remove it too.
263
264 [lintarraysource]: https://github.com/rust-lang/rust/blob/085d71c3efe453863739c1fb68fd9bd1beff214f/src/librustc/lint/builtin.rs#L252-L290
265
266 Next, you see [a reference to `OVERLAPPING_INHERENT_IMPLS` in
267 `rustc_lint/src/lib.rs`][futuresource]. This is defining the lint as a "future
268 compatibility lint":
269
270 ```rust
271 FutureIncompatibleInfo {
272 id: LintId::of(OVERLAPPING_INHERENT_IMPLS),
273 reference: "issue #36889 <https://github.com/rust-lang/rust/issues/36889>",
274 },
275 ```
276
277 Remove this too.
278
279 #### Add the lint to the list of removed lists.
280
281 In `compiler/rustc_lint/src/lib.rs` there is a list of "renamed and removed lints".
282 You can add this lint to the list:
283
284 ```rust
285 store.register_removed("overlapping_inherent_impls", "converted into hard error, see #36889");
286 ```
287
288 where `#36889` is the tracking issue for your lint.
289
290 #### Update the places that issue the lint
291
292 Finally, the last class of references you will see are the places that actually
293 **trigger** the lint itself (i.e., what causes the warnings to appear). These
294 you do not want to delete. Instead, you want to convert them into errors. In
295 this case, the [`add_lint` call][addlintsource] looks like this:
296
297 ```rust
298 self.tcx.sess.add_lint(lint::builtin::OVERLAPPING_INHERENT_IMPLS,
299 node_id,
300 self.tcx.span_of_impl(item1).unwrap(),
301 msg);
302 ```
303
304 We want to convert this into an error. In some cases, there may be an
305 existing error for this scenario. In others, we will need to allocate a
306 fresh diagnostic code. [Instructions for allocating a fresh diagnostic
307 code can be found here.](./diagnostics/diagnostic-codes.md) You may want
308 to mention in the extended description that the compiler behavior
309 changed on this point, and include a reference to the tracking issue for
310 the change.
311
312 Let's say that we've adopted `E0592` as our code. Then we can change the
313 `add_lint()` call above to something like:
314
315 ```rust
316 struct_span_err!(self.tcx.sess, self.tcx.span_of_impl(item1).unwrap(), msg)
317 .emit();
318 ```
319
320 #### Update tests
321
322 Finally, run the test suite. These should be some tests that used to reference
323 the `overlapping_inherent_impls` lint, those will need to be updated. In
324 general, if the test used to have `#[deny(overlapping_inherent_impls)]`, that
325 can just be removed.
326
327 ```
328 ./x.py test
329 ```
330
331 #### All done!
332
333 Open a PR. =)
334
335 [addlintsource]: https://github.com/rust-lang/rust/blob/085d71c3efe453863739c1fb68fd9bd1beff214f/src/librustc_typeck/coherence/inherent.rs#L300-L303
336 [futuresource]: https://github.com/rust-lang/rust/blob/085d71c3efe453863739c1fb68fd9bd1beff214f/src/librustc_lint/lib.rs#L202-L205
337
338 <!-- -Links--------------------------------------------------------------------- -->
339
340 [rfc 1122]: https://github.com/rust-lang/rfcs/blob/master/text/1122-language-semver.md
341 [breaking-change-issue]: https://gist.github.com/nikomatsakis/631ec8b4af9a18b5d062d9d9b7d3d967