Skip to content

Commit 00bdfcc

Browse files
committed
Add cfg_os_version_min feature
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.
1 parent f5c5102 commit 00bdfcc

16 files changed

+600
-2
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -3348,6 +3348,7 @@ dependencies = [
33483348
"rustc_macros",
33493349
"rustc_session",
33503350
"rustc_span",
3351+
"rustc_target",
33513352
"thin-vec",
33523353
]
33533354

compiler/rustc_attr_parsing/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@ rustc_lexer = { path = "../rustc_lexer" }
1717
rustc_macros = { path = "../rustc_macros" }
1818
rustc_session = { path = "../rustc_session" }
1919
rustc_span = { path = "../rustc_span" }
20+
rustc_target = { path = "../rustc_target" }
2021
thin-vec = "0.2.12"
2122
# tidy-alphabetical-end

compiler/rustc_attr_parsing/messages.ftl

+15
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
attr_parsing_apple_version_invalid =
2+
failed parsing version: {$error}
3+
4+
attr_parsing_apple_version_unnecessarily_low =
5+
version is set unnecessarily low, the minimum supported by Rust on this platform is {$os_min}
6+
17
attr_parsing_cfg_predicate_identifier =
28
`cfg` predicate key must be an identifier
39
@@ -11,6 +17,12 @@ attr_parsing_empty_confusables =
1117
attr_parsing_expected_one_cfg_pattern =
1218
expected 1 cfg-pattern
1319
20+
attr_parsing_expected_platform_and_version_literals =
21+
expected two literals, a platform and a version
22+
23+
attr_parsing_expected_platform_literal =
24+
expected a platform literal
25+
1426
attr_parsing_expected_single_version_literal =
1527
expected single version literal
1628
@@ -108,6 +120,9 @@ attr_parsing_unknown_meta_item =
108120
unknown meta item '{$item}'
109121
.label = expected one of {$expected}
110122
123+
attr_parsing_unknown_platform_literal =
124+
unknown platform literal, expected values are: {$possibilities}
125+
111126
attr_parsing_unknown_version_literal =
112127
unknown version literal format, assuming it refers to a future version
113128

compiler/rustc_attr_parsing/src/attributes/cfg.rs

+130
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId};
22
use rustc_ast_pretty::pprust;
33
use rustc_attr_data_structures::RustcVersion;
4+
use rustc_errors::Applicability;
45
use rustc_feature::{Features, GatedCfg, find_gated_cfg};
56
use rustc_session::Session;
67
use rustc_session::config::ExpectedValues;
@@ -9,6 +10,7 @@ use rustc_session::lint::builtin::UNEXPECTED_CFGS;
910
use rustc_session::parse::feature_err;
1011
use rustc_span::symbol::kw;
1112
use rustc_span::{Span, Symbol, sym};
13+
use rustc_target::spec::apple;
1214

