Skip to content

Stabilize #[cfg(version(...))] #141137

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 22 additions & 0 deletions compiler/rustc_attr_data_structures/src/version.rs
Copy link
Member

Choose a reason for hiding this comment

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

I think the example of the stabilization report, let chains, are a particularly bad example here because they're syntax, and syntax is, except for some old legacy stuff, gated pre-expansion, so cfg(version) won't actually help there. It would probably make sense to mention this in the reference that this can't really be used for conditionally making use of syntax, or rather that such uses requires going through an intermediary macro_rules to avoid parsing it on the old version.

Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::fmt::{self, Display};
use std::sync::OnceLock;

use rustc_macros::{
Decodable, Encodable, HashStable_Generic, PrintAttribute, current_rustc_version,
Expand All @@ -16,8 +17,29 @@ pub struct RustcVersion {

impl RustcVersion {
pub const CURRENT: Self = current_rustc_version!();
pub fn current_configurable() -> Self {
*CURRENT_CONFIGURABLE.get_or_init(|| {
if let Ok(override_var) = std::env::var("RUSTC_OVERRIDE_VERSION_STRING")
&& let Some(override_) = Self::parse_str(&override_var)
{
override_
} else {
Self::CURRENT
}
})
}
fn parse_str(value: &str) -> Option<Self> {
// Ignore any suffixes such as "-dev" or "-nightly".
let mut components = value.split('-').next().unwrap().splitn(3, '.');
let major = components.next()?.parse().ok()?;
let minor = components.next()?.parse().ok()?;
let patch = components.next().unwrap_or("0").parse().ok()?;
Some(RustcVersion { major, minor, patch })
}
}

static CURRENT_CONFIGURABLE: OnceLock<RustcVersion> = OnceLock::new();

impl Display for RustcVersion {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch)
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_attr_parsing/src/attributes/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,9 @@ pub fn eval_condition(

// See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details
if sess.psess.assume_incomplete_release {
RustcVersion::CURRENT > min_version
RustcVersion::current_configurable() > min_version
} else {
RustcVersion::CURRENT >= min_version
RustcVersion::current_configurable() >= min_version
}
}
MetaItemKind::List(mis) => {
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/accepted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ declare_features! (
(accepted, cfg_target_feature, "1.27.0", Some(29717)),
/// Allows `cfg(target_vendor = "...")`.
(accepted, cfg_target_vendor, "1.33.0", Some(29718)),
/// Allow conditional compilation depending on rust version
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// Allow conditional compilation depending on rust version
/// Allow conditional compilation depending on Rust version.

(accepted, cfg_version, "CURRENT_RUSTC_VERSION", Some(64796)),
/// Allows implementing `Clone` for closures where possible (RFC 2132).
(accepted, clone_closures, "1.26.0", Some(44490)),
/// Allows coercing non capturing closures to function pointers.
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ const GATED_CFGS: &[GatedCfg] = &[
Features::cfg_target_has_atomic,
),
(sym::sanitize, sym::cfg_sanitize, Features::cfg_sanitize),
(sym::version, sym::cfg_version, Features::cfg_version),
(sym::relocation_model, sym::cfg_relocation_model, Features::cfg_relocation_model),
(sym::sanitizer_cfi_generalize_pointers, sym::cfg_sanitizer_cfi, Features::cfg_sanitizer_cfi),
(sym::sanitizer_cfi_normalize_integers, sym::cfg_sanitizer_cfi, Features::cfg_sanitizer_cfi),
Expand Down
2 changes: 0 additions & 2 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,8 +418,6 @@ declare_features! (
(unstable, cfg_target_thread_local, "1.7.0", Some(29594)),
/// Allows the use of `#[cfg(ub_checks)` to check if UB checks are enabled.
(unstable, cfg_ub_checks, "1.79.0", Some(123499)),
/// Allow conditional compilation depending on rust version
(unstable, cfg_version, "1.45.0", Some(64796)),
/// Allows to use the `#[cfi_encoding = ""]` attribute.
(unstable, cfi_encoding, "1.71.0", Some(89653)),
/// Allows `for<...>` on closures and coroutines.
Expand Down
20 changes: 20 additions & 0 deletions tests/ui/cfg/cfg-version/cfg-version-expand.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//@ run-pass
//@ rustc-env:RUSTC_OVERRIDE_VERSION_STRING=1.50.3

#[cfg(version("1.49.0"))]
const ON_1_49_0: bool = true;
#[cfg(version("1.50"))]
const ON_1_50_0: bool = true;
#[cfg(not(version("1.51")))]
const ON_1_51_0: bool = false;

fn main() {
assert!(ON_1_49_0);
assert!(ON_1_50_0);
assert!(!ON_1_51_0);
assert!(cfg!(version("1.1")));
assert!(cfg!(version("1.49")));
assert!(!cfg!(version("1.51")));
assert!(!cfg!(version("1.52")));
assert!(!cfg!(version("1.100")));
}
Original file line number Diff line number Diff line change
@@ -1,49 +1,35 @@
#[cfg(version(42))] //~ ERROR: expected a version literal
//~^ ERROR `cfg(version)` is experimental and subject to change
fn foo() {}
#[cfg(version(1.20))] //~ ERROR: expected a version literal
//~^ ERROR `cfg(version)` is experimental and subject to change
fn foo() -> bool { true }
#[cfg(version("1.44"))]
//~^ ERROR `cfg(version)` is experimental and subject to change
fn foo() -> bool { true }
#[cfg(not(version("1.44")))]
//~^ ERROR `cfg(version)` is experimental and subject to change
fn foo() -> bool { false }

#[cfg(version("1.43", "1.44", "1.45"))] //~ ERROR: expected single version literal
//~^ ERROR `cfg(version)` is experimental and subject to change
fn bar() -> bool { false }
#[cfg(version(false))] //~ ERROR: expected a version literal
//~^ ERROR `cfg(version)` is experimental and subject to change
fn bar() -> bool { false }
#[cfg(version("foo"))] //~ WARNING: unknown version literal format
//~^ ERROR `cfg(version)` is experimental and subject to change
fn bar() -> bool { false }
#[cfg(version("999"))] //~ WARNING: unknown version literal format
//~^ ERROR `cfg(version)` is experimental and subject to change
fn bar() -> bool { false }
#[cfg(version("-1"))] //~ WARNING: unknown version literal format
//~^ ERROR `cfg(version)` is experimental and subject to change
fn bar() -> bool { false }
#[cfg(version("65536"))] //~ WARNING: unknown version literal format
//~^ ERROR `cfg(version)` is experimental and subject to change
fn bar() -> bool { false }
#[cfg(version("0"))] //~ WARNING: unknown version literal format
//~^ ERROR `cfg(version)` is experimental and subject to change
fn bar() -> bool { true }
#[cfg(version("1.0"))]
//~^ ERROR `cfg(version)` is experimental and subject to change
fn bar() -> bool { true }
#[cfg(version("1.65536.2"))] //~ WARNING: unknown version literal format
//~^ ERROR `cfg(version)` is experimental and subject to change
fn bar() -> bool { false }
#[cfg(version("1.20.0-stable"))] //~ WARNING: unknown version literal format
//~^ ERROR `cfg(version)` is experimental and subject to change
fn bar() {}

fn main() {
assert!(foo());
assert!(bar());
assert!(cfg!(version("1.42"))); //~ ERROR `cfg(version)` is experimental and subject to change
assert!(cfg!(version("1.42")));
}
68 changes: 68 additions & 0 deletions tests/ui/cfg/cfg-version/cfg-version.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
error: expected a version literal
--> $DIR/cfg-version.rs:1:15
|
LL | #[cfg(version(42))]
| ^^

error: expected a version literal
--> $DIR/cfg-version.rs:3:15
|
LL | #[cfg(version(1.20))]
| ^^^^

error: expected single version literal
--> $DIR/cfg-version.rs:10:7
|
LL | #[cfg(version("1.43", "1.44", "1.45"))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: expected a version literal
--> $DIR/cfg-version.rs:12:15
|
LL | #[cfg(version(false))]
| ^^^^^

warning: unknown version literal format, assuming it refers to a future version
--> $DIR/cfg-version.rs:14:15
|
LL | #[cfg(version("foo"))]
| ^^^^^

warning: unknown version literal format, assuming it refers to a future version
--> $DIR/cfg-version.rs:16:15
|
LL | #[cfg(version("999"))]
| ^^^^^

warning: unknown version literal format, assuming it refers to a future version
--> $DIR/cfg-version.rs:18:15
|
LL | #[cfg(version("-1"))]
| ^^^^

warning: unknown version literal format, assuming it refers to a future version
--> $DIR/cfg-version.rs:20:15
|
LL | #[cfg(version("65536"))]
| ^^^^^^^

warning: unknown version literal format, assuming it refers to a future version
--> $DIR/cfg-version.rs:22:15
|
LL | #[cfg(version("0"))]
| ^^^

warning: unknown version literal format, assuming it refers to a future version
--> $DIR/cfg-version.rs:26:15
|
LL | #[cfg(version("1.65536.2"))]
| ^^^^^^^^^^^

warning: unknown version literal format, assuming it refers to a future version
--> $DIR/cfg-version.rs:28:15
|
LL | #[cfg(version("1.20.0-stable"))]
| ^^^^^^^^^^^^^^^

error: aborting due to 4 previous errors; 7 warnings emitted

Loading