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

Add info about stability attribute modifications #1

Closed
Closed
Changes from all commits
Commits
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
184 changes: 158 additions & 26 deletions text/0000-release-channels.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ as well as 'feature staging', which enables the continued development
of experimental language features and libraries APIs while providing
strong stability guarantees in stable releases.

This RFC also covers the topic of altering the meaning of the stability
attributes which Rust uses today to signal the stability of the standard
library and how this interacts with feature staging. Finally, the
interaction between feature staging, experimental features, and Cargo
will be discussed.

# Motivation

We soon intend to [provide stable releases][1] of Rust that offer
Expand Down Expand Up @@ -119,36 +125,104 @@ channel, and the release on the beta channel will be called
called 1.0.0-beta2, -beta3, etc, before being promoted to the stable
channel as 1.0.0 and beginning the release train process in full.

## Stability Attributes

Inspired by the node.js stability levels, Rust has five levels of
stability attributes, `#[stable]`, `#[unstable]`, `#[experimental]`,
`#[deprecated]`, and unmarked. Over time, however, two primary drawbacks
have arisen:

1. The distinction between `#[unstable]` and `#[experimental]` is murky
at best and it's unclear when an API transitions from one to the other.
2. As discussed below, the standard library will not expose
experimental/unstable functionality on the stable and beta release
channels. Users of the nightly channel, however, have no way to
select *which* unstable functionality they would like from the
standard library. A warn-by-default lint will simply warn the crate
on any and all usage of experimental functionality. This coarse
granularity can lead to opting into more features than one would like
when testing out the nightly builds.

To handle these two problems, as well as play into some of the designs
listed below, this RFC proposes the following modifications to today's
stability attributes:

* Remove `#[experimental]` and recommend all users use `#[unstable]`
instead.
* Alter the syntax of all attributes to `#[level(description = "...")]`
to allow more metadata inside each `level`. A deprecation warning will
be provided for migration, but an unused attribute warning will be
issued eventually.
* Add `feature = "name"` to the `#[unstable]` attribute metadata. This
signals that the attribute applies to a feature named `name`.
* Add `since = "version"` to the `#[stable]` attribute metadata. This
signals the version of the language since which the API was stable.

With these modifications, new API surface area becomes a new "language
feature" which is controlled via the `#[feature]` attribute just like
other normal language features. The compiler will disallow all usage of
`#[experimental(feature = "foo")]` apis unless the current crate
declares `#![feature(foo)]`. This enables crates to declare what API
features of the standard library they rely on without opting in to all
experimental API features.

### API Lifecycle

These attributes alter the process of how new APIs are added to the
standard library slightly. First an API will be proposed via the RFC
process, and a name for the API feature being added will be assigned at
that time. When the RFC is accepted, the API will be added to the
standard library with an `#[experimental(feature = "...")]`attribute
indicating what feature the API was assigned to.

After receiving test coverage from nightly users (who have opted into
the feature) or thorough review, the API will be moved from
`#[experimental]` to `#[stable(since = "...")]` where the version listed
is the next stable version of the compiler that the API will be included
in. Note that this is two releases ahead of the current stable compiler
due to the beta period. For example, if the current stable channel is
1.1.0, then new accepted APIs will be tagged with `#[stable(since =
"1.3.0")]` because the current beta channel is the `1.2.0` release and
the nightly channel is the `1.3.0` release.

### Checking `#[feature]`

The names of features will no longer be a hardcoded list in the compiler
due to the free-form nature of the `#[experimental]` feature names.
Instead, the compiler will perform the following steps when inspecting
`#[feature]` attributes lists:

1. The compiler will discover all `#![feature]` directives
enabled for the crate and calculate a list of all enabled features.
2. While compiling, all experimental language features used will be
removed from this list. If a used feature is note enabled, then an
error is generated.
3. A new pass, the stability pass, will be extracted from the current
stability lint pass to detect usage of all experimental APIs. If an
experimental API is used, an error is generated if the feature is not
used, and otherwise the feature is removed from the list.
4. If the remaining list of enabled features is not empty, then the
features were not used when compiling the current crate. The compiler
will generate an error in this case unconditionally.

These steps ensure that the `#[feature]` attribute is used exhaustively
and will check experimental API and language features.

## Feature staging

In builds of Rust distributed through the 'beta' and 'stable' release
channels, it is impossible to turn on experimental language features
by writing the `#[feature(...)]` attribute or to use APIs *from
libraries distributed as part of the main Rust distribution* tagged
with either `#[experimental]` or `#[unstable]`. This is accomplished
primarily through three new lints, `experimental_features`,
`staged_unstable`, and `staged_experimental`, which are set to 'allow'
by default in nightlies, and 'forbid' in beta and stable releases.
channels, it is impossible to turn on experimental features
by writing the `#[feature(...)]` attribute. This is accomplished
primarily through a new lint called `experimental_features`.
This lint is set to `allow` by default in nightlies and `forbid` in beta
and stable releases.

