Skip to content

Implement cfg_os_version_min #136867

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

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3348,6 +3348,7 @@ dependencies = [
"rustc_macros",
"rustc_session",
"rustc_span",
"rustc_target",
"thin-vec",
]

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_attr_parsing/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ rustc_lexer = { path = "../rustc_lexer" }
rustc_macros = { path = "../rustc_macros" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
thin-vec = "0.2.12"
# tidy-alphabetical-end
15 changes: 15 additions & 0 deletions compiler/rustc_attr_parsing/messages.ftl
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
attr_parsing_apple_version_invalid =
failed parsing version: {$error}

attr_parsing_apple_version_unnecessarily_low =
version is set unnecessarily low, the minimum supported by Rust on this platform is {$os_min}

attr_parsing_cfg_predicate_identifier =
`cfg` predicate key must be an identifier

Expand All @@ -11,6 +17,12 @@ attr_parsing_empty_confusables =
attr_parsing_expected_one_cfg_pattern =
expected 1 cfg-pattern

attr_parsing_expected_platform_and_version_literals =
expected two literals, a platform and a version

attr_parsing_expected_platform_literal =
expected a platform literal

attr_parsing_expected_single_version_literal =
expected single version literal

Expand Down Expand Up @@ -108,6 +120,9 @@ attr_parsing_unknown_meta_item =
unknown meta item '{$item}'
.label = expected one of {$expected}

attr_parsing_unknown_platform_literal =
unknown platform literal, expected values are: {$possibilities}

attr_parsing_unknown_version_literal =
unknown version literal format, assuming it refers to a future version

Expand Down
130 changes: 130 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/cfg.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId};
use rustc_ast_pretty::pprust;
use rustc_attr_data_structures::RustcVersion;
use rustc_errors::Applicability;
use rustc_feature::{Features, GatedCfg, find_gated_cfg};
use rustc_session::Session;
use rustc_session::config::ExpectedValues;
Expand All @@ -9,6 +10,7 @@ use rustc_session::lint::builtin::UNEXPECTED_CFGS;
use rustc_session::parse::feature_err;
use rustc_span::symbol::kw;
use rustc_span::{Span, Symbol, sym};
use rustc_target::spec::apple;

use crate::session_diagnostics::{self, UnsupportedLiteralReason};
use crate::{fluent_generated, parse_version};
Expand Down Expand Up @@ -149,6 +151,129 @@ pub fn eval_condition(
RustcVersion::CURRENT >= min_version
}
}
MetaItemKind::List(mis) if cfg.name_or_empty() == sym::os_version_min => {
try_gate_cfg(sym::os_version_min, cfg.span, sess, features);

let (platform, version) = match &mis[..] {
[platform, version] => (platform, version),
[..] => {
dcx.emit_err(session_diagnostics::ExpectedPlatformAndVersionLiterals {
span: cfg.span,
});
return false;
}
};

let (platform_sym, platform_span) = match platform {
MetaItemInner::Lit(MetaItemLit {
kind: LitKind::Str(platform_sym, ..),
span: platform_span,
..
}) => (platform_sym, platform_span),
MetaItemInner::Lit(MetaItemLit { span, .. })
| MetaItemInner::MetaItem(MetaItem { span, .. }) => {
dcx.emit_err(session_diagnostics::ExpectedPlatformLiteral { span: *span });
return false;
}
};

let (version_sym, version_span) = match version {
MetaItemInner::Lit(MetaItemLit {
kind: LitKind::Str(version_sym, ..),
span: version_span,
..
}) => (version_sym, version_span),
MetaItemInner::Lit(MetaItemLit { span, .. })
| MetaItemInner::MetaItem(MetaItem { span, .. }) => {
dcx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: *span });
return false;
}
};

// Always parse version, regardless of current target platform.
let version = match *platform_sym {
// Apple platforms follow the same versioning schema.
sym::macos | sym::ios | sym::tvos | sym::watchos | sym::visionos => {
match version_sym.as_str().parse() {
Ok(version) => {
let os_min = apple::OSVersion::os_minimum_deployment_target(
&platform_sym.as_str(),
);

// It's unnecessary to specify `cfg_target_os(...)` for a platform
// version that is lower than the minimum targetted by `rustc` (instead,
// make the item always available).
//
// This is correct _now_, but once we bump versions next time, we should
// maybe make this a lint so that users can opt-in to supporting older
// `rustc` versions? Or perhaps only fire the warning when Cargo's
// `rust-version` field is above the version where the bump happened? Or
// perhaps keep the version we check against low for a sufficiently long
// time?
if version <= os_min {
sess.dcx()
.create_warn(
session_diagnostics::AppleVersionUnnecessarilyLow {
span: *version_span,
os_min: os_min.fmt_pretty().to_string(),
},
)
.with_span_suggestion(
cfg.span,
"use `target_os` instead",
format!("target_os = \"{platform_sym}\""),
Applicability::MachineApplicable,
)
.emit();
}

PlatformVersion::Apple { os: *platform_sym, version }
}
Err(error) => {
sess.dcx().emit_err(session_diagnostics::AppleVersionInvalid {
span: *version_span,
error,
});
return false;
}
}
}
// FIXME(madsmtm): Handle further platforms as specified in the RFC.
sym::windows | sym::libc => {
#[allow(rustc::untranslatable_diagnostic)] // Temporary
dcx.span_err(*platform_span, "unimplemented platform");
return false;
}
_ => {
// Unknown platform. This is intentionally a warning (and not an error) to be
// future-compatible with later additions.
let known_platforms = [
sym::macos,
sym::ios,
sym::tvos,
sym::watchos,
sym::visionos,
// sym::windows,
// sym::libc,
];
dcx.emit_warn(session_diagnostics::UnknownPlatformLiteral {
span: *platform_span,
possibilities: known_platforms.into_iter().collect(),
});
return false;
}
};

