Skip to content

Commit

Permalink
Add cfg_os_version_min feature
Browse files Browse the repository at this point in the history
Based on in-progress RFC: rust-lang/rfcs#3750.

Only implemented for Apple platforms for now, but written in a way that
should be easily expandable to include other platforms.
  • Loading branch information
madsmtm committed Feb 11, 2025
1 parent 1b9ec41 commit bb55488
Show file tree
Hide file tree
Showing 16 changed files with 569 additions and 2 deletions.
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3460,6 +3460,7 @@ dependencies = [
"rustc_serialize",
"rustc_session",
"rustc_span",
"rustc_target",
]

[[package]]
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 @@ -18,4 +18,5 @@ rustc_macros = { path = "../rustc_macros" }
rustc_serialize = { path = "../rustc_serialize" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
# 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 @@ -9,6 +15,12 @@ attr_parsing_deprecated_item_suggestion =
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 @@ -104,6 +116,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
Expand Up @@ -3,13 +3,15 @@
use rustc_ast::{self as 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;
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
use rustc_session::parse::feature_err;
use rustc_span::{Span, Symbol, kw, sym};
use rustc_target::spec::apple;

use crate::util::UnsupportedLiteralReason;
use crate::{fluent_generated, parse_version, session_diagnostics};
Expand Down Expand Up @@ -150,6 +152,129 @@ pub fn eval_condition(
RustcVersion::CURRENT >= min_version
}
}
ast::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,
}
}
ast::MetaItemKind::List(mis) => {
for mi in mis.iter() {
if mi.meta_item_or_bool().is_none() {
Expand Down Expand Up @@ -251,3 +376,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,14 +1,32 @@
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};

use crate::attributes::util::UnsupportedLiteralReason;
use crate::fluent_generated as fluent;

#[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 @@ -371,6 +389,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 @@ -417,6 +449,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 @@ -405,6 +405,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, "CURRENT_RUSTC_VERSION", 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 @@ -577,6 +577,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 @@ -1139,6 +1140,7 @@ symbols! {
intrinsics_unaligned_volatile_store,
io_stderr,
io_stdout,
ios,
irrefutable_let_patterns,
is,
is_val_statically_known,
Expand Down Expand Up @@ -1224,6 +1226,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 @@ -1449,6 +1452,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 @@ -2064,6 +2068,7 @@ symbols! {
tuple,
tuple_indexing,
tuple_trait,
tvos,
two_phase,
ty,
type_alias_enum_variants,
Expand Down Expand Up @@ -2206,6 +2211,7 @@ symbols! {
vfp2,
vis,
visible_private_types,
visionos,
volatile,
volatile_copy_memory,
volatile_copy_nonoverlapping_memory,
Expand All @@ -2222,6 +2228,7 @@ symbols! {
wasm_abi,
wasm_import_module,
wasm_target_feature,
watchos,
while_let,
windows,
windows_subsystem,
Expand Down
Loading

0 comments on commit bb55488

Please sign in to comment.