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](https://github.com/rust-lang/rust/issues/96901).

I recommend reviewing this PR on a per commit basics, because of some moving parts.

cc `@GuillaumeGomez`
r? `@petrochenkov`
This commit is contained in:
Dylan DPC 2022-05-25 10:48:28 +02:00 committed by GitHub
commit c12a36adc6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 249 additions and 69 deletions

View File

@ -454,6 +454,15 @@ pub fn find_crate_name(sess: &Session, attrs: &[Attribute]) -> Option<Symbol> {
sess.first_attr_value_str_by_name(attrs, sym::crate_name)
}
#[derive(Clone, Debug)]
pub struct Condition {
pub name: Symbol,
pub name_span: Span,
pub value: Option<Symbol>,
pub value_span: Option<Span>,
pub span: Span,
}
/// Tests if a cfg-pattern matches the cfg set
pub fn cfg_matches(
cfg: &ast::MetaItem,
@ -462,70 +471,42 @@ pub fn cfg_matches(
features: Option<&Features>,
) -> bool {
eval_condition(cfg, sess, features, &mut |cfg| {
try_gate_cfg(cfg, sess, features);
let error = |span, msg| {
sess.span_diagnostic.span_err(span, msg);
true
};
if cfg.path.segments.len() != 1 {
return error(cfg.path.span, "`cfg` predicate key must be an identifier");
}
match &cfg.kind {
MetaItemKind::List(..) => {
error(cfg.span, "unexpected parentheses after `cfg` predicate key")
}
MetaItemKind::NameValue(lit) if !lit.kind.is_str() => {
handle_errors(
sess,
lit.span,
AttrError::UnsupportedLiteral(
"literal in `cfg` predicate value must be a string",
lit.kind.is_bytestr(),
),
try_gate_cfg(cfg.name, cfg.span, sess, features);
if let Some(names_valid) = &sess.check_config.names_valid {
if !names_valid.contains(&cfg.name) {
sess.buffer_lint_with_diagnostic(
UNEXPECTED_CFGS,
cfg.span,
lint_node_id,
"unexpected `cfg` condition name",
BuiltinLintDiagnostics::UnexpectedCfg((cfg.name, cfg.name_span), None),
);
true
}
MetaItemKind::NameValue(..) | MetaItemKind::Word => {
let ident = cfg.ident().expect("multi-segment cfg predicate");
let name = ident.name;
let value = cfg.value_str();
if let Some(names_valid) = &sess.check_config.names_valid {
if !names_valid.contains(&name) {
sess.buffer_lint_with_diagnostic(
UNEXPECTED_CFGS,
cfg.span,
lint_node_id,
"unexpected `cfg` condition name",
BuiltinLintDiagnostics::UnexpectedCfg((name, ident.span), None),
);
}
}
if let Some(value) = value {
if let Some(values) = &sess.check_config.values_valid.get(&name) {
if !values.contains(&value) {
sess.buffer_lint_with_diagnostic(
UNEXPECTED_CFGS,
cfg.span,
lint_node_id,
"unexpected `cfg` condition value",
BuiltinLintDiagnostics::UnexpectedCfg(
(name, ident.span),
Some((value, cfg.name_value_literal_span().unwrap())),
),
);
}
}
}
sess.config.contains(&(name, value))
}
}
if let Some(value) = cfg.value {
if let Some(values) = &sess.check_config.values_valid.get(&cfg.name) {
if !values.contains(&value) {
sess.buffer_lint_with_diagnostic(
UNEXPECTED_CFGS,
cfg.span,
lint_node_id,
"unexpected `cfg` condition value",
BuiltinLintDiagnostics::UnexpectedCfg(
(cfg.name, cfg.name_span),
cfg.value_span.map(|vs| (value, vs)),
),
);
}
}
}
sess.config.contains(&(cfg.name, cfg.value))
})
}
fn try_gate_cfg(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) {
let gate = find_gated_cfg(|sym| cfg.has_name(sym));
fn try_gate_cfg(name: Symbol, span: Span, sess: &ParseSess, features: Option<&Features>) {
let gate = find_gated_cfg(|sym| sym == name);
if let (Some(feats), Some(gated_cfg)) = (features, gate) {
gate_cfg(&gated_cfg, cfg.span, sess, feats);
gate_cfg(&gated_cfg, span, sess, feats);
}
}
@ -563,11 +544,11 @@ pub fn eval_condition(
cfg: &ast::MetaItem,
sess: &ParseSess,
features: Option<&Features>,
eval: &mut impl FnMut(&ast::MetaItem) -> bool,
eval: &mut impl FnMut(Condition) -> bool,
) -> bool {
match cfg.kind {
ast::MetaItemKind::List(ref mis) if cfg.name_or_empty() == sym::version => {
try_gate_cfg(cfg, sess, features);
try_gate_cfg(sym::version, cfg.span, sess, features);
let (min_version, span) = match &mis[..] {
[NestedMetaItem::Literal(Lit { kind: LitKind::Str(sym, ..), span, .. })] => {
(sym, span)
@ -649,6 +630,25 @@ pub fn eval_condition(
!eval_condition(mis[0].meta_item().unwrap(), sess, features, eval)
}
sym::target => {
if let Some(features) = features && !features.cfg_target_compact {
feature_err(
sess,
sym::cfg_target_compact,
cfg.span,
&"compact `cfg(target(..))` is experimental and subject to change"
).emit();
}
mis.iter().fold(true, |res, mi| {
let mut mi = mi.meta_item().unwrap().clone();
if let [seg, ..] = &mut mi.path.segments[..] {
seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name));
}
res & eval_condition(&mi, sess, features, eval)
})
}
_ => {
struct_span_err!(
sess.span_diagnostic,
@ -662,7 +662,32 @@ pub fn eval_condition(
}
}
}
ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => eval(cfg),
ast::MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => {
sess.span_diagnostic
.span_err(cfg.path.span, "`cfg` predicate key must be an identifier");
true
}
MetaItemKind::NameValue(ref lit) if !lit.kind.is_str() => {
handle_errors(
sess,
lit.span,
AttrError::UnsupportedLiteral(
"literal in `cfg` predicate value must be a string",
lit.kind.is_bytestr(),
),
);
true
}
ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => {
let ident = cfg.ident().expect("multi-segment cfg predicate");
eval(Condition {
name: ident.name,
name_span: ident.span,
value: cfg.value_str(),
value_span: cfg.name_value_literal_span(),
span: cfg.span,
})
}
}
}

View File

@ -4,6 +4,7 @@
//! The goal is to move the definition of `MetaItem` and things that don't need to be in `syntax`
//! to this crate.
#![feature(let_chains)]
#![feature(let_else)]
#[macro_use]

View File

@ -319,6 +319,8 @@ declare_features! (
(active, cfg_sanitize, "1.41.0", Some(39699), None),
/// Allows `cfg(target_abi = "...")`.
(active, cfg_target_abi, "1.55.0", Some(80970), None),
/// Allows `cfg(target(abi = "..."))`.
(active, cfg_target_compact, "1.63.0", Some(96901), None),
/// Allows `cfg(target_has_atomic_load_store = "...")`.
(active, cfg_target_has_atomic, "1.60.0", Some(94039), None),
/// Allows `cfg(target_has_atomic_equal_alignment = "...")`.

View File

@ -427,6 +427,7 @@ symbols! {
cfg_panic,
cfg_sanitize,
cfg_target_abi,
cfg_target_compact,
cfg_target_feature,
cfg_target_has_atomic,
cfg_target_has_atomic_equal_alignment,
@ -1375,6 +1376,7 @@ symbols! {
sym,
sync,
t32,
target,
target_abi,
target_arch,
target_endian,

View File

@ -89,8 +89,8 @@ impl<'tcx> OnUnimplementedDirective {
None,
)
})?;
attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |item| {
if let Some(symbol) = item.value_str() && let Err(guar) = parse_value(symbol) {
attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |cfg| {
if let Some(value) = cfg.value && let Err(guar) = parse_value(value) {
errored = Some(guar);
}
true
@ -226,14 +226,12 @@ impl<'tcx> OnUnimplementedDirective {
condition,
&tcx.sess.parse_sess,
Some(tcx.features()),
&mut |c| {
c.ident().map_or(false, |ident| {
let value = c.value_str().map(|s| {
OnUnimplementedFormatString(s).format(tcx, trait_ref, &options_map)
});
&mut |cfg| {
let value = cfg.value.map(|v| {
OnUnimplementedFormatString(v).format(tcx, trait_ref, &options_map)
});
options.contains(&(ident.name, value))
})
options.contains(&(cfg.name, value))
},
) {
debug!("evaluate: skipping {:?} due to condition", command);

View File

@ -0,0 +1,17 @@
// check-fail
#![feature(cfg_target_compact)]
#[cfg(target(o::o))]
//~^ ERROR `cfg` predicate key must be an identifier
fn one() {}
#[cfg(target(os = 8))]
//~^ ERROR literal in `cfg` predicate value must be a string
fn two() {}
#[cfg(target(os = "linux", pointer(width = "64")))]
//~^ ERROR invalid predicate `target_pointer`
fn three() {}
fn main() {}

View File

@ -0,0 +1,22 @@
error: `cfg` predicate key must be an identifier
--> $DIR/cfg-target-compact-errors.rs:5:14
|
LL | #[cfg(target(o::o))]
| ^^^^
error[E0565]: literal in `cfg` predicate value must be a string
--> $DIR/cfg-target-compact-errors.rs:9:19
|
LL | #[cfg(target(os = 8))]
| ^
error[E0537]: invalid predicate `target_pointer`
--> $DIR/cfg-target-compact-errors.rs:13:28
|
LL | #[cfg(target(os = "linux", pointer(width = "64")))]
| ^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 3 previous errors
Some errors have detailed explanations: E0537, E0565.
For more information about an error, try `rustc --explain E0537`.

View File

@ -0,0 +1,10 @@
// run-pass
#![feature(cfg_target_compact)]
#[cfg(target(os = "linux", pointer_width = "64"))]
pub fn main() {
}
#[cfg(not(target(os = "linux", pointer_width = "64")))]
pub fn main() {
}

View File

@ -0,0 +1,15 @@
// This test check that we correctly emit an warning for compact cfg
//
// check-pass
// compile-flags:--check-cfg=names() -Z unstable-options
#![feature(cfg_target_compact)]
#[cfg(target(os = "linux", arch = "arm"))]
pub fn expected() {}
#[cfg(target(os = "linux", architecture = "arm"))]
//~^ WARNING unexpected `cfg` condition name
pub fn unexpected() {}
fn main() {}

View File

@ -0,0 +1,10 @@
warning: unexpected `cfg` condition name
--> $DIR/compact-names.rs:11:28
|
LL | #[cfg(target(os = "linux", architecture = "arm"))]
| ^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unexpected_cfgs)]` on by default
warning: 1 warning emitted

View File

@ -0,0 +1,15 @@
// This test check that we correctly emit an warning for compact cfg
//
// check-pass
// compile-flags:--check-cfg=values() -Z unstable-options
#![feature(cfg_target_compact)]
#[cfg(target(os = "linux", arch = "arm"))]
pub fn expected() {}
#[cfg(target(os = "linux", arch = "X"))]
//~^ WARNING unexpected `cfg` condition value
pub fn unexpected() {}
fn main() {}

View File

@ -0,0 +1,11 @@
warning: unexpected `cfg` condition value
--> $DIR/compact-values.rs:11:28
|
LL | #[cfg(target(os = "linux", arch = "X"))]
| ^^^^^^^^^^
|
= note: `#[warn(unexpected_cfgs)]` on by default
= 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
warning: 1 warning emitted

View File

@ -0,0 +1,13 @@
#[cfg(target(os = "x"))] //~ ERROR compact `cfg(target(..))` is experimental
struct Foo(u64, u64);
#[cfg_attr(target(os = "x"), x)] //~ ERROR compact `cfg(target(..))` is experimental
struct Bar(u64, u64);
#[cfg(not(any(all(target(os = "x")))))] //~ ERROR compact `cfg(target(..))` is experimental
fn foo() {}
fn main() {
cfg!(target(os = "x"));
//~^ ERROR compact `cfg(target(..))` is experimental and subject to change
}

View File

@ -0,0 +1,39 @@
error[E0658]: compact `cfg(target(..))` is experimental and subject to change
--> $DIR/feature-gate-cfg-target-compact.rs:1:7
|
LL | #[cfg(target(os = "x"))]
| ^^^^^^^^^^^^^^^^
|
= note: see issue #96901 <https://github.com/rust-lang/rust/issues/96901> for more information
= help: add `#![feature(cfg_target_compact)]` to the crate attributes to enable
error[E0658]: compact `cfg(target(..))` is experimental and subject to change
--> $DIR/feature-gate-cfg-target-compact.rs:4:12
|
LL | #[cfg_attr(target(os = "x"), x)]
| ^^^^^^^^^^^^^^^^
|
= note: see issue #96901 <https://github.com/rust-lang/rust/issues/96901> for more information
= help: add `#![feature(cfg_target_compact)]` to the crate attributes to enable
error[E0658]: compact `cfg(target(..))` is experimental and subject to change
--> $DIR/feature-gate-cfg-target-compact.rs:7:19
|
LL | #[cfg(not(any(all(target(os = "x")))))]
| ^^^^^^^^^^^^^^^^
|
= note: see issue #96901 <https://github.com/rust-lang/rust/issues/96901> for more information
= help: add `#![feature(cfg_target_compact)]` to the crate attributes to enable
error[E0658]: compact `cfg(target(..))` is experimental and subject to change
--> $DIR/feature-gate-cfg-target-compact.rs:11:10
|
LL | cfg!(target(os = "x"));
| ^^^^^^^^^^^^^^^^
|
= note: see issue #96901 <https://github.com/rust-lang/rust/issues/96901> for more information
= help: add `#![feature(cfg_target_compact)]` to the crate attributes to enable
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0658`.