// Figure out actual cfg-status based on current platform.
match version {
PlatformVersion::Apple { os, version } if os.as_str() == sess.target.os => {
let deployment_target = sess.apple_deployment_target();
version <= deployment_target
}
// If a `cfg`-value does not apply to a specific platform, assume
_ => false,
}
}
MetaItemKind::List(mis) => {
for mi in mis.iter() {
if mi.meta_item_or_bool().is_none() {
Expand Down Expand Up @@ -245,3 +370,8 @@ pub fn eval_condition(
}
}
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum PlatformVersion {
Apple { os: Symbol, version: apple::OSVersion },
}
44 changes: 42 additions & 2 deletions compiler/rustc_attr_parsing/src/session_diagnostics.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use std::num::IntErrorKind;
use std::num::{IntErrorKind, ParseIntError};

use rustc_ast as ast;
use rustc_errors::codes::*;
use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level};
use rustc_errors::{
Applicability, Diag, DiagCtxtHandle, DiagSymbolList, Diagnostic, EmissionGuarantee, Level,
};
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_span::{Span, Symbol};

Expand All @@ -16,6 +18,22 @@ pub(crate) enum UnsupportedLiteralReason {
DeprecatedKvPair,
}

#[derive(Diagnostic)]
#[diag(attr_parsing_apple_version_invalid)]
pub(crate) struct AppleVersionInvalid {
#[primary_span]
pub span: Span,
pub error: ParseIntError,
}

#[derive(Diagnostic)]
#[diag(attr_parsing_apple_version_unnecessarily_low)]
pub(crate) struct AppleVersionUnnecessarilyLow {
#[primary_span]
pub span: Span,
pub os_min: String,
}

#[derive(Diagnostic)]
#[diag(attr_parsing_expected_one_cfg_pattern, code = E0536)]
pub(crate) struct ExpectedOneCfgPattern {
Expand Down Expand Up @@ -386,6 +404,20 @@ pub(crate) struct DeprecatedItemSuggestion {
pub details: (),
}

#[derive(Diagnostic)]
#[diag(attr_parsing_expected_platform_and_version_literals)]
pub(crate) struct ExpectedPlatformAndVersionLiterals {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(attr_parsing_expected_platform_literal)]
pub(crate) struct ExpectedPlatformLiteral {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(attr_parsing_expected_single_version_literal)]
pub(crate) struct ExpectedSingleVersionLiteral {
Expand Down Expand Up @@ -432,6 +464,14 @@ pub(crate) struct SoftNoArgs {
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(attr_parsing_unknown_platform_literal)]
pub(crate) struct UnknownPlatformLiteral {
#[primary_span]
pub span: Span,
pub possibilities: DiagSymbolList,
}

#[derive(Diagnostic)]
#[diag(attr_parsing_unknown_version_literal)]
pub(crate) struct UnknownVersionLiteral {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const GATED_CFGS: &[GatedCfg] = &[
),
(sym::sanitize, sym::cfg_sanitize, Features::cfg_sanitize),
(sym::version, sym::cfg_version, Features::cfg_version),
(sym::os_version_min, sym::cfg_os_version_min, Features::cfg_os_version_min),
(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: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,8 @@ declare_features! (
(unstable, cfg_boolean_literals, "1.83.0", Some(131204)),
/// Allows the use of `#[cfg(contract_checks)` to check if contract checks are enabled.
(unstable, cfg_contract_checks, "1.86.0", Some(128044)),
/// Allow conditional compilation depending on target platform version.
(unstable, cfg_os_version_min, "CURRENT_RUSTC_VERSION", Some(136866)),
/// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour.
(unstable, cfg_overflow_checks, "1.71.0", Some(111466)),
/// Provides the relocation model information as cfg entry
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,7 @@ symbols! {
cfg_eval,
cfg_fmt_debug,
cfg_hide,
cfg_os_version_min,
cfg_overflow_checks,
cfg_panic,
cfg_relocation_model,
Expand Down Expand Up @@ -1194,6 +1195,7 @@ symbols! {
intrinsics_unaligned_volatile_store,
io_stderr,
io_stdout,
ios,
irrefutable_let_patterns,
is,
is_val_statically_known,
Expand Down Expand Up @@ -1279,6 +1281,7 @@ symbols! {
loop_break_value,
lt,
m68k_target_feature,
macos,
macro_at_most_once_rep,
macro_attributes_in_derive_output,
macro_escape,
Expand Down Expand Up @@ -1502,6 +1505,7 @@ symbols! {
ord_cmp_method,
os_str_to_os_string,
os_string_as_os_str,
os_version_min,
other,
out,
overflow_checks,
Expand Down Expand Up @@ -2124,6 +2128,7 @@ symbols! {
tuple,
tuple_indexing,
tuple_trait,
tvos,
two_phase,
ty,
type_alias_enum_variants,
Expand Down Expand Up @@ -2270,6 +2275,7 @@ symbols! {
vfp2,
vis,
visible_private_types,
visionos,
volatile,
volatile_copy_memory,
volatile_copy_nonoverlapping_memory,
Expand All @@ -2286,6 +2292,7 @@ symbols! {
wasm_abi,
wasm_import_module,
wasm_target_feature,
watchos,
where_clause_attrs,
while_let,
width,
Expand Down
Loading
Loading