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

RFC: #[cfg(accessible(..) / version(..))] #2523

Merged
merged 50 commits into from
Sep 26, 2019

Conversation

Centril
Copy link
Contributor

@Centril Centril commented Aug 12, 2018

🖼️ Rendered

⏭ Tracking issue (#[cfg(version(..))]

⏭ Tracking issue (#[cfg(accessible(..))]

📝 Summary

Permit users to #[cfg(..)] on whether:

  • they have a certain minimum Rust version (#[cfg(version(1.27))]).
  • a certain external path is accessible (#[cfg(accessible(::std::mem::ManuallyDrop))]).

💖 Thanks

To @eddyb, @rpjohnst, @kennytm, @aturon, and @rkruppe for discussing the feature with me.
To @Mark-Simulacrum, @MajorBreakfast, @alexreg, @alercah, and @joshtriplett for reviewing the draft version of this RFC.

@Centril Centril added the T-lang Relevant to the language team, which will review and decide on the RFC. label Aug 12, 2018
@Centril Centril self-assigned this Aug 12, 2018
@Centril Centril changed the title RFC: #[cfg(accessible(..) / version(..) / nightly)] RFC: #[cfg(accessible(..) / version = ".." / nightly)] Aug 12, 2018
@Centril
Copy link
Contributor Author

Centril commented Aug 12, 2018

cc @MajorBreakfast re. use case for futures. :)

@dekellum
Copy link

I find the recent new life here, and incremental approach you are suggesting, @Centril, to be encouraging. A couple of minor points that may have gelled somewhere in the interim months:

(a) Might changing the name from version, to something like min_rustc_version(1.38) clarify that its both a >= bounds and only relevant to rustc versions (not any other compiler)?

(b) Feature names could be made as specific as necessary to account for changes during the stabilization process. So for example maybe a feature we effectively have now on latest nightly is named async_await_2. Prior versions (_1 or _0) might have been for the await!() macro or even for more minor but still breaking differences, like before rust-lang/rust#64292 was merged. If the feature changes in any other breaking ways before stabilization, then the stable version could be named async_await_3, otherwise `async_await_2 could remain valid on stable releases with the benefit that code started on nightly could potentially just work on some subsequent stable.

I agree that these are implementation details that don't need to be resolved here.

@joshtriplett
Copy link
Member

joshtriplett commented Sep 13, 2019 via email

@Centril
Copy link
Contributor Author

Centril commented Sep 13, 2019

(a) Might changing the name from version, to something like min_rustc_version(1.38) clarify that its both a >= bounds and only relevant to rustc versions (not any other compiler)?

Two points here:

  • It is relevant to all Rust compilers, not just rustc. ;)
  • min_version might be a reasonable attribute name if we collectively think version is too ambiguous. version(>= 1.37) could also work. Tho I feel @joshtriplett provides a good rationale for why version(1.37) makes sense (in fact, Cargo semver = X.Y.Z is the inspiration for the concrete syntax). If people can cope with Cargo.toml's way of encoding bounds then they should be able to cope with version(...).

(b) Feature names could be made as specific as necessary to account for changes during the stabilization process. So for example maybe a feature we effectively have now on latest nightly is named async_await_2. Prior versions (_1 or _0) might have been for the await!() macro or even for more minor but still breaking differences, like before rust-lang/rust#64292 was merged. If the feature changes in any other breaking ways before stabilization, then the stable version could be named async_await_3, otherwise `async_await_2 could remain valid on stable releases with the benefit that code started on nightly could potentially just work on some subsequent stable.

The breaking ways may not affect everyone using a specific feature. As such, it may create needless churn on nightly and additionally discourage folks from using nightly. Speaking as a rustc developer myself, keeping track of this also doesn't sound like much fun to be honest.

@dekellum
Copy link

On keeping the version name:

like a Cargo semver bound,

version(>= 1.37) could also work

I'd personally favor using an explicit ^1.37 or >= 1.37 then, for clarity, just like I tend to do in Cargo.toml. That works for me. Note the former implies "<2", a hypothetical concern.

On my suggestion of versioning or sub-feature name suffixes:

keeping track of this also doesn't sound like much fun to be honest.

I'm just suggesting that the current RFCs feature design, would allow such a scheme as a tool to manage the breakage and selectively allow future-proofing. If you don't want to use that tool, that is your prerogative. :)

This flag requires that a `path` fragment be specified in it inside parenthesis
but not inside a string literal. The `$path` must start with leading `::`
and may not refer to any parts of the own crate (e.g. with `::crate::foo`,
`::self::foo`, or `::super::foo` if such paths are legal).
Copy link
Member

Choose a reason for hiding this comment

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

Does this imply that #[cfg(accessible(path)] can be used with dependencies other than core or std? For example, can I write #[cfg(accessible(::lazy_static::lazy_static)]?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, you can do that.

@Havvy
Copy link
Contributor

Havvy commented Sep 19, 2019

We say that a release announcement of a new version of Rust includes a new version of the language version. Before this RFC, this is purely a rhetorical/theoretical/social device. Nothing can see the language version. It doesn't exist mechanically. This RFC changes that to something that is mechanical, and that should, IMO, be mentioned as a drawback, if only for academic purposes. Any industrial second implementation (Note: I don't expect there to ever be one) would have to understand the feature set of each six week release to properly set their version.

It also semi-locks us into this release versioning scheme. I say semi-locks because we could at any time freeze the cfg(version(...)) version if we decide it's bad or retroactively tie it to rustc's version instead.

`rustc` in the numbering and what features it provides. This is probably not
too unreasonable as we can expect `rustc` to be the reference implementation
and that other ones will probably lag behind. Indeed, this is the experience
with `GHC` and alternative Haskell compilers.
Copy link
Contributor

