Add __isPlatformVersionAtLeast
and __isOSVersionAtLeast
symbols by madsmtm · Pull Request #138944 · rust-lang/rust (original) (raw)
Motivation
When Objective-C code uses @available(...)
, Clang inserts a call to __isPlatformVersionAtLeast (__isOSVersionAtLeast
in older Clang versions). These symbols not being available sometimes ends up causing linker errors. See the new test tests/run-make/apple-c-available-links
for a minimal reproducer.
The workaround is to link libclang_rt.osx.a
, see e.g. alexcrichton/curl-rust#279. But that's very difficult for users to figure out (and the backreferences to that issue indicates that people are still running into this in their own projects every so often).
For another recent example, this is preventing rustc
from using LLVM assertions on macOS, see #62592 (comment) and #134275 (comment).
It is also a blocker for setting the correct minimum OS version in cc-rs, since fixing this in cc-rs
might end up introducing linker errors in places where we weren't before (by default, if using e.g. @available(macos 10.15, *)
, the symbol usually happens to be left out, since clang
defaults to compiling for the host macOS version, and thus things seem to work - but the availability check actually compiles down to nothing, which is a huge correctness footgun for running on older OSes).
(My super secret evil agenda is also to expose some variant of @available
in Rust's std
after rust-lang/rfcs#3750 progresses further, will probably file an ACP for this later. But I believe this PR has value regardless of those future plans, since we'd be making C/Objective-C/Swift interop easier).
Solution
Implement __isPlatformVersionAtLeast
and __isOSVersionAtLeast
as part of the "public ABI" that std
exposes.
This is insta-stable, in the same sense that additions to compiler-builtins
are insta-stable, though the availability of these symbols can probably be considered a "quality of implementation" detail rather than a stable promise.
I originally proposed to implement this in compiler-builtins
, see rust-lang/compiler-builtins#794, but we discussed moving it to std
instead (Zulip thread), which makes the implementation substantially simpler, and we avoid gnarly issues with requiring the user to link libSystem.dylib
(since std
unconditionally does that).
Note that this does not solve the linker errors for (pure) #![no_std]
users, but that's probably fine, if you are using @available
to test the OS version on Apple platforms, you're likely also using std
(and it is still possible to work around by linking libclang_rt.*.a
).
A thing to note about the implementation, I've choosen to stray a bit from LLVM's upstream implementation, and not use _availability_version_check
since it has problems when compiling with an older SDK. Instead, we use sysctl kern.osproductversion
when available to still avoid the costly PList lookup in most cases, but still with a fall back to the PList lookup when that is not available (with the PList fallback being is similar to LLVM's implementation).
Testing
Apple has a lot of different "modes" that they can run binaries in, which can be a bit difficult to find your bearings in, but I've tried to be as thorough as I could in testing them all.
Tested using roughly the equivalent of ./x test library/std -- platform_version
on the following configurations:
- macOS 14.7.3 on a Macbook Pro M2
aarch64-apple-darwin
x86_64-apple-darwin
(under Rosetta)aarch64-apple-ios-macabi
x86_64-apple-ios-macabi
(under Rosetta)aarch64-apple-ios
(using Xcode's "Designed for iPad" setting)aarch64-apple-ios-sim
(in iOS Simulator, as iPhone with iOS 17.5)aarch64-apple-ios-sim
(in iOS Simulator, as iPad with iOS 18.2)aarch64-apple-tvos-sim
(in tvOS Simulator)aarch64-apple-watchos-sim
(in watchOS Simulator)aarch64-apple-ios-sim
(in visionOS simulator, using Xcode's "Designed for iPad" setting)aarch64-apple-visionos-sim
(in visionOS Simulator)
- macOS 15.3.1 VM
aarch64-apple-darwin
aarch64-apple-ios-macabi
- macOS 10.12.6 on an Intel Macbook from 2013
x86_64-apple-darwin
i686-apple-darwin
x86_64-apple-ios
(in iOS Simulator)
- iOS 9.3.6 on a 1st generation iPad Mini
armv7-apple-ios
with an older compiler
Along with manually inspecting the output of version_from_sysctl()
and version_from_plist()
, and verifying that they actually match what's expected.
I believe the only real omissions here would be:
aarch64-apple-ios
on a newer iPhone that hassysctl
available (iOS 11.4 or above).aarch64-apple-ios
on a Vision Pro using Xcode's "Designed for iPad" setting.
But I don't have the hardware available to test those.
@rustbot label O-apple A-linkage -T-compiler -A-meta -A-run-make
try-run: apple