Skip to content

Commit c27133e

Browse files
committed
Preliminary feature staging
This partially implements the feature staging described in the [release channel RFC][rc]. It does not yet fully conform to the RFC as written, but does accomplish its goals sufficiently for the 1.0 alpha release. It has three primary user-visible effects: * On the nightly channel, use of unstable APIs generates a warning. * On the beta channel, use of unstable APIs generates a warning. * On the beta channel, use of feature gates generates a warning. Code that does not trigger these warnings is considered 'stable', modulo pre-1.0 bugs. Disabling the warnings for unstable APIs continues to be done in the existing (i.e. old) style, via `#[allow(...)]`, not that specified in the RFC. I deem this marginally acceptable since any code that must do this is not using the stable dialect of Rust. Use of feature gates is itself gated with the new 'unstable_features' lint, on nightly set to 'allow', and on beta 'warn'. The attribute scheme used here corresponds to an older version of the RFC, with the `#[staged_api]` crate attribute toggling the staging behavior of the stability attributes, but the user impact is only in-tree so I'm not concerned about having to make design changes later (and I may ultimately prefer the scheme here after all, with the `#[staged_api]` crate attribute). Since the Rust codebase itself makes use of unstable features the compiler and build system to a midly elaborate dance to allow it to bootstrap while disobeying these lints (which would otherwise be errors because Rust builds with `-D warnings`). This patch includes one significant hack that causes a regression. Because the `format_args!` macro emits calls to unstable APIs it would trigger the lint. I added a hack to the lint to make it not trigger, but this in turn causes arguments to `println!` not to be checked for feature gates. I don't presently understand macro expansion well enough to fix. This is bug #20661. Closes #16678 [rc]: https://github.com/rust-lang/rfcs/blob/master/text/0507-release-channels.md
1 parent 9f1ead8 commit c27133e

File tree

43 files changed

+272
-34
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+272
-34
lines changed

configure

+12
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,18 @@ then
599599
fi
600600
putvar CFG_RELEASE_CHANNEL
601601

602+
# A magic value that allows the compiler to use unstable features
603+
# during the bootstrap even when doing so would normally be an error
604+
# because of feature staging or because the build turns on
605+
# warnings-as-errors and unstable features default to warnings. The
606+
# build has to match this key in an env var. Meant to be a mild
607+
# deterrent from users just turning on unstable features on the stable
608+
# channel.
609+
# Basing CFG_BOOTSTRAP_KEY on CFG_BOOTSTRAP_KEY lets it get picked up
610+
# during a Makefile reconfig.
611+
CFG_BOOTSTRAP_KEY="${CFG_BOOTSTRAP_KEY-`date +%N`}"
612+
putvar CFG_BOOTSTRAP_KEY
613+
602614
step_msg "looking for build programs"
603615

604616
probe_need CFG_PERL perl

mk/main.mk

+11
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@ ifeq ($(CFG_RELEASE_CHANNEL),stable)
2525
CFG_RELEASE=$(CFG_RELEASE_NUM)
2626
# This is the string used in dist artifact file names, e.g. "0.12.0", "nightly"
2727
CFG_PACKAGE_VERS=$(CFG_RELEASE_NUM)
28+
CFG_DISABLE_UNSTABLE_FEATURES=1
2829
endif
2930
ifeq ($(CFG_RELEASE_CHANNEL),beta)
3031
# The beta channel is temporarily called 'alpha'
3132
CFG_RELEASE=$(CFG_RELEASE_NUM)-alpha$(CFG_BETA_CYCLE)
3233
CFG_PACKAGE_VERS=$(CFG_RELEASE_NUM)-alpha$(CFG_BETA_CYCLE)
34+
CFG_DISABLE_UNSTABLE_FEATURES=1
3335
endif
3436
ifeq ($(CFG_RELEASE_CHANNEL),nightly)
3537
CFG_RELEASE=$(CFG_RELEASE_NUM)-nightly
@@ -319,11 +321,20 @@ export CFG_VERSION_WIN
319321
export CFG_RELEASE
320322
export CFG_PACKAGE_NAME
321323
export CFG_BUILD
324+
export CFG_RELEASE_CHANNEL
322325
export CFG_LLVM_ROOT
323326
export CFG_PREFIX
324327
export CFG_LIBDIR
325328
export CFG_LIBDIR_RELATIVE
326329
export CFG_DISABLE_INJECT_STD_VERSION
330+
ifdef CFG_DISABLE_UNSTABLE_FEATURES
331+
CFG_INFO := $(info cfg: disabling unstable features (CFG_DISABLE_UNSTABLE_FEATURES))
332+
# Turn on feature-staging
333+
export CFG_DISABLE_UNSTABLE_FEATURES
334+
endif
335+
# Subvert unstable feature lints to do the self-build
336+
export CFG_BOOTSTRAP_KEY
337+
export RUSTC_BOOTSTRAP_KEY:=$(CFG_BOOTSTRAP_KEY)
327338

