-
Notifications
You must be signed in to change notification settings - Fork 1.6k
RFC: cfg_target_version #3750
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
RFC: cfg_target_version #3750
Conversation
text/3750-cfg-os-version-min.md
Outdated
A set of comparison functions can be provided by `rustc` for common formats such as 2- and 3-part semantic versioning. | ||
When a platform detects a key it doesn’t support it will return `false` and emit a warning. | ||
|
||
Each target platform will set the minimum API versions it supports. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So there is no way for the user to configure which minimum API version they want to use? That would make it impossible for eg the libc crate to gate api's behind #[cfg(os_version_min)]
corresponding to the libc version that introduced the API in question.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. One of the issues with the original proposals was there was too much going on. This RFC aims to add the minimally useful feature. Adding compiler flags and Cargo configs to control the minimum version can be a future extension.
With this RFC it is still possible for libc to gate APIs like that. However, you'd need a new target in order to set a different minimal libc version.
The idea is great, but I think the syntax can be improved. It makes more sense to have this feature under the existing cfg(target_os("windows", min_version = "...")) Another idea: cfg(target_os = "windows >= ...") |
That may work for windows but not, for example, Linux where you may want to cfg on the kernel version or the libc version (or even both). libc isn't really a |
Right, so this could be done with a cfg(target_libc("glibc", min_version = "...")) But of course this would add more complexity. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for reviving this @ChrisDenton! I'd be willing to implement at least the Apple parts of this, and probably also other parts.
Co-authored-by: Mads Marquart <[email protected]>
text/3750-cfg-os-version-min.md
Outdated
# Future possibilities | ||
[future-possibilities]: #future-possibilities | ||
|
||
The compiler could allow setting a higher minimum OS version than the target's default. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remark: I'll add that rustc
already has a mechanism for doing exactly this on Apple platforms, namely the *_DEPLOYMENT_TARGET
family of environment variables.
text/3750-cfg-os-version-min.md
Outdated
|
||
**Note:** Here it would be important to link to documentation showing the `cfg` predicates and the different version strings that are supported. | ||
|
||
# Reference-level explanation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need to discuss here what happens when you link libraries compiled with different (even though we're not specifying how the user, we will need a mechanism for it at some point)
Specifically, it'd be nice to talk about the pre-compiled standard library, and how it effectively becomes a requirement to use -Zbuild-std
if the user wants to enable this kind of stuff for the standard library.
text/3750-cfg-os-version-min.md
Outdated
|
||
**Note:** Here it would be important to link to documentation showing the `cfg` predicates and the different version strings that are supported. | ||
|
||
# Reference-level explanation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possibly also interesting is soundness concerns (what happens if I use a standard library compiled with a newer version of glibc/Windows APIs/macOS APIs, while I link with a binary compiled for older APIs?).
I don't think there are any soundness concerns, at least not on Apple platforms (the dynamic linker will simply fail to work if using an API for a newer system), but it's important that we're certain of this (and that function pointers for example aren't simply replaced by NULL if loaded on an older OS).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There aren't unsoundness issues for Windows. Either a dll or symbol is available or it isn't and there's an error. EDIT: I'm being told that attempting to use incompatible glibcs will also cause an error.
I don't know if that's true of all OSes but Rust libs do carry around metadata with them so if they declare incompatible versions then the compiler could simply error. And linking together native static libraries compiled for different API versions would seem to be squarely in the realm of "you must know what you're doing" (and that's true more broadly when linking native libs). However, considering the current narrow scope of this RFC, it would not be a situation that arises any more often than it currently does. So it may only worth a mention in future possibilities.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd actually like to update my previous statement, I suspect that it may actually be unsound to use the combination dependency (compiled for newer OS)
+ user_crate (compiled for older OS)
+ link (for older OS)
, since e.g. LLVM may do codegen optimizations that are only valid on newer architectures (which one can do because Apple restricts OS upgrades after a certain point, so we know that newer OS versions only run on newer hardware, and can be required to have for example a certain level of SIMD features).
Related here is #3716.
But I agree that this is only tangentially related to the RFC.
Ok I've rewritten this a bit and further simplified it based on feedback. |
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.
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.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've created a tracking issue for this feature in rust-lang/rust#136866, and filed a PR implementing it (unstably) for Apple platforms in rust-lang/rust#136867. I guess that at least shows feasibility of the RFC.
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.
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.
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.
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.
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.
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.
Co-authored-by: Mads Marquart <[email protected]>
Co-authored-by: Mads Marquart <[email protected]>
Co-authored-by: Mads Marquart <[email protected]>
Co-authored-by: Mads Marquart <[email protected]>
Co-authored-by: Mads Marquart <[email protected]>
Co-authored-by: Mads Marquart <[email protected]>
Add `__isPlatformVersionAtLeast` and `__isOSVersionAtLeast` symbols ## Motivation When Objective-C code uses ``@available(...)`,` Clang inserts a call to [`__isPlatformVersionAtLeast`](https://github.com/llvm/llvm-project/blob/llvmorg-20.1.0/compiler-rt/lib/builtins/os_version_check.c#L276) (`__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`](#136113), 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](https://rust-lang.zulipchat.com/#narrow/channel/219381-t-libs/topic/Provide.20.60__isPlatformVersionAtLeast.60.20in.20.60std.60.3F/with/507880717)), 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](llvm/llvm-project#64227). 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 has `sysctl` 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-job: aarch64-apple try-job: x86_64-apple
Add `__isPlatformVersionAtLeast` and `__isOSVersionAtLeast` symbols ## Motivation When Objective-C code uses ``@available(...)`,` Clang inserts a call to [`__isPlatformVersionAtLeast`](https://github.com/llvm/llvm-project/blob/llvmorg-20.1.0/compiler-rt/lib/builtins/os_version_check.c#L276) (`__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`](#136113), 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](https://rust-lang.zulipchat.com/#narrow/channel/219381-t-libs/topic/Provide.20.60__isPlatformVersionAtLeast.60.20in.20.60std.60.3F/with/507880717)), 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](llvm/llvm-project#64227). 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 has `sysctl` 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-job: aarch64-apple try-job: x86_64-apple*
Add `__isPlatformVersionAtLeast` and `__isOSVersionAtLeast` symbols ## Motivation When Objective-C code uses ``@available(...)`,` Clang inserts a call to [`__isPlatformVersionAtLeast`](https://github.com/llvm/llvm-project/blob/llvmorg-20.1.0/compiler-rt/lib/builtins/os_version_check.c#L276) (`__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`](#136113), 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](https://rust-lang.zulipchat.com/#narrow/channel/219381-t-libs/topic/Provide.20.60__isPlatformVersionAtLeast.60.20in.20.60std.60.3F/with/507880717)), 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](llvm/llvm-project#64227). 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 has `sysctl` 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-job: aarch64-apple try-job: x86_64-apple*
Add `__isPlatformVersionAtLeast` and `__isOSVersionAtLeast` symbols ## Motivation When Objective-C code uses ``@available(...)`,` Clang inserts a call to [`__isPlatformVersionAtLeast`](https://github.com/llvm/llvm-project/blob/llvmorg-20.1.0/compiler-rt/lib/builtins/os_version_check.c#L276) (`__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`](#136113), 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](https://rust-lang.zulipchat.com/#narrow/channel/219381-t-libs/topic/Provide.20.60__isPlatformVersionAtLeast.60.20in.20.60std.60.3F/with/507880717)), 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](llvm/llvm-project#64227). 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 has `sysctl` 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-job: aarch64-apple try-job: x86_64-apple*
Add `__isPlatformVersionAtLeast` and `__isOSVersionAtLeast` symbols ## Motivation When Objective-C code uses ``@available(...)`,` Clang inserts a call to [`__isPlatformVersionAtLeast`](https://github.com/llvm/llvm-project/blob/llvmorg-20.1.0/compiler-rt/lib/builtins/os_version_check.c#L276) (`__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`](#136113), 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](https://rust-lang.zulipchat.com/#narrow/channel/219381-t-libs/topic/Provide.20.60__isPlatformVersionAtLeast.60.20in.20.60std.60.3F/with/507880717)), 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](llvm/llvm-project#64227). 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 has `sysctl` 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-job: aarch64-apple try-job: x86_64-apple*
Add `__isPlatformVersionAtLeast` and `__isOSVersionAtLeast` symbols ## Motivation When Objective-C code uses ``@available(...)`,` Clang inserts a call to [`__isPlatformVersionAtLeast`](https://github.com/llvm/llvm-project/blob/llvmorg-20.1.0/compiler-rt/lib/builtins/os_version_check.c#L276) (`__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`](#136113), 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](https://rust-lang.zulipchat.com/#narrow/channel/219381-t-libs/topic/Provide.20.60__isPlatformVersionAtLeast.60.20in.20.60std.60.3F/with/507880717)), 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](llvm/llvm-project#64227). 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 has `sysctl` 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-job: aarch64-apple try-job: x86_64-apple*
This RFC builds on the work of @rylev and @chriswailes. As suggested by @tmandry I have stripped it down to an MVP that just adds
target_version
(akaos_version_min
in previous RFCs).Rendered