Implement __isPlatformVersionAtLeast
and __isOSVersionAtLeast
by madsmtm · Pull Request #794 · rust-lang/compiler-builtins (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 in compiler-builtins
sometimes ends up causing linker errors.
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 a recent example, this is preventing rustc
from using LLVM assertions on macOS, see rust-lang/rust#62592 (comment) and rust-lang/rust#134275 (comment).
Apart from linker errors above, it is also a blocker for setting the correct minimum OS version in cc-rs, which is a huge correctness footgun: By default, if using e.g. __builtin_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 there's a reason why people write availability checks, which is that otherwise the code won't work on the older OS! But if we want to fix this in cc-rs
, we might end up introducing linker errors in places where they weren't before.
Example crate where this behaviour is illustrated
Cargo.toml
[package] name = "foo" version = "0.0.0" edition = "2024"
[build-dependencies] cc = "1.0"
// foo.c int foo(void) { // Use some API that's a lot newer than the host machine if (__builtin_available(macos 20.0, *)) { return 1; } else { return 0; } }
// build.rs fn main() { println!("cargo:rerun-if-changed=foo.c"); cc::Build::new().file("foo.c").compile("foo"); }
// main.rs unsafe extern "C" { safe fn foo() -> core::ffi::c_int; }
fn main() { println!("{}", foo()); }
(Finally, I'll reveal that my super secret evil agenda is to expose some variant of @available
/__builtin_available
in Rust's std
, and that'll probably be easier if the bulk of the implementation is already here in compiler-builtins
. But I believe adding this here has value regardless of those future plans).
Solution
Implement __isPlatformVersionAtLeast
and __isOSVersionAtLeast
, see the code comments for more details. Of particular note:
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 sysctlbyname
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 (this fallback is similar to LLVM's implementation).
I've also preferred to panic when hitting an unexpected state, instead of silently returning a wrong value, but am unsure what your policies are around this?
Testing
Tested using the equivalent of cargo test --manifest-path testcrate/Cargo.toml --test os_version_check
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.