Skip to content

Commit 11de2c7

Browse files
committed
Add drawback about feature checks prior to rust_version
1 parent c3f74b6 commit 11de2c7

File tree

1 file changed

+5
-3
lines changed

1 file changed

+5
-3
lines changed

text/0000-version-typed-cfgs.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ This section is subject to change prior to stabilization.
253253
- Making the perfect the enemy of the good. RFC 2523 was accepted, and an implementation of its `version()` predicate is ready.
254254
- Increased compiler complexity. This introduces a new concept of "typed" `cfg`s into the compiler, which adds complexity to the parsing and evaluation logic for conditional compilation.
255255
- Subtlety of MSRV-preserving patterns: The need for the "stacked `cfg`" pattern (`#[cfg(rust_version)] #[cfg(rust_version >= ...)]` and `#[cfg_attr(rust_version, cfg(rust_version >= ...))]`) is subtle. While we will add lints to guide users, it's less direct than a simple predicate. However, this subtlety is the explicit tradeoff made to achieve MSRV compatibility.
256+
- The MSRV-preserving pattern still does not allow using the feature to check for versions prior to when this feature was introduced.
256257
- The "stacked `cfg`" pattern does not work inside Cargo, so users will not be able to use this feature in Cargo until their MSRV is bumped. For cases where a dependency needs to be conditional on the Rust version, one can define a "polyfill" crate and make use of the MSRV-aware feature resolver, like the `is_terminal_polyfill` crate does.
257258
- Conditional compilation adds testing complexity. In practice, most crate maintainers only test their MSRV and the latest stable.
258259
- This does not support branching on specific nightly versions. rustversion supports this with syntax like `#[rustversion::since(2025-01-01)]`.
@@ -283,7 +284,7 @@ The syntax of this RFC was [left as an open question](https://github.com/rust-la
283284
* The word `version` does not sufficiently communicate that it's the Rust version we're talking about.
284285
* The mechanism is special-purpose and geared toward one use case (detecting the Rust version).
285286
* The function-call syntax, chosen for consistency with `cfg(accessible())`, isn't obvious enough in its meaning and does not cleanly extend to new kinds of comparisons. A recent poll of the lang team showed that most people opposed extending that syntax to include other kinds of comparisons within the quotes, like `version("< 1.2.3")`. At the same time, it adds another level of nested parantheses, which can be hard for humans to parse.
286-
* Crates supporting old MSRVs won't be able to use the feature until bumping their MSRV.
287+
* Crates supporting old MSRVs won't be able to use the feature until bumping their MSRV. (Note that even in this RFC, crates with existing feature gates using build scripts won't be able remove their build scripts until the last legacy feature gate falls outside their MSRV window.)
287288
* The RFC was accepted more than 6 years ago. During this time we've learned about more adjacent use cases and directions we would like to evolve the language. If designed today, the feature would look much more like this RFC than RFC 2523.
288289
289290
### Alternative 2: `#[cfg(rust_version = "1.85")]` (meaning `>=`)
@@ -341,7 +342,7 @@ This RFC aims to obviate the need for these external dependencies for the common
341342
342343
- **Clang/GCC (`__has_feature`, `__has_attribute`)**: These function-like macros allow for checking for the presence of specific compiler features, rather than the overall language version. For example, `__has_feature(cxx_rvalue_references)` checks for a specific language feature. This approach is more granular but also more verbose if one needs to check for many features at once. This approach was discussed in RFC #2523, but rejected, in part because we wanted to reinforce the idea of Rust as "one language" instead of a common subset with many compiler-specific extensions.
343344
344-
- **Python (`sys.version_info`)**: Python exposes its version at runtime via `sys.version_info`, a tuple of integers `(major, minor, micro, ...)`. Code can check the version with standard tuple comparison, e.g., `if sys.version_info >= (3, 8):`. This component-wise comparison is very similar to the logic proposed in this RFC. However, because Python is interpreted, a file must be syntactically valid for the interpreter that is running it, which makes it difficult to use newer syntax in a file that must also run on an older interpreter. Rust, being a compiled language with a powerful conditional compilation system, does not have this limitation, and this RFC's design takes full advantage of that.
345+
- **Python (`sys.version_info`)**: Python exposes its version at runtime via `sys.version_info`, a tuple of integers `(major, minor, micro, ...)`. Code can check the version with standard tuple comparison, e.g., `if sys.version_info >= (3, 8):`. This component-wise comparison is very similar to the logic proposed in this RFC. However, because Python is interpreted, a file must be syntactically valid for the interpreter that is running it, which makes it difficult to use newer syntax in a file that must also run on an older interpreter. Rust, being a compiled language with a powerful conditional compilation system, does not have this limitation, and this RFC's design takes full advantage of that. Python also supports sophisticated version handling in its packaging ecosystem (PEP 440), including arbitrary precision and pre/post releases.
345346
346347
## Versioning systems
347348
@@ -361,8 +362,9 @@ Operating systems include many versions, including kernel versions, public OS ve
361362
# Unresolved questions
362363
[unresolved-questions]: #unresolved-questions
363364
364-
- How should pre-release identifiers in version strings be handled? This RFC proposes not supporting pre-release identifiers in version strings passed on the command line for now. For comparisons, this RFC proposes that if a pre-release identifier is present in a `cfg` predicate (e.g., `rust_version < "2.0-alpha"`), the pre-release part is ignored for the comparison (so it's treated as `2.0`), and a lint is emitted. This ensures forward compatibility, as comparisons like `cfg(all(foo >= "2.0-alpha", foo < "2.0"))` become trivially false on older compilers, which is a safe outcome. This behavior can be refined before stabilization.
365+
- How should pre-release identifiers in version strings be handled? This RFC proposes not supporting pre-release identifiers in version strings passed on the command line for now. For comparisons, this RFC proposes that if a pre-release identifier is present in a `cfg` predicate (e.g., `rust_version < "2.0-alpha"`), the pre-release part is ignored for the comparison (so it's treated as `2.0`), and a lint is emitted. This ensures forward compatibility for valid versions, as comparisons like `cfg(all(foo >= "2.0-alpha", foo < "2.0"))` become trivially false on older compilers. However, if future semantics define `2.0-alpha < 2.0`, then ignoring the suffix now (treating `2.0-alpha` as `2.0`) means `foo < "2.0"` evaluates to `false` now but could evaluate to `true` later, which is a breaking change. We need to decide if this risk is acceptable or if we should error on pre-releases for now.
365366
- Should the builtin `rust_version` and `rust_edition` be printed with `--print cfg` on the command line? We'd like the eventual answer to be "yes", but existing tools that parse the output might break with the new `rust_version=version("1.99")` syntax. If we can manage the breakage we should; otherwise we can gate it on a future edition.
367+
- How should `cargo` expose version-typed `cfg`s to build scripts? Should `--cfg foo=version("1.0")` result in `CARGO_CFG_FOO=1.0` or `CARGO_CFG_FOO=version("1.0")`? This is technically out of scope for this RFC but important for the ecosystem.
366368
367369
# Future possibilities
368370
[future-possibilities]: #future-possibilities

0 commit comments

Comments
 (0)