328339
######################################################################
329340
# Per-stage targets and runner

src/liballoc/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
5959
#![crate_name = "alloc"]
6060
#![experimental]
61+
#![staged_api]
6162
#![crate_type = "rlib"]
6263
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
6364
html_favicon_url = "http://www.rust-lang.org/favicon.ico",

src/libarena/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
2222
#![crate_name = "arena"]
2323
#![experimental]
24+
#![staged_api]
2425
#![crate_type = "rlib"]
2526
#![crate_type = "dylib"]
2627
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",

src/libcollections/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#![crate_name = "collections"]
1717
#![experimental]
18+
#![staged_api]
1819
#![crate_type = "rlib"]
1920
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
2021
html_favicon_url = "http://www.rust-lang.org/favicon.ico",

src/libcore/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949

5050
#![crate_name = "core"]
5151
#![experimental]
52+
#![staged_api]
5253
#![crate_type = "rlib"]
5354
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
5455
html_favicon_url = "http://www.rust-lang.org/favicon.ico",

src/libflate/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
1717
#![crate_name = "flate"]
1818
#![experimental]
19+
#![staged_api]
1920
#![crate_type = "rlib"]
2021
#![crate_type = "dylib"]
2122
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",

src/libfmt_macros/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
1717
#![crate_name = "fmt_macros"]
1818
#![experimental]
19+
#![staged_api]
1920
#![crate_type = "rlib"]
2021
#![crate_type = "dylib"]
2122
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",

src/libgetopts/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
8080
#![crate_name = "getopts"]
8181
#![experimental = "use the crates.io `getopts` library instead"]
82+
#![staged_api]
8283
#![crate_type = "rlib"]
8384
#![crate_type = "dylib"]
8485
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",

src/libgraphviz/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@
266266
267267
#![crate_name = "graphviz"]
268268
#![experimental]
269+
#![staged_api]
269270
#![crate_type = "rlib"]
270271
#![crate_type = "dylib"]
271272
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",

src/liblibc/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#![crate_name = "libc"]
1212
#![crate_type = "rlib"]
1313
#![cfg_attr(not(feature = "cargo-build"), experimental)]
14+
#![cfg_attr(not(feature = "cargo-build"), staged_api)]
1415
#![no_std]
1516
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
1617
html_favicon_url = "http://www.rust-lang.org/favicon.ico",

src/liblog/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@
157157
158158
#![crate_name = "log"]
159159
#![experimental = "use the crates.io `log` library instead"]
160+
#![staged_api]
160161
#![crate_type = "rlib"]
161162
#![crate_type = "dylib"]
162163
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",

src/librand/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
#![no_std]
2727
#![experimental]
28+
#![staged_api]
2829

2930
#[macro_use]
3031
extern crate core;

src/librbml/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
1818
#![crate_name = "rbml"]
1919
#![experimental]
20+
#![staged_api]
2021
#![crate_type = "rlib"]
2122
#![crate_type = "dylib"]
2223
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",

src/libregex/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#![crate_type = "rlib"]
1818
#![crate_type = "dylib"]
1919
#![experimental = "use the crates.io `regex` library instead"]
20+
#![staged_api]
2021
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
2122
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
2223
html_root_url = "http://doc.rust-lang.org/nightly/",

src/librustc/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
1717
#![crate_name = "rustc"]
1818
#![experimental]
19+
#![staged_api]
1920
#![crate_type = "dylib"]
2021
#![crate_type = "rlib"]
2122
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",

src/librustc/lint/builtin.rs

+67-10
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use middle::{def, pat_util, stability};
3434
use middle::const_eval::{eval_const_expr_partial, const_int, const_uint};
3535
use util::ppaux::{ty_to_string};
3636
use util::nodemap::{FnvHashMap, NodeSet};
37-
use lint::{Context, LintPass, LintArray};
37+
use lint::{Context, LintPass, LintArray, Lint};
3838

3939
use std::collections::hash_map::Entry::{Occupied, Vacant};
4040
use std::num::SignedInt;
@@ -1643,19 +1643,27 @@ declare_lint! {
16431643
"detects use of #[unstable] items (incl. items with no stability attribute)"
16441644
}
16451645