1315
use crate::session_diagnostics::{self, UnsupportedLiteralReason};
1416
use crate::{fluent_generated, parse_version};
@@ -149,6 +151,129 @@ pub fn eval_condition(
149151
RustcVersion::CURRENT >= min_version
150152
}
151153
}
154+
MetaItemKind::List(mis) if cfg.name_or_empty() == sym::os_version_min => {
155+
try_gate_cfg(sym::os_version_min, cfg.span, sess, features);
156+
157+
let (platform, version) = match &mis[..] {
158+
[platform, version] => (platform, version),
159+
[..] => {
160+
dcx.emit_err(session_diagnostics::ExpectedPlatformAndVersionLiterals {
161+
span: cfg.span,
162+
});
163+
return false;
164+
}
165+
};
166+
167+
let (platform_sym, platform_span) = match platform {
168+
MetaItemInner::Lit(MetaItemLit {
169+
kind: LitKind::Str(platform_sym, ..),
170+
span: platform_span,
171+
..
172+
}) => (platform_sym, platform_span),
173+
MetaItemInner::Lit(MetaItemLit { span, .. })
174+
| MetaItemInner::MetaItem(MetaItem { span, .. }) => {
175+
dcx.emit_err(session_diagnostics::ExpectedPlatformLiteral { span: *span });
176+
return false;
177+
}
178+
};
179+
180+
let (version_sym, version_span) = match version {
181+
MetaItemInner::Lit(MetaItemLit {
182+
kind: LitKind::Str(version_sym, ..),
183+
span: version_span,
184+
..
185+
}) => (version_sym, version_span),
186+
MetaItemInner::Lit(MetaItemLit { span, .. })
187+
| MetaItemInner::MetaItem(MetaItem { span, .. }) => {
188+
dcx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: *span });
189+
return false;
190+
}
191+
};
192+
193+
// Always parse version, regardless of current target platform.
194+
let version = match *platform_sym {
195+
// Apple platforms follow the same versioning schema.
196+
sym::macos | sym::ios | sym::tvos | sym::watchos | sym::visionos => {
197+
match version_sym.as_str().parse() {
198+
Ok(version) => {
199+
let os_min = apple::OSVersion::os_minimum_deployment_target(
200+
&platform_sym.as_str(),
201+
);
202+
203+
// It's unnecessary to specify `cfg_target_os(...)` for a platform
204+
// version that is lower than the minimum targetted by `rustc` (instead,
205+
// make the item always available).
206+
//
207+
// This is correct _now_, but once we bump versions next time, we should
208+
// maybe make this a lint so that users can opt-in to supporting older
209+
// `rustc` versions? Or perhaps only fire the warning when Cargo's
210+
// `rust-version` field is above the version where the bump happened? Or
211+
// perhaps keep the version we check against low for a sufficiently long
212+
// time?
213+
if version <= os_min {
214+
sess.dcx()
215+
.create_warn(
216+
session_diagnostics::AppleVersionUnnecessarilyLow {
217+
span: *version_span,
218+
os_min: os_min.fmt_pretty().to_string(),
219+
},
220+
)
221+
.with_span_suggestion(
222+
cfg.span,
223+
"use `target_os` instead",
224+
format!("target_os = \"{platform_sym}\""),
225+
Applicability::MachineApplicable,
226+
)
227+
.emit();
228+
}
229+
230+
PlatformVersion::Apple { os: *platform_sym, version }
231+
}
232+
Err(error) => {
233+
sess.dcx().emit_err(session_diagnostics::AppleVersionInvalid {
234+
span: *version_span,
235+
error,
236+
});
237+
return false;
238+
}
239+
}
240+
}
241+
// FIXME(madsmtm): Handle further platforms as specified in the RFC.
242+
sym::windows | sym::libc => {
243+
#[allow(rustc::untranslatable_diagnostic)] // Temporary
244+
dcx.span_err(*platform_span, "unimplemented platform");
245+
return false;
246+
}
247+
_ => {
248+
// Unknown platform. This is intentionally a warning (and not an error) to be
249+
// future-compatible with later additions.
250+
let known_platforms = [
251+
sym::macos,
252+
sym::ios,
253+
sym::tvos,
254+
sym::watchos,
255+
sym::visionos,
256+
// sym::windows,
257+
// sym::libc,
258+
];
259+
dcx.emit_warn(session_diagnostics::UnknownPlatformLiteral {
260+
span: *platform_span,
261+
possibilities: known_platforms.into_iter().collect(),
262+
});
263+
return false;
264+
}
265+
};
266+
267+
// Figure out actual cfg-status based on current platform.
268+
match version {
269+
PlatformVersion::Apple { os, version } if os.as_str() == sess.target.os => {
270+
let deployment_target = sess.apple_deployment_target();
271+
version <= deployment_target
272+
}
273+
// If a `cfg`-value does not apply to a specific platform, assume
274+
_ => false,
275+
}
276+
}
152277
MetaItemKind::List(mis) => {
153278
for mi in mis.iter() {
154279
if mi.meta_item_or_bool().is_none() {
@@ -245,3 +370,8 @@ pub fn eval_condition(
245370
}
246371
}
247372
}
373+
374+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
375+
enum PlatformVersion {
376+
Apple { os: Symbol, version: apple::OSVersion },
377+
}

compiler/rustc_attr_parsing/src/session_diagnostics.rs

+42-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
use std::num::IntErrorKind;
1+
use std::num::{IntErrorKind, ParseIntError};
22

33
use rustc_ast as ast;
44
use rustc_errors::codes::*;
5-
use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level};
5+
use rustc_errors::{
6+
Applicability, Diag, DiagCtxtHandle, DiagSymbolList, Diagnostic, EmissionGuarantee, Level,
7+
};
68
use rustc_macros::{Diagnostic, Subdiagnostic};
79
use rustc_span::{Span, Symbol};
810

@@ -16,6 +18,22 @@ pub(crate) enum UnsupportedLiteralReason {
1618
DeprecatedKvPair,
1719
}
1820

