Skip to content

Commit 236f2a5

Browse files
committed
Rename the version predicate to since
1 parent ecb3459 commit 236f2a5

File tree

1 file changed

+59
-53
lines changed

1 file changed

+59
-53
lines changed

text/0000-cfg-version.md

Lines changed: 59 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ Allow Rust-version conditional compilation by adding a built-in `--cfg rust=<ver
1010

1111
Say this was added before 1.70, you could do:
1212
```toml
13-
[target.'cfg(not(version(rust, "1.70")))'.dependencies"]
13+
[target.'cfg(not(since(rust, "1.70")))'.dependencies"]
1414
is-terminal = "0.4.16"
1515
```
1616

1717
```rust
1818
fn is_stderr_terminal() -> bool {
19-
#[cfg(version(rust, "1.70"))]
19+
#[cfg(since(rust, "1.70"))]
2020
use std::io::IsTerminal as _;
21-
#[cfg(not(version(rust, "1.70")))]
21+
#[cfg(not(since(rust, "1.70")))]
2222
use is_terminal::IsTerminal as _;
2323

2424
std::io::stderr().is_terminal()
@@ -121,9 +121,9 @@ circumstances may warrant doing so while maintaining an existing [MSRV](https://
121121
rather than raising to what the language or standard library feature needs.
122122
This can be accomplished by conditionally compiling the code for that feature.
123123

124-
For instance, say you have an MSRV of 1.10 and `#[cfg(version)]` feature was available in 1.20, you would have been able to do:
124+
For instance, say you have an MSRV of 1.10 and `#[cfg(since)]` feature was available in 1.20, you would have been able to do:
125125
```rust
126-
#[cfg_attr(version(rust, "1.27"), must_use)]
126+
#[cfg_attr(since(rust, "1.27"), must_use)]
127127
fn double(x: i32) -> i32 {
128128
2 * x
129129
}
@@ -138,7 +138,7 @@ fn main() {
138138
> Side note: if we also had [RFC 3804](https://github.com/rust-lang/rfcs/pull/3804),
139139
> we can give this condition a semantic name and avoid duplicating it, reducing the chance of bugs:
140140
> ```rust
141-
> #[cfg_alias(must_use_exists, version(rust, "1.27"))]
141+
> #[cfg_alias(must_use_exists, since(rust, "1.27"))]
142142
>
143143
> #[cfg_attr(must_use_exists, must_use)]
144144
> fn double(x: i32) -> i32 {
@@ -152,9 +152,9 @@ fn main() {
152152
> }
153153
> ```
154154
155-
Now, let's say `#[cfg(version)]` was stabilized in Rust 1.27 or later, you can check for support for it with:
155+
Now, let's say `#[cfg(since)]` was stabilized in Rust 1.27 or later, you can check for support for it with:
156156
```rust
157-
#[cfg_attr(rust, cfg_attr(version(rust, "1.27"), must_use))]
157+
#[cfg_attr(rust, cfg_attr(since(rust, "1.27"), must_use))]
158158
fn double(x: i32) -> i32 {
159159
2 * x
160160
}
@@ -181,14 +181,14 @@ In that case, you can pass `-Z assume-incomplete-release` to the compiler so tha
181181
# Reference-level explanation
182182
[reference-level-explanation]: #reference-level-explanation
183183

184-
## `version` cfg predicate
184+
## `since` cfg predicate
185185

186-
A `version` cfg predicate will be added to Rust.
186+
A `since` cfg predicate will be added to Rust.
187187
As Cargo mirrors Rust's `#[cfg]` syntax, it too will gain this predicate.
188188

189189
The [syntax](https://doc.rust-lang.org/reference/conditional-compilation.html#grammar-ConfigurationPredicate) is:
190190
```
191-
ConfigurationVersion -> `version` `(` IDENTIFIER `,` ( STRING_LITERAL | RAW_STRING_LITERAL ) `)`
191+
ConfigurationVersion -> `since` `(` IDENTIFIER `,` ( STRING_LITERAL | RAW_STRING_LITERAL ) `)`
192192
```
193193

194194
If `IDENTIFIER` has no value or is undefined, this will evaluate to `false`.
@@ -200,15 +200,15 @@ Note that this excludes support for the `+build` field.
200200

201201
Otherwise, the `IDENTIFIER` will be compared to the string literal according to
202202
[Cargo's `>=` version requirements](https://doc.rust-lang.org/nightly/cargo/reference/specifying-dependencies.html#comparison-requirements).
203-
For example, `#[cfg(version(rust, "1.90"))]` will be treated as `1.95.2 >= 1.90.0`.
203+
For example, `#[cfg(since(rust, "1.90"))]` will be treated as `1.95.2 >= 1.90.0`.
204204

205205
See also `--check-cfg`.
206206

207207
## `--check-cfg`
208208

209209
A new predicate will be added of the form:
210210
```
211-
ConfigurationVersion -> `version` `(` ( STRING_LITERAL | RAW_STRING_LITERAL ) `)`
211+
ConfigurationVersion -> `since` `(` ( STRING_LITERAL | RAW_STRING_LITERAL ) `)`
212212
```
213213

214214
The syntax for the contents of the string literal is a SemVer value without the build field.
@@ -217,21 +217,21 @@ This will specify that the given `--cfg` is valid for all values that match:
217217
- SemVer syntax
218218
- from the specified version and up
219219

220-
When the given `--cfg` is used with the `version` predicate:
220+
When the given `--cfg` is used with the `since` predicate:
221221
- the string literal should not be of a syntax that evaluates to `false`
222222
- the minimum version requirement must specify a subset of what the `--check-cfg` specifies
223223

224-
So given `--check-cfg 'cfg(foo, values(version("1.95.0")))'`,
224+
So given `--check-cfg 'cfg(foo, values(since("1.95.0")))'`,
225225
-`#[cfg(foo = "1.100.0")]`
226226
- ⚠️ `#[cfg(foo = "1.0")]`: not SemVer syntax
227-
-`#[cfg(version(foo, "1.95.0"))]`
228-
-`#[cfg(version(foo, "1.100.0"))]`
229-
-`#[cfg(version(foo, "3.0.0"))]`
230-
-`#[cfg(version(foo, "1.95"))]`
231-
- ⚠️ `#[cfg(version(foo, "1.90.0"))]`: matches a superset of `--check-cfg`
232-
- ⚠️ `#[cfg(version(foo, "1.95.0-0"))]`: matches a superset of `--check-cfg`
233-
- ⚠️ `#[cfg(version(foo, "1"))]`: matches a superset of `--check-cfg`
234-
- ⚠️ `#[cfg(version(foo, "bar"))]`: invalid string literal syntax
227+
-`#[cfg(since(foo, "1.95.0"))]`
228+
-`#[cfg(since(foo, "1.100.0"))]`
229+
-`#[cfg(since(foo, "3.0.0"))]`
230+
-`#[cfg(since(foo, "1.95"))]`
231+
- ⚠️ `#[cfg(since(foo, "1.90.0"))]`: matches a superset of `--check-cfg`
232+
- ⚠️ `#[cfg(since(foo, "1.95.0-0"))]`: matches a superset of `--check-cfg`
233+
- ⚠️ `#[cfg(since(foo, "1"))]`: matches a superset of `--check-cfg`
234+
- ⚠️ `#[cfg(since(foo, "bar"))]`: invalid string literal syntax
235235

236236
## `rust` cfg
237237

@@ -242,7 +242,7 @@ We expect rustc to:
242242
- Translate the `-nightly` pre-release to `-incomplete
243243
- Strip the `-beta.5` pre-release
244244

245-
`rust` will be specified as `--check-cfg 'cfg(rust, values(version("1.95.0")))'`
245+
`rust` will be specified as `--check-cfg 'cfg(rust, values(since("1.95.0")))'`
246246
(or whatever version this gets stabilized in).
247247

248248
This will be reported back through `--print-cfg`.
@@ -256,16 +256,17 @@ Cargo will expose `rust` in:
256256

257257
Clippy has a [`clippy::incompatible_msrv`](https://rust-lang.github.io/rust-clippy/master/index.html#incompatible_msrv) lint
258258
which will fire whenever a standard library item is used with a `#[stable(since)]` newer than `package.rust-version`.
259-
However, it will be perfectly reasonable to use those items when guarded by a `#[cfg(version)]`.
259+
However, it will be perfectly reasonable to use those items when guarded by a `#[cfg(since)]`.
260260

261261
Clippy may wish to:
262-
- Find a way to reduce false positives, e.g. evaluating the `cfg(version)`s that led to the item's usage or disabling the lint within `#[cfg(version)]`
263-
- Suggest `#[cfg(version)]` in the `clippy::incompatible_msrv` diagnostic report (maybe along with offering to bump MSRV)
262+
- Find a way to reduce false positives, e.g. evaluating the `cfg(since)`s that led to the item's usage or disabling the lint within `#[cfg(since)]`
263+
- Suggest `#[cfg(since)]` in the `clippy::incompatible_msrv` diagnostic report (maybe along with offering to bump MSRV)
264264

265265
# Drawbacks
266266
[drawbacks]: #drawbacks
267267

268268
People may be using `--cfg rust` already and would be broken by this change.
269+
There are no compatibility concerns with predicate names.
269270

270271
This does not include a solution for adopting this within `Cargo.toml` without waiting for an MSRV bump.
271272

@@ -281,16 +282,20 @@ Libraries could having ticking time bombs that accidentally break or have undesi
281282
# Rationale and alternatives
282283
[rationale-and-alternatives]: #rationale-and-alternatives
283284

284-
## `version` cfg predicate
285+
## `since` cfg predicate
285286

286-
We could offer a `before` operator but that is already covered by `not(version)`.
287+
We could offer a `before` operator but that is already covered by `not(since)`.
287288

288-
The `version` predicate's name doesn't convey what operation is happening and doesn't leave room for other version operators (e.g. a `before` to go with `since`).
289-
We could call this `since`, `minimum`, or support comparison operators in the spirit of [RFC 3796](https://github.com/rust-lang/rfcs/pull/3796).
290-
The challenge is dealing with clarity around the meaning, especially if we gain support for other data types in `cfg`, like integers, which is of interest for embedded development.
291-
`since` would fit in with `deprecated`.
289+
The `since` name was taken from
290+
[rustversion](https://crates.io/crates/rustversion) and the `#[deprecated(since)]` / `#[stable(since)]` attributes.
291+
This better conveys what operation is being performed than the original `version` name
292+
and leaves room for related predicates like `before`.
293+
We could also call this `minimum`, or support comparison operators in the spirit of [RFC 3796](https://github.com/rust-lang/rfcs/pull/3796).
294+
The risk with a general word like `since` is if we gain support for other data types in cfgs, like integers for embedded development.
295+
The name `since` might apply in some situations but not others and its unclear if we'd want to generalize it past versions.
296+
While having a specific name avoids these concerns.
292297

293-
We could swap the order of parameters and make `rust` a default for the second parameter to allow `#[cfg(version("1.95"))]` as a shorthand.
298+
We could swap the order of parameters and make `rust` a default for the second parameter to allow `#[cfg(since("1.95"))]` as a shorthand.
294299
However, this would look confusing in Cargo and doesn't seem like its offering enough of a benefit to be worth the costs.
295300

296301
The `ConfigurationVersion` is sloppy with the string literal's syntax (relying on `--check-cfg`) so that
@@ -299,8 +304,8 @@ The `ConfigurationVersion` is sloppy with the string literal's syntax (relying o
299304

300305
If we were stricter on the syntax,
301306
we could allow for version numbers to be directly accepted, without quotes
302-
(e.g. `#[cfg(version(rust, 1.95.0))]`).
303-
If we ever decided to support operators (e.g.`#[cfg(version(rust, "=1.95.0"))]`), then we'd need to decide if those also go outside the string or then require a string, being inconsistent.
307+
(e.g. `#[cfg(since(rust, 1.95.0))]`).
308+
If we ever decided to support operators (e.g.`#[cfg(since(rust, "=1.95.0"))]`, see `--check-cfg`), then we'd need to decide if those also go outside the string or then require a string, being inconsistent.
304309
This would also be inconsistent with other uses of `cfg`s
305310
*but* maybe that would just be the start to natively supporting more types in `cfg`,
306311
like integers which are of interest to embedded folks.
@@ -309,33 +314,34 @@ like integers which are of interest to embedded folks.
309314

310315
The `--check-cfg` predicate and the value for `rust` ensures users get warnings about
311316
- Invalid syntax
312-
- Using this with versions from before its supported, e.g. `#[cfg(version(rust, "1.0.0")]`
317+
- Using this with versions from before its supported, e.g. `#[cfg(since(rust, "1.0.0")]`
313318

314319
`--check-cfg` requires a SemVer version, rather than a version requirement,
315320
in case we want the future possibility of relaxing SemVer versions
316321
*and* we want to infer from the fields used in `--check-cfg` to specify the maximum number of fields accepted in comparisons.
317322

318-
We could have the `check-cfg` `version` predicate only apply to the `cfg` `version` predicate,
323+
We could have the `check-cfg` `since` predicate only apply to the `cfg` `since` predicate,
319324
causing `#[cfg(rust = "1.100.0")]` to warn.
320325
However,
321-
- the `version` predicates are a general feature intended to be used with other version numbers where exact matches may be appropriate.
326+
- the `since` predicates are a general feature intended to be used with other version numbers where exact matches may be appropriate.
322327
- this would get in the way of approximating the vendor version by the language version for working around compiler bugs and snapshotting of compiler output.
323328

324329
Possibly there could be a clippy lint specifically about `rust = "<something>"`.
325330
Alternatively, we could try to find a way to structure `--check-cfg` to allow the person defining the `cfg` to decide whether it can be use with `=` or not.
326-
One way of doing this is by allowing the `check-cfg` `version` predicate outside of the `values` predicate,
327-
meaning it works with the `cfg` `version` predicate and not the `=` operator.
328-
Another way would be for the `check-cfg` version predicate to never work with `=` but to instead
329-
allow operators inside of the `cfg` `version` predicate, e.g. `#[cfg(version(rust, "=1.95.0"))]`.
331+
One way of doing this is by allowing the `check-cfg` `since` predicate outside of the `values` predicate,
332+
meaning it works with the `cfg` `since` predicate and not the `=` operator.
333+
Another way would be for the `check-cfg` `since` predicate to never work with `=` but to instead
334+
allow operators inside of the `cfg` `since` predicate, e.g. `#[cfg(since(rust, "=1.95.0"))]`.
335+
However, with the rename of the predicate from `version` to `since`, operators don't fit in as easily.
330336

331337
`--check-cfg` will cause the following to warn:
332338
```rust
333339
fn is_stderr_terminal() -> bool {
334340
#[cfg(rust)]
335-
#[cfg(version(rust, "1.70"))]
341+
#[cfg(since(rust, "1.70"))]
336342
use std::io::IsTerminal as _;
337343
#[cfg(rust)]
338-
#[cfg(not(version(rust, "1.70")))]
344+
#[cfg(not(since(rust, "1.70")))]
339345
use is_terminal::IsTerminal as _;
340346
#[cfg(not(rust))]
341347
use is_terminal::IsTerminal as _;
@@ -373,21 +379,21 @@ The initial implementation treated nightlies as complete.
373379
This was [changed to incomplete](https://github.com/rust-lang/rust/pull/72001) after
374380
[some discussion](https://github.com/rust-lang/rust/issues/64796#issuecomment-624673206).
375381
In particular, this is important for
376-
- the case of package `bleeding-edge` starting to use a new feature behind `#[cfg(version)]` and package `nightly-only` has their toolchain pinned to a nightly before the feature was stabilized (to ensure consistent behavior of unstable features), package `nightly-only` cannot add or update their dependency on `bleeding-edge` without getting a "feature gate needed" error.
382+
- the case of package `bleeding-edge` starting to use a new feature behind `#[cfg(since)]` and package `nightly-only` has their toolchain pinned to a nightly before the feature was stabilized (to ensure consistent behavior of unstable features), package `nightly-only` cannot add or update their dependency on `bleeding-edge` without getting a "feature gate needed" error.
377383
- bisecting nightlies.
378384

379385
This was [changed back to complete](https://github.com/rust-lang/rust/pull/81468) after
380386
[some more discussion](https://github.com/rust-lang/rust/issues/64796#issuecomment-634546711).
381387
In particular, this is important for
382-
- keeping friction down for packages preparing for stabilized-on-nightly features as their `#[cfg(version)]`s can be inserted and "just work" which can be important for getting feedback quickly while the feature is easier to adapt to feedback that can be gained from these users
388+
- keeping friction down for packages preparing for stabilized-on-nightly features as their `#[cfg(since)]`s can be inserted and "just work" which can be important for getting feedback quickly while the feature is easier to adapt to feedback that can be gained from these users
383389
- releasing the package while its in this state puts it at risk to be broken if the feature is changed after stabilization
384390

385391
For RFC 2523, they settled on pre-releases being incomplete,
386392
favoring maintainers to adopt stabilized-on-nightly features immediately
387393
while letting people on pinned nightlies or bisecting nightlies to set a `-Z` to mark the version as incomplete.
388394

389395
In this RFC, we settled translating `-nightly` to `-incomplete` because:
390-
- Maintainers can adopt stabilized-on-nightly features with `#[cfg(version(rust, "1.100.0-0"))]` (the lowest pre-release for `1.100.0`), keeping friction low while explicitly acknowledging that the unstable feature may change
396+
- Maintainers can adopt stabilized-on-nightly features with `#[cfg(since(rust, "1.100.0-0"))]` (the lowest pre-release for `1.100.0`), keeping friction low while explicitly acknowledging that the unstable feature may change
391397
- Allows build scripts to experiment with other logic without less chance of needing to invoke `rustc` (e.g. detecting nightly)
392398
- It provides extra context when approximating the vendor version from the language version when populating build information
393399

@@ -552,12 +558,12 @@ Haskell:
552558
# Future possibilities
553559
[future-possibilities]: #future-possibilities
554560

555-
- In the future the `--check-cfg` `version()` predicate could make the minimum-version field optional,
561+
- In the future the `--check-cfg` `since()` predicate could make the minimum-version field optional,
556562
matching all version numbers.
557563

558564
## Relaxing SemVer
559565

560-
Instead of requiring the `IDENTIFIER` in the `version` predicate to be strictly SemVer `major.minor.patch`,
566+
Instead of requiring the `IDENTIFIER` in the `check-cfg` `since` predicate to be strictly SemVer `major.minor.patch`,
561567
we could allow abbreviated forms like `major.minor` or even `major`.
562568
This would make the predicate more inclusive for other cases, like `edition`.
563569

@@ -583,12 +589,12 @@ See also [`#[cfg(nightly)]`](https://rust-lang.github.io/rfcs/2523-cfg-path-vers
583589
## `cfg_target_version`
584590

585591
Instead of defining a new `#[cfg]` predicate, [RFC 3750](https://github.com/rust-lang/rfcs/pull/3750)
586-
could reuse the `#[cfg(version)]` predicate.
592+
could reuse the `#[cfg(since)]` predicate.
587593

588594
As not all systems use SemVer, we can either
589595
- Contort the version into SemVer
590596
- This can run into problems either with having more precision (e.g. `120.0.1.10` while SemVer only allows `X.Y.Z`) or post-release versions (e.g. `1.2.0.post1` which a SemVer predicate would treat as a pre-release).
591-
- Add an optional third field for specifying the version format (e.g. `#[cfg(version(windows, "10.0.10240", <policy-name>)]`)
597+
- Add an optional third field for specifying the version format (e.g. `#[cfg(since(windows, "10.0.10240", <policy-name>)]`)
592598
- Make `--check-cfg` load-bearing by having the version policy name be specified in the `--check-cfg` predicate
593599

594600
## Provide a way to get a `--cfg`s value
@@ -601,4 +607,4 @@ this would allow an application to approximate the vendor version `--bugreport`
601607

602608
As the ecosystem grows and matures,
603609
the Rust language and standard library may not be the only dependencies users wish to support multiple versions of.
604-
We may want to allow `#(cfg(version(serde, "1.0.900")]`.
610+
We may want to allow `#(cfg(since(serde, "1.0.900")]`.

0 commit comments

Comments
 (0)