1646+
declare_lint!(STAGED_EXPERIMENTAL, Warn,
1647+
"detects use of #[experimental] items in staged builds");
1648+
1649+
declare_lint!(STAGED_UNSTABLE, Warn,
1650+
"detects use of #[unstable] items (incl. items with no stability attribute) \
1651+
in staged builds");
1652+
16461653
/// Checks for use of items with `#[deprecated]`, `#[experimental]` and
16471654
/// `#[unstable]` attributes, or no stability attribute.
16481655
#[derive(Copy)]
16491656
pub struct Stability;
16501657

16511658
impl Stability {
16521659
fn lint(&self, cx: &Context, id: ast::DefId, span: Span) {
1653-
let stability = stability::lookup(cx.tcx, id);
1660+
1661+
let ref stability = stability::lookup(cx.tcx, id);
16541662
let cross_crate = !ast_util::is_local(id);
16551663

16561664
// stability attributes are promises made across crates; only
16571665
// check DEPRECATED for crate-local usage.
1658-
let (lint, label) = match stability {
1666+
let (lint, label) = match *stability {
16591667
// no stability attributes == Unstable
16601668
None if cross_crate => (UNSTABLE, "unmarked"),
16611669
Some(attr::Stability { level: attr::Unstable, .. }) if cross_crate =>
@@ -1667,24 +1675,53 @@ impl Stability {
16671675
_ => return
16681676
};
16691677

1670-
let msg = match stability {
1671-
Some(attr::Stability { text: Some(ref s), .. }) => {
1672-
format!("use of {} item: {}", label, *s)
1678+
output(cx, span, stability, lint, label);
1679+
if cross_crate && stability::is_staged_api(cx.tcx, id) {
1680+
if lint.name == UNSTABLE.name {
1681+
output(cx, span, stability, STAGED_UNSTABLE, label);
1682+
} else if lint.name == EXPERIMENTAL.name {
1683+
output(cx, span, stability, STAGED_EXPERIMENTAL, label);
16731684
}
1674-
_ => format!("use of {} item", label)
1675-
};
1685+
}
16761686

1677-
cx.span_lint(lint, span, msg.index(&FullRange));
1687+
fn output(cx: &Context, span: Span, stability: &Option<attr::Stability>,
1688+
lint: &'static Lint, label: &'static str) {
1689+
let msg = match *stability {
1690+
Some(attr::Stability { text: Some(ref s), .. }) => {
1691+
format!("use of {} item: {}", label, *s)
1692+
}
1693+
_ => format!("use of {} item", label)
1694+
};
1695+
1696+
cx.span_lint(lint, span, msg.index(&FullRange));
1697+
}
16781698
}
16791699

1700+
16801701
fn is_internal(&self, cx: &Context, span: Span) -> bool {
16811702
cx.tcx.sess.codemap().span_is_internal(span)
16821703
}
1704+
16831705
}
16841706

16851707
impl LintPass for Stability {
16861708
fn get_lints(&self) -> LintArray {
1687-
lint_array!(DEPRECATED, EXPERIMENTAL, UNSTABLE)
1709+
lint_array!(DEPRECATED, EXPERIMENTAL, UNSTABLE, STAGED_EXPERIMENTAL, STAGED_UNSTABLE)
1710+
}
1711+
1712+
fn check_crate(&mut self, _: &Context, c: &ast::Crate) {
1713+
// Just mark the #[staged_api] attribute used, though nothing else is done
1714+
// with it during this pass over the source.
1715+
for attr in c.attrs.iter() {
1716+
if attr.name().get() == "staged_api" {
1717+
match attr.node.value.node {
1718+
ast::MetaWord(_) => {
1719+
attr::mark_used(attr);
1720+
}
1721+
_ => (/*pass*/)
1722+
}
1723+
}
1724+
}
16881725
}
16891726

16901727
fn check_view_item(&mut self, cx: &Context, item: &ast::ViewItem) {
@@ -1746,6 +1783,7 @@ impl LintPass for Stability {
17461783
}
17471784
_ => return
17481785
};
1786+
17491787
self.lint(cx, id, span);
17501788
}
17511789

@@ -1878,3 +1916,22 @@ impl LintPass for HardwiredLints {
18781916
)
18791917
}
18801918
}
1919+
1920+
/// Forbids using the `#[feature(...)]` attribute
1921+
#[deriving(Copy)]
1922+
pub struct UnstableFeatures;
1923+
1924+
declare_lint!(UNSTABLE_FEATURES, Allow,
1925+
"enabling unstable features");
1926+
1927+
impl LintPass for UnstableFeatures {
1928+
fn get_lints(&self) -> LintArray {
1929+
lint_array!(UNSTABLE_FEATURES)
1930+
}
1931+
fn check_attribute(&mut self, ctx: &Context, attr: &ast::Attribute) {
1932+
use syntax::attr;
1933+
if attr::contains_name(&[attr.node.value.clone()], "feature") {
1934+
ctx.span_lint(UNSTABLE_FEATURES, attr.span, "unstable feature");
1935+
}
1936+
}
1937+
}

src/librustc/lint/context.rs

+43-3
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ use self::TargetLint::*;
2828
use middle::privacy::ExportedItems;
2929
use middle::ty::{self, Ty};
3030
use session::{early_error, Session};
31+
use session::config::UnstableFeatures;
3132
use lint::{Level, LevelSource, Lint, LintId, LintArray, LintPass, LintPassObject};
32-
use lint::{Default, CommandLine, Node, Allow, Warn, Deny, Forbid};
33+
use lint::{Default, CommandLine, Node, Allow, Warn, Deny, Forbid, ReleaseChannel};
3334
use lint::builtin;
3435
use util::nodemap::FnvHashMap;
3536

@@ -210,6 +211,7 @@ impl LintStore {
210211
UnusedAllocation,
211212
Stability,
212213
MissingCopyImplementations,
214+
UnstableFeatures,
213215
);
214216

215217
add_builtin_with_new!(sess,
@@ -298,6 +300,29 @@ impl LintStore {
298300
}
299301
}
300302
}
303+
304+
fn maybe_stage_features(&mut self, sess: &Session) {
305+
let lvl = match sess.opts.unstable_features {
306+
UnstableFeatures::Default => return,
307+
UnstableFeatures::Disallow => Warn,
308+
UnstableFeatures::Cheat => Allow
309+
};
310+
match self.by_name.get("unstable_features") {
311+
Some(&Id(lint_id)) => self.set_level(lint_id, (lvl, ReleaseChannel)),
312+
Some(&Renamed(_, lint_id)) => self.set_level(lint_id, (lvl, ReleaseChannel)),
313+
None => unreachable!()
314+
}
315+
match self.by_name.get("staged_unstable") {
316+
Some(&Id(lint_id)) => self.set_level(lint_id, (lvl, ReleaseChannel)),
317+
Some(&Renamed(_, lint_id)) => self.set_level(lint_id, (lvl, ReleaseChannel)),
318+
None => unreachable!()
319+
}
320+
match self.by_name.get("staged_experimental") {
321+
Some(&Id(lint_id)) => self.set_level(lint_id, (lvl, ReleaseChannel)),
322+
Some(&Renamed(_, lint_id)) => self.set_level(lint_id, (lvl, ReleaseChannel)),
323+
None => unreachable!()
324+
}
325+
}
301326
}
302327