21+
#[derive(Diagnostic)]
22+
#[diag(attr_parsing_apple_version_invalid)]
23+
pub(crate) struct AppleVersionInvalid {
24+
#[primary_span]
25+
pub span: Span,
26+
pub error: ParseIntError,
27+
}
28+
29+
#[derive(Diagnostic)]
30+
#[diag(attr_parsing_apple_version_unnecessarily_low)]
31+
pub(crate) struct AppleVersionUnnecessarilyLow {
32+
#[primary_span]
33+
pub span: Span,
34+
pub os_min: String,
35+
}
36+
1937
#[derive(Diagnostic)]
2038
#[diag(attr_parsing_expected_one_cfg_pattern, code = E0536)]
2139
pub(crate) struct ExpectedOneCfgPattern {
@@ -386,6 +404,20 @@ pub(crate) struct DeprecatedItemSuggestion {
386404
pub details: (),
387405
}
388406

407+
#[derive(Diagnostic)]
408+
#[diag(attr_parsing_expected_platform_and_version_literals)]
409+
pub(crate) struct ExpectedPlatformAndVersionLiterals {
410+
#[primary_span]
411+
pub span: Span,
412+
}
413+
414+
#[derive(Diagnostic)]
415+
#[diag(attr_parsing_expected_platform_literal)]
416+
pub(crate) struct ExpectedPlatformLiteral {
417+
#[primary_span]
418+
pub span: Span,
419+
}
420+
389421
#[derive(Diagnostic)]
390422
#[diag(attr_parsing_expected_single_version_literal)]
391423
pub(crate) struct ExpectedSingleVersionLiteral {
@@ -432,6 +464,14 @@ pub(crate) struct SoftNoArgs {
432464
pub span: Span,
433465
}
434466

467+
#[derive(Diagnostic)]
468+
#[diag(attr_parsing_unknown_platform_literal)]
469+
pub(crate) struct UnknownPlatformLiteral {
470+
#[primary_span]
471+
pub span: Span,
472+
pub possibilities: DiagSymbolList,
473+
}
474+
435475
#[derive(Diagnostic)]
436476
#[diag(attr_parsing_unknown_version_literal)]
437477
pub(crate) struct UnknownVersionLiteral {

compiler/rustc_feature/src/builtin_attrs.rs

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const GATED_CFGS: &[GatedCfg] = &[
3333
),
3434
(sym::sanitize, sym::cfg_sanitize, Features::cfg_sanitize),
3535
(sym::version, sym::cfg_version, Features::cfg_version),
36+
(sym::os_version_min, sym::cfg_os_version_min, Features::cfg_os_version_min),
3637
(sym::relocation_model, sym::cfg_relocation_model, Features::cfg_relocation_model),
3738
(sym::sanitizer_cfi_generalize_pointers, sym::cfg_sanitizer_cfi, Features::cfg_sanitizer_cfi),
3839
(sym::sanitizer_cfi_normalize_integers, sym::cfg_sanitizer_cfi, Features::cfg_sanitizer_cfi),

compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,8 @@ declare_features! (
395395
(unstable, cfg_boolean_literals, "1.83.0", Some(131204)),
396396
/// Allows the use of `#[cfg(contract_checks)` to check if contract checks are enabled.
397397
(unstable, cfg_contract_checks, "1.86.0", Some(128044)),
398+
/// Allow conditional compilation depending on target platform version.
399+
(unstable, cfg_os_version_min, "CURRENT_RUSTC_VERSION", Some(136866)),
398400
/// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour.
399401
(unstable, cfg_overflow_checks, "1.71.0", Some(111466)),
400402
/// Provides the relocation model information as cfg entry

compiler/rustc_span/src/symbol.rs

+7
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,7 @@ symbols! {
611611
cfg_eval,
612612
cfg_fmt_debug,
613613
cfg_hide,
614+
cfg_os_version_min,
614615
cfg_overflow_checks,
615616
cfg_panic,
616617
cfg_relocation_model,
@@ -1194,6 +1195,7 @@ symbols! {
11941195
intrinsics_unaligned_volatile_store,
11951196
io_stderr,
11961197
io_stdout,
1198+
ios,
11971199
irrefutable_let_patterns,
11981200
is,
11991201
is_val_statically_known,
@@ -1279,6 +1281,7 @@ symbols! {
12791281
loop_break_value,
12801282
lt,
12811283
m68k_target_feature,
1284+
macos,
12821285
macro_at_most_once_rep,
12831286
macro_attributes_in_derive_output,
12841287
macro_escape,
@@ -1502,6 +1505,7 @@ symbols! {
15021505
ord_cmp_method,
15031506
os_str_to_os_string,
15041507
os_string_as_os_str,
1508+
os_version_min,
15051509
other,
15061510
out,
15071511
overflow_checks,
@@ -2124,6 +2128,7 @@ symbols! {
21242128
tuple,
21252129
tuple_indexing,
21262130
tuple_trait,
2131+
tvos,
21272132
two_phase,
21282133
ty,
21292134
type_alias_enum_variants,
@@ -2270,6 +2275,7 @@ symbols! {
22702275
vfp2,
22712276
vis,
22722277
visible_private_types,
2278+
visionos,
22732279
volatile,
22742280
volatile_copy_memory,
22752281
volatile_copy_nonoverlapping_memory,
@@ -2286,6 +2292,7 @@ symbols! {
22862292
wasm_abi,
22872293
wasm_import_module,
22882294
wasm_target_feature,
2295+
watchos,
22892296
where_clause_attrs,
22902297
while_let,
22912298
width,

0 commit comments

Comments
 (0)