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

introduce implied_by in #[unstable] attribute #99212

Merged
merged 6 commits into from
Jul 20, 2022

Conversation

davidtwco
Copy link
Member

@davidtwco davidtwco commented Jul 13, 2022

Requested by the library team on Zulip.

If part of a feature is stabilized and a new feature is added for the remaining parts, then the implied_by meta-item can be added to #[unstable] to indicate which now-stable feature was used previously.

error: the feature `foo` has been partially stabilized since 1.62.0 and is succeeded by the feature `foobar`
  --> $DIR/stability-attribute-implies-using-unstable.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)]
   |         ^^^^^^^^^^^^^^^
help: if you are using features which are still unstable, change to using `foobar`
   |
LL | #![feature(foobar)]
   |            ~~~~~~
help: if you are using features which are now stable, remove this line
   |
LL - #![feature(foo)]
   |

When a #![feature(..)] attribute still exists for the now-stable attribute, then there this has two effects:

  • There will not be an stability error for uses of items from the implied feature which are still unstable (until the #![feature(..)] is removed or updated to the new feature).
  • There will be an improved diagnostic for the remaining use of the feature attribute for the now-stable feature.
        /// 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() {}
        /// ```

In the Zulip discussion, this was envisioned as implies on #[stable] but I went with implied_by on #[unstable] because it means that only the unstable attribute needs to be changed in future, not the new stable attribute, which seems less error-prone. It also isn't particularly feasible for me to detect whether items from the implied feature are used and then only suggest updating or removing the #![feature(..)] as appropriate, so I always do both.

There's some new information in the cross-crate metadata as a result of this change, that's a little unfortunate, but without requiring that the #[unstable] and #[stable] attributes both contain the implication information, it's necessary:

    /// This mapping is necessary unless both the `#[stable]` and `#[unstable]` attributes should
    /// specify their implications (both `implies` and `implied_by`). If only one of the two
    /// attributes do (as in the current implementation, `implied_by` in `#[unstable]`), then this
    /// mapping is necessary for diagnostics. When a "unnecessary feature attribute" error is
    /// reported, only the `#[stable]` attribute information is available, so the map is necessary
    /// to know that the feature implies another feature. If it were reversed, and the `#[stable]`
    /// attribute had an `implies` meta item, then a map would be necessary when avoiding a "use of
    /// unstable feature" error for a feature that was implied.

I also change some comments to documentation comments in the compiler, add a helper for going from a Span to a Span for the entire line, and fix a incorrect part of the pre-existing stability attribute diagnostics.

cc @yaahc

@rustbot rustbot added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Jul 13, 2022
@rust-highfive
Copy link
Collaborator

r? @michaelwoerister

(rust-highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jul 13, 2022
) {
tcx.struct_span_lint_hir(lint::builtin::STABLE_FEATURES, hir::CRATE_HIR_ID, span, |lint| {
lint.build(&format!(
"the feature `{feature}` has been partially stabilized since {since} and is succeeded \
Copy link
Member Author

Choose a reason for hiding this comment

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

I would have made these translatable given that's what I'm doing everywhere else but I have a patch coming for rustc_passes in the next few days so it can wait for that. :)

@CAD97
Copy link
Contributor

CAD97 commented Jul 13, 2022

What's the behavior of implied_by = "still_unstable" and implied_by = "not_a_feature"?

The former is useful for feature subsetting while still unstable, e.g. min_specialization. The latter could be useful for feature renaming, but should probably be prevented as likely an unintended typo.

@davidtwco
Copy link
Member Author

What's the behavior of implied_by = "still_unstable" and implied_by = "not_a_feature"?

The former is useful for feature subsetting while still unstable, e.g. min_specialization. The latter could be useful for feature renaming, but should probably be prevented as likely an unintended typo.

implied_by = "still_unstable" works right now, and the behaviour is the exact same as implied_by = "stable". This could be changed but it sounds like it might be useful to t-libs, so I don't mind leaving it as is.

As for implied_by = "not_a_feature", I don't really know what would happen if you did this, I suspect nothing would happen (but we might crash, I haven't checked). If it is feasible, I'll add a check somewhere to prevent this.

@bors

This comment was marked as resolved.

Change some regular comments into documentation comments.

Signed-off-by: David Wood <david.wood@huawei.com>
When an unexpected meta item is provided to `#[stable]`, the diagnostic
lists "since" and "note" as expected meta-items, however the surrounding
code actually expects "feature" and "since".

Signed-off-by: David Wood <david.wood@huawei.com>
@michaelwoerister
Copy link
Member

The implementation looks good to me. Thanks, @davidtwco!

Maybe someone from @rust-lang/libs wants to take another look to make sure that this PR actually implements the desired behavior? Otherwise, feel free to r=me.

@michaelwoerister
Copy link
Member

As for implied_by = "not_a_feature", I don't really know what would happen if you did this, I suspect nothing would happen (but we might crash, I haven't checked). If it is feasible, I'll add a check somewhere to prevent this.

Adding a check like this sounds like a good idea. Here maybe?: https://github.com/rust-lang/rust/pull/99212/files#diff-5f57ad10e1bdde3d046b258d390fd2ecc6f1511158aa130cebb72093da16ef29R955-R958

@m-ou-se
Copy link
Member

m-ou-se commented Jul 20, 2022

cc @rust-lang/libs-api

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 <david.wood@huawei.com>
Adds a simple helper function to the `SourceMap` for extending a `Span`
to encompass the entire line it is on - useful for suggestions where
removing a line is the suggested action.

Signed-off-by: David Wood <david.wood@huawei.com>
Improves the diagnostic when a feature attribute is specified
unnecessarily but the feature implies another (i.e. it was partially
stabilized) to refer to the implied feature.

Signed-off-by: David Wood <david.wood@huawei.com>
@davidtwco
Copy link
Member Author

As for implied_by = "not_a_feature", I don't really know what would happen if you did this, I suspect nothing would happen (but we might crash, I haven't checked). If it is feasible, I'll add a check somewhere to prevent this.

Adding a check like this sounds like a good idea. Here maybe?: #99212 (files)

Added!

@m-ou-se
Copy link
Member

m-ou-se commented Jul 20, 2022

I think this is a useful improvement, but there's one thing that concerns me. It's important that #[unstable] things are considered unstable, such that we can always change them without feeling pressure from expectations to keep the unstable features somewhat 'stable'. So, while this implied_by feature definitely makes unstable features more ergonomic, I'm worried that the more ergonomic we make them, the more we (accidentally) grow expectations around the stability and ergonomics of unstable features. For example, while it's nice that nightly users will not have to change their #![feature(..)] lines as often, there should not be an end goal of making that happen in all cases, as that would suggest some kind of stability of unstable features.

I don't think that's a blocker here. Improving ergonomics is good. But something to keep in mind for #[unstable] related changes in general.

@michaelwoerister
Copy link
Member

OK, r=me with the test fixed.

Add a check confirming that features referenced in `implied_by` meta
items actually exist.

Signed-off-by: David Wood <david.wood@huawei.com>
@davidtwco
Copy link
Member Author

@bors r=michaelwoerister

@bors
Copy link
Contributor

bors commented Jul 20, 2022

📌 Commit e587299 has been approved by michaelwoerister

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 20, 2022
bors added a commit to rust-lang-ci/rust that referenced this pull request Jul 20, 2022
…askrgr

Rollup of 9 pull requests

Successful merges:

 - rust-lang#99212 (introduce `implied_by` in `#[unstable]` attribute)
 - rust-lang#99352 (Use `typeck_results` to avoid duplicate `ast_ty_to_ty` call)
 - rust-lang#99355 (better error for bad depth parameter on macro metavar expr)
 - rust-lang#99480 (Diagnostic width span is not added when '0$' is used as width in format strings)
 - rust-lang#99488 (compiletest: Allow using revisions with debuginfo tests.)
 - rust-lang#99489 (rustdoc UI fixes)
 - rust-lang#99508 (Avoid `Symbol` to `String` conversions)
 - rust-lang#99510 (adapt assembly/static-relocation-model test for LLVM change)
 - rust-lang#99516 (Use new tracking issue for proc_macro::tracked_*.)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit 857afc7 into rust-lang:master Jul 20, 2022
@rustbot rustbot added this to the 1.64.0 milestone Jul 20, 2022
@davidtwco davidtwco deleted the partial-stability-implies branch July 21, 2022 14:17
@tbodt tbodt mentioned this pull request Jul 21, 2022
@pnkfelix
Copy link
Member

Hey @davidtwco , is there any chance that this PR was the cause of the performance regression reported in rollup PR #99520? You can see my line of reasoning about why I thought it might be, over in zulip.

@davidtwco
Copy link
Member Author

Hey @davidtwco , is there any chance that this PR was the cause of the performance regression reported in rollup PR #99520? You can see my line of reasoning about why I thought it might be, over in zulip.

It's possible because of some additions to the cross-crate metadata. I've had another look at the changes and can't think of any ways to do less work that it currently does (it should just be writing a HashMap if there are elements in it, which there might only be one or two of, depends how much the library team are already using this).

@yaahc
Copy link
Member

yaahc commented Jul 28, 2022

I don't think we've landed any PRs yet that need this, only #99573 which is still open and has another path to landing that we can use instead, so if there are perf issues with the impl as is and y'all feel like pumping the breaks that should be okay on our side for now at least.

bors pushed a commit to rust-lang-ci/rust that referenced this pull request Jul 29, 2022
Original message:
Rollup merge of rust-lang#99212 - davidtwco:partial-stability-implies, r=michaelwoerister

introduce `implied_by` in `#[unstable]` attribute

Requested by the library team [on Zulip](https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/better.20support.20for.20partial.20stabilizations/near/285581519).

If part of a feature is stabilized and a new feature is added for the remaining parts, then the `implied_by` meta-item can be added to `#[unstable]` to indicate which now-stable feature was used previously.

```diagnostic
error: the feature `foo` has been partially stabilized since 1.62.0 and is succeeded by the feature `foobar`
  --> $DIR/stability-attribute-implies-using-unstable.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)]
   |         ^^^^^^^^^^^^^^^
help: if you are using features which are still unstable, change to using `foobar`
   |
LL | #![feature(foobar)]
   |            ~~~~~~
help: if you are using features which are now stable, remove this line
   |
LL - #![feature(foo)]
   |
```

When a `#![feature(..)]` attribute still exists for the now-stable attribute, then there this has two effects:

- There will not be an stability error for uses of items from the implied feature which are still unstable (until the `#![feature(..)]` is removed or updated to the new feature).
- There will be an improved diagnostic for the remaining use of the feature attribute for the now-stable feature.

```rust
        /// 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() {}
        /// ```
```

In the Zulip discussion, this was envisioned as `implies` on `#[stable]` but I went with `implied_by` on `#[unstable]` because it means that only the unstable attribute needs to be changed in future, not the new stable attribute, which seems less error-prone. It also isn't particularly feasible for me to detect whether items from the implied feature are used and then only suggest updating _or_ removing the `#![feature(..)]` as appropriate, so I always do both.

There's some new information in the cross-crate metadata as a result of this change, that's a little unfortunate, but without requiring that the `#[unstable]` and `#[stable]` attributes both contain the implication information, it's necessary:

```rust
    /// This mapping is necessary unless both the `#[stable]` and `#[unstable]` attributes should
    /// specify their implications (both `implies` and `implied_by`). If only one of the two
    /// attributes do (as in the current implementation, `implied_by` in `#[unstable]`), then this
    /// mapping is necessary for diagnostics. When a "unnecessary feature attribute" error is
    /// reported, only the `#[stable]` attribute information is available, so the map is necessary
    /// to know that the feature implies another feature. If it were reversed, and the `#[stable]`
    /// attribute had an `implies` meta item, then a map would be necessary when avoiding a "use of
    /// unstable feature" error for a feature that was implied.
```

I also change some comments to documentation comments in the compiler, add a helper for going from a `Span` to a `Span` for the entire line, and fix a incorrect part of the pre-existing stability attribute diagnostics.

cc `@yaahc`
bors added a commit to rust-lang-ci/rust that referenced this pull request Aug 12, 2022
…ercote

passes: load `defined_lib_features` query less

Hopefully addresses the perf regressions from rust-lang#99212 (see rust-lang#99905).

Re-structure the stability checks for library features to avoid calling `defined_lib_features` for any more crates than necessary for each of the implications or local feature attributes that need validation.

r? `@ghost` (just checking perf at first)
@steffahn
Copy link
Member

steffahn commented Feb 2, 2023

Hi, out of curiosity, since I just found out this had actually been implemented, I wanted to try this on the only occurrence currently in std… and it doesn’t work at all?

#![feature(const_btree_new)]

use std::collections::BTreeMap;

const fn f() -> BTreeMap<(), ()> {
    let x = BTreeMap::new();
    let l = x.len();
    x
}
error: `BTreeMap::<K, V, A>::len` is not yet stable as a const fn
 --> src/lib.rs:7:13
  |
7 |     let l = x.len();
  |             ^^^^^^^
  |
  = help: add `#![feature(const_btree_len)]` to the crate attributes to enable

(playground)

I’m not doing anything wrong, am I? I guess I should open an issue?

@steffahn
Copy link
Member

steffahn commented Feb 2, 2023

Oh… maybe it’s a problem because it isn’t using unstable/stable but instead rustc_const_unstable/rustc_const_stable!?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants