Add `__isPlatformVersionAtLeast` and `__isOSVersionAtLeast` symbols
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. https://github.com/alexcrichton/curl-rust/issues/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 https://github.com/rust-lang/rust/pull/62592#issuecomment-510670657 and https://github.com/rust-lang/rust/pull/134275#issuecomment-2543067830.
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 https://github.com/rust-lang/rfcs/pull/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 https://github.com/rust-lang/compiler-builtins/pull/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-ioswith 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-ioson a newer iPhone that hassysctlavailable (iOS 11.4 or above). -
aarch64-apple-ioson 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
r? @Amanieu
rustbot has assigned @Amanieu. They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.
Use r? to explicitly pick a reviewer
This PR modifies run-make tests.
cc @jieyouxu
triagebot.toml has been modified, there may have been changes to the review queue.
cc @davidtwco, @wesleywiser
The job mingw-check-tidy failed! Check out the build log: (web) (plain)
Click to see the possible cause of the failure (guessed by this bot)
info: removing rustup binaries
info: rustup is uninstalled
##[group]Image checksum input
mingw-check-tidy
# We use the ghcr base image because ghcr doesn't have a rate limit
# and the mingw-check-tidy job doesn't cache docker images in CI.
FROM ghcr.io/rust-lang/ubuntu:22.04
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
g++ \
make \
---
COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/
COPY host-x86_64/mingw-check/validate-error-codes.sh /scripts/
# NOTE: intentionally uses python2 for x.py so we can test it still works.
# validate-toolstate only runs in our CI, so it's ok for it to only support python3.
ENV SCRIPT TIDY_PRINT_DIFF=1 python2.7 ../x.py test \
--stage 0 src/tools/tidy tidyselftest --extra-checks=py,cpp
#
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
# pip-compile --allow-unsafe --generate-hashes reuse-requirements.in
---
#12 2.784 Building wheels for collected packages: reuse
#12 2.785 Building wheel for reuse (pyproject.toml): started
#12 2.999 Building wheel for reuse (pyproject.toml): finished with status 'done'
#12 3.000 Created wheel for reuse: filename=reuse-4.0.3-cp310-cp310-manylinux_2_35_x86_64.whl size=132720 sha256=0c2fd2aaf7b0bf8d6e131220aff14712a774c2ca462f3204d25460cbcf610b63
#12 3.001 Stored in directory: /tmp/pip-ephem-wheel-cache-yy62o543/wheels/3d/8d/0a/e0fc6aba4494b28a967ab5eaf951c121d9c677958714e34532
#12 3.003 Successfully built reuse
#12 3.004 Installing collected packages: boolean-py, binaryornot, tomlkit, reuse, python-debian, markupsafe, license-expression, jinja2, chardet, attrs
#12 3.405 Successfully installed attrs-23.2.0 binaryornot-0.4.4 boolean-py-4.0 chardet-5.2.0 jinja2-3.1.4 license-expression-30.3.0 markupsafe-2.1.5 python-debian-0.1.49 reuse-4.0.3 tomlkit-0.13.0
#12 3.405 WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
#12 3.937 Collecting virtualenv
#12 3.972 Downloading virtualenv-20.29.3-py3-none-any.whl (4.3 MB)
#12 4.034 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.3/4.3 MB 72.0 MB/s eta 0:00:00
#12 4.089 Collecting platformdirs<5,>=3.9.1
#12 4.093 Downloading platformdirs-4.3.7-py3-none-any.whl (18 kB)
#12 4.110 Collecting distlib<1,>=0.3.7
#12 4.113 Downloading distlib-0.3.9-py2.py3-none-any.whl (468 kB)
#12 4.120 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 469.0/469.0 KB 96.6 MB/s eta 0:00:00
#12 4.156 Collecting filelock<4,>=3.12.2
#12 4.159 Downloading filelock-3.18.0-py3-none-any.whl (16 kB)
#12 4.241 Installing collected packages: distlib, platformdirs, filelock, virtualenv
#12 4.426 Successfully installed distlib-0.3.9 filelock-3.18.0 platformdirs-4.3.7 virtualenv-20.29.3
#12 4.426 WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
#12 DONE 4.5s
#13 [7/8] COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/
#13 DONE 0.0s
---
DirectMap4k: 116672 kB
DirectMap2M: 5126144 kB
DirectMap1G: 13631488 kB
##[endgroup]
Executing TIDY_PRINT_DIFF=1 python2.7 ../x.py test --stage 0 src/tools/tidy tidyselftest --extra-checks=py,cpp
+ TIDY_PRINT_DIFF=1 python2.7 ../x.py test --stage 0 src/tools/tidy tidyselftest --extra-checks=py,cpp
##[group]Building bootstrap
Finished `dev` profile [unoptimized] target(s) in 0.05s
##[endgroup]
WARN: currently no CI rustc builds have rustc debug assertions enabled. Please either set `rust.debug-assertions` to `false` if you want to use download CI rustc or set `rust.download-rustc` to `false`.
[TIMING] core::build_steps::tool::LibcxxVersionTool { target: x86_64-unknown-linux-gnu } -- 0.220
---
fmt check
fmt: checked 5934 files
tidy check
tidy: Skipping binary file check, read-only filesystem
##[error]tidy error: /checkout/tests/run-make/apple-c-available-links/foo.c:3: line longer than 100 chars
removing old virtual environment
creating virtual environment at '/checkout/obj/build/venv' using 'python3.10' and 'venv'
creating virtual environment at '/checkout/obj/build/venv' using 'python3.10' and 'virtualenv'
Requirement already satisfied: pip in ./build/venv/lib/python3.10/site-packages (25.0.1)
linting python files
All checks passed!
checking python file formatting
26 files already formatted
checking C++ file formatting
some tidy checks failed
Command has failed. Rerun with -v to see more details.
Build completed unsuccessfully in 0:01:50
local time: Tue Mar 25 20:16:56 UTC 2025
network time: Tue, 25 Mar 2025 20:16:56 GMT
##[error]Process completed with exit code 1.
Post job cleanup.
r? @tgross35
Happy to review the implementation, but @Amanieu mind confirming you are okay providing these symbols from std? It seems reasonable to me but as far as I know we don't have anything like this. (Also no other builtins that effectively need std API).
The job mingw-check-tidy failed! Check out the build log: (web) (plain)
Click to see the possible cause of the failure (guessed by this bot)
info: removing rustup binaries
info: rustup is uninstalled
##[group]Image checksum input
mingw-check-tidy
# We use the ghcr base image because ghcr doesn't have a rate limit
# and the mingw-check-tidy job doesn't cache docker images in CI.
FROM ghcr.io/rust-lang/ubuntu:22.04
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
g++ \
make \
---
COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/
COPY host-x86_64/mingw-check/validate-error-codes.sh /scripts/
# NOTE: intentionally uses python2 for x.py so we can test it still works.
# validate-toolstate only runs in our CI, so it's ok for it to only support python3.
ENV SCRIPT TIDY_PRINT_DIFF=1 python2.7 ../x.py test \
--stage 0 src/tools/tidy tidyselftest --extra-checks=py,cpp
#
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
# pip-compile --allow-unsafe --generate-hashes reuse-requirements.in
---
#12 3.246 Building wheels for collected packages: reuse
#12 3.247 Building wheel for reuse (pyproject.toml): started
#12 3.460 Building wheel for reuse (pyproject.toml): finished with status 'done'
#12 3.461 Created wheel for reuse: filename=reuse-4.0.3-cp310-cp310-manylinux_2_35_x86_64.whl size=132719 sha256=5bb60f62728aaedff7162745ce743c7f2f55069b3e7f82e6a37d70df455797cc
#12 3.461 Stored in directory: /tmp/pip-ephem-wheel-cache-9ijbv9a0/wheels/3d/8d/0a/e0fc6aba4494b28a967ab5eaf951c121d9c677958714e34532
#12 3.464 Successfully built reuse
#12 3.464 Installing collected packages: boolean-py, binaryornot, tomlkit, reuse, python-debian, markupsafe, license-expression, jinja2, chardet, attrs
#12 3.860 Successfully installed attrs-23.2.0 binaryornot-0.4.4 boolean-py-4.0 chardet-5.2.0 jinja2-3.1.4 license-expression-30.3.0 markupsafe-2.1.5 python-debian-0.1.49 reuse-4.0.3 tomlkit-0.13.0
#12 3.860 WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
#12 4.414 Collecting virtualenv
#12 4.470 Downloading virtualenv-20.30.0-py3-none-any.whl (4.3 MB)
#12 4.727 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.3/4.3 MB 17.0 MB/s eta 0:00:00
#12 4.789 Collecting platformdirs<5,>=3.9.1
#12 4.800 Downloading platformdirs-4.3.7-py3-none-any.whl (18 kB)
#12 4.825 Collecting distlib<1,>=0.3.7
#12 4.835 Downloading distlib-0.3.9-py2.py3-none-any.whl (468 kB)
#12 4.851 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 469.0/469.0 KB 31.8 MB/s eta 0:00:00
#12 4.895 Collecting filelock<4,>=3.12.2
#12 4.905 Downloading filelock-3.18.0-py3-none-any.whl (16 kB)
#12 4.987 Installing collected packages: distlib, platformdirs, filelock, virtualenv
#12 5.171 Successfully installed distlib-0.3.9 filelock-3.18.0 platformdirs-4.3.7 virtualenv-20.30.0
#12 5.172 WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
#12 DONE 5.3s
#13 [7/8] COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/
#13 DONE 0.0s
---
DirectMap4k: 124864 kB
DirectMap2M: 8263680 kB
DirectMap1G: 10485760 kB
##[endgroup]
Executing TIDY_PRINT_DIFF=1 python2.7 ../x.py test --stage 0 src/tools/tidy tidyselftest --extra-checks=py,cpp
+ TIDY_PRINT_DIFF=1 python2.7 ../x.py test --stage 0 src/tools/tidy tidyselftest --extra-checks=py,cpp
##[group]Building bootstrap
Finished `dev` profile [unoptimized] target(s) in 0.05s
##[endgroup]
WARN: currently no CI rustc builds have rustc debug assertions enabled. Please either set `rust.debug-assertions` to `false` if you want to use download CI rustc or set `rust.download-rustc` to `false`.
[TIMING] core::build_steps::tool::LibcxxVersionTool { target: x86_64-unknown-linux-gnu } -- 0.216
---
Diff in /checkout/library/std/src/sys/platform_version/darwin/compiler_builtins.rs:49:
#![allow(non_snake_case)]
-use super::{current_version, pack_os_version, OSVersion};
+use super::{OSVersion, current_version, pack_os_version};
/// Whether the current platform's OS version is higher than or equal to the given version.
///
Diff in /checkout/library/std/src/sys/platform_version/darwin/compiler_builtins.rs:122:
0 // PLATFORM_UNKNOWN
};
debug_assert_eq!(
- platform,
- BASE_TARGET_PLATFORM,
+ platform, BASE_TARGET_PLATFORM,
"invalid platform provided to __isPlatformVersionAtLeast",
);
fmt: checked 5946 files
Build completed unsuccessfully in 0:01:22
local time: Fri Apr 4 12:01:51 UTC 2025
It's a bit of a grey area since it's not clear how much it is Rust's responsibility to provide builtin symbols that are only used by C code. Arguably the more "correct" approach is to tell users of C code that we only provide symbols for Rust code and that they need to separately add a dependency on libclang_rt or libgcc, but that is a lot of hassle and thing usually just work without it.
I'm not opposed to adding this in libstd for now, but it might be worth looking into a better story for how to handle builtins that are needed by C code but not Rust code (e.g. emulated TLS, clear_cache, enable_execute_stack, etc).
Thanks for the review so far. I've answered or fixed your comments in separate commits, to make it easier for you to review (if you're already halfway through a second review). I can squash once everything is ready. @rustbot ready
:warning: Warning :warning:
-
This PR is based on an upstream commit that is 68 days old.
It's recommended to update your branch according to the rustc-dev-guide.
Error: Invalid triagebot.toml at position 47:2:
TOML parse error at line 47, column 2
|
47 | [glacier]
| ^^^^^^^
unknown field `glacier`, expected one of `relabel`, `assign`, `ping`, `nominate`, `prioritize`, `major-change`, `close`, `autolabel`, `notify-zulip`, `github-releases`, `review-submitted`, `review-requested`, `shortcut`, `note`, `concern`, `mentions`, `no-merges`, `validate-config`, `pr-tracking`, `transfer`, `merge-conflicts`, `bot-pull-requests`, `rendered-link`, `canonicalize-issue-links`, `issue-links`, `no-mentions`, `behind-upstream`
Please file an issue on GitHub at triagebot if there's a problem with this bot, or reach out on #t-infra on Zulip.
Error: Invalid triagebot.toml at position 47:2:
TOML parse error at line 47, column 2
|
47 | [glacier]
| ^^^^^^^
unknown field `glacier`, expected one of `relabel`, `assign`, `ping`, `nominate`, `prioritize`, `major-change`, `close`, `autolabel`, `notify-zulip`, `github-releases`, `review-submitted`, `review-requested`, `shortcut`, `note`, `concern`, `mentions`, `no-merges`, `validate-config`, `pr-tracking`, `transfer`, `merge-conflicts`, `bot-pull-requests`, `rendered-link`, `canonicalize-issue-links`, `issue-links`, `no-mentions`, `behind-upstream`
Please file an issue on GitHub at triagebot if there's a problem with this bot, or reach out on #t-infra on Zulip.
Error: Invalid triagebot.toml at position 47:2:
TOML parse error at line 47, column 2
|
47 | [glacier]
| ^^^^^^^
unknown field `glacier`, expected one of `relabel`, `assign`, `ping`, `nominate`, `prioritize`, `major-change`, `close`, `autolabel`, `notify-zulip`, `github-releases`, `review-submitted`, `review-requested`, `shortcut`, `note`, `concern`, `mentions`, `no-merges`, `validate-config`, `pr-tracking`, `transfer`, `merge-conflicts`, `bot-pull-requests`, `rendered-link`, `canonicalize-issue-links`, `issue-links`, `no-mentions`, `behind-upstream`
Please file an issue on GitHub at triagebot if there's a problem with this bot, or reach out on #t-infra on Zulip.
Thanks for the review, and no problem about the delay! I've rebased, and (I think) I've resolved or responded to your comments now.
A bit unsure about the rustc_std_internal_symbols stuff that Björn suggested, so I've kept that as a separate commit for now, to make it easier to review.
Feel free to tell me to squash or rebase the commits however you want them.
@bors2 try jobs=x86_64-apple,aarch64-apple
Unknown value for argument "jobs".
@bors2 try
:hourglass: Trying commit 7852c5c6623ecafd55e497f7a58e6e3b9e6b7ef3 with merge d3ba06607e48b5631ac8fde4471e52876f853625…
To cancel the try build, run the command @bors2 try cancel.
Have squashed and fixed the nit, will try to get a green try job: @bors2 try
I think we still need to discuss the guarantees we want to give by providing this, see the thread in https://github.com/rust-lang/rust/pull/138944#discussion_r2034186256.
:hourglass: Trying commit 516742accd6b351b8a517f6a07591c657676a5f1 with merge 813f50b8884dd211abb13c71a36e1ed9e3c0fa8b…
To cancel the try build, run the command @bors2 try cancel.
Fixed two rustdoc link warnings ("bare URLs are not automatically turned into clickable links"). @bors2 try
:hourglass: Trying commit 516742accd6b351b8a517f6a07591c657676a5f1 with merge 9ddcf15c1be50cd825edf308fe391cef9ddcad16…
To cancel the try build, run the command @bors2 try cancel.
I meant another commit.
@bors2 try cancel @bors2 try
Try build cancelled. Cancelled workflows:
- https://github.com/rust-lang/rust/actions/runs/15624394507
:hourglass: Trying commit 5ec7157996bfd52ad8d39f43d8abb7f0c23dc20f with merge 2008cdddc0284a96c2bc7bcb00c241044dfda87e…
To cancel the try build, run the command @bors2 try cancel.
Error was:
foo.c:11:9: error: unrecognized platform name visionos
visionos 1000.0,
^
1 error generated.
Fixed building test with older Clang (modified tests/run-make/apple-c-available-links/*).
@bors2 try
:hourglass: Trying commit 779ac2f186a50d86e83ab73b7e464377e411dc9c with merge ea9a6dd3b41c9b6d6905b41e7516084cda77dfb3…
To cancel the try build, run the command @bors2 try cancel.