@gnzlbg gnzlbg Sep 19, 2019

Choose a reason for hiding this comment

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

A drawback of this approach is that using #[cfg(version(1.28))] would make libsyntax fail to parse this syntax on all pre-existing toolchains.

That is, if you have to support toolchains older than whatever toolchain version this attribute is this shipped in, you need to put the #[cfg(version(...))] behind a feature gated module like this to avoid older toolchains from trying to parse it:

// In the module where you actually want to use `#[cfg(version)]`
cfg_if! {
    if #[cfg(toolchain_supports_cfg_version)] {
        mod internal;
        use internal::*;
    }
}

// and then in a separate internal.rs file
#[cfg(version(1.42))]
struct Foo;

and then have a build.rs that detects the rust toolchain version and sets --cfg toolchain_supports_cfg_version.

Copy link
Contributor

@gnzlbg gnzlbg Sep 19, 2019

Choose a reason for hiding this comment

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

and then have a build.rs that detects the rust toolchain version and sets --cfg toolchain_supports_cfg_version.

I mean, at that point, we can just implement this as a library that can be easily used from build.rs to define such config macros, which would allow the code above to just be written as:

// In the module where you actually want to use `#[cfg(version)]`
#[cfg(rust_ge_1_42_0)]
struct Foo;

Copy link
Contributor

@gnzlbg gnzlbg Sep 19, 2019

Choose a reason for hiding this comment

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

Note that the following syntax does not have this problem and is IIRC backward compatible down to Rust 1.0:

#[cfg(rust_version = "1.42.0")] // in the cargo sense
struct Foo;

Copy link
Member

Choose a reason for hiding this comment

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

@gnzlbg I think everyone considering using this feature understands that it will only work going forward, in versions that support version and accessible. That's still quite useful.

Copy link
Contributor

Choose a reason for hiding this comment

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

@joshtriplett when it comes to older versions, there's a difference between "doesn't work" and "can't be parsed". I'd like to avoid the second situation if possible.

Copy link
Member

Choose a reason for hiding this comment

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

This does bring though the idea that whichever release version becomes stable will probably become the new minimum stable rust version that a lot of libraries will bump to. It might be good to pair it's release with a major feature(s) so that the most amount of people can be benefit from them.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's probably not that difficult to support two versions; it's mostly just a bit of parsing inside or outside the string and they'll be uniform otherwise.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@XAMPPRocky that's pretty clever =P will just have to find some major feature to couple with heh.

Copy link
Contributor

Choose a reason for hiding this comment

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

This does bring though the idea that whichever release version becomes stable will probably become the new minimum stable rust version that a lot of libraries will bump to.

At best, depending on how cfg(version) is shipped, projects might be able to just use it for detecting newer features, but it will live alongside the feature-detection code that these projects are already using. The "old" feature detection code is simple and low maintenance, so bumping the MSRV to be able to exclusively use cfg(version) is something that doesn't add much value, while compromising something that's important for these project: the only reason a project would do feature detection is to be able to support older toolchains, so the project must see value in that.

I think that maybe eventually all projects might be able to just use cfg(version), but that will be a consequence of them bumping the MSRV due to other issues, and not because there is a lot of value in bumping the MSRV to a toolchain that supports cfg(version).

Copy link
Member

Choose a reason for hiding this comment

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

At best, depending on how cfg(version) is shipped, projects might be able to just use it for detecting newer features

I don't think cfg(version) has to be immediately valuable. Eventually more features will be covered under cfg(version) than those features that aren't, and then it will be more compelling for more libraries.

so bumping the MSRV to be able to exclusively use cfg(version) is something that doesn't add much value

Unless your MSRV is one version behind cfg(version) your value won't be exclusively cfg(version). If your MSRV is 1.15 for example and cfg(version) was released in 1.40.0 you'd be getting everything from 1.16.0–1.40.0, and you'd be able to use cfg(version) to ease future feature development.

@ogoffart
Copy link

Another point is that things that are gated sill need to use valid rust grammar.

Let's assume that the version 1.42 adds support for throwing function, and using .if:

#[cfg(version(1.42))]
fn foo(x : bool) -> u32 throw(SomeError) {
    x.if { something() } else { 0 }
}

This won't compile in current rust, even after this rfc is implemented, because this is not valid grammar, and rustc still check the grammar of items even if they are going to be removed by attribute macro or cfg attribute.

I suppose it would not make sense to relax the parsing rules of items that are disabled by #[cfg]

So one must wrap the item inside a macro such as cfg-if!{...} but which would not expand the items for the disabled configuration. Something like this should work: #[cfg($cfg)] identity!{ $($body)* }.

Btw, I've made a crate with a macro if_rust_version with macro_rules that allows conditional like that depending on the rust version: https://github.com/ogoffart/if_rust_version

@rfcbot rfcbot added the finished-final-comment-period The final comment period is finished for this RFC. label Sep 22, 2019
@rfcbot
Copy link
Collaborator

rfcbot commented Sep 22, 2019

The final comment period, with a disposition to merge, as per the review above, is now complete.

As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed.

The RFC will be merged soon.

@rfcbot rfcbot removed the final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised. label Sep 22, 2019
@Centril Centril merged commit 7afbf18 into rust-lang:master Sep 26, 2019
@Centril
Copy link
Contributor Author

Centril commented Sep 26, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-cfg Conditional compilation related proposals & ideas A-paths Path related proposals & ideas A-versioning Versioning related proposals & ideas disposition-merge This RFC is in PFCP or FCP with a disposition to merge it. finished-final-comment-period The final comment period is finished for this RFC. T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

Successfully merging this pull request may close these issues.