From edcb3f0c913a340d581c4450c9d314579955f37d Mon Sep 17 00:00:00 2001 From: llogiq Date: Wed, 3 Jun 2015 15:56:51 +0200 Subject: [PATCH 01/27] new RFC: deprecation --- text/0000-deprecation.md | 42 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 text/0000-deprecation.md diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md new file mode 100644 index 00000000000..ace7e7e7d24 --- /dev/null +++ b/text/0000-deprecation.md @@ -0,0 +1,42 @@ +- Feature Name: A plan for deprecating APIs within Rust +- Start Date: 2015-06-03 +- RFC PR: +- Rust Issue: + +# Summary + +There has been an ongoing [discussion on internals](https://internals.rust-lang.org/t/thoughts-on-aggressive-deprecation-in-libstd/2176/55) about how we are going to evolve the standard library. This RFC tries to condense the consensus + +# Motivation + +We want to guide the deprecation efforts to allow std to evolve freely to get the best possible API while ensuring minimum-breakage backwards compatibility for users and allow std authors to remove API items for a given version of Rust. Basically have our cake and eat it. Yum, cake. + +Of course we cannot really keep and remove a feature at the same time. To square this circle, we can follow the process outlined herein. + +# Detailed design + +We already declare deprecation in terms of Rust versions (like "1.0", "1.2"). The current attribute looks like `#[deprecated(since = "1.0.0", reason="foo")]`. This should be extended to add an optional `removed_at` key, to state that the item should be made inaccessible at that version. Note that while this allows for marking items as deprecated, there is absolutely no provision to actually *remove* items. In fact this proposal bans removing an API type outright, unless security concerns are deemed more important than the resulting breakage from removing it or the API item has some fault that means it cannot be used correctly at all (thus leaving the API in place would result in the same level of breakage than removing it). + +Currently every rustc version implements only its own version, having multiple versions is possible using something like multirust, though this does not work within a build. Also currently rustc versions do not guarantee interoperability. This RFC aims to change this situation. + +First, crates should state their target version using a `#![version = "1.0.0"]` attribute. Cargo should insert the current rust version by default on `cargo new` and *warn* if no version is defined on all other commands. It may optionally *note* that the specified target version is outdated on `cargo package`. [crates.io](https://crates.io) may deny packages that do not declare a version to give the target version requirement more weight to library authors. Cargo should also be able to hold back a new library version if its declared target version is newer than the rust version installed on the system. In those cases, cargo should emit a warning urging the user to upgrade their rust installation. + +`rustc` should use this target version definition to check for deprecated items. If no target version is defined, deprecation checking is deactivated (as we cannot assume a specific rust version), however a warning stating the same should be issued (as with cargo – we should probably make cargo not warn on build to get rid of duplicate warnings). Otherwise, use of API items whose `since` attribute is less or equal to the target version of the crate should trigger a warning, while API items whose `removed_at` attribute is less or equal to the target version should trigger an error. + +`rustdoc` should mark deprecated APIs as such (e.g. make them in a lighter gray font) and relegate removed APIs to a section below all others (and that may be hidden via a checkbox). We should not completely remove the documentation, as users of libraries that target old versions may still have a use for them, but neither should we let them clutter the docs. + +# Drawbacks + +By requiring full backwards-compatibility, we will never be able to actually remove stuff from the APIs, which will probably lead to some bloat. + +# Alternatives + +* Follow a more agressive strategy that actually removes stuff from the API. This would make it easier for the libstd creators at some cost for library and application writers, as they are required to keep up to date or face breakage +* Hide deprecated items in the docs: This could be done either by putting them into a linked extra page or by adding a "show deprecated" checkbox that may be default be checked or not, depending on who you ask. This will however confuse people, who see the deprecated APIs in some code, but cannot find them in the docs anymore +* Allow to distinguish "soft" and "hard" deprecation, so that an API can be marked as "soft" deprecated to dissuade new uses before hard deprecation is decided. Allowing people to specify deprecation in future version appears to have much of the same benefits without needing a new attribute key. +* Decide deprecation on a per-case basis. This is what we do now. The proposal just adds a well-defined process to it +* Never deprecate anything. Evolve the API by adding stuff only. Rust would be crushed by the weight of its own cruft before 2.0 even has a chance to land. Users will be uncertain which APIs to use + +# Unresolved questions + +Should we allow library writers to use the same features for deprecating their API items? From 282ed615b673dae01bb5dbedd5c9c8daaae6bb30 Mon Sep 17 00:00:00 2001 From: llogiq Date: Wed, 3 Jun 2015 16:25:21 +0200 Subject: [PATCH 02/27] word wrapped, thanks to steveklabnik --- text/0000-deprecation.md | 93 ++++++++++++++++++++++++++++++++-------- 1 file changed, 75 insertions(+), 18 deletions(-) diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md index ace7e7e7d24..7a48f1d055a 100644 --- a/text/0000-deprecation.md +++ b/text/0000-deprecation.md @@ -5,38 +5,95 @@ # Summary -There has been an ongoing [discussion on internals](https://internals.rust-lang.org/t/thoughts-on-aggressive-deprecation-in-libstd/2176/55) about how we are going to evolve the standard library. This RFC tries to condense the consensus +There has been an ongoing [discussion on internals](https://internals.rust-lang.org/t/thoughts-on-aggressive-deprecation-in-libstd/2176/55) about how we are going to evolve the standard library. This RFC tries to condense the consensus. # Motivation We want to guide the deprecation efforts to allow std to evolve freely to get the best possible API while ensuring minimum-breakage backwards compatibility for users and allow std authors to remove API items for a given version of Rust. Basically have our cake and eat it. Yum, cake. -Of course we cannot really keep and remove a feature at the same time. To square this circle, we can follow the process outlined herein. +Of course we cannot really keep and remove a feature at the same time. +To square this circle, we can follow the process outlined herein. # Detailed design -We already declare deprecation in terms of Rust versions (like "1.0", "1.2"). The current attribute looks like `#[deprecated(since = "1.0.0", reason="foo")]`. This should be extended to add an optional `removed_at` key, to state that the item should be made inaccessible at that version. Note that while this allows for marking items as deprecated, there is absolutely no provision to actually *remove* items. In fact this proposal bans removing an API type outright, unless security concerns are deemed more important than the resulting breakage from removing it or the API item has some fault that means it cannot be used correctly at all (thus leaving the API in place would result in the same level of breakage than removing it). - -Currently every rustc version implements only its own version, having multiple versions is possible using something like multirust, though this does not work within a build. Also currently rustc versions do not guarantee interoperability. This RFC aims to change this situation. - -First, crates should state their target version using a `#![version = "1.0.0"]` attribute. Cargo should insert the current rust version by default on `cargo new` and *warn* if no version is defined on all other commands. It may optionally *note* that the specified target version is outdated on `cargo package`. [crates.io](https://crates.io) may deny packages that do not declare a version to give the target version requirement more weight to library authors. Cargo should also be able to hold back a new library version if its declared target version is newer than the rust version installed on the system. In those cases, cargo should emit a warning urging the user to upgrade their rust installation. - -`rustc` should use this target version definition to check for deprecated items. If no target version is defined, deprecation checking is deactivated (as we cannot assume a specific rust version), however a warning stating the same should be issued (as with cargo – we should probably make cargo not warn on build to get rid of duplicate warnings). Otherwise, use of API items whose `since` attribute is less or equal to the target version of the crate should trigger a warning, while API items whose `removed_at` attribute is less or equal to the target version should trigger an error. - -`rustdoc` should mark deprecated APIs as such (e.g. make them in a lighter gray font) and relegate removed APIs to a section below all others (and that may be hidden via a checkbox). We should not completely remove the documentation, as users of libraries that target old versions may still have a use for them, but neither should we let them clutter the docs. +We already declare deprecation in terms of Rust versions (like "1.0", +"1.2"). The current attribute looks like `#[deprecated(since = "1.0.0", +reason="foo")]`. This should be extended to add an optional +`removed_at` key, to state that the item should be made inaccessible at +that version. Note that while this allows for marking items as +deprecated, there is purposely no provision to actually *remove* items. +In fact this proposal bans removing an API type outright, unless +security concerns are deemed more important than the resulting breakage +from removing it or the API item has some fault that means it cannot be +used correctly at all (thus leaving the API in place would result in +the same level of breakage than removing it). + +Currently every rustc version implements only its own version, having +multiple versions is possible using something like multirust, though +this does not work within a build. Also currently rustc versions do not +guarantee interoperability. This RFC aims to change this situation. + +First, crates should state their target version using a `#![version = +"1.0.0"]` attribute. Cargo should insert the current rust version by +default on `cargo new` and *warn* if no version is defined on all other +commands. It may optionally *note* that the specified target version is +outdated on `cargo package`. [crates.io](https://crates.io) may deny +packages that do not declare a version to give the target version +requirement more weight to library authors. Cargo should also be able +to hold back a new library version if its declared target version is +newer than the rust version installed on the system. In those cases, +cargo should emit a warning urging the user to upgrade their rust +installation. + +`rustc` should use this target version definition to check for +deprecated items. If no target version is defined, deprecation checking +is deactivated (as we cannot assume a specific rust version), however a +warning stating the same should be issued (as with cargo – we should +probably make cargo not warn on build to get rid of duplicate +warnings). Otherwise, use of API items whose `since` attribute is less +or equal to the target version of the crate should trigger a warning, +while API items whose `removed_at` attribute is less or equal to the +target version should trigger an error. + +`rustdoc` should mark deprecated APIs as such (e.g. make them in a +lighter gray font) and relegate removed APIs to a section below all +others (and that may be hidden via a checkbox). We should not +completely remove the documentation, as users of libraries that target +old versions may still have a use for them, but neither should we let +them clutter the docs. # Drawbacks -By requiring full backwards-compatibility, we will never be able to actually remove stuff from the APIs, which will probably lead to some bloat. +By requiring full backwards-compatibility, we will never be able to +actually remove stuff from the APIs, which will probably lead to some +bloat. Other successful languages have lived with this for multiple +decades, so it appears the tradeoff has seen some confirmation already. # Alternatives -* Follow a more agressive strategy that actually removes stuff from the API. This would make it easier for the libstd creators at some cost for library and application writers, as they are required to keep up to date or face breakage -* Hide deprecated items in the docs: This could be done either by putting them into a linked extra page or by adding a "show deprecated" checkbox that may be default be checked or not, depending on who you ask. This will however confuse people, who see the deprecated APIs in some code, but cannot find them in the docs anymore -* Allow to distinguish "soft" and "hard" deprecation, so that an API can be marked as "soft" deprecated to dissuade new uses before hard deprecation is decided. Allowing people to specify deprecation in future version appears to have much of the same benefits without needing a new attribute key. -* Decide deprecation on a per-case basis. This is what we do now. The proposal just adds a well-defined process to it -* Never deprecate anything. Evolve the API by adding stuff only. Rust would be crushed by the weight of its own cruft before 2.0 even has a chance to land. Users will be uncertain which APIs to use +* Follow a more agressive strategy that actually removes stuff from the +API. This would make it easier for the libstd creators at some cost for +library and application writers, as they are required to keep up to +date or face breakage * Hide deprecated items in the docs: This could +be done either by putting them into a linked extra page or by adding a +"show deprecated" checkbox that may be default be checked or not, +depending on who you ask. This will however confuse people, who see the +deprecated APIs in some code, but cannot find them in the docs anymore +* Allow to distinguish "soft" and "hard" deprecation, so that an API +can be marked as "soft" deprecated to dissuade new uses before hard +deprecation is decided. Allowing people to specify deprecation in +future version appears to have much of the same benefits without +needing a new attribute key. * Decide deprecation on a per-case basis. +This is what we do now. The proposal just adds a well-defined process +to it * Never deprecate anything. Evolve the API by adding stuff only. +Rust would be crushed by the weight of its own cruft before 2.0 even +has a chance to land. Users will be uncertain which APIs to use * We +could extend the deprecation feature to cover libraries. As Cargo.toml +already defines the target versions of dependencies (unless declared as +`"*"`), we could use much of the same machinery to allow library +authors to join the process # Unresolved questions -Should we allow library writers to use the same features for deprecating their API items? +Should we allow library writers to use the same features for +deprecating their API items? From 94acabc8910139983f22e473d8aa517dfcdc7617 Mon Sep 17 00:00:00 2001 From: llogiq Date: Wed, 3 Jun 2015 16:30:39 +0200 Subject: [PATCH 03/27] clarified how cargo gets rust version --- text/0000-deprecation.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md index 7a48f1d055a..c7e84780a3f 100644 --- a/text/0000-deprecation.md +++ b/text/0000-deprecation.md @@ -37,7 +37,11 @@ First, crates should state their target version using a `#![version = "1.0.0"]` attribute. Cargo should insert the current rust version by default on `cargo new` and *warn* if no version is defined on all other commands. It may optionally *note* that the specified target version is -outdated on `cargo package`. [crates.io](https://crates.io) may deny +outdated on `cargo package`. To get the current rust version, cargo +could query rustc -V (with some postprocessing) or use some as yet +undefined symbol exported by the rust libraries. + +[crates.io](https://crates.io) may deny packages that do not declare a version to give the target version requirement more weight to library authors. Cargo should also be able to hold back a new library version if its declared target version is From 043d6a62a0322da374a3a3d2351e1445e29cc211 Mon Sep 17 00:00:00 2001 From: llogiq Date: Thu, 4 Jun 2015 16:06:09 +0200 Subject: [PATCH 04/27] Added paragraph on how rustc should handle future target versions --- text/0000-deprecation.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md index c7e84780a3f..e07679259bd 100644 --- a/text/0000-deprecation.md +++ b/text/0000-deprecation.md @@ -57,7 +57,11 @@ probably make cargo not warn on build to get rid of duplicate warnings). Otherwise, use of API items whose `since` attribute is less or equal to the target version of the crate should trigger a warning, while API items whose `removed_at` attribute is less or equal to the -target version should trigger an error. +target version should trigger an error. + +Also if the target definition has a higher version than `rustc`, it +should warn that it probably has to be updated in order to build the +crate. `rustdoc` should mark deprecated APIs as such (e.g. make them in a lighter gray font) and relegate removed APIs to a section below all From 2f3acfb81bf1f0dc157cfe0e589a962f85c6c4f3 Mon Sep 17 00:00:00 2001 From: llogiq Date: Wed, 10 Jun 2015 07:17:06 +0200 Subject: [PATCH 05/27] Fleshed out the Motivation section a bit --- text/0000-deprecation.md | 61 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md index e07679259bd..37b219f9658 100644 --- a/text/0000-deprecation.md +++ b/text/0000-deprecation.md @@ -5,14 +5,65 @@ # Summary -There has been an ongoing [discussion on internals](https://internals.rust-lang.org/t/thoughts-on-aggressive-deprecation-in-libstd/2176/55) about how we are going to evolve the standard library. This RFC tries to condense the consensus. +There has been an ongoing [discussion on +internals](https://internals.rust-lang.org/t/thoughts-on-aggressive-deprecation-in-libstd/2176/55) +about how we are going to evolve the standard library. This RFC tries +to condense the consensus. -# Motivation +As a starting point, the current deprecation feature allows a developer +to annotate their API items with `#[deprecated(since="1.1.0")]` and +have suitable warnings shown if the feature is used. -We want to guide the deprecation efforts to allow std to evolve freely to get the best possible API while ensuring minimum-breakage backwards compatibility for users and allow std authors to remove API items for a given version of Rust. Basically have our cake and eat it. Yum, cake. +# Motivation -Of course we cannot really keep and remove a feature at the same time. -To square this circle, we can follow the process outlined herein. +We want to: + +1. evolve the `std` API, including making items unavailable with new +versions +2. with minimal -- next to no -- breakage +3. be able to plug security/safety holes +4. avoid confusing users +5. stay backwards-compatible so people can continue to use dependencies +written for older versions (except where point 3. forbids this) +6. give users sensible defaults +7. and an update plan when they want to use a more current version + +This was quite short, so let me explain a bit: We want Rust to be +successful, and since the 1.0.0 release, there is an expectation of +stability. Therefore the first order of business when evolving the +`std` API is: **Don't break people's code**. + +In practice there will be some qualification, e.g. if fixing a security +hole requires breaking an API, it is nonetheless acceptable, because +the API was broken to begin with, as is code using it. So breaking this +code is acceptable. + +On the other hand, we really want to make features inaccessible in a +newer version, not just mark them as deprecated. Otherwise we would +bloat our APIs with deprecated features that no one uses (see Java). To +do this, it's not enough to hide the feature from the docs, as that +would be confusing (see point 4.) to those who encounter a hidden API. + +Not breaking code also mean we do not want to have the deprecation +feature interfere with a project's dependencies, which would teach +people to disable or ignore the warnings until their builds break. On +the other hand, we don't want to have all unavailable APIs show up +for library writers, as that -- apart from defeating the purpose of the +deprecation feature -- would create a confusing mirror world, +which is directly in conflict to point 4. + +We also want the feature to be *usable* to the programmer, therefore +any additional code we require should be minimal. If the feature is too +obscure, or too complicated to use, people will just +`#![allow(deprecate)]` and complain when their build finally breaks. + +Note that we expect many more *users* than *writers* of the `std` APIs, +so the wants of the former should count higher than those of the latter. + +Ideally, this can be done so that all parts play well together: Cargo +could help with setup (and possibly reporting), rustc warnings / error +reporting should be extended to inform people of pending or active +deprecations, rustdoc needs some way of reflecting the API lifecycle. # Detailed design From d95558d8dad8b97f8cf65b93fd6b7c5683badfc9 Mon Sep 17 00:00:00 2001 From: llogiq Date: Thu, 11 Jun 2015 23:30:35 +0200 Subject: [PATCH 06/27] added future/legacy flags to alternatives --- text/0000-deprecation.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md index 37b219f9658..eef3c8b6c74 100644 --- a/text/0000-deprecation.md +++ b/text/0000-deprecation.md @@ -130,6 +130,15 @@ decades, so it appears the tradeoff has seen some confirmation already. # Alternatives +* Opt-in and / or opt-out "feature-flags" (e.g. `#[legacy(..)]` for +opting out of a change) was suggested. The big problem is that this +relies on the user being able to change their dependencies, which may +not be possible for legal, organizational or other reasons. In +contrast, a defined target version doesn't ever need to change. +Depending on the specific case, it may be useful to allow a combination +of `#![legacy(..)]`, `#![future(..)]` and `#![target(..)]` where each +API version can declare the currently active feature and permit or +forbid use of the opt-in/out flags. * Follow a more agressive strategy that actually removes stuff from the API. This would make it easier for the libstd creators at some cost for library and application writers, as they are required to keep up to @@ -155,4 +164,5 @@ authors to join the process # Unresolved questions Should we allow library writers to use the same features for -deprecating their API items? +deprecating their API items? I think we should at least make sure that +our design and implementation allow this in the future. From 06b4163177924e1f1c20df145f541279711ce02c Mon Sep 17 00:00:00 2001 From: llogiq Date: Thu, 11 Jun 2015 23:45:45 +0200 Subject: [PATCH 07/27] More explanation about security issues --- text/0000-deprecation.md | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md index eef3c8b6c74..ac8dbd90269 100644 --- a/text/0000-deprecation.md +++ b/text/0000-deprecation.md @@ -33,16 +33,24 @@ successful, and since the 1.0.0 release, there is an expectation of stability. Therefore the first order of business when evolving the `std` API is: **Don't break people's code**. -In practice there will be some qualification, e.g. if fixing a security -hole requires breaking an API, it is nonetheless acceptable, because -the API was broken to begin with, as is code using it. So breaking this -code is acceptable. - -On the other hand, we really want to make features inaccessible in a -newer version, not just mark them as deprecated. Otherwise we would -bloat our APIs with deprecated features that no one uses (see Java). To -do this, it's not enough to hide the feature from the docs, as that -would be confusing (see point 4.) to those who encounter a hidden API. +In practice there will be some qualification, e.g. if an API is +*inherently* unsafe, it should be acceptable to remove it completely, +as any code using it was in fact broken to begin with. Therefore it is +acceptable to make this code stop working altogether. + +On the other hand, if an API permits unsafe uses, and a safer +alternative is available, we may want to *retroactively deprecate* it, +so that people will get warnings even if they specified an older target +version. We may want to have a different kind of warning than the +standard deprecation warning, as there are already some crates (e.g. +compiletest.rs) on crates.io that declare `#![deny(deprecate)]`, so +those warnings would turn to errors. + +We also really want to make features inaccessible in a newer version, +not just mark them as deprecated. Otherwise we would bloat our APIs +with deprecated features that no one uses (see Java). To do this, it's +not enough to hide the feature from the docs, as that would be +confusing (see point 4.) to those who encounter a hidden API. Not breaking code also mean we do not want to have the deprecation feature interfere with a project's dependencies, which would teach From 1764038ed2eb83737a0bb671587c8798f46a92aa Mon Sep 17 00:00:00 2001 From: llogiq Date: Mon, 15 Jun 2015 07:19:27 +0200 Subject: [PATCH 08/27] Clarified the paragraph about cargo/crates.io, also added Policy section --- text/0000-deprecation.md | 71 +++++++++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 20 deletions(-) diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md index ac8dbd90269..4957eb6ddd4 100644 --- a/text/0000-deprecation.md +++ b/text/0000-deprecation.md @@ -81,32 +81,49 @@ reason="foo")]`. This should be extended to add an optional `removed_at` key, to state that the item should be made inaccessible at that version. Note that while this allows for marking items as deprecated, there is purposely no provision to actually *remove* items. -In fact this proposal bans removing an API type outright, unless -security concerns are deemed more important than the resulting breakage -from removing it or the API item has some fault that means it cannot be -used correctly at all (thus leaving the API in place would result in -the same level of breakage than removing it). +In fact this proposal strongly advises not to remove an API type, +unless security concerns are deemed more important than the resulting +breakage from removing it or the API item has some fault that means it +cannot be used correctly at all (thus leaving the API in place would +result in the same level of breakage than removing it). Currently every rustc version implements only its own version, having multiple versions is possible using something like multirust, though this does not work within a build. Also currently rustc versions do not guarantee interoperability. This RFC aims to change this situation. -First, crates should state their target version using a `#![version = -"1.0.0"]` attribute. Cargo should insert the current rust version by -default on `cargo new` and *warn* if no version is defined on all other -commands. It may optionally *note* that the specified target version is -outdated on `cargo package`. To get the current rust version, cargo -could query rustc -V (with some postprocessing) or use some as yet -undefined symbol exported by the rust libraries. - -[crates.io](https://crates.io) may deny -packages that do not declare a version to give the target version -requirement more weight to library authors. Cargo should also be able -to hold back a new library version if its declared target version is -newer than the rust version installed on the system. In those cases, -cargo should emit a warning urging the user to upgrade their rust -installation. +First, crates should state their target version using a +`#![target(std= "1.2.0"]` attribute on the main module. The version +string format is the one that cargo currently uses. + +Cargo should insert the current rust version by default on `cargo new` +and *warn* if no version is defined on all other commands. It may +optionally *note* if the specified target version is outdated on `cargo +package` or even `cargo build --release`. To get the current rust +version, cargo could query rustc -V (with some postprocessing) or use a +symbol exported by the rust libraries (e.g. `rustc::target_version`). + +Cargo should also be able to 'hold back' a new library version if its +declared target version is newer than the rust version installed on the +system. In those cases, cargo should emit a warning urging the user to +upgrade their rust installation. + +In the case of packages on crates.io, we could offer a mapping of +target versions to crate versions for each crate, so the corresponding +crate version can directly be used without further search. + +In the case of crates from git, the only reliable way to implement it +is to search the history for a suitable target version definition. Note +that we'd expect the target version to go up monotonously, so a binary +search should be possible, also we can filter out all commits that do +not touch lib.rs/mod.rs. + +This is a very complex feature to implement, so stopping with an error +and referring to the user to do the search is an acceptable option. + +[crates.io](https://crates.io) may start denying new packages that do +not declare a version to give the target version requirement more +weight to library authors. `rustc` should use this target version definition to check for deprecated items. If no target version is defined, deprecation checking @@ -129,6 +146,20 @@ completely remove the documentation, as users of libraries that target old versions may still have a use for them, but neither should we let them clutter the docs. +## Policy + +Even if this proposal reduces breakage arising from new versions +considerably, we should still exercise some care on evolving the APIs. +We already have a `beta` and `nightly` release train representing +future versions, this should be taken into account. + +In general, the Tarzan principle should be followed where applicable +(First grab a vine, *then* let go of the previous vine). In terms of +API evolution, this means not deprecating a feature before a +replacement has been stabilized. It is still possible to deprecate a +feature in a future version, to inform users of its impending +departure. + # Drawbacks By requiring full backwards-compatibility, we will never be able to From ef9c63207a35b89477a0b027a3610b0d5a61e700 Mon Sep 17 00:00:00 2001 From: llogiq Date: Mon, 15 Jun 2015 17:52:51 +0200 Subject: [PATCH 09/27] clarification on insecurity and future-proofing --- text/0000-deprecation.md | 83 +++++++++++++++++++++++++++++----------- 1 file changed, 60 insertions(+), 23 deletions(-) diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md index 4957eb6ddd4..5c8c26dfbdd 100644 --- a/text/0000-deprecation.md +++ b/text/0000-deprecation.md @@ -21,7 +21,7 @@ We want to: 1. evolve the `std` API, including making items unavailable with new versions 2. with minimal -- next to no -- breakage -3. be able to plug security/safety holes +3. be able to plug security/safety holes 4. avoid confusing users 5. stay backwards-compatible so people can continue to use dependencies written for older versions (except where point 3. forbids this) @@ -33,18 +33,20 @@ successful, and since the 1.0.0 release, there is an expectation of stability. Therefore the first order of business when evolving the `std` API is: **Don't break people's code**. -In practice there will be some qualification, e.g. if an API is -*inherently* unsafe, it should be acceptable to remove it completely, -as any code using it was in fact broken to begin with. Therefore it is -acceptable to make this code stop working altogether. - -On the other hand, if an API permits unsafe uses, and a safer -alternative is available, we may want to *retroactively deprecate* it, -so that people will get warnings even if they specified an older target -version. We may want to have a different kind of warning than the +In practice there will be some qualification, e.g. if an API is +*inherently* unsafe, it should be acceptable make it completely +unavailable, as any code using it was in fact broken to begin with. +Therefore it is acceptable to make this code stop working altogether, +as long as the resulting error is not too confusing (which again means +we should make the item inaccessible instead of removing it). + +If an API permits unsafe uses, and a safer alternative is available, we +may want to mark it as insecure in addition to deprecating it, so that +people will get warnings even if they specified an older target +version. We want to have a different kind of warning than the standard deprecation warning, as there are already some crates (e.g. compiletest.rs) on crates.io that declare `#![deny(deprecate)]`, so -those warnings would turn to errors. +those warnings would turn to errors. We also really want to make features inaccessible in a newer version, not just mark them as deprecated. Otherwise we would bloat our APIs @@ -126,18 +128,35 @@ not declare a version to give the target version requirement more weight to library authors. `rustc` should use this target version definition to check for -deprecated items. If no target version is defined, deprecation checking -is deactivated (as we cannot assume a specific rust version), however a -warning stating the same should be issued (as with cargo – we should -probably make cargo not warn on build to get rid of duplicate -warnings). Otherwise, use of API items whose `since` attribute is less -or equal to the target version of the crate should trigger a warning, -while API items whose `removed_at` attribute is less or equal to the -target version should trigger an error. - -Also if the target definition has a higher version than `rustc`, it -should warn that it probably has to be updated in order to build the -crate. +deprecated items. If the target version is specified, use of API items +whose `since` attribute is less or equal to the target version of the +crate should trigger a warning, while API items whose `removed_at` +attribute is less or equal to the target version should trigger an +error. + +We can also define a `future deprecation` lint set to `Allow` by +default to allow people being proactive about items that are going to +be deprecated. + +Also if the target definition has a higher version than `rustc`, it +should briefly warn that it probably has to be updated in order to +build the crate. However, `rustc` should try to build the code anyway; +further errors may give the user additional information. + +If *no* target version is defined, deprecation checking is deactivated +(as we cannot assume a specific rust version), however a note +stating the same should be printed (as with cargo – we should probably +make cargo not warn on build to get rid of duplicate warnings). Since +all current code comes without a target version, we have to assume +a minimal version 1.0.0. + +In addition to the note, the `std` authors could opt to create a new +`#[since="1.2.0"]` attribute, which would allow rustc to infer the +minimal target version of some code from the API features it uses in +absence of a specified version. Deprecation warnings/errors should then +refer to the inferred target versions as well as the APIs that led to +the inference of the latest version (at least perhaps on calling +`rustc` with `-v`). `rustdoc` should mark deprecated APIs as such (e.g. make them in a lighter gray font) and relegate removed APIs to a section below all @@ -146,6 +165,24 @@ completely remove the documentation, as users of libraries that target old versions may still have a use for them, but neither should we let them clutter the docs. +## Dealing with insecure items + +Since just removing insecure items, though tempting, would lead to user +confusion, a new `#[insecure(reason = "...")]` attribute should be +added to all insecure API items. An `insecure_api` lint that by default +raises `Error` can catch all uses of those items. To distinguish +between items *some uses of which* may be insecure and *inherently* +insecure items, either a second entry `inherent = true` could be added +or a `#[maybe_insecure(reason = "...")]` annotation could take the +latter part. + +The rationale for defining a separate attribute is that it avoids +mixing separate concerns (versioning and security), and that we want to +allow warnings/errors on dependencies regardless of specified target +versions. It also allows us to show the reason (from the attr) in the +lint message, which will be specific to the insecurity at hand and +hopefully be helpful to the user. + ## Policy Even if this proposal reduces breakage arising from new versions From 9d18ae4152b8932232349e523611eae1475cb836 Mon Sep 17 00:00:00 2001 From: llogiq Date: Wed, 17 Jun 2015 13:28:20 +0200 Subject: [PATCH 10/27] added Cargo.toml-based target to Alternatives section --- text/0000-deprecation.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md index 5c8c26dfbdd..2dfef8e7c95 100644 --- a/text/0000-deprecation.md +++ b/text/0000-deprecation.md @@ -206,11 +206,19 @@ decades, so it appears the tradeoff has seen some confirmation already. # Alternatives -* Opt-in and / or opt-out "feature-flags" (e.g. `#[legacy(..)]` for -opting out of a change) was suggested. The big problem is that this -relies on the user being able to change their dependencies, which may -not be possible for legal, organizational or other reasons. In -contrast, a defined target version doesn't ever need to change. +* Have a flag in `Cargo.toml` instead of the crate root. This however +requires an argument to `rustc`, because Cargo (in addition to those +not using it) somehow has to pass it to `rustc`. Requiring such an +argument on every non-cargoized build would increase room for error and +thus pessimize usability. Also apart from availability of dependencies, +which arguably is Cargo's main raison d'être, we currently do not have +a precedent where Cargo.toml has direct effect on the working of a +crate's code. +* Opt-in and / or opt-out "feature-flags" (e.g. `#[legacy(..)]`) was +suggested. The big problem is that this relies on the user being able +to change their dependencies, which may not be possible for legal, +organizational or other reasons. In contrast, a defined target version +doesn't ever need to change. Depending on the specific case, it may be useful to allow a combination of `#![legacy(..)]`, `#![future(..)]` and `#![target(..)]` where each API version can declare the currently active feature and permit or From e59e20c3332d83eb75de7f5857c6d4ab44ad5c09 Mon Sep 17 00:00:00 2001 From: llogiq Date: Fri, 19 Jun 2015 18:12:04 +0200 Subject: [PATCH 11/27] Almost complete rewrite. --- text/0000-deprecation.md | 423 +++++++++++++++++++-------------------- 1 file changed, 207 insertions(+), 216 deletions(-) diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md index 2dfef8e7c95..7ebcbf537f8 100644 --- a/text/0000-deprecation.md +++ b/text/0000-deprecation.md @@ -1,252 +1,243 @@ -- Feature Name: A plan for deprecating APIs within Rust +- Feature Name: Target Version - Start Date: 2015-06-03 - RFC PR: - Rust Issue: # Summary -There has been an ongoing [discussion on +This RFC proposes a small number of extensions to improve the user +experience around different rust versions, while at the same time +giving Rust developers more freedom to make changes without breaking +anyones code. + +Namely the following items: + +1. Add a `--target=`* command line argument to rustc. +This will be used for deprecation checking and for selecting code paths +in the compiler. +2. Add an (optional for now) `rust = "..."` dependency to Cargo.toml, +which `cargo new` pre-fills with the current rust version +3. Allow `std` APIs to declare an `#[insecure(level="Warn", reason="...")]` +attribute that will produce a warning or error, depending on level, +that cannot be switched off (even with `-Awarning`) +4. Add a `removed_at="..."` item to `#[deprecated]` attributes that +allows making API items unavailable starting from certain target +versions. +5. Add a number of warnings to steer users in the direction of using the +most recent Rust version that makes sense to them, while making it +easy for library writers to support a wide range of Rust versions + +## Background + +A good number of policies and mechanisms around versioning regarding +the Rust language and APIs have already been submitted, some accepted. +As a background, in no particular order: + +* [#0572 Feature gates](https://github.com/rust-lang/rfcs/blob/master/text/0572-rustc-attribute.md) +* [#1105 API Evolution](https://github.com/rust-lang/rfcs/blob/master/text/1105-api-evolution.md) +* [#1122 Language SemVer](https://github.com/rust-lang/rfcs/blob/master/text/1122-language-semver.md) +* [#1150 Rename Attribute](https://github.com/rust-lang/pull/1150) + +In addition, there has been an ongoing [discussion on internals](https://internals.rust-lang.org/t/thoughts-on-aggressive-deprecation-in-libstd/2176/55) -about how we are going to evolve the standard library. This RFC tries -to condense the consensus. +about how we are going to evolve the standard library, which this +proposal is mostly based on. -As a starting point, the current deprecation feature allows a developer -to annotate their API items with `#[deprecated(since="1.1.0")]` and -have suitable warnings shown if the feature is used. +Finally, the recent discussion on the first breaking change +([RFC PR #1156 Adjust default object bounds](https://github.com/rust-lang/rfcs/pull/1156)) +has made it clear that we need a more flexible way of dealing with +(breaking) changes. + +The current setup allows the `std` API to declare `#[unstable]` and +`#[deprecated]` flags, whereas users can opt-in to unstable features +with `#![feature]` flags that are customarily added to the crate root. +On usage of deprecated APIs, a warning is shown unless suppressed. +`cargo` does this for dependencies by default by calling `rustc` with +the `-Awarnings` argument. # Motivation -We want to: - -1. evolve the `std` API, including making items unavailable with new -versions -2. with minimal -- next to no -- breakage -3. be able to plug security/safety holes -4. avoid confusing users -5. stay backwards-compatible so people can continue to use dependencies -written for older versions (except where point 3. forbids this) -6. give users sensible defaults -7. and an update plan when they want to use a more current version - -This was quite short, so let me explain a bit: We want Rust to be -successful, and since the 1.0.0 release, there is an expectation of -stability. Therefore the first order of business when evolving the -`std` API is: **Don't break people's code**. - -In practice there will be some qualification, e.g. if an API is -*inherently* unsafe, it should be acceptable make it completely -unavailable, as any code using it was in fact broken to begin with. -Therefore it is acceptable to make this code stop working altogether, -as long as the resulting error is not too confusing (which again means -we should make the item inaccessible instead of removing it). - -If an API permits unsafe uses, and a safer alternative is available, we -may want to mark it as insecure in addition to deprecating it, so that -people will get warnings even if they specified an older target -version. We want to have a different kind of warning than the -standard deprecation warning, as there are already some crates (e.g. -compiletest.rs) on crates.io that declare `#![deny(deprecate)]`, so -those warnings would turn to errors. - -We also really want to make features inaccessible in a newer version, -not just mark them as deprecated. Otherwise we would bloat our APIs -with deprecated features that no one uses (see Java). To do this, it's -not enough to hide the feature from the docs, as that would be -confusing (see point 4.) to those who encounter a hidden API. - -Not breaking code also mean we do not want to have the deprecation -feature interfere with a project's dependencies, which would teach -people to disable or ignore the warnings until their builds break. On -the other hand, we don't want to have all unavailable APIs show up -for library writers, as that -- apart from defeating the purpose of the -deprecation feature -- would create a confusing mirror world, -which is directly in conflict to point 4. - -We also want the feature to be *usable* to the programmer, therefore -any additional code we require should be minimal. If the feature is too -obscure, or too complicated to use, people will just -`#![allow(deprecate)]` and complain when their build finally breaks. - -Note that we expect many more *users* than *writers* of the `std` APIs, -so the wants of the former should count higher than those of the latter. - -Ideally, this can be done so that all parts play well together: Cargo -could help with setup (and possibly reporting), rustc warnings / error -reporting should be extended to inform people of pending or active -deprecations, rustdoc needs some way of reflecting the API lifecycle. +## 1. Language / `std` Evolution + +The following motivates items 1 and 2 (and to a lesser extent 5) + +With the current setup, we can already evolve the language and APIs, +albeit in a very limited way. For example, it is virtually impossible +to actually remove an API, because that would break code. Even minor +breaking changes (as [RFC PR #1156](https://github.com/rust-lang/rfcs/pull/1156) +cited above) generate huge discussion, because they call the general +stability of the language and environment into question. + +The problem with opt-out, as defined by +[RFC #1122](https://github.com/rust-lang/rfcs/blob/master/text/1122-language-semver.md) +is that code which previously compiled without problems stops working +on a Rust upgrade, and requires manual intervention to get working +again. + +This has the long-term potential of fragmenting the Rust ecosystem into +crates/projects using certain Rust versions and thus should be avoided +at some (but not all) cost. + +Note that a similar problem exists with deprecation as defined: People +may get used to deprecation warnings or just turn them off until their +build breaks. Worse, the current setup creates a mirror world for +libraries, in which deprecation doesn't exist! + +## 2. User Experience + +The following motivates items 2, 4 and 5. + +Currently, there is no way to make an API item unavailable via +deprecation. This means the API will only ever expand, with a lot of +churn (case in point, the Java language has about 20% deprecated API +surface [citation needed]). To better lead users to the right APIs and +to allow for more effective language/API evolution, this proposal adds +the `removed_at=""` item to the `#[deprecated]` attribute. + +This allows us to effectively remove an API item from a certain target +version while avoiding breaking code written for older target versions. + +Also rustc can emit better error messages than it could were the API +items actually removed. In the best case, the error messages can steer +the user to a working replacement. + +We want to avoid users setting `#[allow(deprecate)]` on their code to +get rid of the warnings. On the other hand, there have been instances +of failing builds because code was marked with `#![deny(deprecate)]`, +namely `compiletest.rs` and all crates using it. This shows that the +current system has room for improvement. + +We want to avoid failing builds because of wrong or missing target +version definition. Therefor supplying useful defaults is of the +essence. + +The documentation can be optionally reduced to items relating to the +current target version (e.g. by a switch), or deprecated items +relegated to a separate space, to reduce clutter and possible user +confusion. + +## 3. Security Considerations + +I believe that *should* a security issue in one of our APIs be found, +a swift and effective response will be required, and the current rules +make no provisions for it. Thus proposal item 3. # Detailed design -We already declare deprecation in terms of Rust versions (like "1.0", -"1.2"). The current attribute looks like `#[deprecated(since = "1.0.0", -reason="foo")]`. This should be extended to add an optional -`removed_at` key, to state that the item should be made inaccessible at -that version. Note that while this allows for marking items as -deprecated, there is purposely no provision to actually *remove* items. -In fact this proposal strongly advises not to remove an API type, -unless security concerns are deemed more important than the resulting -breakage from removing it or the API item has some fault that means it -cannot be used correctly at all (thus leaving the API in place would -result in the same level of breakage than removing it). - -Currently every rustc version implements only its own version, having -multiple versions is possible using something like multirust, though -this does not work within a build. Also currently rustc versions do not -guarantee interoperability. This RFC aims to change this situation. - -First, crates should state their target version using a -`#![target(std= "1.2.0"]` attribute on the main module. The version -string format is the one that cargo currently uses. - -Cargo should insert the current rust version by default on `cargo new` -and *warn* if no version is defined on all other commands. It may -optionally *note* if the specified target version is outdated on `cargo -package` or even `cargo build --release`. To get the current rust -version, cargo could query rustc -V (with some postprocessing) or use a -symbol exported by the rust libraries (e.g. `rustc::target_version`). - -Cargo should also be able to 'hold back' a new library version if its -declared target version is newer than the rust version installed on the -system. In those cases, cargo should emit a warning urging the user to -upgrade their rust installation. - -In the case of packages on crates.io, we could offer a mapping of -target versions to crate versions for each crate, so the corresponding -crate version can directly be used without further search. - -In the case of crates from git, the only reliable way to implement it -is to search the history for a suitable target version definition. Note -that we'd expect the target version to go up monotonously, so a binary -search should be possible, also we can filter out all commits that do -not touch lib.rs/mod.rs. - -This is a very complex feature to implement, so stopping with an error -and referring to the user to do the search is an acceptable option. - -[crates.io](https://crates.io) may start denying new packages that do -not declare a version to give the target version requirement more -weight to library authors. - -`rustc` should use this target version definition to check for -deprecated items. If the target version is specified, use of API items -whose `since` attribute is less or equal to the target version of the -crate should trigger a warning, while API items whose `removed_at` -attribute is less or equal to the target version should trigger an -error. - -We can also define a `future deprecation` lint set to `Allow` by +Cargo parses the additional `rust = "..."` dependency as if it was a +library. The usual rules for version parsing apply. If no `rust` +dependency is supplied, it can either default to `*`. + +Cargo should also supply the current Rust version (which can be either +supplied by calling `rustc -V` or by linking to a rust library defining +a version object) on `cargo new`. Cargo supplies the given target +version to `rustc` via the `--target` command line argument. + +Cargo *may* also warn on `cargo package` if no `rust` version was +supplied. [crates.io](https://crates.io) *could* require a version +attribute on upload and display the required rust version on the site. + +One nice aspect of this is that `rust` looks just like yet another +dependency and effectively follows the same rules. + +`rustc` needs to accept the `--target ` command line argument. +If no argument is supplied, `rustc` defaults to its own version. The +same version syntax as Cargo applies: + +* `*` effectively means *any version*. For API items, it means +deprecation checking is disabled. For language changes, it means using +the 1.0.0 code paths (for now, we may opt to change this in the +future), because anything else would break all current code. +* `1.x` or e.g. `>=1.2.0` sets the target version to the minor version. +Deprecation checking and language code path selection occur relative +to the lowest given version. This might also affect stability handling, +though this RFC doesn't specify this as of yet. +* `1.0 - <2.0` as above, the *lowest* supplied version has to be +assumed for code path selection. However, deprecation checking should +assume the *highest* supplied version, if any. + +If the target version is *higher* than the current `rustc` version, +`rustc` should show a warning to suggest that it may need to be updated +in order to compile the crate and then try to compile the crate on a +best-effort basis. + +Optionally, we can define a `future deprecation` lint set to `Allow` by default to allow people being proactive about items that are going to -be deprecated. - -Also if the target definition has a higher version than `rustc`, it -should briefly warn that it probably has to be updated in order to -build the crate. However, `rustc` should try to build the code anyway; -further errors may give the user additional information. - -If *no* target version is defined, deprecation checking is deactivated -(as we cannot assume a specific rust version), however a note -stating the same should be printed (as with cargo – we should probably -make cargo not warn on build to get rid of duplicate warnings). Since -all current code comes without a target version, we have to assume -a minimal version 1.0.0. - -In addition to the note, the `std` authors could opt to create a new -`#[since="1.2.0"]` attribute, which would allow rustc to infer the -minimal target version of some code from the API features it uses in -absence of a specified version. Deprecation warnings/errors should then -refer to the inferred target versions as well as the APIs that led to -the inference of the latest version (at least perhaps on calling -`rustc` with `-v`). - -`rustdoc` should mark deprecated APIs as such (e.g. make them in a -lighter gray font) and relegate removed APIs to a section below all -others (and that may be hidden via a checkbox). We should not -completely remove the documentation, as users of libraries that target -old versions may still have a use for them, but neither should we let -them clutter the docs. - -## Dealing with insecure items - -Since just removing insecure items, though tempting, would lead to user -confusion, a new `#[insecure(reason = "...")]` attribute should be -added to all insecure API items. An `insecure_api` lint that by default -raises `Error` can catch all uses of those items. To distinguish -between items *some uses of which* may be insecure and *inherently* -insecure items, either a second entry `inherent = true` could be added -or a `#[maybe_insecure(reason = "...")]` annotation could take the -latter part. - -The rationale for defining a separate attribute is that it avoids -mixing separate concerns (versioning and security), and that we want to -allow warnings/errors on dependencies regardless of specified target -versions. It also allows us to show the reason (from the attr) in the -lint message, which will be specific to the insecurity at hand and -hopefully be helpful to the user. - -## Policy - -Even if this proposal reduces breakage arising from new versions -considerably, we should still exercise some care on evolving the APIs. -We already have a `beta` and `nightly` release train representing -future versions, this should be taken into account. - -In general, the Tarzan principle should be followed where applicable -(First grab a vine, *then* let go of the previous vine). In terms of -API evolution, this means not deprecating a feature before a -replacement has been stabilized. It is still possible to deprecate a -feature in a future version, to inform users of its impending -departure. +be deprecated. + +`rustc` should also show a warning or error, depending on level, on +encountering usage of API items marked as `#[insecure]`. The attribute +has two values: + +* `level` can either be `Warning` or `Error` and default to `Error` +* `reason` contains a description on why usage of this item was deemed + a security risk. This attribute is mandatory. + +While `rustdoc` already parses the deprecation flags, it should in +addition relegate items removed in the current version to a separate +area below the other documentation and optically mark them as removed. +We should not completely remove them, because that would confuse users +who see the API items in code written for older target versions. + +Also, `rustdoc` should show if API items are marked with `#[insecure]`, +including displaying the `reason` prominently. + +# Optional Extension: Legacy flags + +The `#[deprecated]` attribute could get an optional `legacy="xy` +entry, which could effectively group a set of APIs under the given +name. Users can then declare the `#[legacy]` flag as defined in +[RFC #1122](https://github.com/rust-lang/rfcs/blob/master/text/1122-language-semver.md) +to specifically allow usage of the grouped APIs, thus selectively +removing the deprecation warning. + +This would create a nice feature parity between language code paths and +`std` API deprecation checking. # Drawbacks By requiring full backwards-compatibility, we will never be able to actually remove stuff from the APIs, which will probably lead to some -bloat. Other successful languages have lived with this for multiple -decades, so it appears the tradeoff has seen some confirmation already. +bloat. However, the cost of maintaining the outdated APIs is far +outweighted by the benefits. Case in point: Other successful languages +have lived with this for multiple decades, so it appears the tradeoff +has seen some confirmation already. + +Cargo and `rustc` need some code to manage the additional rules. I +estimate the effort to be reasonably low. # Alternatives -* Have a flag in `Cargo.toml` instead of the crate root. This however -requires an argument to `rustc`, because Cargo (in addition to those -not using it) somehow has to pass it to `rustc`. Requiring such an -argument on every non-cargoized build would increase room for error and -thus pessimize usability. Also apart from availability of dependencies, -which arguably is Cargo's main raison d'être, we currently do not have -a precedent where Cargo.toml has direct effect on the working of a -crate's code. -* Opt-in and / or opt-out "feature-flags" (e.g. `#[legacy(..)]`) was -suggested. The big problem is that this relies on the user being able +* It was suggested that opt-in and opt-out (e.g. by `#[legacy(..)]`) +could be sufficient to work around any breaking code on API or language +changes. The big problem here is that this relies on the user being able to change their dependencies, which may not be possible for legal, organizational or other reasons. In contrast, a defined target version -doesn't ever need to change. +doesn't ever need to change + Depending on the specific case, it may be useful to allow a combination -of `#![legacy(..)]`, `#![future(..)]` and `#![target(..)]` where each -API version can declare the currently active feature and permit or -forbid use of the opt-in/out flags. +of `#![legacy(..)]`, `#![feature(..)]` and the target version where +each Rust version can declare the currently active feature set and +permit or forbid use of the opt-in/out flags + * Follow a more agressive strategy that actually removes stuff from the API. This would make it easier for the libstd creators at some cost for library and application writers, as they are required to keep up to -date or face breakage * Hide deprecated items in the docs: This could -be done either by putting them into a linked extra page or by adding a -"show deprecated" checkbox that may be default be checked or not, -depending on who you ask. This will however confuse people, who see the -deprecated APIs in some code, but cannot find them in the docs anymore +date or face breakage. The risk of breaking existing code makes this +strategy very unattractive + +* Hide deprecated items in the docs: This could be done either by +putting them into a linked extra page or by adding a "show deprecated" +checkbox that may be default be checked or not, depending on who you +ask. This will however confuse people, who see the deprecated APIs in +some code, but cannot find them in the docs anymore + * Allow to distinguish "soft" and "hard" deprecation, so that an API can be marked as "soft" deprecated to dissuade new uses before hard deprecation is decided. Allowing people to specify deprecation in future version appears to have much of the same benefits without -needing a new attribute key. * Decide deprecation on a per-case basis. -This is what we do now. The proposal just adds a well-defined process -to it * Never deprecate anything. Evolve the API by adding stuff only. -Rust would be crushed by the weight of its own cruft before 2.0 even -has a chance to land. Users will be uncertain which APIs to use * We -could extend the deprecation feature to cover libraries. As Cargo.toml -already defines the target versions of dependencies (unless declared as -`"*"`), we could use much of the same machinery to allow library -authors to join the process +needing a new attribute key. # Unresolved questions -Should we allow library writers to use the same features for -deprecating their API items? I think we should at least make sure that -our design and implementation allow this in the future. +I no longer have any. Please join the discussion to add yours. From 42af880bb795b767bea1ce515228ea5fa3be3d9c Mon Sep 17 00:00:00 2001 From: llogiq Date: Thu, 25 Jun 2015 09:45:14 +0200 Subject: [PATCH 12/27] Renamed --target to --target-version, reworded Cargo entry default --- text/0000-deprecation.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md index 7ebcbf537f8..11bb3156c8c 100644 --- a/text/0000-deprecation.md +++ b/text/0000-deprecation.md @@ -12,14 +12,15 @@ anyones code. Namely the following items: -1. Add a `--target=`* command line argument to rustc. +1. Add a `--target-version=`* command line argument to rustc. This will be used for deprecation checking and for selecting code paths in the compiler. 2. Add an (optional for now) `rust = "..."` dependency to Cargo.toml, which `cargo new` pre-fills with the current rust version -3. Allow `std` APIs to declare an `#[insecure(level="Warn", reason="...")]` -attribute that will produce a warning or error, depending on level, -that cannot be switched off (even with `-Awarning`) +3. Allow `std` APIs to declare an +`#[insecure(level="Warn", reason="...")]` attribute that will produce +a warning or error, depending on level, that cannot be switched off +(even with `-Awarning`) 4. Add a `removed_at="..."` item to `#[deprecated]` attributes that allows making API items unavailable starting from certain target versions. @@ -126,12 +127,12 @@ make no provisions for it. Thus proposal item 3. Cargo parses the additional `rust = "..."` dependency as if it was a library. The usual rules for version parsing apply. If no `rust` -dependency is supplied, it can either default to `*`. +dependency is supplied, it defaults to `*`. Cargo should also supply the current Rust version (which can be either supplied by calling `rustc -V` or by linking to a rust library defining a version object) on `cargo new`. Cargo supplies the given target -version to `rustc` via the `--target` command line argument. +version to `rustc` via the `--target-version` command line argument. Cargo *may* also warn on `cargo package` if no `rust` version was supplied. [crates.io](https://crates.io) *could* require a version @@ -140,9 +141,9 @@ attribute on upload and display the required rust version on the site. One nice aspect of this is that `rust` looks just like yet another dependency and effectively follows the same rules. -`rustc` needs to accept the `--target ` command line argument. -If no argument is supplied, `rustc` defaults to its own version. The -same version syntax as Cargo applies: +`rustc` needs to accept the `--target-version ` command line +argument. If no argument is supplied, `rustc` defaults to its own +version. The same version syntax as Cargo applies: * `*` effectively means *any version*. For API items, it means deprecation checking is disabled. For language changes, it means using From c4cf32589a8126e243d9839e864cb44627609d0b Mon Sep 17 00:00:00 2001 From: llogiq Date: Thu, 25 Jun 2015 23:58:46 +0200 Subject: [PATCH 13/27] added open question about cargo and detailed design about feature flag integration --- text/0000-deprecation.md | 41 ++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md index 11bb3156c8c..5b5138a3b67 100644 --- a/text/0000-deprecation.md +++ b/text/0000-deprecation.md @@ -12,11 +12,12 @@ anyones code. Namely the following items: -1. Add a `--target-version=`* command line argument to rustc. -This will be used for deprecation checking and for selecting code paths -in the compiler. +1. Add a `--target-version=`** command line argument to +rustc. This will be used for deprecation checking and for selecting +code paths in the compiler. 2. Add an (optional for now) `rust = "..."` dependency to Cargo.toml, -which `cargo new` pre-fills with the current rust version +which `cargo new` pre-fills with the current rust version +(alternatively this could be a package attribute) 3. Allow `std` APIs to declare an `#[insecure(level="Warn", reason="...")]` attribute that will produce a warning or error, depending on level, that cannot be switched off @@ -24,8 +25,8 @@ a warning or error, depending on level, that cannot be switched off 4. Add a `removed_at="..."` item to `#[deprecated]` attributes that allows making API items unavailable starting from certain target versions. -5. Add a number of warnings to steer users in the direction of using the -most recent Rust version that makes sense to them, while making it +5. Add a number of warnings to steer users in the direction of using +the most recent Rust version that makes sense to them, while making it easy for library writers to support a wide range of Rust versions ## Background @@ -39,8 +40,8 @@ As a background, in no particular order: * [#1122 Language SemVer](https://github.com/rust-lang/rfcs/blob/master/text/1122-language-semver.md) * [#1150 Rename Attribute](https://github.com/rust-lang/pull/1150) -In addition, there has been an ongoing [discussion on -internals](https://internals.rust-lang.org/t/thoughts-on-aggressive-deprecation-in-libstd/2176/55) +In addition, there has been an ongoing +[discussion on internals](https://internals.rust-lang.org/t/thoughts-on-aggressive-deprecation-in-libstd/2176/55) about how we are going to evolve the standard library, which this proposal is mostly based on. @@ -90,10 +91,13 @@ The following motivates items 2, 4 and 5. Currently, there is no way to make an API item unavailable via deprecation. This means the API will only ever expand, with a lot of -churn (case in point, the Java language has about 20% deprecated API -surface [citation needed]). To better lead users to the right APIs and -to allow for more effective language/API evolution, this proposal adds -the `removed_at=""` item to the `#[deprecated]` attribute. +churn (case in point, the Java language as of Version 8 lists 462 +deprecated constructs, including methods, constants and complete +classes, in relation to 4241 classes, this makes for about 10% of +deprecated API surface. Note that this is but a rough estimate). To +better lead users to the right APIs and to allow for more effective +language/API evolution, this proposal adds the `removed_at=""` +item to the `#[deprecated]` attribute. This allows us to effectively remove an API item from a certain target version while avoiding breaking code written for older target versions. @@ -104,7 +108,7 @@ the user to a working replacement. We want to avoid users setting `#[allow(deprecate)]` on their code to get rid of the warnings. On the other hand, there have been instances -of failing builds because code was marked with `#![deny(deprecate)]`, +of failing builds because code was marked with `#![deny(warnings)]`, namely `compiletest.rs` and all crates using it. This shows that the current system has room for improvement. @@ -147,7 +151,7 @@ version. The same version syntax as Cargo applies: * `*` effectively means *any version*. For API items, it means deprecation checking is disabled. For language changes, it means using -the 1.0.0 code paths (for now, we may opt to change this in the +the `1.0.0` code paths (for now, we may opt to change this in the future), because anything else would break all current code. * `1.x` or e.g. `>=1.2.0` sets the target version to the minor version. Deprecation checking and language code path selection occur relative @@ -166,6 +170,11 @@ Optionally, we can define a `future deprecation` lint set to `Allow` by default to allow people being proactive about items that are going to be deprecated. +`rustc` should resolve the `#[feature]` flags against the upper bound +of the specified target version instead the current version, but +default to the current version if no target version is specified or the +specified version has no upper bound. + `rustc` should also show a warning or error, depending on level, on encountering usage of API items marked as `#[insecure]`. The attribute has two values: @@ -185,7 +194,7 @@ including displaying the `reason` prominently. # Optional Extension: Legacy flags -The `#[deprecated]` attribute could get an optional `legacy="xy` +The `#[deprecated]` attribute could get an optional `legacy="xy"` entry, which could effectively group a set of APIs under the given name. Users can then declare the `#[legacy]` flag as defined in [RFC #1122](https://github.com/rust-lang/rfcs/blob/master/text/1122-language-semver.md) @@ -241,4 +250,4 @@ needing a new attribute key. # Unresolved questions -I no longer have any. Please join the discussion to add yours. +Should the rust = "" be a *dependency* or a package attribute? From 151d52357d170f7dfbb91bc9b09d0d4886d14067 Mon Sep 17 00:00:00 2001 From: llogiq Date: Fri, 26 Jun 2015 23:42:12 +0200 Subject: [PATCH 14/27] made 'rust' a package attribute instead of a pseudo-dependency --- text/0000-deprecation.md | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md index 5b5138a3b67..cdd3e05bd58 100644 --- a/text/0000-deprecation.md +++ b/text/0000-deprecation.md @@ -15,19 +15,21 @@ Namely the following items: 1. Add a `--target-version=`** command line argument to rustc. This will be used for deprecation checking and for selecting code paths in the compiler. -2. Add an (optional for now) `rust = "..."` dependency to Cargo.toml, -which `cargo new` pre-fills with the current rust version -(alternatively this could be a package attribute) +2. Add an optional `rust = "..."` package attribute to Cargo.toml, +which `cargo new` pre-fills with the current rust version. 3. Allow `std` APIs to declare an `#[insecure(level="Warn", reason="...")]` attribute that will produce a warning or error, depending on level, that cannot be switched off (even with `-Awarning`) 4. Add a `removed_at="..."` item to `#[deprecated]` attributes that allows making API items unavailable starting from certain target -versions. +versions. 5. Add a number of warnings to steer users in the direction of using the most recent Rust version that makes sense to them, while making it easy for library writers to support a wide range of Rust versions +6. (optional) add a `legacy="..."` item to `#[deprecated]` attributes +that allows grouping API items under a legacy flag that is already +defined in RFC #1122 (see below) ## Background @@ -129,9 +131,9 @@ make no provisions for it. Thus proposal item 3. # Detailed design -Cargo parses the additional `rust = "..."` dependency as if it was a -library. The usual rules for version parsing apply. If no `rust` -dependency is supplied, it defaults to `*`. +Cargo parses the additional `rust = "..."` package attribute. The usual +rules for version parsing apply. If no `rust` attribute is supplied, it +defaults to `*`. Cargo should also supply the current Rust version (which can be either supplied by calling `rustc -V` or by linking to a rust library defining @@ -139,11 +141,12 @@ a version object) on `cargo new`. Cargo supplies the given target version to `rustc` via the `--target-version` command line argument. Cargo *may* also warn on `cargo package` if no `rust` version was -supplied. [crates.io](https://crates.io) *could* require a version -attribute on upload and display the required rust version on the site. +supplied. A few versions in the future, Cargo could also warn of +missing version attributes on build or other actions, at least if the +crate is a library. -One nice aspect of this is that `rust` looks just like yet another -dependency and effectively follows the same rules. +[crates.io](https://crates.io) *could* require a version attribute on +upload and display the required rust version on the site. `rustc` needs to accept the `--target-version ` command line argument. If no argument is supplied, `rustc` defaults to its own @@ -202,7 +205,9 @@ to specifically allow usage of the grouped APIs, thus selectively removing the deprecation warning. This would create a nice feature parity between language code paths and -`std` API deprecation checking. +`std` API deprecation checking. Also it would lessen the pain for users +who want to upgrade their systems one feature at a time and can use the +legacy flags to effectively manage their usage of deprecated items. # Drawbacks @@ -250,4 +255,4 @@ needing a new attribute key. # Unresolved questions -Should the rust = "" be a *dependency* or a package attribute? +None From 186fd9170cdc61033ca56e50d6cad011e271b5e5 Mon Sep 17 00:00:00 2001 From: llogiq Date: Sun, 28 Jun 2015 22:54:07 +0200 Subject: [PATCH 15/27] formatting improvements --- text/0000-deprecation.md | 85 ++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md index cdd3e05bd58..62d8964df39 100644 --- a/text/0000-deprecation.md +++ b/text/0000-deprecation.md @@ -13,23 +13,23 @@ anyones code. Namely the following items: 1. Add a `--target-version=`** command line argument to -rustc. This will be used for deprecation checking and for selecting -code paths in the compiler. + rustc. This will be used for deprecation checking and for selecting + code paths in the compiler. 2. Add an optional `rust = "..."` package attribute to Cargo.toml, -which `cargo new` pre-fills with the current rust version. + which `cargo new` pre-fills with the current rust version. 3. Allow `std` APIs to declare an -`#[insecure(level="Warn", reason="...")]` attribute that will produce -a warning or error, depending on level, that cannot be switched off -(even with `-Awarning`) + `#[insecure(level="Warn", reason="...")]` attribute that will + produce a warning or error, depending on level, that cannot be + switched off (even with `-Awarning`) 4. Add a `removed_at="..."` item to `#[deprecated]` attributes that -allows making API items unavailable starting from certain target -versions. + allows making API items unavailable starting from certain target + versions. 5. Add a number of warnings to steer users in the direction of using -the most recent Rust version that makes sense to them, while making it -easy for library writers to support a wide range of Rust versions + the most recent Rust version that makes sense to them, while making + it easy for library writers to support a wide range of Rust versions 6. (optional) add a `legacy="..."` item to `#[deprecated]` attributes -that allows grouping API items under a legacy flag that is already -defined in RFC #1122 (see below) + that allows grouping API items under a legacy flag that is already + defined in RFC #1122 (see below) ## Background @@ -153,16 +153,16 @@ argument. If no argument is supplied, `rustc` defaults to its own version. The same version syntax as Cargo applies: * `*` effectively means *any version*. For API items, it means -deprecation checking is disabled. For language changes, it means using -the `1.0.0` code paths (for now, we may opt to change this in the -future), because anything else would break all current code. -* `1.x` or e.g. `>=1.2.0` sets the target version to the minor version. -Deprecation checking and language code path selection occur relative -to the lowest given version. This might also affect stability handling, -though this RFC doesn't specify this as of yet. + deprecation checking is disabled. For language changes, it means + using the `1.0.0` code paths (for now, we may opt to change this in + the future), because anything else would break all current code. +* `1.x` or e.g. `>=1.2.0` sets the target version to the minor version. + Deprecation checking and language code path selection occur relative + to the lowest given version. This might also affect stability + handling, though this RFC doesn't specify this as of yet. * `1.0 - <2.0` as above, the *lowest* supplied version has to be -assumed for code path selection. However, deprecation checking should -assume the *highest* supplied version, if any. + assumed for code path selection. However, deprecation checking should + assume the *highest* supplied version, if any. If the target version is *higher* than the current `rustc` version, `rustc` should show a warning to suggest that it may need to be updated @@ -224,34 +224,35 @@ estimate the effort to be reasonably low. # Alternatives * It was suggested that opt-in and opt-out (e.g. by `#[legacy(..)]`) -could be sufficient to work around any breaking code on API or language -changes. The big problem here is that this relies on the user being able -to change their dependencies, which may not be possible for legal, -organizational or other reasons. In contrast, a defined target version -doesn't ever need to change + could be sufficient to work around any breaking code on API or + language changes. The big problem here is that this relies on the + user being able to change their dependencies, which may not be + possible for legal, organizational or other reasons. In contrast, a + defined target version doesn't ever need to change -Depending on the specific case, it may be useful to allow a combination -of `#![legacy(..)]`, `#![feature(..)]` and the target version where -each Rust version can declare the currently active feature set and -permit or forbid use of the opt-in/out flags + Depending on the specific case, it may be useful to allow a + combination of `#![legacy(..)]`, `#![feature(..)]` and the target + version where each Rust version can declare the currently active + feature set and permit or forbid use of the opt-in/out flags * Follow a more agressive strategy that actually removes stuff from the -API. This would make it easier for the libstd creators at some cost for -library and application writers, as they are required to keep up to -date or face breakage. The risk of breaking existing code makes this -strategy very unattractive + API. This would make it easier for the libstd creators at some cost + for library and application writers, as they are required to keep up + to date or face breakage. The risk of breaking existing code makes + this strategy very unattractive * Hide deprecated items in the docs: This could be done either by -putting them into a linked extra page or by adding a "show deprecated" -checkbox that may be default be checked or not, depending on who you -ask. This will however confuse people, who see the deprecated APIs in -some code, but cannot find them in the docs anymore + putting them into a linked extra page or by adding a "show + deprecated" checkbox that may be default be checked or not, depending + on who you ask. This will however confuse people, who see the + deprecated APIs in some code, but cannot find them in the docs + anymore * Allow to distinguish "soft" and "hard" deprecation, so that an API -can be marked as "soft" deprecated to dissuade new uses before hard -deprecation is decided. Allowing people to specify deprecation in -future version appears to have much of the same benefits without -needing a new attribute key. + can be marked as "soft" deprecated to dissuade new uses before hard + deprecation is decided. Allowing people to specify deprecation in + future version appears to have much of the same benefits without + needing a new attribute key. # Unresolved questions From ce6d4e79b6043c78eca8e5c0c1c41e2bb1790347 Mon Sep 17 00:00:00 2001 From: llogiq Date: Sat, 4 Jul 2015 13:25:14 +0200 Subject: [PATCH 16/27] Added previous proposal to alternatives, added bikeshedding to unresolved questions --- text/0000-deprecation.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md index 62d8964df39..67e6bbfaa13 100644 --- a/text/0000-deprecation.md +++ b/text/0000-deprecation.md @@ -223,6 +223,14 @@ estimate the effort to be reasonably low. # Alternatives +* An earlier version of this proposal suggested using a crate attribute + instead of a cargo package attribute and a compiler option, to also + allow cargo-less use cases without manual interaction. However it was + determined that those cases usually target the current Rust version + anyway, and the current proposal allows us to default to the current + version for rustc, while the earlier proposal would have defaulted to + 1.0.0 by necessity of not breaking existing code + * It was suggested that opt-in and opt-out (e.g. by `#[legacy(..)]`) could be sufficient to work around any breaking code on API or language changes. The big problem here is that this relies on the @@ -252,8 +260,10 @@ estimate the effort to be reasonably low. can be marked as "soft" deprecated to dissuade new uses before hard deprecation is decided. Allowing people to specify deprecation in future version appears to have much of the same benefits without - needing a new attribute key. + requiring a new attribute key # Unresolved questions -None +The names for the cargo package attribute and the rustc compiler option +are still subject to bikeshedding (however, discussion has stalled, +suggesting the current names are good enough). From 3b9ca85bc43ca9c8769344c9fa41176a8c889ef6 Mon Sep 17 00:00:00 2001 From: llogiq Date: Thu, 9 Jul 2015 07:12:51 +0200 Subject: [PATCH 17/27] #[insecure]-flagging removed from RFC, added some open questions (as prompted by @alexcrichton) --- text/0000-deprecation.md | 59 +++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md index 67e6bbfaa13..d04ef3698db 100644 --- a/text/0000-deprecation.md +++ b/text/0000-deprecation.md @@ -17,17 +17,13 @@ Namely the following items: code paths in the compiler. 2. Add an optional `rust = "..."` package attribute to Cargo.toml, which `cargo new` pre-fills with the current rust version. -3. Allow `std` APIs to declare an - `#[insecure(level="Warn", reason="...")]` attribute that will - produce a warning or error, depending on level, that cannot be - switched off (even with `-Awarning`) -4. Add a `removed_at="..."` item to `#[deprecated]` attributes that +3. Add a `removed_at="..."` item to `#[deprecated]` attributes that allows making API items unavailable starting from certain target versions. -5. Add a number of warnings to steer users in the direction of using +4. Add a number of warnings to steer users in the direction of using the most recent Rust version that makes sense to them, while making it easy for library writers to support a wide range of Rust versions -6. (optional) add a `legacy="..."` item to `#[deprecated]` attributes +5. (optional) add a `legacy="..."` item to `#[deprecated]` attributes that allows grouping API items under a legacy flag that is already defined in RFC #1122 (see below) @@ -45,7 +41,7 @@ As a background, in no particular order: In addition, there has been an ongoing [discussion on internals](https://internals.rust-lang.org/t/thoughts-on-aggressive-deprecation-in-libstd/2176/55) about how we are going to evolve the standard library, which this -proposal is mostly based on. +proposal is somewhat based on. Finally, the recent discussion on the first breaking change ([RFC PR #1156 Adjust default object bounds](https://github.com/rust-lang/rfcs/pull/1156)) @@ -63,7 +59,7 @@ the `-Awarnings` argument. ## 1. Language / `std` Evolution -The following motivates items 1 and 2 (and to a lesser extent 5) +The following motivates items 1 and 2 (and to a lesser extent 4) With the current setup, we can already evolve the language and APIs, albeit in a very limited way. For example, it is virtually impossible @@ -89,7 +85,7 @@ libraries, in which deprecation doesn't exist! ## 2. User Experience -The following motivates items 2, 4 and 5. +The following motivates items 2, 3 and 4. Currently, there is no way to make an API item unavailable via deprecation. This means the API will only ever expand, with a lot of @@ -123,12 +119,6 @@ current target version (e.g. by a switch), or deprecated items relegated to a separate space, to reduce clutter and possible user confusion. -## 3. Security Considerations - -I believe that *should* a security issue in one of our APIs be found, -a swift and effective response will be required, and the current rules -make no provisions for it. Thus proposal item 3. - # Detailed design Cargo parses the additional `rust = "..."` package attribute. The usual @@ -178,23 +168,12 @@ of the specified target version instead the current version, but default to the current version if no target version is specified or the specified version has no upper bound. -`rustc` should also show a warning or error, depending on level, on -encountering usage of API items marked as `#[insecure]`. The attribute -has two values: - -* `level` can either be `Warning` or `Error` and default to `Error` -* `reason` contains a description on why usage of this item was deemed - a security risk. This attribute is mandatory. - While `rustdoc` already parses the deprecation flags, it should in addition relegate items removed in the current version to a separate area below the other documentation and optically mark them as removed. We should not completely remove them, because that would confuse users who see the API items in code written for older target versions. -Also, `rustdoc` should show if API items are marked with `#[insecure]`, -including displaying the `reason` prominently. - # Optional Extension: Legacy flags The `#[deprecated]` attribute could get an optional `legacy="xy"` @@ -219,7 +198,11 @@ have lived with this for multiple decades, so it appears the tradeoff has seen some confirmation already. Cargo and `rustc` need some code to manage the additional rules. I -estimate the effort to be reasonably low. +estimate the effort to be reasonably low. For *compiler changes* +however, unless it's a genuine bug and unless there could be programs +relying on the old behaviours, both the old and new code paths have to +be maintained in the compiler, which is the biggest cost of +implementing this RFC. # Alternatives @@ -264,6 +247,20 @@ estimate the effort to be reasonably low. # Unresolved questions -The names for the cargo package attribute and the rustc compiler option -are still subject to bikeshedding (however, discussion has stalled, -suggesting the current names are good enough). +* The names for the cargo package attribute and the rustc compiler + option are still subject to bikeshedding (however, discussion has + stalled, suggesting the current names are good enough). + +* How do we determine if something is a genuine bug (and should be + changed retroactively)? + +* If we agree that something needs to be changed retroactively (i.e. in + older versions), do we also release the old versions anew? Which + ones? Should we nominate LTS versions? Who would maintain them? + +* Is *forward-compatibility* sufficiently handled? Seeing that e.g. + adding an item to a trait could break code using that trait, changing + a trait would require both versions being interoperable, which could + be impossible in the general case. This would needed to be handled by + finding a new name for the trait or supplying a default + implementation. From f0021d0cf1ed2aa08169472da5822e5f824bafac Mon Sep 17 00:00:00 2001 From: llogiq Date: Fri, 4 Sep 2015 18:19:49 +0200 Subject: [PATCH 18/27] RFC to make stability attributes public --- text/0000-deprecation.md | 316 +++++++++------------------------------ 1 file changed, 71 insertions(+), 245 deletions(-) diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md index d04ef3698db..b775757e8ea 100644 --- a/text/0000-deprecation.md +++ b/text/0000-deprecation.md @@ -1,266 +1,92 @@ -- Feature Name: Target Version -- Start Date: 2015-06-03 +- Feature Name: Public Stability +- Start Date: 2015-09-03 - RFC PR: - Rust Issue: # Summary -This RFC proposes a small number of extensions to improve the user -experience around different rust versions, while at the same time -giving Rust developers more freedom to make changes without breaking -anyones code. - -Namely the following items: - -1. Add a `--target-version=`** command line argument to - rustc. This will be used for deprecation checking and for selecting - code paths in the compiler. -2. Add an optional `rust = "..."` package attribute to Cargo.toml, - which `cargo new` pre-fills with the current rust version. -3. Add a `removed_at="..."` item to `#[deprecated]` attributes that - allows making API items unavailable starting from certain target - versions. -4. Add a number of warnings to steer users in the direction of using - the most recent Rust version that makes sense to them, while making - it easy for library writers to support a wide range of Rust versions -5. (optional) add a `legacy="..."` item to `#[deprecated]` attributes - that allows grouping API items under a legacy flag that is already - defined in RFC #1122 (see below) - -## Background - -A good number of policies and mechanisms around versioning regarding -the Rust language and APIs have already been submitted, some accepted. -As a background, in no particular order: - -* [#0572 Feature gates](https://github.com/rust-lang/rfcs/blob/master/text/0572-rustc-attribute.md) -* [#1105 API Evolution](https://github.com/rust-lang/rfcs/blob/master/text/1105-api-evolution.md) -* [#1122 Language SemVer](https://github.com/rust-lang/rfcs/blob/master/text/1122-language-semver.md) -* [#1150 Rename Attribute](https://github.com/rust-lang/pull/1150) - -In addition, there has been an ongoing -[discussion on internals](https://internals.rust-lang.org/t/thoughts-on-aggressive-deprecation-in-libstd/2176/55) -about how we are going to evolve the standard library, which this -proposal is somewhat based on. - -Finally, the recent discussion on the first breaking change -([RFC PR #1156 Adjust default object bounds](https://github.com/rust-lang/rfcs/pull/1156)) -has made it clear that we need a more flexible way of dealing with -(breaking) changes. - -The current setup allows the `std` API to declare `#[unstable]` and -`#[deprecated]` flags, whereas users can opt-in to unstable features -with `#![feature]` flags that are customarily added to the crate root. -On usage of deprecated APIs, a warning is shown unless suppressed. -`cargo` does this for dependencies by default by calling `rustc` with -the `-Awarnings` argument. +This RFC proposes to make the stability attributes `#[deprecate]`, `#[stable]` +and `#[unstable]` publicly available, removing some and adding other +restrictions while keeping everything mostly the same for APIs shipped with +Rust. # Motivation -## 1. Language / `std` Evolution - -The following motivates items 1 and 2 (and to a lesser extent 4) - -With the current setup, we can already evolve the language and APIs, -albeit in a very limited way. For example, it is virtually impossible -to actually remove an API, because that would break code. Even minor -breaking changes (as [RFC PR #1156](https://github.com/rust-lang/rfcs/pull/1156) -cited above) generate huge discussion, because they call the general -stability of the language and environment into question. - -The problem with opt-out, as defined by -[RFC #1122](https://github.com/rust-lang/rfcs/blob/master/text/1122-language-semver.md) -is that code which previously compiled without problems stops working -on a Rust upgrade, and requires manual intervention to get working -again. +Library authors want a way to evolve their APIs without too much breakage. To +this end, Rust has long employed the aforementioned attributes. Now that Rust +is somewhat stable, it's time to open them up so that others can use them. -This has the long-term potential of fragmenting the Rust ecosystem into -crates/projects using certain Rust versions and thus should be avoided -at some (but not all) cost. - -Note that a similar problem exists with deprecation as defined: People -may get used to deprecation warnings or just turn them off until their -build breaks. Worse, the current setup creates a mirror world for -libraries, in which deprecation doesn't exist! - -## 2. User Experience - -The following motivates items 2, 3 and 4. - -Currently, there is no way to make an API item unavailable via -deprecation. This means the API will only ever expand, with a lot of -churn (case in point, the Java language as of Version 8 lists 462 -deprecated constructs, including methods, constants and complete -classes, in relation to 4241 classes, this makes for about 10% of -deprecated API surface. Note that this is but a rough estimate). To -better lead users to the right APIs and to allow for more effective -language/API evolution, this proposal adds the `removed_at=""` -item to the `#[deprecated]` attribute. - -This allows us to effectively remove an API item from a certain target -version while avoiding breaking code written for older target versions. - -Also rustc can emit better error messages than it could were the API -items actually removed. In the best case, the error messages can steer -the user to a working replacement. - -We want to avoid users setting `#[allow(deprecate)]` on their code to -get rid of the warnings. On the other hand, there have been instances -of failing builds because code was marked with `#![deny(warnings)]`, -namely `compiletest.rs` and all crates using it. This shows that the -current system has room for improvement. - -We want to avoid failing builds because of wrong or missing target -version definition. Therefor supplying useful defaults is of the -essence. - -The documentation can be optionally reduced to items relating to the -current target version (e.g. by a switch), or deprecated items -relegated to a separate space, to reduce clutter and possible user -confusion. +A pre-RFC on rust-users has seen a good number of supportive voices, which +suggests that the feature will improve the life of rust library authors +considerably. # Detailed design -Cargo parses the additional `rust = "..."` package attribute. The usual -rules for version parsing apply. If no `rust` attribute is supplied, it -defaults to `*`. - -Cargo should also supply the current Rust version (which can be either -supplied by calling `rustc -V` or by linking to a rust library defining -a version object) on `cargo new`. Cargo supplies the given target -version to `rustc` via the `--target-version` command line argument. - -Cargo *may* also warn on `cargo package` if no `rust` version was -supplied. A few versions in the future, Cargo could also warn of -missing version attributes on build or other actions, at least if the -crate is a library. - -[crates.io](https://crates.io) *could* require a version attribute on -upload and display the required rust version on the site. - -`rustc` needs to accept the `--target-version ` command line -argument. If no argument is supplied, `rustc` defaults to its own -version. The same version syntax as Cargo applies: - -* `*` effectively means *any version*. For API items, it means - deprecation checking is disabled. For language changes, it means - using the `1.0.0` code paths (for now, we may opt to change this in - the future), because anything else would break all current code. -* `1.x` or e.g. `>=1.2.0` sets the target version to the minor version. - Deprecation checking and language code path selection occur relative - to the lowest given version. This might also affect stability - handling, though this RFC doesn't specify this as of yet. -* `1.0 - <2.0` as above, the *lowest* supplied version has to be - assumed for code path selection. However, deprecation checking should - assume the *highest* supplied version, if any. - -If the target version is *higher* than the current `rustc` version, -`rustc` should show a warning to suggest that it may need to be updated -in order to compile the crate and then try to compile the crate on a -best-effort basis. - -Optionally, we can define a `future deprecation` lint set to `Allow` by -default to allow people being proactive about items that are going to -be deprecated. - -`rustc` should resolve the `#[feature]` flags against the upper bound -of the specified target version instead the current version, but -default to the current version if no target version is specified or the -specified version has no upper bound. - -While `rustdoc` already parses the deprecation flags, it should in -addition relegate items removed in the current version to a separate -area below the other documentation and optically mark them as removed. -We should not completely remove them, because that would confuse users -who see the API items in code written for older target versions. - -# Optional Extension: Legacy flags - -The `#[deprecated]` attribute could get an optional `legacy="xy"` -entry, which could effectively group a set of APIs under the given -name. Users can then declare the `#[legacy]` flag as defined in -[RFC #1122](https://github.com/rust-lang/rfcs/blob/master/text/1122-language-semver.md) -to specifically allow usage of the grouped APIs, thus selectively -removing the deprecation warning. - -This would create a nice feature parity between language code paths and -`std` API deprecation checking. Also it would lessen the pain for users -who want to upgrade their systems one feature at a time and can use the -legacy flags to effectively manage their usage of deprecated items. +Add another stability level variant `Undefined`, to be used whenever a +`#[deprecate]` attribute is without a `#[stable]` or `#[unstable]`. This lifts +the restriction to have the latter attributes whenever the former is used. To +keep the restriction for ASWRs, we add an `undefined_stability` lint that is +`Allow` by default, but set to `Warn` in the Rust build process, that catches +`Undefined` stability attributes (can be done within the Stability lint pass). + +Remove the rust API restriction on `#[deprecate]`, `#[stable]` and +`#[unstable]`. + +On all attributes the `version` field of the attribute should be checked to be +valid semver as per [RFC +#1122](https://github.com/rust-lang/rfcs/blob/master/text/1122-language-semver.md). +It is per this RFC redefined to mean the version of the crate (or rust +distribution, for `std`) as declared in `Cargo.toml`. + +The `issue` field of the `#[`(`un`)`stable]` attributes is defined per this RFC +to mean the suffix to a URL to the issue tracker (it may also be the complete +URL). Optionally rustdoc may link the issue from the documentation; a new +`--issue-tracker-url-prefix=...` option will be prefixed to all links. + +The `feature` field is defined to contain a feature name to be used with +`cfg(feature = "...")`. To check this, Cargo could put the list of *available* +features in a space-separated `CRATE_FEATURES_AVAILABLE` environment variable. +Alternative build processes can also set this. To simplify things, putting a +`"*"` in the environment variable should disable the check. Otherwise the +stability lint can check if the feature has been declared. + +The `reason` field is defined to contain a human-readable text with a +suggestion what to use instead and a rationale. This is how the field is used +currently. See the Alternatives section for a less conservative (but more +work-intensive) proposal. + +The language reference should be extended to describe this feature. # Drawbacks -By requiring full backwards-compatibility, we will never be able to -actually remove stuff from the APIs, which will probably lead to some -bloat. However, the cost of maintaining the outdated APIs is far -outweighted by the benefits. Case in point: Other successful languages -have lived with this for multiple decades, so it appears the tradeoff -has seen some confirmation already. - -Cargo and `rustc` need some code to manage the additional rules. I -estimate the effort to be reasonably low. For *compiler changes* -however, unless it's a genuine bug and unless there could be programs -relying on the old behaviours, both the old and new code paths have to -be maintained in the compiler, which is the biggest cost of -implementing this RFC. +* Work to be done will take time not to invest in other improvements +* There could be attribute definitions in the codebase that do not adhere to +the outlined design, and would have to be changed to fit. It is unclear whether +this is a real drawback +* Once the feature is public, we can no longer change its design +* Someone could misuse the API to e.g. add malicious links into their rustdoc. +However this is possible via plain links even now # Alternatives -* An earlier version of this proposal suggested using a crate attribute - instead of a cargo package attribute and a compiler option, to also - allow cargo-less use cases without manual interaction. However it was - determined that those cases usually target the current Rust version - anyway, and the current proposal allows us to default to the current - version for rustc, while the earlier proposal would have defaulted to - 1.0.0 by necessity of not breaking existing code - -* It was suggested that opt-in and opt-out (e.g. by `#[legacy(..)]`) - could be sufficient to work around any breaking code on API or - language changes. The big problem here is that this relies on the - user being able to change their dependencies, which may not be - possible for legal, organizational or other reasons. In contrast, a - defined target version doesn't ever need to change - - Depending on the specific case, it may be useful to allow a - combination of `#![legacy(..)]`, `#![feature(..)]` and the target - version where each Rust version can declare the currently active - feature set and permit or forbid use of the opt-in/out flags - -* Follow a more agressive strategy that actually removes stuff from the - API. This would make it easier for the libstd creators at some cost - for library and application writers, as they are required to keep up - to date or face breakage. The risk of breaking existing code makes - this strategy very unattractive - -* Hide deprecated items in the docs: This could be done either by - putting them into a linked extra page or by adding a "show - deprecated" checkbox that may be default be checked or not, depending - on who you ask. This will however confuse people, who see the - deprecated APIs in some code, but cannot find them in the docs - anymore - -* Allow to distinguish "soft" and "hard" deprecation, so that an API - can be marked as "soft" deprecated to dissuade new uses before hard - deprecation is decided. Allowing people to specify deprecation in - future version appears to have much of the same benefits without - requiring a new attribute key +* Do nothing +* Optionally the deprecation lint chould check the current version as set by +cargo in the CARGO_CRATE_VERSION environment variable (the rust build process +should set this environment variable, too). This would allow future +deprecations to be shown in the docs early, but not warned against by the +stability lint (there could however be a `future-deprecation` lint that should +be `Allow` by default). +* The `reason` field definition could be reduced to stating the *rationale* for +deprecating the API item. A new `instead` field then contains the full path to +a replacement item (trait, method, function, etc.). Since this path is well +defined, it can be checked against. However, some provision needs to be made to +allow those paths to be extended with a crate (e.g. for items that have been +moved to different crates). The upside is that this would open up the +possibility for rustdoc to link to the replacement, the downside is that the +check could potentially be costly. # Unresolved questions -* The names for the cargo package attribute and the rustc compiler - option are still subject to bikeshedding (however, discussion has - stalled, suggesting the current names are good enough). - -* How do we determine if something is a genuine bug (and should be - changed retroactively)? - -* If we agree that something needs to be changed retroactively (i.e. in - older versions), do we also release the old versions anew? Which - ones? Should we nominate LTS versions? Who would maintain them? - -* Is *forward-compatibility* sufficiently handled? Seeing that e.g. - adding an item to a trait could break code using that trait, changing - a trait would require both versions being interoperable, which could - be impossible in the general case. This would needed to be handled by - finding a new name for the trait or supplying a default - implementation. +* Is the current design (as outlined herein) good enough to be made public? +* What other restrictions should we introduce now to avoid being bound to a +possibly flawed design? From a4b0b93c040df16c80cc7dbff2e6df1124bb00e7 Mon Sep 17 00:00:00 2001 From: llogiq Date: Fri, 4 Sep 2015 21:08:47 +0200 Subject: [PATCH 19/27] rewrote to start from clean slate, reduce surface area --- text/0000-deprecation.md | 124 ++++++++++++++++++++------------------- 1 file changed, 64 insertions(+), 60 deletions(-) diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md index b775757e8ea..2f38d0f589c 100644 --- a/text/0000-deprecation.md +++ b/text/0000-deprecation.md @@ -5,67 +5,76 @@ # Summary -This RFC proposes to make the stability attributes `#[deprecate]`, `#[stable]` -and `#[unstable]` publicly available, removing some and adding other -restrictions while keeping everything mostly the same for APIs shipped with -Rust. +This RFC proposes to allow library authors to use a `#[deprecate]` attribute, +with `since="`(version)`"`, `reason="`(free text)`"` and +`surrogate="`(text or surrogate declaration)`"` fields. The compiler can then +warn on deprecated items, while `rustdoc` can document their deprecation +accordingly. # Motivation -Library authors want a way to evolve their APIs without too much breakage. To -this end, Rust has long employed the aforementioned attributes. Now that Rust -is somewhat stable, it's time to open them up so that others can use them. +Library authors want a way to evolve their APIs; which also involves +deprecating items. To do this cleanly, they need to document their intentions +and give their users enough time to react. -A pre-RFC on rust-users has seen a good number of supportive voices, which -suggests that the feature will improve the life of rust library authors -considerably. +Currently there is no support from the language for this oft-wanted feature +(despite a similar feature existing for the sole purpose of evolving the Rust +standard library). This RFC aims to rectify that, while giving a pleasant +interface to use while maximizing usefulness of the metadata introduced. # Detailed design -Add another stability level variant `Undefined`, to be used whenever a -`#[deprecate]` attribute is without a `#[stable]` or `#[unstable]`. This lifts -the restriction to have the latter attributes whenever the former is used. To -keep the restriction for ASWRs, we add an `undefined_stability` lint that is -`Allow` by default, but set to `Warn` in the Rust build process, that catches -`Undefined` stability attributes (can be done within the Stability lint pass). - -Remove the rust API restriction on `#[deprecate]`, `#[stable]` and -`#[unstable]`. - -On all attributes the `version` field of the attribute should be checked to be -valid semver as per [RFC -#1122](https://github.com/rust-lang/rfcs/blob/master/text/1122-language-semver.md). -It is per this RFC redefined to mean the version of the crate (or rust -distribution, for `std`) as declared in `Cargo.toml`. - -The `issue` field of the `#[`(`un`)`stable]` attributes is defined per this RFC -to mean the suffix to a URL to the issue tracker (it may also be the complete -URL). Optionally rustdoc may link the issue from the documentation; a new -`--issue-tracker-url-prefix=...` option will be prefixed to all links. - -The `feature` field is defined to contain a feature name to be used with -`cfg(feature = "...")`. To check this, Cargo could put the list of *available* -features in a space-separated `CRATE_FEATURES_AVAILABLE` environment variable. -Alternative build processes can also set this. To simplify things, putting a -`"*"` in the environment variable should disable the check. Otherwise the -stability lint can check if the feature has been declared. - -The `reason` field is defined to contain a human-readable text with a -suggestion what to use instead and a rationale. This is how the field is used -currently. See the Alternatives section for a less conservative (but more -work-intensive) proposal. - -The language reference should be extended to describe this feature. +Public API items (both plain `fn`s, methods, trait- and inherent +`impl`ementations as well as `const` definitions) can be given a `#[deprecate]` +attribute. + +This attribute *must* have the `since` field, which contains the version of the +crate that deprecated the item, as defined by Cargo.toml (thus following the +semver scheme). It makes no sense to put a version number higher than the +current newest version here, and this is not checked (but could be by external +lints, e.g. [rust-clippy](https://github.com/Manishearth/rust-clippy). + +Other optional fields are: + +* `reason` should contain a human-readable string outlining the reason for +deprecating the item. While this field is not required, library authors are +strongly advised to make use of it to convey the reason to users of their +library. The string is required to be plain unformatted text (for now) so that +rustdoc can include it in the item's documentation without messing up the +formatting. +* `surrogate` should be the full path to an API item that will replace the +functionality of the deprecated item, optionally (if the surrogate is in a +different crate) followed by `@` and either a crate name (so that +`https://crates.io/crates/` followed by the name is a live link) or the URL to +a repository or other location where a surrogate can be obtained. Links must be +plain FTP, FTPS, HTTP or HTTPS links. The intention is to allow rustdoc (and +possibly other tools in the future, e.g. IDEs) to act on the included +information. + +On use of a *deprecated* item, `rustc` should `warn` of the deprecation. Note +that during Cargo builds, warnings on dependencies get silenced. Note that +while this has the upside of keeping things tidy, it has a downside when it +comes to deprecation: + +Let's say I have my `llogiq` crate that depends on `foobar` which uses a +deprecated item of `serde`. I will never get the warning about this unless I +try to build `foobar` directly. We may want to create a service like `crater` +to warn on use of deprecated items in library crates, however this is outside +the scope of this RFC. + +`rustdoc` should show deprecation on items, with a `[deprecated since x.y.z]` +box that may optionally show the reason and/or link to the surrogate if +available. + +The language reference should be extended to describe this feature as outlined +in this RFC. Authors shall be advised to leave their users enough time to react +before *removing* a deprecated item. # Drawbacks -* Work to be done will take time not to invest in other improvements -* There could be attribute definitions in the codebase that do not adhere to -the outlined design, and would have to be changed to fit. It is unclear whether -this is a real drawback +* The required checks for the `since` and `surrogate` fields are potentially +quite complex. * Once the feature is public, we can no longer change its design -* Someone could misuse the API to e.g. add malicious links into their rustdoc. -However this is possible via plain links even now # Alternatives @@ -75,18 +84,13 @@ cargo in the CARGO_CRATE_VERSION environment variable (the rust build process should set this environment variable, too). This would allow future deprecations to be shown in the docs early, but not warned against by the stability lint (there could however be a `future-deprecation` lint that should -be `Allow` by default). -* The `reason` field definition could be reduced to stating the *rationale* for -deprecating the API item. A new `instead` field then contains the full path to -a replacement item (trait, method, function, etc.). Since this path is well -defined, it can be checked against. However, some provision needs to be made to -allow those paths to be extended with a crate (e.g. for items that have been -moved to different crates). The upside is that this would open up the -possibility for rustdoc to link to the replacement, the downside is that the -check could potentially be costly. +be `Allow` by default) +* `reason` could include markdown formatting +* The `surrogate` could simply be plain text, which would remove much of the +complexity here # Unresolved questions -* Is the current design (as outlined herein) good enough to be made public? * What other restrictions should we introduce now to avoid being bound to a possibly flawed design? +* Can / Should the `std` library make use of the `#[deprecate]` extensions? From e487613345bbad2594121398621cae542864f7cb Mon Sep 17 00:00:00 2001 From: llogiq Date: Sun, 6 Sep 2015 08:24:41 +0200 Subject: [PATCH 20/27] Clarified API items+versioning, more alternatives --- text/0000-deprecation.md | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md index 2f38d0f589c..b4af43eb389 100644 --- a/text/0000-deprecation.md +++ b/text/0000-deprecation.md @@ -25,14 +25,19 @@ interface to use while maximizing usefulness of the metadata introduced. # Detailed design Public API items (both plain `fn`s, methods, trait- and inherent -`impl`ementations as well as `const` definitions) can be given a `#[deprecate]` -attribute. +`impl`ementations as well as `const` definitions, type definitions, struct +fields and enum variants) can be given a `#[deprecate]` attribute. -This attribute *must* have the `since` field, which contains the version of the -crate that deprecated the item, as defined by Cargo.toml (thus following the -semver scheme). It makes no sense to put a version number higher than the -current newest version here, and this is not checked (but could be by external -lints, e.g. [rust-clippy](https://github.com/Manishearth/rust-clippy). +This attribute *must* have the `since` field, which contains at least the +version of the crate that deprecated the item, as defined by Cargo.toml +(thus following the semver scheme). It makes no sense to put a version number +higher than the current newest version here, and this is not checked (but +could be by external lints, e.g. +[rust-clippy](https://github.com/Manishearth/rust-clippy). + +Following semantic versioning would mean that the supplied value could +actually be a version *range*, which could imply an end-of-life for the +feature. Other optional fields are: @@ -79,12 +84,14 @@ quite complex. # Alternatives * Do nothing +* make the `since` field optional * Optionally the deprecation lint chould check the current version as set by cargo in the CARGO_CRATE_VERSION environment variable (the rust build process should set this environment variable, too). This would allow future deprecations to be shown in the docs early, but not warned against by the stability lint (there could however be a `future-deprecation` lint that should be `Allow` by default) +* require either `reason` or `surrogate` be present * `reason` could include markdown formatting * The `surrogate` could simply be plain text, which would remove much of the complexity here @@ -94,3 +101,4 @@ complexity here * What other restrictions should we introduce now to avoid being bound to a possibly flawed design? * Can / Should the `std` library make use of the `#[deprecate]` extensions? +* Bikeshedding: Are the names good enough? From 0aaf9d45e055c16bba13b73ab2695f2c71a038ea Mon Sep 17 00:00:00 2001 From: llogiq Date: Tue, 8 Sep 2015 22:00:47 +0200 Subject: [PATCH 21/27] Require since to be exact version, add `d` --- text/0000-deprecation.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md index b4af43eb389..731598af069 100644 --- a/text/0000-deprecation.md +++ b/text/0000-deprecation.md @@ -5,7 +5,7 @@ # Summary -This RFC proposes to allow library authors to use a `#[deprecate]` attribute, +This RFC proposes to allow library authors to use a `#[deprecated]` attribute, with `since="`(version)`"`, `reason="`(free text)`"` and `surrogate="`(text or surrogate declaration)`"` fields. The compiler can then warn on deprecated items, while `rustdoc` can document their deprecation @@ -26,18 +26,17 @@ interface to use while maximizing usefulness of the metadata introduced. Public API items (both plain `fn`s, methods, trait- and inherent `impl`ementations as well as `const` definitions, type definitions, struct -fields and enum variants) can be given a `#[deprecate]` attribute. +fields and enum variants) can be given a `#[deprecated]` attribute. -This attribute *must* have the `since` field, which contains at least the +This attribute *must* have the `since` field, which contains the exact version of the crate that deprecated the item, as defined by Cargo.toml (thus following the semver scheme). It makes no sense to put a version number higher than the current newest version here, and this is not checked (but could be by external lints, e.g. [rust-clippy](https://github.com/Manishearth/rust-clippy). -Following semantic versioning would mean that the supplied value could -actually be a version *range*, which could imply an end-of-life for the -feature. +It is required that the version be fully specified (e.g. no wildcards or +ranges). Other optional fields are: @@ -54,7 +53,7 @@ different crate) followed by `@` and either a crate name (so that a repository or other location where a surrogate can be obtained. Links must be plain FTP, FTPS, HTTP or HTTPS links. The intention is to allow rustdoc (and possibly other tools in the future, e.g. IDEs) to act on the included -information. +information. The `surrogate` field can have multiple values. On use of a *deprecated* item, `rustc` should `warn` of the deprecation. Note that during Cargo builds, warnings on dependencies get silenced. Note that @@ -100,5 +99,7 @@ complexity here * What other restrictions should we introduce now to avoid being bound to a possibly flawed design? -* Can / Should the `std` library make use of the `#[deprecate]` extensions? +* How should the multiple values in the `surrogate` field work? Just split by +some delimiter? +* Can / Should the `std` library make use of the `#[deprecated]` extensions? * Bikeshedding: Are the names good enough? From 449ec5330079eda27556a71b88c0f55eee8247ba Mon Sep 17 00:00:00 2001 From: llogiq Date: Wed, 9 Sep 2015 07:50:58 +0200 Subject: [PATCH 22/27] Incorporated chris-morgan's suggestion --- text/0000-deprecation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md index 731598af069..97b0d1f6832 100644 --- a/text/0000-deprecation.md +++ b/text/0000-deprecation.md @@ -6,8 +6,8 @@ # Summary This RFC proposes to allow library authors to use a `#[deprecated]` attribute, -with `since="`(version)`"`, `reason="`(free text)`"` and -`surrogate="`(text or surrogate declaration)`"` fields. The compiler can then +with `since = "`*version*`"`, `reason = "`*free text*`"` and +`surrogate = "`*text or surrogate declaration*`"` fields. The compiler can then warn on deprecated items, while `rustdoc` can document their deprecation accordingly. From 0c82b6bf272259df14ac561d750bc2ca184b5e1d Mon Sep 17 00:00:00 2001 From: llogiq Date: Wed, 9 Sep 2015 08:16:13 +0200 Subject: [PATCH 23/27] More alternatives, relation to internal feature --- text/0000-deprecation.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md index 97b0d1f6832..d2d751c2fa4 100644 --- a/text/0000-deprecation.md +++ b/text/0000-deprecation.md @@ -74,6 +74,9 @@ The language reference should be extended to describe this feature as outlined in this RFC. Authors shall be advised to leave their users enough time to react before *removing* a deprecated item. +The internally used feature can either be subsumed by this or possibly renamed +to avoid a name clash. + # Drawbacks * The required checks for the `since` and `surrogate` fields are potentially @@ -94,6 +97,8 @@ be `Allow` by default) * `reason` could include markdown formatting * The `surrogate` could simply be plain text, which would remove much of the complexity here +* The `surrogate` field could be left out and added later. However, this would +lead people to describe it in the `reason` field # Unresolved questions From feec81762ec2ddb6c5486462723cc84db2f2abb8 Mon Sep 17 00:00:00 2001 From: llogiq Date: Wed, 16 Sep 2015 17:11:16 +0200 Subject: [PATCH 24/27] Rename "surrogate" to "use", "since" now optional Also added an alternative to "use" which resolves more cleverly. --- text/0000-deprecation.md | 57 ++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md index d2d751c2fa4..3ef33765724 100644 --- a/text/0000-deprecation.md +++ b/text/0000-deprecation.md @@ -6,8 +6,8 @@ # Summary This RFC proposes to allow library authors to use a `#[deprecated]` attribute, -with `since = "`*version*`"`, `reason = "`*free text*`"` and -`surrogate = "`*text or surrogate declaration*`"` fields. The compiler can then +with optional `since = "`*version*`"`, `reason = "`*free text*`"` and +`use = "`*substitute declaration*`"` fields. The compiler can then warn on deprecated items, while `rustdoc` can document their deprecation accordingly. @@ -26,34 +26,30 @@ interface to use while maximizing usefulness of the metadata introduced. Public API items (both plain `fn`s, methods, trait- and inherent `impl`ementations as well as `const` definitions, type definitions, struct -fields and enum variants) can be given a `#[deprecated]` attribute. - -This attribute *must* have the `since` field, which contains the exact -version of the crate that deprecated the item, as defined by Cargo.toml -(thus following the semver scheme). It makes no sense to put a version number -higher than the current newest version here, and this is not checked (but -could be by external lints, e.g. -[rust-clippy](https://github.com/Manishearth/rust-clippy). - -It is required that the version be fully specified (e.g. no wildcards or -ranges). - -Other optional fields are: - +fields and enum variants) can be given a `#[deprecated]` attribute. All +possible fields are optional: + +* `since` is defined to contain the exact version of the crate that +deprecated the item, as defined by Cargo.toml (thus following the semver +scheme). It makes no sense to put a version number higher than the current +newest version here, and this is not checked (but could be by external +lints, e.g. [rust-clippy](https://github.com/Manishearth/rust-clippy). +To maximize usefulness, the version should be fully specified (e.g. no +wildcards or ranges). * `reason` should contain a human-readable string outlining the reason for deprecating the item. While this field is not required, library authors are strongly advised to make use of it to convey the reason to users of their library. The string is required to be plain unformatted text (for now) so that rustdoc can include it in the item's documentation without messing up the formatting. -* `surrogate` should be the full path to an API item that will replace the -functionality of the deprecated item, optionally (if the surrogate is in a +* `use` should be the full path to an API item that will replace the +functionality of the deprecated item, optionally (if the replacement is in a different crate) followed by `@` and either a crate name (so that `https://crates.io/crates/` followed by the name is a live link) or the URL to a repository or other location where a surrogate can be obtained. Links must be plain FTP, FTPS, HTTP or HTTPS links. The intention is to allow rustdoc (and possibly other tools in the future, e.g. IDEs) to act on the included -information. The `surrogate` field can have multiple values. +information. The `use` field can have multiple values. On use of a *deprecated* item, `rustc` should `warn` of the deprecation. Note that during Cargo builds, warnings on dependencies get silenced. Note that @@ -67,7 +63,7 @@ to warn on use of deprecated items in library crates, however this is outside the scope of this RFC. `rustdoc` should show deprecation on items, with a `[deprecated since x.y.z]` -box that may optionally show the reason and/or link to the surrogate if +box that may optionally show the reason and/or link to the replacement if available. The language reference should be extended to describe this feature as outlined @@ -79,32 +75,37 @@ to avoid a name clash. # Drawbacks -* The required checks for the `since` and `surrogate` fields are potentially +* The required checks for the `since` and `use` fields are potentially quite complex. * Once the feature is public, we can no longer change its design # Alternatives * Do nothing -* make the `since` field optional +* make the `since` field required and check that it's a single version * Optionally the deprecation lint chould check the current version as set by cargo in the CARGO_CRATE_VERSION environment variable (the rust build process should set this environment variable, too). This would allow future deprecations to be shown in the docs early, but not warned against by the stability lint (there could however be a `future-deprecation` lint that should be `Allow` by default) -* require either `reason` or `surrogate` be present +* require either `reason` or `use` be present * `reason` could include markdown formatting -* The `surrogate` could simply be plain text, which would remove much of the +* The `use` could simply be plain text, which would remove much of the complexity here -* The `surrogate` field could be left out and added later. However, this would -lead people to describe it in the `reason` field +* The `use` field contents could make use of the context in finding +replacements, e.g. extern crates, so that `time::precise_time_ns` would resolve +to the `time::precise_time_ns` API in the `time` crate, provided an +`extern crate time;` declaration is present +* The `use` field could be left out and added later. However, this would +lead people to describe a replacement in the `reason` field, as is already +happening in the case of rustc-private deprecation # Unresolved questions * What other restrictions should we introduce now to avoid being bound to a possibly flawed design? -* How should the multiple values in the `surrogate` field work? Just split by -some delimiter? +* How should the multiple values in the `use` field work? Just split by +comma or some other delimiter? * Can / Should the `std` library make use of the `#[deprecated]` extensions? * Bikeshedding: Are the names good enough? From b7597d1c807d79c5b7e30c728c3a9991aa18834f Mon Sep 17 00:00:00 2001 From: llogiq Date: Fri, 30 Oct 2015 00:42:25 +0100 Subject: [PATCH 25/27] improvements thanks to brson's comments --- text/0000-deprecation.md | 58 +++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md index 3ef33765724..9fe4f438de0 100644 --- a/text/0000-deprecation.md +++ b/text/0000-deprecation.md @@ -9,7 +9,7 @@ This RFC proposes to allow library authors to use a `#[deprecated]` attribute, with optional `since = "`*version*`"`, `reason = "`*free text*`"` and `use = "`*substitute declaration*`"` fields. The compiler can then warn on deprecated items, while `rustdoc` can document their deprecation -accordingly. +accordingly. # Motivation @@ -29,29 +29,26 @@ Public API items (both plain `fn`s, methods, trait- and inherent fields and enum variants) can be given a `#[deprecated]` attribute. All possible fields are optional: -* `since` is defined to contain the exact version of the crate that -deprecated the item, as defined by Cargo.toml (thus following the semver -scheme). It makes no sense to put a version number higher than the current -newest version here, and this is not checked (but could be by external -lints, e.g. [rust-clippy](https://github.com/Manishearth/rust-clippy). -To maximize usefulness, the version should be fully specified (e.g. no -wildcards or ranges). +* `since` is defined to contain the version of the crate at the time of +deprecating the item, following the semver scheme. It makes no sense to put a +version number higher than the current newest version here, and this is not +checked (but could be by external lints, e.g. +[rust-clippy](https://github.com/Manishearth/rust-clippy). * `reason` should contain a human-readable string outlining the reason for deprecating the item. While this field is not required, library authors are -strongly advised to make use of it to convey the reason to users of their -library. The string is required to be plain unformatted text (for now) so that -rustdoc can include it in the item's documentation without messing up the -formatting. -* `use` should be the full path to an API item that will replace the -functionality of the deprecated item, optionally (if the replacement is in a -different crate) followed by `@` and either a crate name (so that -`https://crates.io/crates/` followed by the name is a live link) or the URL to -a repository or other location where a surrogate can be obtained. Links must be -plain FTP, FTPS, HTTP or HTTPS links. The intention is to allow rustdoc (and -possibly other tools in the future, e.g. IDEs) to act on the included -information. The `use` field can have multiple values. - -On use of a *deprecated* item, `rustc` should `warn` of the deprecation. Note +strongly advised to make use of it to convey the reason for the deprecation to +users of their library. The string is interpreted as plain unformatted text +(for now) so that rustdoc can include it in the item's documentation without +messing up the formatting. +* `use`, if included, must be the import path (or a comma-separated list of +paths) to a set of API items that will replace the functionality of the +deprecated item. All crates in scope can be reached by this path. E.g. let's +say my `foo()` item was superceded by either the `bar()` or `baz()` functions +in the `bar` crate, I can `#[deprecate(use="bar::{bar,baz}")] foo()`, as long +as I have the `bar` crate in the library path. Rustc checks if the item is +actually available, otherwise returning an error. + +On use of a *deprecated* item, `rustc` will `warn` of the deprecation. Note that during Cargo builds, warnings on dependencies get silenced. Note that while this has the upside of keeping things tidy, it has a downside when it comes to deprecation: @@ -62,11 +59,11 @@ try to build `foobar` directly. We may want to create a service like `crater` to warn on use of deprecated items in library crates, however this is outside the scope of this RFC. -`rustdoc` should show deprecation on items, with a `[deprecated since x.y.z]` -box that may optionally show the reason and/or link to the replacement if -available. +`rustdoc` will show deprecation on items, with a `[deprecated]` +box that may optionally show the version, reason and/or link to the replacement +if available. -The language reference should be extended to describe this feature as outlined +The language reference will be extended to describe this feature as outlined in this RFC. Authors shall be advised to leave their users enough time to react before *removing* a deprecated item. @@ -83,7 +80,7 @@ quite complex. * Do nothing * make the `since` field required and check that it's a single version -* Optionally the deprecation lint chould check the current version as set by +* Optionally the deprecation lint could check the current version as set by cargo in the CARGO_CRATE_VERSION environment variable (the rust build process should set this environment variable, too). This would allow future deprecations to be shown in the docs early, but not warned against by the @@ -93,13 +90,12 @@ be `Allow` by default) * `reason` could include markdown formatting * The `use` could simply be plain text, which would remove much of the complexity here -* The `use` field contents could make use of the context in finding -replacements, e.g. extern crates, so that `time::precise_time_ns` would resolve -to the `time::precise_time_ns` API in the `time` crate, provided an -`extern crate time;` declaration is present * The `use` field could be left out and added later. However, this would lead people to describe a replacement in the `reason` field, as is already happening in the case of rustc-private deprecation +* Optionally, `cargo` could offer a new dependency category: "doc-dependencies" +which are used to pull in other crates' documentations to link them (this is +obviously not only relevant to deprecation). # Unresolved questions From c8a726c090c1cdb9acb9d95304ca4f8f021363f6 Mon Sep 17 00:00:00 2001 From: llogiq Date: Fri, 30 Oct 2015 05:56:41 +0100 Subject: [PATCH 26/27] Change `use` to semicolon-delimited, expand example --- text/0000-deprecation.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md index 9fe4f438de0..1c2168974cb 100644 --- a/text/0000-deprecation.md +++ b/text/0000-deprecation.md @@ -40,13 +40,14 @@ strongly advised to make use of it to convey the reason for the deprecation to users of their library. The string is interpreted as plain unformatted text (for now) so that rustdoc can include it in the item's documentation without messing up the formatting. -* `use`, if included, must be the import path (or a comma-separated list of +* `use`, if included, must be the import path (or a semicolon-delimited list of paths) to a set of API items that will replace the functionality of the deprecated item. All crates in scope can be reached by this path. E.g. let's say my `foo()` item was superceded by either the `bar()` or `baz()` functions -in the `bar` crate, I can `#[deprecate(use="bar::{bar,baz}")] foo()`, as long -as I have the `bar` crate in the library path. Rustc checks if the item is -actually available, otherwise returning an error. +in the `bar` crate, in conjunction with the `bruzz(_)` function in the `baz` +crate, I can `#[deprecate(use="bar::{bar,baz};baz::bruzz")] foo()`, as long +as I have the `bar` and `baz` crates in the library path. Rustc checks if the +item is actually available, otherwise returning an error. On use of a *deprecated* item, `rustc` will `warn` of the deprecation. Note that during Cargo builds, warnings on dependencies get silenced. Note that From 8ec2ccd11da542b6d5fb914a2485a2b27682c447 Mon Sep 17 00:00:00 2001 From: llogiq Date: Thu, 19 Nov 2015 09:21:29 +0100 Subject: [PATCH 27/27] relegated use to alternatives, user story, clippy --- text/0000-deprecation.md | 96 ++++++++++++++++++++++++---------------- 1 file changed, 58 insertions(+), 38 deletions(-) diff --git a/text/0000-deprecation.md b/text/0000-deprecation.md index 1c2168974cb..0528bb261f7 100644 --- a/text/0000-deprecation.md +++ b/text/0000-deprecation.md @@ -6,10 +6,9 @@ # Summary This RFC proposes to allow library authors to use a `#[deprecated]` attribute, -with optional `since = "`*version*`"`, `reason = "`*free text*`"` and -`use = "`*substitute declaration*`"` fields. The compiler can then -warn on deprecated items, while `rustdoc` can document their deprecation -accordingly. +with optional `since = "`*version*`"` and `reason = "`*free text*`"`fields. The +compiler can then warn on deprecated items, while `rustdoc` can document their +deprecation accordingly. # Motivation @@ -30,29 +29,20 @@ fields and enum variants) can be given a `#[deprecated]` attribute. All possible fields are optional: * `since` is defined to contain the version of the crate at the time of -deprecating the item, following the semver scheme. It makes no sense to put a -version number higher than the current newest version here, and this is not -checked (but could be by external lints, e.g. -[rust-clippy](https://github.com/Manishearth/rust-clippy). +deprecating the item, following the semver scheme. Rustc does not know about +versions, thus the content of this field is not checked (but will be by external +lints, e.g. [rust-clippy](https://github.com/Manishearth/rust-clippy). * `reason` should contain a human-readable string outlining the reason for deprecating the item. While this field is not required, library authors are strongly advised to make use of it to convey the reason for the deprecation to users of their library. The string is interpreted as plain unformatted text (for now) so that rustdoc can include it in the item's documentation without messing up the formatting. -* `use`, if included, must be the import path (or a semicolon-delimited list of -paths) to a set of API items that will replace the functionality of the -deprecated item. All crates in scope can be reached by this path. E.g. let's -say my `foo()` item was superceded by either the `bar()` or `baz()` functions -in the `bar` crate, in conjunction with the `bruzz(_)` function in the `baz` -crate, I can `#[deprecate(use="bar::{bar,baz};baz::bruzz")] foo()`, as long -as I have the `bar` and `baz` crates in the library path. Rustc checks if the -item is actually available, otherwise returning an error. On use of a *deprecated* item, `rustc` will `warn` of the deprecation. Note -that during Cargo builds, warnings on dependencies get silenced. Note that -while this has the upside of keeping things tidy, it has a downside when it -comes to deprecation: +that during Cargo builds, warnings on dependencies get silenced. While this has +the upside of keeping things tidy, it has a downside when it comes to +deprecation: Let's say I have my `llogiq` crate that depends on `foobar` which uses a deprecated item of `serde`. I will never get the warning about this unless I @@ -60,9 +50,8 @@ try to build `foobar` directly. We may want to create a service like `crater` to warn on use of deprecated items in library crates, however this is outside the scope of this RFC. -`rustdoc` will show deprecation on items, with a `[deprecated]` -box that may optionally show the version, reason and/or link to the replacement -if available. +`rustdoc` will show deprecation on items, with a `[deprecated]` box that may +optionally show the version and reason where available. The language reference will be extended to describe this feature as outlined in this RFC. Authors shall be advised to leave their users enough time to react @@ -71,38 +60,69 @@ before *removing* a deprecated item. The internally used feature can either be subsumed by this or possibly renamed to avoid a name clash. +# Intended Use + +Crate author Anna wants to evolve her crate's API. She has found that one +type, `Foo`, has a better implementation in the `rust-foo` crate. Also she has +written a `frob(Foo)` function to replace the earlier `Foo::frobnicate(self)` +method. + +So Anna first bumps the version of her crate (because deprecation is always +done on a version change) from `0.1.1` to `0.2.1`. She also adds the following +prefix to the `Foo` type: + +``` +extern crate rust_foo; + +#[deprecated(since = "0.2.1", use="rust_foo::Foo", + reason="The rust_foo version is more advanced, and this crates' will likely be discontinued")] +struct Foo { .. } +``` + +Users of her crate will see the following once they `cargo update` and `build`: + +``` +src/foo_use.rs:27:5: 27:8 warning: Foo is marked deprecated as of version 0.2.1 +src/foo_use.rs:27:5: 27:8 note: The rust_foo version is more advanced, and this crates' will likely be discontinued +``` + +Rust-clippy will likely gain more sophisticated checks for deprecation: + +* `future_deprecation` will warn on items marked as deprecated, but with a +version lower than their crates', while `current_deprecation` will warn only on +those items marked as deprecated where the version is equal or lower to the +crates' one. +* `deprecation_syntax` will check that the `since` field really contains a +semver number and not some random string. + +Clippy users can then activate the clippy checks and deactivate the standard +deprecation checks. + # Drawbacks -* The required checks for the `since` and `use` fields are potentially -quite complex. * Once the feature is public, we can no longer change its design # Alternatives * Do nothing * make the `since` field required and check that it's a single version -* Optionally the deprecation lint could check the current version as set by -cargo in the CARGO_CRATE_VERSION environment variable (the rust build process -should set this environment variable, too). This would allow future -deprecations to be shown in the docs early, but not warned against by the -stability lint (there could however be a `future-deprecation` lint that should -be `Allow` by default) * require either `reason` or `use` be present * `reason` could include markdown formatting -* The `use` could simply be plain text, which would remove much of the -complexity here -* The `use` field could be left out and added later. However, this would -lead people to describe a replacement in the `reason` field, as is already -happening in the case of rustc-private deprecation +* rename the `reason` field to `note` to clarify it's broader usage. +* add a `note` field and make `reason` a field with specific meaning, perhaps +even predefine a number of valid reason strings, as JEP277 currently does +* Add a `use` field containing a plain text of what to use instead +* Add a `use` field containing a path to some function, type, etc. to replace +the current feature. Currently with the rustc-private feature, people are +describing a replacement in the `reason` field, which is clearly not the +original intention of the field * Optionally, `cargo` could offer a new dependency category: "doc-dependencies" which are used to pull in other crates' documentations to link them (this is -obviously not only relevant to deprecation). +obviously not only relevant to deprecation) # Unresolved questions * What other restrictions should we introduce now to avoid being bound to a possibly flawed design? -* How should the multiple values in the `use` field work? Just split by -comma or some other delimiter? * Can / Should the `std` library make use of the `#[deprecated]` extensions? * Bikeshedding: Are the names good enough?