Skip to content

Commit 90aa62a

Browse files
committed
Implement RFC 2523, #[cfg(version(..))]
1 parent f05a524 commit 90aa62a

File tree

11 files changed

+227
-19
lines changed

11 files changed

+227
-19
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3593,6 +3593,7 @@ dependencies = [
35933593
"rustc_session",
35943594
"rustc_span",
35953595
"serialize",
3596+
"version_check",
35963597
]
35973598

35983599
[[package]]
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# `cfg_version`
2+
3+
The tracking issue for this feature is: [#64796]
4+
5+
[#64796]: https://github.com/rust-lang/rust/issues/64796
6+
7+
------------------------
8+
9+
The `cfg_version` feature makes it possible to execute different code
10+
depending on the compiler version.
11+
12+
## Examples
13+
14+
```rust
15+
#![feature(cfg_version)]
16+
17+
#[cfg(version("1.42"))]
18+
fn a() {
19+
// ...
20+
}
21+
22+
#[cfg(not(version("1.42")))]
23+
fn a() {
24+
// ...
25+
}
26+
27+
fn b() {
28+
if cfg!(version("1.42")) {
29+
// ...
30+
} else {
31+
// ...
32+
}
33+
}
34+
```

src/librustc_attr/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ rustc_feature = { path = "../librustc_feature" }
1919
rustc_macros = { path = "../librustc_macros" }
2020
rustc_session = { path = "../librustc_session" }
2121
rustc_ast = { path = "../librustc_ast" }
22+
version_check = "0.9"

src/librustc_attr/builtin.rs

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use super::{find_by_name, mark_used};
44

5-
use rustc_ast::ast::{self, Attribute, MetaItem, MetaItemKind, NestedMetaItem};
5+
use rustc_ast::ast::{self, Attribute, Lit, LitKind, MetaItem, MetaItemKind, NestedMetaItem};
66
use rustc_ast_pretty::pprust;
77
use rustc_errors::{struct_span_err, Applicability, Handler};
88
use rustc_feature::{find_gated_cfg, is_builtin_attr_name, Features, GatedCfg};
@@ -11,6 +11,7 @@ use rustc_session::parse::{feature_err, ParseSess};
1111
use rustc_span::hygiene::Transparency;
1212
use rustc_span::{symbol::sym, symbol::Symbol, Span};
1313
use std::num::NonZeroU32;
14+
use version_check::Version;
1415

1516
pub fn is_builtin_attr(attr: &Attribute) -> bool {
1617
attr.is_doc_comment() || attr.ident().filter(|ident| is_builtin_attr_name(ident.name)).is_some()
@@ -568,11 +569,8 @@ pub fn find_crate_name(attrs: &[Attribute]) -> Option<Symbol> {
568569

569570
/// Tests if a cfg-pattern matches the cfg set
570571
pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) -> bool {
571-
eval_condition(cfg, sess, &mut |cfg| {
572-
let gate = find_gated_cfg(|sym| cfg.check_name(sym));
573-
if let (Some(feats), Some(gated_cfg)) = (features, gate) {
574-
gate_cfg(&gated_cfg, cfg.span, sess, feats);
575-
}
572+
eval_condition(cfg, sess, features, &mut |cfg| {
573+
try_gate_cfg(cfg, sess, features);
576574
let error = |span, msg| {
577575
sess.span_diagnostic.span_err(span, msg);
578576
true
@@ -603,6 +601,13 @@ pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Feat
603601
})
604602
}
605603

604+
fn try_gate_cfg(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) {
605+
let gate = find_gated_cfg(|sym| cfg.check_name(sym));
606+
if let (Some(feats), Some(gated_cfg)) = (features, gate) {
607+
gate_cfg(&gated_cfg, cfg.span, sess, feats);
608+
}
609+
}
610+
606611
fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &ParseSess, features: &Features) {
607612
let (cfg, feature, has_feature) = gated_cfg;
608613
if !has_feature(features) && !cfg_span.allows_unstable(*feature) {
@@ -616,9 +621,42 @@ fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &ParseSess, features: &F
616621
pub fn eval_condition(
617622
cfg: &ast::MetaItem,
618623
sess: &ParseSess,
624+
features: Option<&Features>,
619625
eval: &mut impl FnMut(&ast::MetaItem) -> bool,
620626
) -> bool {
621627
match cfg.kind {
628+
ast::MetaItemKind::List(ref mis) if cfg.name_or_empty() == sym::version => {
629+
try_gate_cfg(cfg, sess, features);
630+
let (min_version, span) = match &mis[..] {
631+
[NestedMetaItem::Literal(Lit { kind: LitKind::Str(sym, ..), span, .. })] => {
632+
(sym, span)
633+
}
634+
[NestedMetaItem::Literal(Lit { span, .. })
635+
| NestedMetaItem::MetaItem(MetaItem { span, .. })] => {
636+
sess.span_diagnostic
637+
.struct_span_err(*span, &*format!("expected string literal"))
638+
.emit();
639+
return false;
640+
}
641+
[..] => {
642+
sess.span_diagnostic
643+
.struct_span_err(cfg.span, "expected single string literal")
644+
.emit();
645+
return false;
646+
}
647+
};
648+
let min_version = match Version::parse(&min_version.as_str()) {
649+
Some(ver) => ver,
650+
None => {
651+
sess.span_diagnostic.struct_span_err(*span, "invalid version string").emit();
652+
return false;
653+
}
654+
};
655+
let version = option_env!("CFG_VERSION").unwrap_or("unknown rustc version");
656+
let version = Version::parse(version).unwrap();
657+
658+
version >= min_version
659+
}
622660
ast::MetaItemKind::List(ref mis) => {
623661
for mi in mis.iter() {
624662
if !mi.is_meta_item() {
@@ -634,12 +672,12 @@ pub fn eval_condition(
634672
// The unwraps below may look dangerous, but we've already asserted
635673
// that they won't fail with the loop above.
636674
match cfg.name_or_empty() {
637-
sym::any => {
638-
mis.iter().any(|mi| eval_condition(mi.meta_item().unwrap(), sess, eval))
639-
}
640-
sym::all => {
641-
mis.iter().all(|mi| eval_condition(mi.meta_item().unwrap(), sess, eval))
642-
}
675+
sym::any => mis
676+
.iter()
677+
.any(|mi| eval_condition(mi.meta_item().unwrap(), sess, features, eval)),
678+
sym::all => mis
679+
.iter()
680+
.all(|mi| eval_condition(mi.meta_item().unwrap(), sess, features, eval)),
643681
sym::not => {
644682
if mis.len() != 1 {
645683
struct_span_err!(
@@ -652,7 +690,7 @@ pub fn eval_condition(
652690
return false;
653691
}
654692

655-
!eval_condition(mis[0].meta_item().unwrap(), sess, eval)
693+
!eval_condition(mis[0].meta_item().unwrap(), sess, features, eval)
656694
}
657695
_ => {
658696
struct_span_err!(

src/librustc_attr/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
//! The goal is to move the definition of `MetaItem` and things that don't need to be in `syntax`
55
//! to this crate.
66
7+
#![feature(or_patterns)]
8+
79
mod builtin;
810

911
pub use builtin::*;

src/librustc_feature/active.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,9 @@ declare_features! (
562562
/// Allows the use of `#[target_feature]` on safe functions.
563563
(active, target_feature_11, "1.45.0", Some(69098), None),
564564

565+
/// Allow conditional compilation depending on rust version
566+
(active, cfg_version, "1.45.0", Some(64796), None),
567+
565568
// -------------------------------------------------------------------------
566569
// feature-group-end: actual feature gates
567570
// -------------------------------------------------------------------------

src/librustc_feature/builtin_attrs.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const GATED_CFGS: &[GatedCfg] = &[
2626
(sym::target_has_atomic, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)),
2727
(sym::target_has_atomic_load_store, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)),
2828
(sym::sanitize, sym::cfg_sanitize, cfg_fn!(cfg_sanitize)),
29+
(sym::version, sym::cfg_version, cfg_fn!(cfg_version)),
2930
];
3031

3132
/// Find a gated cfg determined by the `pred`icate which is given the cfg's name.

src/librustc_span/symbol.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ symbols! {
192192
cfg_target_has_atomic,
193193
cfg_target_thread_local,
194194
cfg_target_vendor,
195+
cfg_version,
195196
char,
196197
clippy,
197198
clone,
@@ -805,6 +806,7 @@ symbols! {
805806
var,
806807
vec,
807808
Vec,
809+
version,
808810
vis,
809811
visible_private_types,
810812
volatile,

src/librustc_trait_selection/traits/on_unimplemented.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ impl<'tcx> OnUnimplementedDirective {
8181
None,
8282
)
8383
})?;
84-
attr::eval_condition(cond, &tcx.sess.parse_sess, &mut |_| true);
84+
attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |_| true);
8585
Some(cond.clone())
8686
};
8787

@@ -208,11 +208,16 @@ impl<'tcx> OnUnimplementedDirective {
208208

209209
for command in self.subcommands.iter().chain(Some(self)).rev() {
210210
if let Some(ref condition) = command.condition {
211-
if !attr::eval_condition(condition, &tcx.sess.parse_sess, &mut |c| {
212-
c.ident().map_or(false, |ident| {
213-
options.contains(&(ident.name, c.value_str().map(|s| s.to_string())))
214-
})
215-
}) {
211+
if !attr::eval_condition(
212+
condition,
213+
&tcx.sess.parse_sess,
214+
Some(tcx.features()),
215+
&mut |c| {
216+
c.ident().map_or(false, |ident| {
217+
options.contains(&(ident.name, c.value_str().map(|s| s.to_string())))
218+
})
219+
},
220+
) {
216221
debug!("evaluate: skipping {:?} due to condition", command);
217222
continue;
218223
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#[cfg(version("1.44"))]
2+
//~^ ERROR `cfg(version)` is experimental and subject to change
3+
fn foo() -> bool { true }
4+
#[cfg(not(version("1.44")))]
5+
//~^ ERROR `cfg(version)` is experimental and subject to change
6+
fn foo() -> bool { false }
7+
8+
#[cfg(version("1.43", "1.44", "1.45"))] //~ ERROR: expected single string literal
9+
//~^ ERROR `cfg(version)` is experimental and subject to change
10+
fn bar() -> bool { false }
11+
#[cfg(version(false))] //~ ERROR: expected string literal
12+
//~^ ERROR `cfg(version)` is experimental and subject to change
13+
fn bar() -> bool { false }
14+
#[cfg(version("foo"))] //~ ERROR: invalid version string
15+
//~^ ERROR `cfg(version)` is experimental and subject to change
16+
fn bar() -> bool { false }
17+
#[cfg(version("999"))]
18+
//~^ ERROR `cfg(version)` is experimental and subject to change
19+
fn bar() -> bool { false }
20+
#[cfg(version("0"))]
21+
//~^ ERROR `cfg(version)` is experimental and subject to change
22+
fn bar() -> bool { true }
23+
24+
fn main() {
25+
assert!(foo());
26+
assert!(bar());
27+
assert!(cfg!(version("1.42"))); //~ ERROR `cfg(version)` is experimental and subject to change
28+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
error[E0658]: `cfg(version)` is experimental and subject to change
2+
--> $DIR/feature-gate-cfg-version.rs:1:7
3+
|
4+
LL | #[cfg(version("1.44"))]
5+
| ^^^^^^^^^^^^^^^
6+
|
7+
= note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
8+
= help: add `#![feature(cfg_version)]` to the crate attributes to enable
9+
10+
error[E0658]: `cfg(version)` is experimental and subject to change
11+
--> $DIR/feature-gate-cfg-version.rs:4:11
12+
|
13+
LL | #[cfg(not(version("1.44")))]
14+
| ^^^^^^^^^^^^^^^
15+
|
16+
= note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
17+
= help: add `#![feature(cfg_version)]` to the crate attributes to enable
18+
19+
error[E0658]: `cfg(version)` is experimental and subject to change
20+
--> $DIR/feature-gate-cfg-version.rs:8:7
21+
|
22+
LL | #[cfg(version("1.43", "1.44", "1.45"))]
23+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
24+
|
25+
= note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
26+
= help: add `#![feature(cfg_version)]` to the crate attributes to enable
27+
28+
error: expected single string literal
29+
--> $DIR/feature-gate-cfg-version.rs:8:7
30+
|
31+
LL | #[cfg(version("1.43", "1.44", "1.45"))]
32+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
33+
34+
error[E0658]: `cfg(version)` is experimental and subject to change
35+
--> $DIR/feature-gate-cfg-version.rs:11:7
36+
|
37+
LL | #[cfg(version(false))]
38+
| ^^^^^^^^^^^^^^
39+
|
40+
= note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
41+
= help: add `#![feature(cfg_version)]` to the crate attributes to enable
42+
43+
error: expected string literal
44+
--> $DIR/feature-gate-cfg-version.rs:11:15
45+
|
46+
LL | #[cfg(version(false))]
47+
| ^^^^^
48+
49+
error[E0658]: `cfg(version)` is experimental and subject to change
50+
--> $DIR/feature-gate-cfg-version.rs:14:7
51+
|
52+
LL | #[cfg(version("foo"))]
53+
| ^^^^^^^^^^^^^^
54+
|
55+
= note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
56+
= help: add `#![feature(cfg_version)]` to the crate attributes to enable
57+
58+
error: invalid version string
59+
--> $DIR/feature-gate-cfg-version.rs:14:15
60+
|
61+
LL | #[cfg(version("foo"))]
62+
| ^^^^^
63+
64+
error[E0658]: `cfg(version)` is experimental and subject to change
65+
--> $DIR/feature-gate-cfg-version.rs:17:7
66+
|
67+
LL | #[cfg(version("999"))]
68+
| ^^^^^^^^^^^^^^
69+
|
70+
= note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
71+
= help: add `#![feature(cfg_version)]` to the crate attributes to enable
72+
73+
error[E0658]: `cfg(version)` is experimental and subject to change
74+
--> $DIR/feature-gate-cfg-version.rs:20:7
75+
|
76+
LL | #[cfg(version("0"))]
77+
| ^^^^^^^^^^^^
78+
|
79+
= note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
80+
= help: add `#![feature(cfg_version)]` to the crate attributes to enable
81+
82+
error[E0658]: `cfg(version)` is experimental and subject to change
83+
--> $DIR/feature-gate-cfg-version.rs:27:18
84+
|
85+
LL | assert!(cfg!(version("1.42")));
86+
| ^^^^^^^^^^^^^^^
87+
|
88+
= note: see issue #64796 <https://github.com/rust-lang/rust/issues/64796> for more information
89+
= help: add `#![feature(cfg_version)]` to the crate attributes to enable
90+
91+
error: aborting due to 11 previous errors
92+
93+
For more information about this error, try `rustc --explain E0658`.

0 commit comments

Comments
 (0)