The `experimental_features` lint simply looks for all 'feature'
attributes and emits the message 'experimental feature'.

The `staged_unstable` and `staged_experimental` behave exactly like
the existing `unstable` and `experimental` lints, emitting the message
'unstable' and 'experimental', except that they only apply to crates
marked with the `#[staged_api]` attribute. If this attribute is not
present then the lints have no effect.

All crates in the Rust distribution are marked `#[staged_api]`.
Libraries in the Cargo registry are not bound to participate in
feature staging because they are not required to be
`#[staged_api]`. Crates maintained by the Rust project (the rust-lang
org on GitHub) but not included in the main Rust distribution are not
`#[staged_api]`.

The decision to set the feature staging lints is driven by a new field
The decision to set the feature staging lint is driven by a new field
of the compilation `Session`, `disable_staged_features`. When set to
true the lint pass will configure the three feature staging lints to
true the lint pass will configure the feature staging lint to
'forbid', with a `LintSource` of `ReleaseChannel`. Once set to
'forbid' it is not possible for code to programmaticaly disable the
lint. When a `ReleaseChannel` lint is triggered, in addition to the
Expand All @@ -161,16 +235,15 @@ In feature-staged builds of Rust, rustdoc sets
be possible for rustdoc to successfully run against e.g. the
accompanying std crate, as rustdoc runs the lint pass. Additionally,
in feature-staged builds, rustdoc does not generate documentation for
experimental and unstable APIs for crates with the `#[staged_api]`
attribute.
unstable APIs for crates.

With staged features disabled, the Rust build itself is not possible,
and some portion of the test suite will fail. To build the compiler
itself and keep the test suite working the build system activates
a hack via environment variables to disable the feature staging lints,
a hack via environment variables to disable the feature staging lint,
a mechanism that is not be available under typical use. The build
system additionally includes a way to run the test suite with the
feature staging lints enabled, providing a means of tracking what
feature staging lint enabled, providing a means of tracking what
portion of the test suite can be run without invoking experimental
features.

Expand All @@ -181,6 +254,57 @@ the short term this will be worked-around with hacks in the
compiler. It's likely that these hacks can be removed before 1.0 if
globs and `macro_rules!` imports become stable.

## Features and Cargo

Over time, it has become clear that with an ever-growing number of Rust
releases that crates will want to be able to manage what versions of
rust they indicate they can be compiled with. Some specific use cases are:

* Although upgrades are highly encouraged, not all users upgrade
immediately. Cargo should be able to help out with the process of
downloading a new dependency and indicating that a newer version of
the Rust compiler is required.
* Not all users will be able to continuously upgrade. Some enterprises,
for example, may upgrade rarely for technical reasons. In doing so,
however, a large portion of the crates.io ecosystem becomes unusable
once accepted features begin to propagate.
* Developers may wish to prepare new releases of libraries during the
beta channel cycle in order to have libraries ready for the next
stable release. In this window, however, published versions will not
be compatible with the current stable compiler (they use new
features).

To solve this problem, Cargo and crates.io will grow the knowledge of
the minimum required Rust language version required to compile a crate.
Currently the Rust language version coincides with the version of the
`rustc` compiler.

To calculate this information, Cargo will compile crates just before
publishing. In this process, the Rust compiler will record all used
language features as well as all used `#[stable]` APIs. Each compiler
will contain archival knowledge of what stable version of the compiler
language features were added to, and each `#[stable]` API has the
`since` metadata to tell which version of the compiler it was released
in. The compiler will calculate the maximum of all these versions
(language plus library features) to pass to Cargo. If any `#[feature]`
directive is detected, however, the required Rust language version is
"nightly".

Cargo will then pass this required language version to crates.io which
will both store it in the index as well as present it as part of the UI.
Each crate will have a "badge" indicating what version of the Rust
compiler is needed to compile it. The "badge" may indicate that the
nightly or beta channels must be used if the version required has not
yet been released (this happens when a crate is published on a
non-stable channel). If the required language version is "nightly", then
the crate will permanently indicate that it requires the "nightly"
version of the language.

When resolving dependencies, Cargo will discard all incompatible
candidates based on the version of the available compiler. This will
enable authors to publish crates which rely on the current beta channel
while not interfering with users taking advantage of the stable channel.

# Drawbacks

Adding multiple release channels and reducing the release cycle from
Expand Down Expand Up @@ -211,6 +335,14 @@ are very popular. This pain will partially be alleviated by a proposed
[Cargo]: https://github.com/rust-lang/rfcs/pull/403
[1]: http://blog.rust-lang.org/2014/10/30/Stability.html

With respect to stability attributes and Cargo, the proposed design is
very specific to the standard library and the Rust compiler without
being intended for use by third-party libraries. It is planned to extend
Cargo's own support for features (distinct from Rust features) to enable
this form of feature development in a first-class method through Cargo.
At this time, however, there are no concrete plans for this design and
it is unlikely to happen soon.

# Alternatives

Leave feature gates and experimental APIs exposed to the stable
Expand Down