Skip to content

Commit bb55488

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 1b9ec41 commit bb55488

16 files changed

+569
-2
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -3460,6 +3460,7 @@ dependencies = [
34603460
"rustc_serialize",
34613461
"rustc_session",
34623462
"rustc_span",
3463+
"rustc_target",
34633464
]
34643465

34653466
[[package]]

compiler/rustc_attr_parsing/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@ rustc_macros = { path = "../rustc_macros" }
1818
rustc_serialize = { path = "../rustc_serialize" }
1919
rustc_session = { path = "../rustc_session" }
2020
rustc_span = { path = "../rustc_span" }
21+
rustc_target = { path = "../rustc_target" }
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
@@ -9,6 +15,12 @@ attr_parsing_deprecated_item_suggestion =
915
attr_parsing_expected_one_cfg_pattern =
1016
expected 1 cfg-pattern
1117
18+
attr_parsing_expected_platform_and_version_literals =
19+
expected two literals, a platform and a version
20+
21+
attr_parsing_expected_platform_literal =
22+
expected a platform literal
23+
1224
attr_parsing_expected_single_version_literal =
1325
expected single version literal
1426
@@ -104,6 +116,9 @@ attr_parsing_unknown_meta_item =
104116
unknown meta item '{$item}'
105117
.label = expected one of {$expected}
106118
119+
attr_parsing_unknown_platform_literal =
120+
unknown platform literal, expected values are: {$possibilities}
121+
107122
attr_parsing_unknown_version_literal =
108123
unknown version literal format, assuming it refers to a future version
109124

compiler/rustc_attr_parsing/src/attributes/cfg.rs

+130
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
use rustc_ast::{self as ast, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId};
44
use rustc_ast_pretty::pprust;
55
use rustc_attr_data_structures::RustcVersion;
6+
use rustc_errors::Applicability;
67
use rustc_feature::{Features, GatedCfg, find_gated_cfg};
78
use rustc_session::Session;
89
use rustc_session::config::ExpectedValues;
910
use rustc_session::lint::BuiltinLintDiag;
1011
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
1112
use rustc_session::parse::feature_err;
1213
use rustc_span::{Span, Symbol, kw, sym};
14+
use rustc_target::spec::apple;
1315

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

compiler/rustc_attr_parsing/src/session_diagnostics.rs

+42-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,32 @@
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

911
use crate::attributes::util::UnsupportedLiteralReason;
1012
use crate::fluent_generated as fluent;
1113

14+
#[derive(Diagnostic)]
15+
#[diag(attr_parsing_apple_version_invalid)]
16+
pub(crate) struct AppleVersionInvalid {
17+
#[primary_span]
18+
pub span: Span,
19+
pub error: ParseIntError,
20+
}
21+
22+
#[derive(Diagnostic)]
23+
#[diag(attr_parsing_apple_version_unnecessarily_low)]
24+
pub(crate) struct AppleVersionUnnecessarilyLow {
25+
#[primary_span]
26+
pub span: Span,
27+
pub os_min: String,
28+
}
29+
1230
#[derive(Diagnostic)]
1331
#[diag(attr_parsing_expected_one_cfg_pattern, code = E0536)]
1432
pub(crate) struct ExpectedOneCfgPattern {
@@ -371,6 +389,20 @@ pub(crate) struct DeprecatedItemSuggestion {
371389
pub details: (),
372390
}
373391

392+
#[derive(Diagnostic)]
393+
#[diag(attr_parsing_expected_platform_and_version_literals)]
394+
pub(crate) struct ExpectedPlatformAndVersionLiterals {
395+
#[primary_span]
396+
pub span: Span,
397+
}
398+
399+
#[derive(Diagnostic)]
400+
#[diag(attr_parsing_expected_platform_literal)]
401+
pub(crate) struct ExpectedPlatformLiteral {
402+
#[primary_span]
403+
pub span: Span,
404+
}
405+
374406
#[derive(Diagnostic)]
375407
#[diag(attr_parsing_expected_single_version_literal)]
376408
pub(crate) struct ExpectedSingleVersionLiteral {
@@ -417,6 +449,14 @@ pub(crate) struct SoftNoArgs {
417449
pub span: Span,
418450
}
419451

452+
#[derive(Diagnostic)]
453+
#[diag(attr_parsing_unknown_platform_literal)]
454+
pub(crate) struct UnknownPlatformLiteral {
455+
#[primary_span]
456+
pub span: Span,
457+
pub possibilities: DiagSymbolList,
458+
}
459+
420460
#[derive(Diagnostic)]
421461
#[diag(attr_parsing_unknown_version_literal)]
422462
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
@@ -405,6 +405,8 @@ declare_features! (
405405
(unstable, cfg_boolean_literals, "1.83.0", Some(131204)),
406406
/// Allows the use of `#[cfg(contract_checks)` to check if contract checks are enabled.
407407
(unstable, cfg_contract_checks, "CURRENT_RUSTC_VERSION", Some(128044)),
408+
/// Allow conditional compilation depending on target platform version.
409+
(unstable, cfg_os_version_min, "CURRENT_RUSTC_VERSION", Some(136866)),
408410
/// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour.
409411
(unstable, cfg_overflow_checks, "1.71.0", Some(111466)),
410412
/// Provides the relocation model information as cfg entry

compiler/rustc_span/src/symbol.rs

+7
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,7 @@ symbols! {
577577
cfg_eval,
578578
cfg_fmt_debug,
579579
cfg_hide,
580+
cfg_os_version_min,
580581
cfg_overflow_checks,
581582
cfg_panic,
582583
cfg_relocation_model,
@@ -1139,6 +1140,7 @@ symbols! {
11391140
intrinsics_unaligned_volatile_store,
11401141
io_stderr,
11411142
io_stdout,
1143+
ios,
11421144
irrefutable_let_patterns,
11431145
is,
11441146
is_val_statically_known,
@@ -1224,6 +1226,7 @@ symbols! {
12241226
loop_break_value,
12251227
lt,
12261228
m68k_target_feature,
1229+
macos,
12271230
macro_at_most_once_rep,
12281231
macro_attributes_in_derive_output,
12291232
macro_escape,
@@ -1449,6 +1452,7 @@ symbols! {
14491452
ord_cmp_method,
14501453
os_str_to_os_string,
14511454
os_string_as_os_str,
1455+
os_version_min,
14521456
other,
14531457
out,
14541458
overflow_checks,
@@ -2064,6 +2068,7 @@ symbols! {
20642068
tuple,
20652069
tuple_indexing,
20662070
tuple_trait,
2071+
tvos,
20672072
two_phase,
20682073
ty,
20692074
type_alias_enum_variants,
@@ -2206,6 +2211,7 @@ symbols! {
22062211
vfp2,
22072212
vis,
22082213
visible_private_types,
2214+
visionos,
22092215
volatile,
22102216
volatile_copy_memory,
22112217
volatile_copy_nonoverlapping_memory,
@@ -2222,6 +2228,7 @@ symbols! {
22222228
wasm_abi,
22232229
wasm_import_module,
22242230
wasm_target_feature,
2231+
watchos,
22252232
while_let,
22262233
windows,
22272234
windows_subsystem,

0 commit comments

Comments
 (0)