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
Merged
Changes from 1 commit
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
ab8d6b3
rfc, cfg-path-version: initial version.
Centril Aug 10, 2018
4670370
rfc, cfg-path-version: fix unbalanced parens.
Centril Aug 10, 2018
e0f0fea
rfc, cfg-path-version: fix typo.
Centril Aug 10, 2018
d564d75
rfc, cfg-path-version: fix incorrect summary.
Centril Aug 10, 2018
091e470
rfc, cfg-path-version: fix typos.
Centril Aug 11, 2018
8477b57
rfc, cfg-path-version: use semver caret requirements.
Centril Aug 11, 2018
d44a484
rfc, cfg-path-version: bnf consistency.
Centril Aug 11, 2018
c06eb95
rfc, cfg-path-version: use version = '..' and motivate it.
Centril Aug 11, 2018
eb3ec27
rfc, cfg-path-version: define 'nightly' in terms of #![feature(..)]
Centril Aug 11, 2018
65dd139
rfc, cfg-path-version: note that path_exists only sees public things …
Centril Aug 11, 2018
144ff93
rfc, cfg-path-version: use path_exists = '..'.
Centril Aug 11, 2018
13bdfac
rfc, cfg-path-version: note on canary builds.
Centril Aug 11, 2018
c54db7f
rfc, cfg-path-version: meta grammar changes..
Centril Aug 11, 2018
a5f69b9
rfc, cfg-path-version: optional dependencies are out of scope.
Centril Aug 11, 2018
8832ea4
rfc, cfg-path-version: note use case of working around compiler bugs.
Centril Aug 11, 2018
fb30214
rfc, cfg-path-version: talk about the lint.
Centril Aug 11, 2018
7201651
rfc, cfg-path-version: crater stuff not a drawback.
Centril Aug 11, 2018
2fce9e6
rfc, cfg-path-version: clarify re. old versions using this.
Centril Aug 11, 2018
fdf3ed7
rfc, cfg-path-version: implementation questions re. path_exists.
Centril Aug 11, 2018
6442367
rfc, cfg-path-version: justify meta grammar changes.
Centril Aug 11, 2018
de60af4
rfc, cfg-path-version: justify preventing relative paths.
Centril Aug 11, 2018
72ee5fa
rfc, cfg-path-version: justify nightly instead of if_possible_feature.
Centril Aug 11, 2018
77bf1d6
rfc, cfg-path-version: path_exists -> accessible + talk about fields.
Centril Aug 12, 2018
8c9858a
rfc, cfg-path-version: refresh start date.
Centril Aug 12, 2018
09f23a8
rfc, cfg-path-version: fix typo
Centril Aug 12, 2018
7ca8a5f
rfc, cfg-path-version: reword a bit
Centril Aug 12, 2018
c4d7c2c
rfc, cfg-path-version: remove abnf highlighting
Centril Aug 12, 2018
9cedd5a
rfc, cfg-path-version: fix typo.
Centril Aug 14, 2018
3abb76f
rfc, cfg-path-version: talk about SomeFeature
Centril Aug 14, 2018
4601f1c
rfc, cfg-path-version: add `usable` to bikeshed.
Centril Aug 19, 2018
4cad91d
rfc, cfg-path-version: version = '..' => version(..)
Centril Aug 19, 2018
457da19
rfc, cfg-path-version: fix eddybs review comments.
Centril Aug 19, 2018
e7840f4
rfc, cfg-path-version: remove #[cfg(nightly)] from proposal.
Centril Aug 19, 2018
1cf87f6
rfc, inferred-type-aliases: discuss semantics of version(..) in depth.
Centril Aug 19, 2018
210e909
rfc, cfg-path-version: fix bugs.
Centril Aug 31, 2018
592de81
rfc, cfg-path-version: talk more about relative paths.
Centril Aug 31, 2018
7aa9cef
rfc, cfg-path-version: more rigorous bikeshed wrt. accessible.
Centril Aug 31, 2018
63b4eac
rfc, cfg-path-version: clang as prior art.
Centril Aug 31, 2018
f0dc1e6
rfc, cfg-path-version: discuss rust_feature.
Centril Aug 31, 2018
c6d47e7
rfc, cfg-path-version: elaborate on reachable.
Centril Aug 31, 2018
0b12c54
rfc, cfg-path-version: add error-chain#101 as an example.
Centril Aug 31, 2018
16ef0b2
rfc, cfg-path-version: discuss has_attr as future work.
Centril Aug 31, 2018
3333882
rfc, cfg-path-version: fix word wrap.
Centril Aug 31, 2018
e0de3bb
rfc, cfg-path-version: highlight the risks of unstable.
Centril Nov 29, 2018
96ec9e7
rfc, cfg-path-version: consider attributes & macros.
Centril Nov 29, 2018
40bc0f5
rfc, cfg-path-version: fix wording in motivation.
Centril Jan 11, 2019
4618306
rfc, cfg-path-version: fix formal grammar to correspond to guide.
Centril Jan 11, 2019
ca5a92b
rfc, cfg-path-version: mention target_has_atomic in rationale.
Centril Jan 11, 2019
5b4b21a
rfc, cfg-path-version: clarify stability policy re. accessible(..).
Centril Feb 17, 2019
e488a3a
RFC 2523
Centril Sep 26, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
rfc, cfg-path-version: use version = ".." and motivate it.
  • Loading branch information
