Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Target Version #1147

Closed
wants to merge 20 commits into from
Closed
Changes from 12 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
8bf55ca
new RFC: deprecation
llogiq Jun 3, 2015
cfc7d64
word wrapped, thanks to steveklabnik
llogiq Jun 3, 2015
20666af
clarified how cargo gets rust version
llogiq Jun 3, 2015
fb16b8a
Merge branch 'master' of https://github.com/rust-lang/rfcs
llogiq Jun 4, 2015
873f241
Added paragraph on how rustc should handle future target versions
llogiq Jun 4, 2015
0754c8c
Fleshed out the Motivation section a bit
llogiq Jun 10, 2015
984cc70
added future/legacy flags to alternatives
llogiq Jun 11, 2015
f5c7c56
More explanation about security issues
llogiq Jun 11, 2015
5ffff4a
Clarified the paragraph about cargo/crates.io, also added Policy section
llogiq Jun 15, 2015
1b0fe8c
clarification on insecurity and future-proofing
llogiq Jun 15, 2015
704c985
added Cargo.toml-based target to Alternatives section
llogiq Jun 17, 2015
afb54c9
Almost complete rewrite.
llogiq Jun 19, 2015
045c03e
Renamed --target to --target-version, reworded Cargo entry default
llogiq Jun 25, 2015
2e44ed0
added open question about cargo and detailed design about feature fla…
llogiq Jun 25, 2015
97b32e8
made 'rust' a package attribute instead of a pseudo-dependency
llogiq Jun 26, 2015
62bda96
formatting improvements
llogiq Jun 28, 2015
b8f1490
Added previous proposal to alternatives, added bikeshedding to unreso…
llogiq Jul 4, 2015
d1e2725
Merge branch 'master' of https://github.com/rust-lang/rfcs
llogiq Jul 4, 2015
955430b
#[insecure]-flagging removed from RFC, added some open questions (as …
llogiq Jul 9, 2015
22108ae
Merge branch 'master' of https://github.com/rust-lang/rfcs
llogiq Jul 9, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
243 changes: 243 additions & 0 deletions text/0000-deprecation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
- Feature Name: Target Version
- Start Date: 2015-06-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 string> command line argument to rustc.
This will be used for deprecation checking and for selecting code paths
in the compiler.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rustc already has a --target flag, used for a completely separate purpose (selecting target triples).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch. @bstrie: Care for some bikeshedding?

  • --std (from C, but people could confuse it w/ nostd)
  • --semver (too abbreviated)
  • --source (from Java)
  • --lang-version
  • --target-version

What do you prefer?

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, which this
proposal is mostly 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.

# Motivation

## 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="<version>"` 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

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 `*`.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either this or what? Just wondering, from the wording this seems a bit ambiguous.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it should originally read 'or 1.0.0', because that would keep code without a rust dependency from breaking. However, that may also introduce spurious warnings/errors, so I removed it.


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 <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 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. 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

* 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
needing a new attribute key.

# Unresolved questions

I no longer have any. Please join the discussion to add yours.