From 317885e0bf7d7f3f85b08c57e74e56cbaece731b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 12 Jun 2015 14:07:29 -0400 Subject: [PATCH 1/3] Amend the Language Semver specification to reflect the criteria from this RFC. --- text/1122-language-semver.md | 93 ++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 40 deletions(-) diff --git a/text/1122-language-semver.md b/text/1122-language-semver.md index ed0985d606d..2296b763186 100644 --- a/text/1122-language-semver.md +++ b/text/1122-language-semver.md @@ -14,37 +14,29 @@ how to go about making such changes. With the release of 1.0, we need to establish clear policy on what precisely constitutes a "minor" vs "major" change to the Rust language itself (as opposed to libraries, which are covered by [RFC 1105]). -**This RFC proposes that minor releases may only contain breaking +**This RFC proposes that minor releases may contain breaking changes that fix compiler bugs or other type-system issues**. Primarily, this means soundness issues where "innocent" code can cause undefined behavior (in the technical sense), but it also covers cases like compiler bugs and tightening up the semantics of "underspecified" parts of the language (more details below). -However, simply landing all breaking changes immediately could be very +Simply landing all breaking changes immediately could be very disruptive to the ecosystem. Therefore, **the RFC also proposes specific measures to mitigate the impact of breaking changes**, and some criteria when those measures might be appropriate. -In rare cases, it may be deemed a good idea to make a breaking change +In rare cases, it may be deemed worthwhile to make a breaking change that is not a soundness problem or compiler bug, but rather correcting -a defect in design. Such cases should be rare. But if such a change is -deemed worthwhile, then the guidelines given here can still be used to -mitigate its impact. +a defect in design. **These changes are to be avoided, but may be +permissible if the impact is judged to be negligible (and thus there +is expected to be no breakage in practice).** This RFC also includes +criteria for how to estimate the impact of a change and advice on the +timing of such changes. # Detailed design -The detailed design is broken into two major sections: how to address -soundness changes, and how to address other, opt-in style changes. We -do not discuss non-breaking changes here, since obviously those are -safe. - -### Soundness changes - -When compiler or type-system bugs are encountered in the language -itself (as opposed to in a library), clearly they ought to be -fixed. However, it is important to fix them in such a way as to -minimize the impact on the ecosystem. +### Evaluating the impact of a change The first step then is to evaluate the impact of the fix on the crates found in the `crates.io` website (using e.g. the crater tool). If @@ -56,6 +48,8 @@ problem, which helps those people who are affected to migrate their code. A description of the problem should also appear in the relevant subteam report. +### Techniques for easing the transition + In cases where the impact seems larger, any effort to ease the transition is sure to be welcome. The following are suggestions for possible steps we could take (not all of which will be applicable to @@ -80,6 +74,8 @@ all scenarios): However, this option may frequently not be available, because the source of a compilation error is often hard to pin down with precision. + +### Other factors to consider Some of the factors that should be taken into consideration when deciding whether and how to minimize the impact of a fix: @@ -142,10 +138,19 @@ opt out will thus be removed in a later release. But in some cases, particularly those cases where the severity of the problem is relatively small, it could be an option to leave the "opt out" mechanism in place permanently. In either case, use of the "opt out" -API would trigger the deprecation lint. +API would trigger the deprecation lint. Note that we should make every +effort to ensure that crates which employ this opt out can be used +compatibly with crates that do not. -Note that we should make every effort to ensure that crates which -employ this opt out can be used compatibly with crates that do not. +Opt outs should be specified using the `#[legacy(foo)]` attribute. +This attribute intentionally ignores unrecognized opt-outs (such as +`foo`) to allow for forwards compatibility with opt-outs that may be +added in later compiler releases (in such cases, older compilers will +naturally perform the legacy behavior). + +Ideally, opt-outs should be constructed in as targeted a fashion as +possible. That means it is generally better, for example, to have +users opt out individual items than an entire crate at once. #### Changes that alter dynamic semantics versus typing rules @@ -229,6 +234,34 @@ future as well. The `-Z` flags are of course explicitly unstable, but some of the `-C`, rustdoc, and linker-specific flags are expected to evolve over time (see e.g. [#24451]). +#### Other kinds of breaking changes + +From time to time, we may find a flaw in a design that is neither a +soundness concern nor a bug fix, but rather simply a suboptimal +decision. In general, it is best to find ways to correct such errors +without making breaking changes, such as improved error messages or +deprecation. However, if the impact of making the change is judged to +be negligible, we can also consider fixing the problem, presuming that +the following criteria are met: + +- All data indicates that correcting this flaw will break extremely little + or no existing code (such as crates.io testing, communication with production + users of Rust or other private developers, etc). +- The feature was only recently stabilized, preferably in the previous + cycle. This minimizes the possibility that a large body of code has + crept up that relies on this feature. + - If and when we establish LTS releases, we should never make + changes to features marked as stable in a LTS release (except for + soundness reasons). +- There is no backwards compatible way to repair the problem. + +Naturally, all of the concerns listed above in the section "Other +Factors to Consider" also apply here. For example, we should consider +the quality of the error messages that result from the breaking +change, and evaluate whether it is possible to write code that works +both before/after the change (which enables users to span compiler +versions). + # Drawbacks The primary drawback is that making breaking changes are disruptive, @@ -269,26 +302,6 @@ observe on `crates.io` will be of the total breakage that will occur: it is certainly possible that all crates on `crates.io` work fine, but the change still breaks a large body of code we do not have access to. -**What attribute should we use to "opt out" of soundness changes?** -The section on breaking changes indicated that it may sometimes be -appropriate to includ an "opt out" that people can use to temporarily -revert to older, unsound type rules, but did not specify precisely -what that opt-out should look like. Ideally, we would identify a -specific attribute in advance that will be used for such purposes. In -the past, we have simply created ad-hoc attributes (e.g., -`#[old_orphan_check]`), but because custom attributes are forbidden by -stable Rust, this has the unfortunate side-effect of meaning that code -which opts out of the newer rules cannot be compiled on older -compilers (even though it's using the older type system rules). If we -introduce an attribute in advance we will not have this problem. - -**Are there any other circumstances in which we might perform a -breaking change?** In particular, it may happen from time to time that -we wish to alter some detail of a stable component. If we believe that -this change will not affect anyone, such a change may be worth doing, -but we'll have to work out more precise guidelines. [RFC 1156] is an -example. - [RFC 1105]: https://github.com/rust-lang/rfcs/pull/1105 [RFC 320]: https://github.com/rust-lang/rfcs/pull/320 [#744]: https://github.com/rust-lang/rfcs/issues/744 From e17464cb6436f98a19640e8a05e952a4b5b6cfd4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 16 Jun 2015 17:45:57 -0400 Subject: [PATCH 2/3] Adjust policy text to include warnings in 1.2, hard error in 1.3, and remove the legacy attribute (also adjust text of RFC #1122 slightly). --- text/0000-adjust-default-object-bounds.md | 243 ++++++++++++++++++++++ text/1122-language-semver.md | 28 ++- 2 files changed, 262 insertions(+), 9 deletions(-) create mode 100644 text/0000-adjust-default-object-bounds.md diff --git a/text/0000-adjust-default-object-bounds.md b/text/0000-adjust-default-object-bounds.md new file mode 100644 index 00000000000..a85a7d4126e --- /dev/null +++ b/text/0000-adjust-default-object-bounds.md @@ -0,0 +1,243 @@ +- Feature Name: N/A +- Start Date: 2015-06-4 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary + +Adjust the object default bound algorithm for cases like `&'x +Box` and `&'x Arc`. The existing algorithm would default +to `&'x Box`. The proposed change is to default to `&'x +Box`. + +Note: This is a **BREAKING CHANGE**. The change has +[been implemented][branch] and its impact has been evaluated. It was +[found][crater] to cause **no root regressions** on `crates.io`. +Nonetheless, to minimize impact, this RFC proposes phasing in the +change as follows: + +- In Rust 1.2, a warning will be issued for code which will break when the + defaults are changed. This warning can be disabled by using explicit + bounds. The warning will only be issued when explicit bounds would be required + in the future anyway. +- In Rust 1.3, the change will be made permanent. Any code that has + not been updated by that time will break. + +# Motivation + +When we instituted default object bounds, [RFC 599] specified that +`&'x Box` (and `&'x mut Box`) should expand to `&'x +Box` (and `&'x mut Box`). This is in contrast to a +`Box` type that appears outside of a reference (e.g., `Box`), +which defaults to using `'static` (`Box`). This +decision was made because it meant that a function written like so +would accept the broadest set of possible objects: + +```rust +fn foo(x: &Box) { +} +``` + +In particular, under the current defaults, `foo` can be supplied an +object which references borrowed data. Given that `foo` is taking the +argument by reference, it seemed like a good rule. Experience has +shown otherwise (see below for some of the problems encountered). + +This RFC proposes changing the default object bound rules so that the +default is drawn from the innermost type that encloses the trait +object. If there is no such type, the default is `'static`. The type +is a reference (e.g., `&'r Trait`), then the default is the lifetime +`'r` of that reference. Otherwise, the type must in practice be some +user-declared type, and the default is derived from the declaration: +if the type declares a lifetime bound, then this lifetime bound is +used, otherwise `'static` is used. This means that (e.g.) `&'r +Box` would default to `&'r Box`, and `&'r +Ref<'q, Trait>` (from `RefCell`) would default to `&'r Ref<'q, +Trait+'q>`. + +### Problems with the current default. + +**Same types, different expansions.** One problem is fairly +predictable: the current default means that identical types differ in +their interpretation based on where they appear. This is something we +have striven to avoid in general. So, as an example, this code +[will not type-check](http://is.gd/Yaak1l): + +```rust +trait Trait { } + +struct Foo { + field: Box +} + +fn do_something(f: &mut Foo, x: &mut Box) { + mem::swap(&mut f.field, &mut *x); +} +``` + +Even though `x` is a reference to a `Box` and the type of +`field` is a `Box`, the expansions differ. `x` expands to `&'x +mut Box` and the field expands to `Box`. In +general, we have tried to ensure that if the type is *typed precisely +the same* in a type definition and a fn definition, then those two +types are equal (note that fn definitions allow you to omit things +that cannot be omitted in types, so some types that you can enter in a +fn definition, like `&i32`, cannot appear in a type definition). + +Now, the same is of course true for the type `Trait` itself, which +appears identically in different contexts and is expanded in different +ways. This is not a problem here because the type `Trait` is unsized, +which means that it cannot be swapped or moved, and hence the main +sources of type mismatches are avoided. + +**Mental model.** In general the mental model of the newer rules seems +simpler: once you move a trait object into the heap (via `Box`, or +`Arc`), you must explicitly indicate whether it can contain borrowed +data or not. So long as you manipulate by reference, you don't have +to. In contrast, the current rules are more subtle, since objects in +the heap may still accept borrowed data, if you have a reference to +the box. + +**Poor interaction with the dropck rules.** When implementing the +newer dropck rules specified by [RFC 769], we found a +[rather subtle problem] that would arise with the current defaults. +The precise problem is spelled out in appendix below, but the TL;DR is +that if you wish to pass an array of boxed objects, the current +defaults can be actively harmful, and hence force you to specify +explicit lifetimes, whereas the newer defaults do something +reasonable. + +# Detailed design + +The rules for user-defined types from RFC 599 are altered as follows +(text that is not changed is italicized): + +- *If `SomeType` contains a single where-clause like `T:'a`, where + `T` is some type parameter on `SomeType` and `'a` is some + lifetime, then the type provided as value of `T` will have a + default object bound of `'a`. An example of this is + `std::cell::Ref`: a usage like `Ref<'x, X>` would change the + default for object types appearing in `X` to be `'a`.* +- If `SomeType` contains no where-clauses of the form `T:'a`, then + the "base default" is used. The base default depends on the overall context: + - in a fn body, the base default is a fresh inference variable. + - outside of a fn body, such in a fn signature, the base default + is `'static`. + Hence `Box` would typically be a default of `'static` for `X`, + regardless of whether it appears underneath an `&` or not. + (Note that in a fn body, the inference is strong enough to adopt `'static` + if that is the necessary bound, or a looser bound if that would be helpful.) +- *If `SomeType` contains multiple where-clauses of the form `T:'a`, + then the default is cleared and explicit lifetiem bounds are + required. There are no known examples of this in the standard + library as this situation arises rarely in practice.* + +# Timing and breaking change implications + +This is a breaking change, and hence it behooves us to evaluate the +impact and describe a procedure for making the change as painless as +possible. One nice propery of this change is that it only affects +*defaults*, which means that it is always possible to write code that +compiles both before and after the change by avoiding defaults in +those cases where the new and old compiler disagree. + +The estimated impact of this change is very low, for two reasons: +- A recent test of crates.io found [no regressions][crater] caused by + this change (however, a [previous run] (from before Rust 1.0) found 8 + regressions). +- This feature was only recently stabilized as part of Rust 1.0 (and + was only added towards the end of the release cycle), so there + hasn't been time for a large body of dependent code to arise + outside of crates.io. + +Nonetheless, to minimize impact, this RFC proposes phasing in the +change as follows: + +- In Rust 1.2, a warning will be issued for code which will break when the + defaults are changed. This warning can be disabled by using explicit + bounds. The warning will only be issued when explicit bounds would be required + in the future anyway. + - Specifically, types that were written `&Box` where the + (boxed) trait object may contain references should now be written + `&Box` to disable the warning. +- In Rust 1.3, the change will be made permanent. Any code that has + not been updated by that time will break. + +# Drawbacks + +The primary drawback is that this is a breaking change, as discussed +in the previous section. + +# Alternatives + +Keep the current design, with its known drawbacks. + +# Unresolved questions + +None. + +# Appendix: Details of the dropck problem + +This appendix goes into detail about the sticky interaction with +dropck that was uncovered. The problem arises if you have a function +that wishes to take a mutable slice of objects, like so: + +```rust +fn do_it(x: &mut [Box]) { ... } +``` + +Here, `&mut [..]` is used because the objects are `FnMut` objects, and +hence require `&mut self` to call. This function in turn is expanded +to: + +```rust +fn do_it<'x>(x: &'x mut [Box]) { ... } +``` + +Now callers might try to invoke the function as so: + +```rust +do_it(&mut [Box::new(val1), Box::new(val2)]) +``` + +Unfortunately, this code fails to compile -- in fact, it cannot be +made to compile without changing the definition of `do_it`, due to a +sticky interaction between dropck and variance. The problem is that +dropck requires that all data in the box strictly outlives the +lifetime of the box's owner. This is to prevent cyclic +content. Therefore, the type of the objects must be `Box` +where `'R` is some region that strictly outlives the array itself (as +the array is the owner of the objects). However, the signature of +`do_it` demands that the reference to the array has the same lifetime +as the trait objects within (and because this is an `&mut` reference +and hence invariant, no approximation is permitted). This implies that +the array must live for at least the region `'R`. But we defined the +region `'R` to be some region that outlives the array, so we have a +quandry. + +The solution is to change the definition of `do_it` in one of two +ways: + +```rust +// Use explicit lifetimes to make it clear that the reference is not +// required to have the same lifetime as the objects themselves: +fn do_it1<'a,'b>(x: &'a mut [Box]) { ... } + +// Specifying 'static is easier, but then the closures cannot +// capture the stack: +fn do_it2(x: &'a mut [Box]) { ... } +``` + +Under the proposed RFC, `do_it2` would be the default. If one wanted +to use lifetimes, then one would have to use explicit lifetime +overrides as shown in `do_it1`. This is consistent with the mental +model of "once you box up an object, you must add annotations for it +to contain borrowed data". + +[RFC 599]: 0599-default-object-bound.md +[RFC 769]: 0769-sound-generic-drop.md +[rather subtle problem]: https://github.com/rust-lang/rust/pull/25212#issuecomment-100244929 +[crater]: https://gist.github.com/brson/085d84d43c6a9a8d4dc3 +[branch]: https://github.com/nikomatsakis/rust/tree/better-object-defaults +[previous run]: https://gist.github.com/brson/80f9b80acef2e7ab37ee +[RFC 1122]: https://github.com/rust-lang/rfcs/pull/1122 diff --git a/text/1122-language-semver.md b/text/1122-language-semver.md index 2296b763186..fe13868f47e 100644 --- a/text/1122-language-semver.md +++ b/text/1122-language-semver.md @@ -242,7 +242,8 @@ decision. In general, it is best to find ways to correct such errors without making breaking changes, such as improved error messages or deprecation. However, if the impact of making the change is judged to be negligible, we can also consider fixing the problem, presuming that -the following criteria are met: +the following criteria are met (in addition to the criteria listed +above in the section "Other Factors to Consider"): - All data indicates that correcting this flaw will break extremely little or no existing code (such as crates.io testing, communication with production @@ -255,12 +256,23 @@ the following criteria are met: soundness reasons). - There is no backwards compatible way to repair the problem. -Naturally, all of the concerns listed above in the section "Other -Factors to Consider" also apply here. For example, we should consider -the quality of the error messages that result from the breaking -change, and evaluate whether it is possible to write code that works -both before/after the change (which enables users to span compiler -versions). +When we do decide to make such a change, we should follow one of the +following ways to ease the transition, in order of preference: + +1. When possible, issue a release that gives warnings when changes will be required. + - These warnings should be as targeted as possible -- if the warning + is too broad, it can easily cause more annoyance than the change + itself. + - To maximize the chance of these warnings being taken seriously, + the warning should not be a lint. The only way to disable it is to + make a change that will be forwards compatible with the new + version. + - After a suitable time has elapsed (at least one cycle, possibly + more), make the actual change. +2. If warnings are not possible, then include a `#[legacy]` attribute + that allows the old behavior to be restored. + - Ideally, this `#[legacy]` attribute will only persist for a fixed + number of cycles. # Drawbacks @@ -269,8 +281,6 @@ even when done with the best of intentions. The alternatives list some ways that we could avoid breaking changes altogether, and the downsides of each. -## Notes on phasing - # Alternatives **Rather than simply fixing soundness bugs, we could issue new major From 0b056f7a3b57c085ac73d221c844fa9aed6d252a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 16 Jun 2015 18:17:22 -0400 Subject: [PATCH 3/3] Reverse changes to #1122, which I will extract into a distinct RFC for more visibility --- text/1122-language-semver.md | 107 ++++++++++++++--------------------- 1 file changed, 42 insertions(+), 65 deletions(-) diff --git a/text/1122-language-semver.md b/text/1122-language-semver.md index fe13868f47e..ed0985d606d 100644 --- a/text/1122-language-semver.md +++ b/text/1122-language-semver.md @@ -14,29 +14,37 @@ how to go about making such changes. With the release of 1.0, we need to establish clear policy on what precisely constitutes a "minor" vs "major" change to the Rust language itself (as opposed to libraries, which are covered by [RFC 1105]). -**This RFC proposes that minor releases may contain breaking +**This RFC proposes that minor releases may only contain breaking changes that fix compiler bugs or other type-system issues**. Primarily, this means soundness issues where "innocent" code can cause undefined behavior (in the technical sense), but it also covers cases like compiler bugs and tightening up the semantics of "underspecified" parts of the language (more details below). -Simply landing all breaking changes immediately could be very +However, simply landing all breaking changes immediately could be very disruptive to the ecosystem. Therefore, **the RFC also proposes specific measures to mitigate the impact of breaking changes**, and some criteria when those measures might be appropriate. -In rare cases, it may be deemed worthwhile to make a breaking change +In rare cases, it may be deemed a good idea to make a breaking change that is not a soundness problem or compiler bug, but rather correcting -a defect in design. **These changes are to be avoided, but may be -permissible if the impact is judged to be negligible (and thus there -is expected to be no breakage in practice).** This RFC also includes -criteria for how to estimate the impact of a change and advice on the -timing of such changes. +a defect in design. Such cases should be rare. But if such a change is +deemed worthwhile, then the guidelines given here can still be used to +mitigate its impact. # Detailed design -### Evaluating the impact of a change +The detailed design is broken into two major sections: how to address +soundness changes, and how to address other, opt-in style changes. We +do not discuss non-breaking changes here, since obviously those are +safe. + +### Soundness changes + +When compiler or type-system bugs are encountered in the language +itself (as opposed to in a library), clearly they ought to be +fixed. However, it is important to fix them in such a way as to +minimize the impact on the ecosystem. The first step then is to evaluate the impact of the fix on the crates found in the `crates.io` website (using e.g. the crater tool). If @@ -48,8 +56,6 @@ problem, which helps those people who are affected to migrate their code. A description of the problem should also appear in the relevant subteam report. -### Techniques for easing the transition - In cases where the impact seems larger, any effort to ease the transition is sure to be welcome. The following are suggestions for possible steps we could take (not all of which will be applicable to @@ -74,8 +80,6 @@ all scenarios): However, this option may frequently not be available, because the source of a compilation error is often hard to pin down with precision. - -### Other factors to consider Some of the factors that should be taken into consideration when deciding whether and how to minimize the impact of a fix: @@ -138,19 +142,10 @@ opt out will thus be removed in a later release. But in some cases, particularly those cases where the severity of the problem is relatively small, it could be an option to leave the "opt out" mechanism in place permanently. In either case, use of the "opt out" -API would trigger the deprecation lint. Note that we should make every -effort to ensure that crates which employ this opt out can be used -compatibly with crates that do not. - -Opt outs should be specified using the `#[legacy(foo)]` attribute. -This attribute intentionally ignores unrecognized opt-outs (such as -`foo`) to allow for forwards compatibility with opt-outs that may be -added in later compiler releases (in such cases, older compilers will -naturally perform the legacy behavior). +API would trigger the deprecation lint. -Ideally, opt-outs should be constructed in as targeted a fashion as -possible. That means it is generally better, for example, to have -users opt out individual items than an entire crate at once. +Note that we should make every effort to ensure that crates which +employ this opt out can be used compatibly with crates that do not. #### Changes that alter dynamic semantics versus typing rules @@ -234,46 +229,6 @@ future as well. The `-Z` flags are of course explicitly unstable, but some of the `-C`, rustdoc, and linker-specific flags are expected to evolve over time (see e.g. [#24451]). -#### Other kinds of breaking changes - -From time to time, we may find a flaw in a design that is neither a -soundness concern nor a bug fix, but rather simply a suboptimal -decision. In general, it is best to find ways to correct such errors -without making breaking changes, such as improved error messages or -deprecation. However, if the impact of making the change is judged to -be negligible, we can also consider fixing the problem, presuming that -the following criteria are met (in addition to the criteria listed -above in the section "Other Factors to Consider"): - -- All data indicates that correcting this flaw will break extremely little - or no existing code (such as crates.io testing, communication with production - users of Rust or other private developers, etc). -- The feature was only recently stabilized, preferably in the previous - cycle. This minimizes the possibility that a large body of code has - crept up that relies on this feature. - - If and when we establish LTS releases, we should never make - changes to features marked as stable in a LTS release (except for - soundness reasons). -- There is no backwards compatible way to repair the problem. - -When we do decide to make such a change, we should follow one of the -following ways to ease the transition, in order of preference: - -1. When possible, issue a release that gives warnings when changes will be required. - - These warnings should be as targeted as possible -- if the warning - is too broad, it can easily cause more annoyance than the change - itself. - - To maximize the chance of these warnings being taken seriously, - the warning should not be a lint. The only way to disable it is to - make a change that will be forwards compatible with the new - version. - - After a suitable time has elapsed (at least one cycle, possibly - more), make the actual change. -2. If warnings are not possible, then include a `#[legacy]` attribute - that allows the old behavior to be restored. - - Ideally, this `#[legacy]` attribute will only persist for a fixed - number of cycles. - # Drawbacks The primary drawback is that making breaking changes are disruptive, @@ -281,6 +236,8 @@ even when done with the best of intentions. The alternatives list some ways that we could avoid breaking changes altogether, and the downsides of each. +## Notes on phasing + # Alternatives **Rather than simply fixing soundness bugs, we could issue new major @@ -312,6 +269,26 @@ observe on `crates.io` will be of the total breakage that will occur: it is certainly possible that all crates on `crates.io` work fine, but the change still breaks a large body of code we do not have access to. +**What attribute should we use to "opt out" of soundness changes?** +The section on breaking changes indicated that it may sometimes be +appropriate to includ an "opt out" that people can use to temporarily +revert to older, unsound type rules, but did not specify precisely +what that opt-out should look like. Ideally, we would identify a +specific attribute in advance that will be used for such purposes. In +the past, we have simply created ad-hoc attributes (e.g., +`#[old_orphan_check]`), but because custom attributes are forbidden by +stable Rust, this has the unfortunate side-effect of meaning that code +which opts out of the newer rules cannot be compiled on older +compilers (even though it's using the older type system rules). If we +introduce an attribute in advance we will not have this problem. + +**Are there any other circumstances in which we might perform a +breaking change?** In particular, it may happen from time to time that +we wish to alter some detail of a stable component. If we believe that +this change will not affect anyone, such a change may be worth doing, +but we'll have to work out more precise guidelines. [RFC 1156] is an +example. + [RFC 1105]: https://github.com/rust-lang/rfcs/pull/1105 [RFC 320]: https://github.com/rust-lang/rfcs/pull/320 [#744]: https://github.com/rust-lang/rfcs/issues/744