Centril committed Aug 11, 2018
commit c06eb9550226468d86e1ca9fe408eb68c1e99f16
61 changes: 40 additions & 21 deletions text/0000-cfg-path-version.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
Permit users to `#[cfg(..)]` on whether:

+ they are on a `nightly` compiler (`#[cfg(nightly)]`).
+ they have a certain minimum Rust version (`#[cfg(version("1.27"))]`).
+ they have a certain minimum Rust version (`#[cfg(version = "1.27")]`).
+ a certain external path exists `#[cfg(path_exists(::std::mem::ManuallyDrop))]`.

# Motivation
Expand Down Expand Up @@ -171,15 +171,15 @@ to test if a path exists in some library in the ecosystem. This can for example
be useful if you need to support lower minor versions of a library but also
add support for features in a higher minor version.

## `#[cfg(version("1.27"))]`
## `#[cfg(version = "1.27")]`

Until now, we have only improved our support for library features but never
any language features. By checking if we are on a certain minimum version of
Rust or any version above it, we can conditionally support such new features.
For example:

```rust
#[cfg_attr(version("1.27"), must_use)]
#[cfg_attr(version = "1.27", must_use)]
fn double(x: i32) -> i32 {
2 * x
}
Expand All @@ -194,11 +194,11 @@ fn main() {
Another example is opting into the system allocator on Rust 1.28 and beyond:

```rust
#[cfg(version("1.28"))
#[cfg(version = "1.28")]
// or: #[cfg(path_exists(::std::alloc::System))]
use std::alloc::System;

#[cfg_attr(version("1.28"), global_allocator)]
#[cfg_attr(version = "1.28", global_allocator)]
static GLOBAL: System = System;