303328
/// Context for lint checking.
@@ -380,6 +405,7 @@ pub fn raw_emit_lint(sess: &Session, lint: &'static Lint,
380405
if level == Allow { return }
381406

382407
let name = lint.name_lower();
408+
let mut def = None;
383409
let mut note = None;
384410
let msg = match source {
385411
Default => {
@@ -394,7 +420,13 @@ pub fn raw_emit_lint(sess: &Session, lint: &'static Lint,
394420
}, name.replace("_", "-"))
395421
},
396422
Node(src) => {
397-
note = Some(src);
423+
def = Some(src);
424+
msg.to_string()
425+
}
426+
ReleaseChannel => {
427+
let release_channel = option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)");
428+
note = Some(format!("this feature may not be used in the {} release channel",
429+
release_channel));
398430
msg.to_string()
399431
}
400432
};
@@ -410,7 +442,11 @@ pub fn raw_emit_lint(sess: &Session, lint: &'static Lint,
410442
_ => sess.bug("impossible level in raw_emit_lint"),
411443
}
412444

413-
for span in note.into_iter() {
445+
for note in note.into_iter() {
446+
sess.note(note.index(&FullRange));
447+
}
448+
449+
for span in def.into_iter() {
414450
sess.span_note(span, "lint level defined here");
415451
}
416452
}
@@ -767,6 +803,10 @@ impl LintPass for GatherNodeLevels {
767803
/// Consumes the `lint_store` field of the `Session`.
768804
pub fn check_crate(tcx: &ty::ctxt,
769805
exported_items: &ExportedItems) {
806+
807+
// If this is a feature-staged build of rustc then flip several lints to 'forbid'
808+
tcx.sess.lint_store.borrow_mut().maybe_stage_features(&tcx.sess);
809+
770810
let krate = tcx.map.krate();
771811
let mut cx = Context::new(tcx, krate, exported_items);
772812

0 commit comments

Comments
 (0)