Skip to content

Commit 224aec2

Browse files
committed
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 <[email protected]>
1 parent a1d5af2 commit 224aec2

12 files changed

+150
-4
lines changed

compiler/rustc_attr/src/builtin.rs

+27-1
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,26 @@ pub enum StabilityLevel {
142142
/// Relevant `rust-lang/rust` issue.
143143
issue: Option<NonZeroU32>,
144144
is_soft: bool,
145+
/// If part of a feature is stabilized and a new feature is added for the remaining parts,
146+
/// then the `implied_by` attribute is used to indicate which now-stable feature previously
147+
/// contained a item.
148+
///
149+
/// ```pseudo-Rust
150+
/// #[unstable(feature = "foo", issue = "...")]
151+
/// fn foo() {}
152+
/// #[unstable(feature = "foo", issue = "...")]
153+
/// fn foobar() {}
154+
/// ```
155+
///
156+
/// ...becomes...
157+
///
158+
/// ```pseudo-Rust
159+
/// #[stable(feature = "foo", since = "1.XX.X")]
160+
/// fn foo() {}
161+
/// #[unstable(feature = "foobar", issue = "...", implied_by = "foo")]
162+
/// fn foobar() {}
163+
/// ```
164+
implied_by: Option<Symbol>,
145165
},
146166
/// `#[stable]`
147167
Stable {
@@ -256,6 +276,7 @@ where
256276
let mut issue = None;
257277
let mut issue_num = None;
258278
let mut is_soft = false;
279+
let mut implied_by = None;
259280
for meta in metas {
260281
let Some(mi) = meta.meta_item() else {
261282
handle_errors(
@@ -321,6 +342,11 @@ where
321342
}
322343
is_soft = true;
323344
}
345+
sym::implied_by => {
346+
if !get(mi, &mut implied_by) {
347+
continue 'outer;
348+
}
349+
}
324350
_ => {
325351
handle_errors(
326352
&sess.parse_sess,
@@ -345,7 +371,7 @@ where
345371
);
346372
continue;
347373
}
348-
let level = Unstable { reason, issue: issue_num, is_soft };
374+
let level = Unstable { reason, issue: issue_num, is_soft, implied_by };
349375
if sym::unstable == meta_name {
350376
stab = Some((Stability { level, feature }, attr.span));
351377
} else {

compiler/rustc_middle/src/middle/stability.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,9 @@ impl<'tcx> TyCtxt<'tcx> {
423423

424424
match stability {
425425
Some(Stability {
426-
level: attr::Unstable { reason, issue, is_soft }, feature, ..
426+
level: attr::Unstable { reason, issue, is_soft, implied_by },
427+
feature,
428+
..
427429
}) => {
428430
if span.allows_unstable(feature) {
429431
debug!("stability: skipping span={:?} since it is internal", span);
@@ -433,6 +435,13 @@ impl<'tcx> TyCtxt<'tcx> {
433435
return EvalResult::Allow;
434436
}
435437

438+
// If this item was previously part of a now-stabilized feature which is still
439+
// active (i.e. the user hasn't removed the attribute for the stabilized feature
440+
// yet) then allow use of this item.
441+
if let Some(implied_by) = implied_by && self.features().active(implied_by) {
442+
return EvalResult::Allow;
443+
}
444+
436445
// When we're compiling the compiler itself we may pull in
437446
// crates from crates.io, but those crates may depend on other
438447
// crates also pulled in from crates.io. We want to ideally be

compiler/rustc_passes/src/stability.rs

+1
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,7 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index {
637637
reason: Some(Symbol::intern(reason)),
638638
issue: NonZeroU32::new(27812),
639639
is_soft: false,
640+
implied_by: None,
640641
},
641642
feature: sym::rustc_private,
642643
};

compiler/rustc_resolve/src/macros.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -796,9 +796,16 @@ impl<'a> Resolver<'a> {
796796
) {
797797
let span = path.span;
798798
if let Some(stability) = &ext.stability {
799-
if let StabilityLevel::Unstable { reason, issue, is_soft } = stability.level {
799+
if let StabilityLevel::Unstable { reason, issue, is_soft, implied_by } = stability.level
800+
{
800801
let feature = stability.feature;
801-
if !self.active_features.contains(&feature) && !span.allows_unstable(feature) {
802+
803+
let is_allowed = |feature| {
804+
self.active_features.contains(&feature) || span.allows_unstable(feature)
805+
};
806+
let allowed_by_implication =
807+
implied_by.map(|feature| is_allowed(feature)).unwrap_or(false);
808+
if !is_allowed(feature) && !allowed_by_implication {
802809
let lint_buffer = &mut self.lint_buffer;
803810
let soft_handler =
804811
|lint, span, msg: &_| lint_buffer.buffer_lint(lint, node_id, span, msg);

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,7 @@ symbols! {
800800
impl_lint_pass,
801801
impl_macros,
802802
impl_trait_in_bindings,
803+
implied_by,
803804
import,
804805
import_shadowing,
805806
imported_main,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#![feature(staged_api)]
2+
#![stable(feature = "stability_attribute_implies", since = "1.0.0")]
3+
4+
#[stable(feature = "foo", since = "1.62.0")]
5+
pub fn foo() {}
6+
7+
#[unstable(feature = "foobar", issue = "1", implied_by = "foo")]
8+
pub fn foobar() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// aux-build:stability-attribute-implies.rs
2+
3+
// Tests that despite the `foobar` feature being implied by now-stable feature `foo`, if `foobar`
4+
// isn't allowed in this crate then an error will be emitted.
5+
6+
extern crate stability_attribute_implies;
7+
use stability_attribute_implies::{foo, foobar};
8+
//~^ ERROR use of unstable library feature 'foobar'
9+
10+
fn main() {
11+
foo(); // no error - stable
12+
foobar(); //~ ERROR use of unstable library feature 'foobar'
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0658]: use of unstable library feature 'foobar'
2+
--> $DIR/stability-attribute-implies-no-feature.rs:7:40
3+
|
4+
LL | use stability_attribute_implies::{foo, foobar};
5+
| ^^^^^^
6+
|
7+
= note: see issue #1 <https://github.com/rust-lang/rust/issues/1> for more information
8+
= help: add `#![feature(foobar)]` to the crate attributes to enable
9+
10+
error[E0658]: use of unstable library feature 'foobar'
11+
--> $DIR/stability-attribute-implies-no-feature.rs:12:5
12+
|
13+
LL | foobar();
14+
| ^^^^^^
15+
|
16+
= note: see issue #1 <https://github.com/rust-lang/rust/issues/1> for more information
17+
= help: add `#![feature(foobar)]` to the crate attributes to enable
18+
19+
error: aborting due to 2 previous errors
20+
21+
For more information about this error, try `rustc --explain E0658`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// aux-build:stability-attribute-implies.rs
2+
#![deny(stable_features)]
3+
#![feature(foo)]
4+
//~^ ERROR the feature `foo` has been stable since 1.62.0 and no longer requires an attribute to enable
5+
6+
// Tests that the use of `implied_by` in the `#[unstable]` attribute results in a diagnostic
7+
// mentioning partial stabilization, and that given the implied unstable feature is unused (there
8+
// is no `foobar` call), that the compiler suggests removing the flag.
9+
10+
extern crate stability_attribute_implies;
11+
use stability_attribute_implies::foo;
12+
13+
fn main() {
14+
foo();
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: the feature `foo` has been stable since 1.62.0 and no longer requires an attribute to enable
2+
--> $DIR/stability-attribute-implies-using-stable.rs:3:12
3+
|
4+
LL | #![feature(foo)]
5+
| ^^^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/stability-attribute-implies-using-stable.rs:2:9
9+
|
10+
LL | #![deny(stable_features)]
11+
| ^^^^^^^^^^^^^^^
12+
13+
error: aborting due to previous error
14+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// aux-build:stability-attribute-implies.rs
2+
#![deny(stable_features)]
3+
#![feature(foo)]
4+
//~^ ERROR the feature `foo` has been stable since 1.62.0 and no longer requires an attribute to enable
5+
6+
// Tests that the use of `implied_by` in the `#[unstable]` attribute results in a diagnostic
7+
// mentioning partial stabilization and that given the implied unstable feature is used (there is a
8+
// `foobar` call), that the compiler suggests changing to that feature and doesn't error about its
9+
// use.
10+
11+
extern crate stability_attribute_implies;
12+
use stability_attribute_implies::{foo, foobar};
13+
14+
fn main() {
15+
foo();
16+
foobar(); // no error!
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: the feature `foo` has been stable since 1.62.0 and no longer requires an attribute to enable
2+
--> $DIR/stability-attribute-implies-using-unstable.rs:3:12
3+
|
4+
LL | #![feature(foo)]
5+
| ^^^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/stability-attribute-implies-using-unstable.rs:2:9
9+
|
10+
LL | #![deny(stable_features)]
11+
| ^^^^^^^^^^^^^^^
12+
13+
error: aborting due to previous error
14+

0 commit comments

Comments
 (0)