fn main() {
Expand All @@ -209,7 +209,7 @@ fn main() {
}
```

Note that you won't be able to make use of `#[cfg(version(..))` for these
Note that you won't be able to make use of `#[cfg(version = "..")]` for these
particular features since they were introduced before this RFC's features
get stabilized. However, there will be features in the future to use this
mechanism on.
Expand All @@ -224,7 +224,7 @@ To the `cfg` attribute , a `nightly` flag is added.
If and only if a Rust compiler considers itself to be on a nightly channel
it the `nightly` flag be considered active.

## `#[cfg(version("<semver>"))]`
## `#[cfg(version = "<semver>")]`

To the `cfg` attribute, a `version` flag is added.
This flag requires that a string literal be specified in it inside parenthesis.
Expand All @@ -238,7 +238,7 @@ semver : \d(.\d)?(.\d)? ;

If and only if a Rust compiler considers itself to have a version which is
greater or equal to the version in the `semver` string will the
`#[cfg(version("<string>")]` flag be considered active.
`#[cfg(version = "<string>")]` flag be considered active.
Greater or equal is defined in terms of [caret requirements].

## `#[cfg(path_exists($path))]`
Expand All @@ -265,7 +265,7 @@ a type constructor, it is possible to use the `::foo::bar::<T>::item` syntax.
## `cfg_attr` and `cfg!`

Note that the above sections also apply to the attribute `#[cfg_attr(..)]`
as well as the special macro `cfg!(..)` in that `nightly`, `version(..)`,
as well as the special macro `cfg!(..)` in that `nightly`, `version = ".."`,
and `path_exists(..)` are added to those as well.

# Drawbacks
Expand Down Expand Up @@ -303,11 +303,11 @@ or not via an indirection, we can just check if the path exists directly.
This way, a user does not have to look up the minimum version number for
the feature.

You may think that `version(..)` subsumes `path_exists(..)`.
You may think that `version = ".."` subsumes `path_exists(..)`.
However, we argue that it does not. This is the case because at the time of
enabling the `nightly` feature that enables the path in the standard library,
we do not yet know what minimum version it will be supported under.
If we try to support it with `version(..)`, it is possible that we may
If we try to support it with `version = ".."`, it is possible that we may
need to update the minimum version some small number of times.
However, doing so even once means that you will need to release new versions
of your crate. If you instead use `path_exists(..)` you won't need to use
Expand Down Expand Up @@ -339,14 +339,14 @@ However, as we've argued and demonstrated in the [guide-level-explanation],
the ability to `#[cfg(nightly)]` really shines when used in conjunction with
`#[cfg(path_exists($path))]`.

## `version(..)`
## `version = ".."`

When it comes to `version(..)`, it is needed to support conditional compilation
When it comes to `version = ".."`, it is needed to support conditional compilation
of language features as opposed to library features as previously shown.
Also, as we've seen, `version(..)` does not subsume `path_exists(..)` but is
Also, as we've seen, `version = ".."` does not subsume `path_exists(..)` but is
rather a complementary mechanism.

One problem specific to `version(..)` is that it might get too `rustc` specific.
One problem specific to `version = ".."` is that it might get too `rustc` specific.
It might be difficult for other Rust implementations than `rustc` to work with
this version numbering as libraries will compile against `rustc`s release
numbering. However, it is possible for other implementations to follow
Expand All @@ -355,7 +355,26 @@ 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.


### The bikeshed
### The bikeshed - Argument syntax

We have two options with respect to how the `version` flag may be specified:

1. `version = "<semver>"`
2. `version("<semver>")`

The syntax in 2. is currently an error in `#[cfg(..)]` as you may witness with:

```rust
// error[E0565]: unsupported literal
#[cfg(abracadabra("1.27"))] fn bar() {}
```

We could allow this syntax. However, we have chosen the syntax in 1.
This with consistency with flags such as `target_feature = "bmi"`.
Another reason to go with `version = ".."` is that `version("..")`
looks like a list.
Centril marked this conversation as resolved.
Show resolved Hide resolved

### The bikeshed - Attribute name

Naturally, there are other possible names for the flag. For example:

Expand All @@ -368,9 +387,9 @@ while also short and sweet. However, `min_version` is a good alternative
to consider because it telegraphs the `>=` nature of the flag.

As for the `version_string` syntax, it could also be adjusted such that
you could write `version(">= 1.27")`. We could also support exact version
you could write `version = ">= 1.27"`. We could also support exact version
checking (`==`) as well as checking if the compiler is below a certain version
(`<=`). However, as a first iteration, `version("1.27")` is simple and covers
(`<=`). However, as a first iteration, `version = "1.27"` is simple and covers
most use cases.

## [version_check] as an alternative
Expand Down Expand Up @@ -419,7 +438,7 @@ fn main() {
}
```

The [version_check] crate also supports testing for a minimum `version(..)` with:
The [version_check] crate also supports testing for a minimum `version = ".."` with:

```rust
extern crate version_check;
Expand All @@ -433,12 +452,12 @@ However, this is quite verbose in comparison and requires you to invent
ad-hoc and crate-specific names for your `#[cfg(..)]` flags such as
`MIN_COMPILER_1_13` that will not be the same for every crate.
You will also need to repeat this per version you want to support.
This causes the mechanism to scale poorly as compared to `version("1.27")`
This causes the mechanism to scale poorly as compared to `version = "1.27"`
which we argue is simple and intuitive.

## Conditional compilation on feature gates

An alternative to `version(..)` and `path_exists(..)` is to allow users
An alternative to `version = ".."` and `path_exists(..)` is to allow users
to query where a certain feature gate is stable or not.
However, it has been argued that allowing this would essentially stabilize
the names of the gates which we've historically not done.
Expand Down