Skip to content

Commit c12a36a

Browse files
authored
Rollup merge of #96913 - Urgau:rfc3239-part2, r=petrochenkov
RFC3239: Implement `cfg(target)` - Part 2 This pull-request implements the compact `cfg(target(..))` part of [RFC 3239](#96901). I recommend reviewing this PR on a per commit basics, because of some moving parts. cc `@GuillaumeGomez` r? `@petrochenkov`
2 parents 11faf2e + b9ae3db commit c12a36a

14 files changed

+247
-67
lines changed

compiler/rustc_attr/src/builtin.rs

+83-58
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,15 @@ pub fn find_crate_name(sess: &Session, attrs: &[Attribute]) -> Option<Symbol> {
454454
sess.first_attr_value_str_by_name(attrs, sym::crate_name)
455455
}
456456

457+
#[derive(Clone, Debug)]
458+
pub struct Condition {
459+
pub name: Symbol,
460+
pub name_span: Span,
461+
pub value: Option<Symbol>,
462+
pub value_span: Option<Span>,
463+
pub span: Span,
464+
}
465+
457466
/// Tests if a cfg-pattern matches the cfg set
458467
pub fn cfg_matches(
459468
cfg: &ast::MetaItem,
@@ -462,70 +471,42 @@ pub fn cfg_matches(
462471
features: Option<&Features>,
463472
) -> bool {
464473
eval_condition(cfg, sess, features, &mut |cfg| {
465-
try_gate_cfg(cfg, sess, features);
466-
let error = |span, msg| {
467-
sess.span_diagnostic.span_err(span, msg);
468-
true
469-
};
470-
if cfg.path.segments.len() != 1 {
471-
return error(cfg.path.span, "`cfg` predicate key must be an identifier");
472-
}
473-
match &cfg.kind {
474-
MetaItemKind::List(..) => {
475-
error(cfg.span, "unexpected parentheses after `cfg` predicate key")
476-
}
477-
MetaItemKind::NameValue(lit) if !lit.kind.is_str() => {
478-
handle_errors(
479-
sess,
480-
lit.span,
481-
AttrError::UnsupportedLiteral(
482-
"literal in `cfg` predicate value must be a string",
483-
lit.kind.is_bytestr(),
484-
),
474+
try_gate_cfg(cfg.name, cfg.span, sess, features);
475+
if let Some(names_valid) = &sess.check_config.names_valid {
476+
if !names_valid.contains(&cfg.name) {
477+
sess.buffer_lint_with_diagnostic(
478+
UNEXPECTED_CFGS,
479+
cfg.span,
480+
lint_node_id,
481+
"unexpected `cfg` condition name",
482+
BuiltinLintDiagnostics::UnexpectedCfg((cfg.name, cfg.name_span), None),
485483
);
486-
true
487484
}
488-
MetaItemKind::NameValue(..) | MetaItemKind::Word => {
489-
let ident = cfg.ident().expect("multi-segment cfg predicate");
490-
let name = ident.name;
491-
let value = cfg.value_str();
492-
if let Some(names_valid) = &sess.check_config.names_valid {
493-
if !names_valid.contains(&name) {
494-
sess.buffer_lint_with_diagnostic(
495-
UNEXPECTED_CFGS,
496-
cfg.span,
497-
lint_node_id,
498-
"unexpected `cfg` condition name",
499-
BuiltinLintDiagnostics::UnexpectedCfg((name, ident.span), None),
500-
);
501-
}
502-
}
503-
if let Some(value) = value {
504-
if let Some(values) = &sess.check_config.values_valid.get(&name) {
505-
if !values.contains(&value) {
506-
sess.buffer_lint_with_diagnostic(
507-
UNEXPECTED_CFGS,
508-
cfg.span,
509-
lint_node_id,
510-
"unexpected `cfg` condition value",
511-
BuiltinLintDiagnostics::UnexpectedCfg(
512-
(name, ident.span),
513-
Some((value, cfg.name_value_literal_span().unwrap())),
514-
),
515-
);
516-
}
517-
}
485+
}
486+
if let Some(value) = cfg.value {
487+
if let Some(values) = &sess.check_config.values_valid.get(&cfg.name) {
488+
if !values.contains(&value) {
489+
sess.buffer_lint_with_diagnostic(
490+
UNEXPECTED_CFGS,
491+
cfg.span,
492+
lint_node_id,
493+
"unexpected `cfg` condition value",
494+
BuiltinLintDiagnostics::UnexpectedCfg(
495+
(cfg.name, cfg.name_span),
496+
cfg.value_span.map(|vs| (value, vs)),
497+
),
498+
);
518499
}
519-
sess.config.contains(&(name, value))
520500
}
521501
}
502+
sess.config.contains(&(cfg.name, cfg.value))
522503
})
523504
}
524505

525-
fn try_gate_cfg(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) {
526-
let gate = find_gated_cfg(|sym| cfg.has_name(sym));
506+
fn try_gate_cfg(name: Symbol, span: Span, sess: &ParseSess, features: Option<&Features>) {
507+
let gate = find_gated_cfg(|sym| sym == name);
527508
if let (Some(feats), Some(gated_cfg)) = (features, gate) {
528-
gate_cfg(&gated_cfg, cfg.span, sess, feats);
509+
gate_cfg(&gated_cfg, span, sess, feats);
529510
}
530511
}
531512

@@ -563,11 +544,11 @@ pub fn eval_condition(
563544
cfg: &ast::MetaItem,
564545
sess: &ParseSess,
565546
features: Option<&Features>,
566-
eval: &mut impl FnMut(&ast::MetaItem) -> bool,
547+
eval: &mut impl FnMut(Condition) -> bool,
567548
) -> bool {
568549
match cfg.kind {
569550
ast::MetaItemKind::List(ref mis) if cfg.name_or_empty() == sym::version => {
570-
try_gate_cfg(cfg, sess, features);
551+
try_gate_cfg(sym::version, cfg.span, sess, features);
571552
let (min_version, span) = match &mis[..] {
572553
[NestedMetaItem::Literal(Lit { kind: LitKind::Str(sym, ..), span, .. })] => {
573554
(sym, span)
@@ -649,6 +630,25 @@ pub fn eval_condition(
649630

650631
!eval_condition(mis[0].meta_item().unwrap(), sess, features, eval)
651632
}
633+
sym::target => {
634+
if let Some(features) = features && !features.cfg_target_compact {
635+
feature_err(
636+
sess,
637+
sym::cfg_target_compact,
638+
cfg.span,
639+
&"compact `cfg(target(..))` is experimental and subject to change"
640+
).emit();
641+
}
642+
643+
mis.iter().fold(true, |res, mi| {
644+
let mut mi = mi.meta_item().unwrap().clone();
645+
if let [seg, ..] = &mut mi.path.segments[..] {
646+
seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name));
647+
}
648+
649+
res & eval_condition(&mi, sess, features, eval)
650+
})
651+
}
652652
_ => {
653653
struct_span_err!(
654654
sess.span_diagnostic,
@@ -662,7 +662,32 @@ pub fn eval_condition(
662662
}
663663
}
664664
}
665-
ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => eval(cfg),
665+
ast::MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => {
666+
sess.span_diagnostic
667+
.span_err(cfg.path.span, "`cfg` predicate key must be an identifier");
668+
true
669+
}
670+
MetaItemKind::NameValue(ref lit) if !lit.kind.is_str() => {
671+
handle_errors(
672+
sess,
673+
lit.span,
674+
AttrError::UnsupportedLiteral(
675+
"literal in `cfg` predicate value must be a string",
676+
lit.kind.is_bytestr(),
677+
),
678+
);
679+
true
680+
}
681+
ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => {
682+
let ident = cfg.ident().expect("multi-segment cfg predicate");
683+
eval(Condition {
684+
name: ident.name,
685+
name_span: ident.span,
686+
value: cfg.value_str(),
687+
value_span: cfg.name_value_literal_span(),
688+
span: cfg.span,
689+
})
690+
}
666691
}
667692
}
668693

compiler/rustc_attr/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
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(let_chains)]
78
#![feature(let_else)]
89

910
#[macro_use]

compiler/rustc_feature/src/active.rs

+2
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,8 @@ declare_features! (
319319
(active, cfg_sanitize, "1.41.0", Some(39699), None),
320320
/// Allows `cfg(target_abi = "...")`.
321321
(active, cfg_target_abi, "1.55.0", Some(80970), None),
322+
/// Allows `cfg(target(abi = "..."))`.
323+
(active, cfg_target_compact, "1.63.0", Some(96901), None),
322324
/// Allows `cfg(target_has_atomic_load_store = "...")`.
323325
(active, cfg_target_has_atomic, "1.60.0", Some(94039), None),
324326
/// Allows `cfg(target_has_atomic_equal_alignment = "...")`.

compiler/rustc_span/src/symbol.rs

+2
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,7 @@ symbols! {
427427
cfg_panic,
428428
cfg_sanitize,
429429
cfg_target_abi,
430+
cfg_target_compact,
430431
cfg_target_feature,
431432
cfg_target_has_atomic,
432433
cfg_target_has_atomic_equal_alignment,
@@ -1375,6 +1376,7 @@ symbols! {
13751376
sym,
13761377
sync,
13771378
t32,
1379+
target,
13781380
target_abi,
13791381
target_arch,
13801382
target_endian,

compiler/rustc_trait_selection/src/traits/on_unimplemented.rs

+7-9
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,8 @@ impl<'tcx> OnUnimplementedDirective {
8989
None,
9090
)
9191
})?;
92-
attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |item| {
93-
if let Some(symbol) = item.value_str() && let Err(guar) = parse_value(symbol) {
92+
attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |cfg| {
93+
if let Some(value) = cfg.value && let Err(guar) = parse_value(value) {
9494
errored = Some(guar);
9595
}
9696
true
@@ -226,14 +226,12 @@ impl<'tcx> OnUnimplementedDirective {
226226
condition,
227227
&tcx.sess.parse_sess,
228228
Some(tcx.features()),
229-
&mut |c| {
230-
c.ident().map_or(false, |ident| {
231-
let value = c.value_str().map(|s| {
232-
OnUnimplementedFormatString(s).format(tcx, trait_ref, &options_map)
233-
});
229+
&mut |cfg| {
230+
let value = cfg.value.map(|v| {
231+
OnUnimplementedFormatString(v).format(tcx, trait_ref, &options_map)
232+
});
234233

235-
options.contains(&(ident.name, value))
236-
})
234+
options.contains(&(cfg.name, value))
237235
},
238236
) {
239237
debug!("evaluate: skipping {:?} due to condition", command);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// check-fail
2+
3+
#![feature(cfg_target_compact)]
4+
5+
#[cfg(target(o::o))]
6+
//~^ ERROR `cfg` predicate key must be an identifier
7+
fn one() {}
8+
9+
#[cfg(target(os = 8))]
10+
//~^ ERROR literal in `cfg` predicate value must be a string
11+
fn two() {}
12+
13+
#[cfg(target(os = "linux", pointer(width = "64")))]
14+
//~^ ERROR invalid predicate `target_pointer`
15+
fn three() {}
16+
17+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error: `cfg` predicate key must be an identifier
2+
--> $DIR/cfg-target-compact-errors.rs:5:14
3+
|
4+
LL | #[cfg(target(o::o))]
5+
| ^^^^
6+
7+
error[E0565]: literal in `cfg` predicate value must be a string
8+
--> $DIR/cfg-target-compact-errors.rs:9:19
9+
|
10+
LL | #[cfg(target(os = 8))]
11+
| ^
12+
13+
error[E0537]: invalid predicate `target_pointer`
14+
--> $DIR/cfg-target-compact-errors.rs:13:28
15+
|
16+
LL | #[cfg(target(os = "linux", pointer(width = "64")))]
17+
| ^^^^^^^^^^^^^^^^^^^^^
18+
19+
error: aborting due to 3 previous errors
20+
21+
Some errors have detailed explanations: E0537, E0565.
22+
For more information about an error, try `rustc --explain E0537`.

src/test/ui/cfg/cfg-target-compact.rs

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// run-pass
2+
#![feature(cfg_target_compact)]
3+
4+
#[cfg(target(os = "linux", pointer_width = "64"))]
5+
pub fn main() {
6+
}
7+
8+
#[cfg(not(target(os = "linux", pointer_width = "64")))]
9+
pub fn main() {
10+
}
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// This test check that we correctly emit an warning for compact cfg
2+
//
3+
// check-pass
4+
// compile-flags:--check-cfg=names() -Z unstable-options
5+
6+
#![feature(cfg_target_compact)]
7+
8+
#[cfg(target(os = "linux", arch = "arm"))]
9+
pub fn expected() {}
10+
11+
#[cfg(target(os = "linux", architecture = "arm"))]
12+
//~^ WARNING unexpected `cfg` condition name
13+
pub fn unexpected() {}
14+
15+
fn main() {}
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
warning: unexpected `cfg` condition name
2+
--> $DIR/compact-names.rs:11:28
3+
|
4+
LL | #[cfg(target(os = "linux", architecture = "arm"))]
5+
| ^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `#[warn(unexpected_cfgs)]` on by default
8+
9+
warning: 1 warning emitted
10+
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// This test check that we correctly emit an warning for compact cfg
2+
//
3+
// check-pass
4+
// compile-flags:--check-cfg=values() -Z unstable-options
5+
6+
#![feature(cfg_target_compact)]
7+
8+
#[cfg(target(os = "linux", arch = "arm"))]
9+
pub fn expected() {}
10+
11+
#[cfg(target(os = "linux", arch = "X"))]
12+
//~^ WARNING unexpected `cfg` condition value
13+
pub fn unexpected() {}
14+
15+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
warning: unexpected `cfg` condition value
2+
--> $DIR/compact-values.rs:11:28
3+
|
4+
LL | #[cfg(target(os = "linux", arch = "X"))]
5+
| ^^^^^^^^^^
6+
|
7+
= note: `#[warn(unexpected_cfgs)]` on by default
8+
= note: expected values for `target_arch` are: aarch64, arm, avr, bpf, hexagon, m68k, mips, mips64, msp430, nvptx64, powerpc, powerpc64, riscv32, riscv64, s390x, sparc, sparc64, wasm32, wasm64, x86, x86_64
9+
10+
warning: 1 warning emitted
11+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#[cfg(target(os = "x"))] //~ ERROR compact `cfg(target(..))` is experimental
2+
struct Foo(u64, u64);
3+
4+
#[cfg_attr(target(os = "x"), x)] //~ ERROR compact `cfg(target(..))` is experimental
5+
struct Bar(u64, u64);
6+
7+
#[cfg(not(any(all(target(os = "x")))))] //~ ERROR compact `cfg(target(..))` is experimental
8+
fn foo() {}
9+
10+
fn main() {
11+
cfg!(target(os = "x"));
12+
//~^ ERROR compact `cfg(target(..))` is experimental and subject to change
13+
}

0 commit comments

Comments
 (0)