From 224aec213d62aa11758c864c871c341b7e7e7681 Mon Sep 17 00:00:00 2001 From: David Wood Date: Wed, 13 Jul 2022 13:10:37 +0100 Subject: [PATCH] middle: add `implies_by` to `#[unstable]` If part of a feature is stabilized and a new feature is added for the remaining parts, then the `implied_by` attribute can be used to indicate which now-stable feature previously contained a item. If the now-stable feature is still active (if the user has only just updated rustc, for example) then there will not be an stability error for uses of the item from the implied feature. Signed-off-by: David Wood --- compiler/rustc_attr/src/builtin.rs | 28 ++++++++++++++++++- compiler/rustc_middle/src/middle/stability.rs | 11 +++++++- compiler/rustc_passes/src/stability.rs | 1 + compiler/rustc_resolve/src/macros.rs | 11 ++++++-- compiler/rustc_span/src/symbol.rs | 1 + .../auxiliary/stability-attribute-implies.rs | 8 ++++++ .../stability-attribute-implies-no-feature.rs | 13 +++++++++ ...bility-attribute-implies-no-feature.stderr | 21 ++++++++++++++ ...tability-attribute-implies-using-stable.rs | 15 ++++++++++ ...lity-attribute-implies-using-stable.stderr | 14 ++++++++++ ...bility-attribute-implies-using-unstable.rs | 17 +++++++++++ ...ty-attribute-implies-using-unstable.stderr | 14 ++++++++++ 12 files changed, 150 insertions(+), 4 deletions(-) create mode 100644 src/test/ui/stability-attribute/auxiliary/stability-attribute-implies.rs create mode 100644 src/test/ui/stability-attribute/stability-attribute-implies-no-feature.rs create mode 100644 src/test/ui/stability-attribute/stability-attribute-implies-no-feature.stderr create mode 100644 src/test/ui/stability-attribute/stability-attribute-implies-using-stable.rs create mode 100644 src/test/ui/stability-attribute/stability-attribute-implies-using-stable.stderr create mode 100644 src/test/ui/stability-attribute/stability-attribute-implies-using-unstable.rs create mode 100644 src/test/ui/stability-attribute/stability-attribute-implies-using-unstable.stderr diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 16e56c6d851..64a6f91f022 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -142,6 +142,26 @@ pub enum StabilityLevel { /// Relevant `rust-lang/rust` issue. issue: Option, is_soft: bool, + /// If part of a feature is stabilized and a new feature is added for the remaining parts, + /// then the `implied_by` attribute is used to indicate which now-stable feature previously + /// contained a item. + /// + /// ```pseudo-Rust + /// #[unstable(feature = "foo", issue = "...")] + /// fn foo() {} + /// #[unstable(feature = "foo", issue = "...")] + /// fn foobar() {} + /// ``` + /// + /// ...becomes... + /// + /// ```pseudo-Rust + /// #[stable(feature = "foo", since = "1.XX.X")] + /// fn foo() {} + /// #[unstable(feature = "foobar", issue = "...", implied_by = "foo")] + /// fn foobar() {} + /// ``` + implied_by: Option, }, /// `#[stable]` Stable { @@ -256,6 +276,7 @@ fn find_stability_generic<'a, I>( let mut issue = None; let mut issue_num = None; let mut is_soft = false; + let mut implied_by = None; for meta in metas { let Some(mi) = meta.meta_item() else { handle_errors( @@ -321,6 +342,11 @@ fn find_stability_generic<'a, I>( } is_soft = true; } + sym::implied_by => { + if !get(mi, &mut implied_by) { + continue 'outer; + } + } _ => { handle_errors( &sess.parse_sess, @@ -345,7 +371,7 @@ fn find_stability_generic<'a, I>( ); continue; } - let level = Unstable { reason, issue: issue_num, is_soft }; + let level = Unstable { reason, issue: issue_num, is_soft, implied_by }; if sym::unstable == meta_name { stab = Some((Stability { level, feature }, attr.span)); } else { diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 96e068a3601..63f66be1507 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -423,7 +423,9 @@ pub fn eval_stability_allow_unstable( match stability { Some(Stability { - level: attr::Unstable { reason, issue, is_soft }, feature, .. + level: attr::Unstable { reason, issue, is_soft, implied_by }, + feature, + .. }) => { if span.allows_unstable(feature) { debug!("stability: skipping span={:?} since it is internal", span); @@ -433,6 +435,13 @@ pub fn eval_stability_allow_unstable( return EvalResult::Allow; } + // If this item was previously part of a now-stabilized feature which is still + // active (i.e. the user hasn't removed the attribute for the stabilized feature + // yet) then allow use of this item. + if let Some(implied_by) = implied_by && self.features().active(implied_by) { + return EvalResult::Allow; + } + // When we're compiling the compiler itself we may pull in // crates from crates.io, but those crates may depend on other // crates also pulled in from crates.io. We want to ideally be diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index da87f557a55..a15a42bd76f 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -637,6 +637,7 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index { reason: Some(Symbol::intern(reason)), issue: NonZeroU32::new(27812), is_soft: false, + implied_by: None, }, feature: sym::rustc_private, }; diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 54dd15270a1..2b5eb12a8a8 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -796,9 +796,16 @@ fn check_stability_and_deprecation( ) { let span = path.span; if let Some(stability) = &ext.stability { - if let StabilityLevel::Unstable { reason, issue, is_soft } = stability.level { + if let StabilityLevel::Unstable { reason, issue, is_soft, implied_by } = stability.level + { let feature = stability.feature; - if !self.active_features.contains(&feature) && !span.allows_unstable(feature) { + + let is_allowed = |feature| { + self.active_features.contains(&feature) || span.allows_unstable(feature) + }; + let allowed_by_implication = + implied_by.map(|feature| is_allowed(feature)).unwrap_or(false); + if !is_allowed(feature) && !allowed_by_implication { let lint_buffer = &mut self.lint_buffer; let soft_handler = |lint, span, msg: &_| lint_buffer.buffer_lint(lint, node_id, span, msg); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index ec1d2c39b80..2ac1ecfe87e 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -800,6 +800,7 @@ impl_lint_pass, impl_macros, impl_trait_in_bindings, + implied_by, import, import_shadowing, imported_main, diff --git a/src/test/ui/stability-attribute/auxiliary/stability-attribute-implies.rs b/src/test/ui/stability-attribute/auxiliary/stability-attribute-implies.rs new file mode 100644 index 00000000000..468be1bc144 --- /dev/null +++ b/src/test/ui/stability-attribute/auxiliary/stability-attribute-implies.rs @@ -0,0 +1,8 @@ +#![feature(staged_api)] +#![stable(feature = "stability_attribute_implies", since = "1.0.0")] + +#[stable(feature = "foo", since = "1.62.0")] +pub fn foo() {} + +#[unstable(feature = "foobar", issue = "1", implied_by = "foo")] +pub fn foobar() {} diff --git a/src/test/ui/stability-attribute/stability-attribute-implies-no-feature.rs b/src/test/ui/stability-attribute/stability-attribute-implies-no-feature.rs new file mode 100644 index 00000000000..947f9f73eff --- /dev/null +++ b/src/test/ui/stability-attribute/stability-attribute-implies-no-feature.rs @@ -0,0 +1,13 @@ +// aux-build:stability-attribute-implies.rs + +// Tests that despite the `foobar` feature being implied by now-stable feature `foo`, if `foobar` +// isn't allowed in this crate then an error will be emitted. + +extern crate stability_attribute_implies; +use stability_attribute_implies::{foo, foobar}; +//~^ ERROR use of unstable library feature 'foobar' + +fn main() { + foo(); // no error - stable + foobar(); //~ ERROR use of unstable library feature 'foobar' +} diff --git a/src/test/ui/stability-attribute/stability-attribute-implies-no-feature.stderr b/src/test/ui/stability-attribute/stability-attribute-implies-no-feature.stderr new file mode 100644 index 00000000000..c2331f6766c --- /dev/null +++ b/src/test/ui/stability-attribute/stability-attribute-implies-no-feature.stderr @@ -0,0 +1,21 @@ +error[E0658]: use of unstable library feature 'foobar' + --> $DIR/stability-attribute-implies-no-feature.rs:7:40 + | +LL | use stability_attribute_implies::{foo, foobar}; + | ^^^^^^ + | + = note: see issue #1 for more information + = help: add `#![feature(foobar)]` to the crate attributes to enable + +error[E0658]: use of unstable library feature 'foobar' + --> $DIR/stability-attribute-implies-no-feature.rs:12:5 + | +LL | foobar(); + | ^^^^^^ + | + = note: see issue #1 for more information + = help: add `#![feature(foobar)]` to the crate attributes to enable + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/stability-attribute/stability-attribute-implies-using-stable.rs b/src/test/ui/stability-attribute/stability-attribute-implies-using-stable.rs new file mode 100644 index 00000000000..527639ec70b --- /dev/null +++ b/src/test/ui/stability-attribute/stability-attribute-implies-using-stable.rs @@ -0,0 +1,15 @@ +// aux-build:stability-attribute-implies.rs +#![deny(stable_features)] +#![feature(foo)] +//~^ ERROR the feature `foo` has been stable since 1.62.0 and no longer requires an attribute to enable + +// Tests that the use of `implied_by` in the `#[unstable]` attribute results in a diagnostic +// mentioning partial stabilization, and that given the implied unstable feature is unused (there +// is no `foobar` call), that the compiler suggests removing the flag. + +extern crate stability_attribute_implies; +use stability_attribute_implies::foo; + +fn main() { + foo(); +} diff --git a/src/test/ui/stability-attribute/stability-attribute-implies-using-stable.stderr b/src/test/ui/stability-attribute/stability-attribute-implies-using-stable.stderr new file mode 100644 index 00000000000..c8767e85a68 --- /dev/null +++ b/src/test/ui/stability-attribute/stability-attribute-implies-using-stable.stderr @@ -0,0 +1,14 @@ +error: the feature `foo` has been stable since 1.62.0 and no longer requires an attribute to enable + --> $DIR/stability-attribute-implies-using-stable.rs:3:12 + | +LL | #![feature(foo)] + | ^^^ + | +note: the lint level is defined here + --> $DIR/stability-attribute-implies-using-stable.rs:2:9 + | +LL | #![deny(stable_features)] + | ^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/stability-attribute/stability-attribute-implies-using-unstable.rs b/src/test/ui/stability-attribute/stability-attribute-implies-using-unstable.rs new file mode 100644 index 00000000000..d6ad4d3510e --- /dev/null +++ b/src/test/ui/stability-attribute/stability-attribute-implies-using-unstable.rs @@ -0,0 +1,17 @@ +// aux-build:stability-attribute-implies.rs +#![deny(stable_features)] +#![feature(foo)] +//~^ ERROR the feature `foo` has been stable since 1.62.0 and no longer requires an attribute to enable + +// Tests that the use of `implied_by` in the `#[unstable]` attribute results in a diagnostic +// mentioning partial stabilization and that given the implied unstable feature is used (there is a +// `foobar` call), that the compiler suggests changing to that feature and doesn't error about its +// use. + +extern crate stability_attribute_implies; +use stability_attribute_implies::{foo, foobar}; + +fn main() { + foo(); + foobar(); // no error! +} diff --git a/src/test/ui/stability-attribute/stability-attribute-implies-using-unstable.stderr b/src/test/ui/stability-attribute/stability-attribute-implies-using-unstable.stderr new file mode 100644 index 00000000000..35cbac6035c --- /dev/null +++ b/src/test/ui/stability-attribute/stability-attribute-implies-using-unstable.stderr @@ -0,0 +1,14 @@ +error: the feature `foo` has been stable since 1.62.0 and no longer requires an attribute to enable + --> $DIR/stability-attribute-implies-using-unstable.rs:3:12 + | +LL | #![feature(foo)] + | ^^^ + | +note: the lint level is defined here + --> $DIR/stability-attribute-implies-using-unstable.rs:2:9 + | +LL | #![deny(stable_features)] + | ^^^^^^^^^^^^^^^ + +error: aborting due to previous error +