From e5250ec5eef76d00f3e708c108da24593fcc6a52 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Sun, 1 Mar 2026 10:42:29 +0530 Subject: [PATCH 1/8] tools: remote-test-server: Add UEFI run support Tested with OVMF on QEMU with Linux as the host running remote-test-client. The instructions for running tests are provided below: 1. Use rust-lld linker: `bootstrap.toml` ```toml [rust] lld = true [target.x86_64-unknown-uefi] linker = "rust-lld" ``` 2. Use a custom startup.nsh script to auto-launch remote-test-server. This is optional. ```shell fs1: run.efi --sequential --bind "10.0.2.15:12345" --batch ``` 3. Launch remote-test-server in QEMU. I am using uefi-run script [0]. ```shell uefi-run build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-uefi/release/remote-test-server.efi -n --startup startup.nsh --tcp-port 12345 ``` 4. Run tests: ```shell RUST_TEST_THREADS=1 TEST_DEVICE_ADDR="localhost:12345" ./x.py test tests/ui/abi --target x86_64-unknown-uefi --stage 1 ``` [0]: https://github.com/Ayush1325/dotfiles/blob/2d13056bf8ca1931a234b72967d9e857c4afbf1a/uefi/.local/bin/uefi-run Signed-off-by: Ayush Singh --- src/tools/remote-test-server/src/main.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tools/remote-test-server/src/main.rs b/src/tools/remote-test-server/src/main.rs index c0820e4a6854f..b4d30289cabb9 100644 --- a/src/tools/remote-test-server/src/main.rs +++ b/src/tools/remote-test-server/src/main.rs @@ -123,6 +123,8 @@ fn main() { let listener = bind_socket(config.bind); let (work, tmp): (PathBuf, PathBuf) = if cfg!(target_os = "android") { ("/data/local/tmp/work".into(), "/data/local/tmp/work/tmp".into()) + } else if cfg!(target_os = "uefi") { + ("tmp\\work".into(), "tmp\\work\\tmp".into()) } else { let mut work_dir = env::temp_dir(); work_dir.push("work"); @@ -274,6 +276,8 @@ fn handle_run(socket: TcpStream, work: &Path, tmp: &Path, lock: &Mutex<()>, conf } else if cfg!(target_vendor = "apple") { // On Apple platforms, the environment variable is named differently. "DYLD_LIBRARY_PATH" + } else if cfg!(target_os = "uefi") { + "path" } else { "LD_LIBRARY_PATH" }; From affe60917ce26bb2dfc0d100bdbe29d5804fe17f Mon Sep 17 00:00:00 2001 From: Aelin Reidel Date: Mon, 2 Mar 2026 15:18:53 +0100 Subject: [PATCH 2/8] tests: codegen-llvm: iter-repeat-n-trivial-drop: Allow non-zero lower bound to __rust_alloc size LLVM emits a lower bound of 8 for the size parameter to __rust_alloc when targeting x86_64-unknown-hermit. Since that is also completely valid, relax the lower bound check. --- tests/codegen-llvm/iter-repeat-n-trivial-drop.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/codegen-llvm/iter-repeat-n-trivial-drop.rs b/tests/codegen-llvm/iter-repeat-n-trivial-drop.rs index a4e5c885a139a..98e906d852f74 100644 --- a/tests/codegen-llvm/iter-repeat-n-trivial-drop.rs +++ b/tests/codegen-llvm/iter-repeat-n-trivial-drop.rs @@ -47,7 +47,7 @@ pub fn iter_repeat_n_next(it: &mut std::iter::RepeatN) -> Option Vec { - // CHECK: %[[ADDR:.+]] = tail call {{(noalias )?}}noundef dereferenceable_or_null(1234) ptr @{{.*}}__rust_alloc(i64 noundef {{(range\(i64 0, -9223372036854775808\) )?}}1234, i64 noundef {{(range\(i64 1, -9223372036854775807\) )?}}1) + // CHECK: %[[ADDR:.+]] = tail call {{(noalias )?}}noundef dereferenceable_or_null(1234) ptr @{{.*}}__rust_alloc(i64 noundef {{(range\(i64 [0-9]+, -9223372036854775808\) )?}}1234, i64 noundef {{(range\(i64 1, -9223372036854775807\) )?}}1) // CHECK: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(1234) %[[ADDR]], i8 42, i64 1234, let n = 1234_usize; From 13ddff53be423b17615837846dc959592c32d93d Mon Sep 17 00:00:00 2001 From: Tim Neumann Date: Mon, 2 Mar 2026 21:06:11 +0100 Subject: [PATCH 3/8] Adapt codegen test to accept operand bundles --- tests/codegen-llvm/issues/issue-37945.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/codegen-llvm/issues/issue-37945.rs b/tests/codegen-llvm/issues/issue-37945.rs index 23d0eab8ae466..3167a045575a8 100644 --- a/tests/codegen-llvm/issues/issue-37945.rs +++ b/tests/codegen-llvm/issues/issue-37945.rs @@ -1,5 +1,8 @@ //@ compile-flags: -Copt-level=3 -Zmerge-functions=disabled //@ ignore-32bit LLVM has a bug with them +//@ revisions: new old +//@ [old] max-llvm-major-version: 22 +//@ [new] min-llvm-version: 23 // Check that LLVM understands that `Iter` pointer is not null. Issue #37945. @@ -11,8 +14,9 @@ use std::slice::Iter; pub fn is_empty_1(xs: Iter) -> bool { // CHECK-LABEL: @is_empty_1( // CHECK-NEXT: start: - // CHECK-NEXT: [[A:%.*]] = icmp ne ptr {{%xs.0|%xs.1}}, null - // CHECK-NEXT: tail call void @llvm.assume(i1 [[A]]) + // old-NEXT: [[A:%.*]] = icmp ne ptr {{%xs.0|%xs.1}}, null + // old-NEXT: tail call void @llvm.assume(i1 [[A]]) + // new-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr {{%xs.0|%xs.1}}) ] // The order between %xs.0 and %xs.1 on the next line doesn't matter // and different LLVM versions produce different order. // CHECK-NEXT: [[B:%.*]] = icmp eq ptr {{%xs.0, %xs.1|%xs.1, %xs.0}} @@ -24,8 +28,9 @@ pub fn is_empty_1(xs: Iter) -> bool { pub fn is_empty_2(xs: Iter) -> bool { // CHECK-LABEL: @is_empty_2 // CHECK-NEXT: start: - // CHECK-NEXT: [[C:%.*]] = icmp ne ptr {{%xs.0|%xs.1}}, null - // CHECK-NEXT: tail call void @llvm.assume(i1 [[C]]) + // old-NEXT: [[C:%.*]] = icmp ne ptr {{%xs.0|%xs.1}}, null + // old-NEXT: tail call void @llvm.assume(i1 [[C]]) + // new-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr {{%xs.0|%xs.1}}) ] // The order between %xs.0 and %xs.1 on the next line doesn't matter // and different LLVM versions produce different order. // CHECK-NEXT: [[D:%.*]] = icmp eq ptr {{%xs.0, %xs.1|%xs.1, %xs.0}} From 6d6493a7986e66e3a99b41b3f563f83faa4fd2b0 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Mon, 3 Nov 2025 17:24:53 +0100 Subject: [PATCH 4/8] tweak r-a default settings 1. Explain that you need to make paths absolute in helix settings 2. Use the correct compiler (set `RUSTC` and `CARGO` to stage0, instead of `RUSTUP_TOOLCHAIN=nightly`) 3. Add comments to vscode and zed settings --- src/bootstrap/src/core/build_steps/setup.rs | 3 +++ src/etc/rust_analyzer_helix.toml | 8 ++++++-- src/etc/rust_analyzer_settings.json | 14 +++++++++++++- src/etc/rust_analyzer_zed.json | 14 +++++++++++++- 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index 7dfd566a58ccb..f4e6a20ec8497 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -607,6 +607,7 @@ Select which editor you would like to set up [default: None]: "; "1c43ead340b20792b91d02b08494ee68708e7e09f56b6766629b4b72079208f1", "eec09a09452682060afd23dd5d3536ccac5615b3cdbf427366446901215fb9f6", "cb653043852d9d5ff4a5be56407b859ff9928be055ad3f307eb309aad04765e6", + "e28b1930d16d3d8bbdeed7bd4a995613e648b49e08c9b6f5271880f520637fed", ], EditorKind::Vim | EditorKind::VsCode => &[ "ea67e259dedf60d4429b6c349a564ffcd1563cf41c920a856d1f5b16b4701ac8", @@ -626,6 +627,7 @@ Select which editor you would like to set up [default: None]: "; "02a49ac2d31f00ef6e4531c44e00dac51cea895112e480553f1ba060b3942a47", "0aa4748848de0d1cb7ece92a0123c8897fef6de2f58aff8fda1426f098b7a798", "e5e357862e5d6d0d9da335e9823c07b8a7dc42bbf18d72cc5206ad1049cd8fcc", + "a68fd5828e75f3e921f265e29ce1e9efa554083c3773fdb4b8e1ab3b2d9dc6cd", ], EditorKind::Zed => &[ "bbce727c269d1bd0c98afef4d612eb4ce27aea3c3a8968c5f10b31affbc40b6c", @@ -636,6 +638,7 @@ Select which editor you would like to set up [default: None]: "; "5ef83292111d9a8bb63b6afc3abf42d0bc78fe24985f0d2e039e73258b5dab8f", "74420c13094b530a986b37c4f1d23cb58c0e8e2295f5858ded129fb1574e66f9", "2d3b592c089b2ad2c528686a1e371af49922edad1c59accd5d5f31612a441568", + "0767a2398ccc253274b184adbb9e018ce931bd0ef45baad06dad19b652c52951", ], } } diff --git a/src/etc/rust_analyzer_helix.toml b/src/etc/rust_analyzer_helix.toml index c9ee1ca181d50..ae2c2d9064886 100644 --- a/src/etc/rust_analyzer_helix.toml +++ b/src/etc/rust_analyzer_helix.toml @@ -9,6 +9,9 @@ # x fmt --check # ``` # (if that doesn't work -- do `x clean` first) +# +# NOTE: helix doesn't support getting workspace root in configs, for this config +# to work replace WORKSPACE_ROOT with the path to your rustc checkout [language-server.rust-analyzer.config] linkedProjects = [ @@ -33,7 +36,7 @@ overrideCommand = [ [language-server.rust-analyzer.config.rustfmt] overrideCommand = [ - "build/host/rustfmt/bin/rustfmt", + "WORKSPACE_ROOT/build/host/rustfmt/bin/rustfmt", "--edition=2024" ] @@ -64,7 +67,8 @@ overrideCommand = [ ] [language-server.rust-analyzer.environment] -RUSTUP_TOOLCHAIN = "nightly" +RUSTC = "WORKSPACE_ROOT/build/host/stage0/bin/rustc" +CARGO = "WORKSPACE_ROOT/build/host/stage0/bin/cargo" [[language]] name = "rust" diff --git a/src/etc/rust_analyzer_settings.json b/src/etc/rust_analyzer_settings.json index 8e94fbaa163e8..ea5bb544c7004 100644 --- a/src/etc/rust_analyzer_settings.json +++ b/src/etc/rust_analyzer_settings.json @@ -1,3 +1,14 @@ +// This config uses a separate build directory for rust-analyzer, +// so that r-a's checks don't block user `x` commands and vice-verse. +// R-a's build directory is located in `build-rust-analyzer`. +// +// To download rustfmt and proc macro server for r-a run the following command +// (proc macro server is downloaded automatically with pretty much any command, +// this specific one also downloads rustfmt): +// ``` +// x fmt --check +// ``` +// (if that doesn't work -- do `x clean` first) { "git.detectSubmodulesLimit": 20, "rust-analyzer.linkedProjects": [ @@ -40,7 +51,8 @@ "build-rust-analyzer" ], "rust-analyzer.server.extraEnv": { - "RUSTUP_TOOLCHAIN": "nightly" + "RUSTC": "${workspaceFolder}/build/host/stage0/bin/rustc", + "CARGO": "${workspaceFolder}/build/host/stage0/bin/cargo" }, "files.associations": { "*.fixed": "rust", diff --git a/src/etc/rust_analyzer_zed.json b/src/etc/rust_analyzer_zed.json index df1f7334556c3..e69800c0624d9 100644 --- a/src/etc/rust_analyzer_zed.json +++ b/src/etc/rust_analyzer_zed.json @@ -1,3 +1,14 @@ +// This config uses a separate build directory for rust-analyzer, +// so that r-a's checks don't block user `x` commands and vice-verse. +// R-a's build directory is located in `build-rust-analyzer`. +// +// To download rustfmt and proc macro server for r-a run the following command +// (proc macro server is downloaded automatically with pretty much any command, +// this specific one also downloads rustfmt): +// ``` +// x fmt --check +// ``` +// (if that doesn't work -- do `x clean` first) { "lsp": { "rust-analyzer": { @@ -55,7 +66,8 @@ }, "server": { "extraEnv": { - "RUSTUP_TOOLCHAIN": "nightly" + "RUSTC": "build/host/stage0/bin/rustc", + "CARGO": "build/host/stage0/bin/cargo" } } } From 390f683cfca9e3c39d73e9df776a65ec0f141290 Mon Sep 17 00:00:00 2001 From: joboet Date: Mon, 2 Mar 2026 12:52:30 +0100 Subject: [PATCH 5/8] std: move non-path functions into dedicated module in PAL --- .../std/src/sys/net/connection/socket/unix.rs | 2 +- library/std/src/sys/pal/teeos/conf.rs | 5 + library/std/src/sys/pal/teeos/mod.rs | 1 + library/std/src/sys/pal/teeos/os.rs | 6 -- library/std/src/sys/pal/unix/conf.rs | 97 +++++++++++++++++ .../src/sys/pal/unix/{os => conf}/tests.rs | 0 library/std/src/sys/pal/unix/mod.rs | 1 + library/std/src/sys/pal/unix/os.rs | 101 ++---------------- .../std/src/sys/pal/unix/stack_overflow.rs | 4 +- library/std/src/sys/pal/wasi/conf.rs | 4 + library/std/src/sys/pal/wasi/mod.rs | 2 + library/std/src/sys/pal/wasi/os.rs | 5 - library/std/src/sys/process/unix/unix.rs | 2 +- library/std/src/sys/thread/teeos.rs | 4 +- library/std/src/sys/thread/unix.rs | 2 +- 15 files changed, 124 insertions(+), 112 deletions(-) create mode 100644 library/std/src/sys/pal/teeos/conf.rs create mode 100644 library/std/src/sys/pal/unix/conf.rs rename library/std/src/sys/pal/unix/{os => conf}/tests.rs (100%) create mode 100644 library/std/src/sys/pal/wasi/conf.rs diff --git a/library/std/src/sys/net/connection/socket/unix.rs b/library/std/src/sys/net/connection/socket/unix.rs index 5e20c0ffdfa6a..f3c860804de03 100644 --- a/library/std/src/sys/net/connection/socket/unix.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -684,7 +684,7 @@ fn on_resolver_failure() { use crate::sys; // If the version fails to parse, we treat it the same as "not glibc". - if let Some(version) = sys::os::glibc_version() { + if let Some(version) = sys::pal::conf::glibc_version() { if version < (2, 26) { unsafe { libc::res_init() }; } diff --git a/library/std/src/sys/pal/teeos/conf.rs b/library/std/src/sys/pal/teeos/conf.rs new file mode 100644 index 0000000000000..1d13a283c458b --- /dev/null +++ b/library/std/src/sys/pal/teeos/conf.rs @@ -0,0 +1,5 @@ +// Hardcoded to return 4096, since `sysconf` is only implemented as a stub. +pub fn page_size() -> usize { + // unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }; + 4096 +} diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs index f76c26d3c966c..7d2ecdbf7ec4b 100644 --- a/library/std/src/sys/pal/teeos/mod.rs +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -6,6 +6,7 @@ #![allow(unused_variables)] #![allow(dead_code)] +pub mod conf; pub mod os; #[path = "../unix/time.rs"] pub mod time; diff --git a/library/std/src/sys/pal/teeos/os.rs b/library/std/src/sys/pal/teeos/os.rs index c09b84f42bab9..c86fa555e4117 100644 --- a/library/std/src/sys/pal/teeos/os.rs +++ b/library/std/src/sys/pal/teeos/os.rs @@ -7,12 +7,6 @@ use crate::ffi::{OsStr, OsString}; use crate::path::PathBuf; use crate::{fmt, io, path}; -// Hardcoded to return 4096, since `sysconf` is only implemented as a stub. -pub fn page_size() -> usize { - // unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }; - 4096 -} - // Everything below are stubs and copied from unsupported.rs pub fn getcwd() -> io::Result { diff --git a/library/std/src/sys/pal/unix/conf.rs b/library/std/src/sys/pal/unix/conf.rs new file mode 100644 index 0000000000000..a97173a1a35a1 --- /dev/null +++ b/library/std/src/sys/pal/unix/conf.rs @@ -0,0 +1,97 @@ +#[cfg(test)] +mod tests; + +#[cfg(not(target_os = "espidf"))] +pub fn page_size() -> usize { + unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } +} + +/// Returns the value for [`confstr(key, ...)`][posix_confstr]. Currently only +/// used on Darwin, but should work on any unix (in case we need to get +/// `_CS_PATH` or `_CS_V[67]_ENV` in the future). +/// +/// [posix_confstr]: +/// https://pubs.opengroup.org/onlinepubs/9699919799/functions/confstr.html +// +// FIXME: Support `confstr` in Miri. +#[cfg(all(target_vendor = "apple", not(miri)))] +pub fn confstr( + key: crate::ffi::c_int, + size_hint: Option, +) -> crate::io::Result { + use crate::ffi::OsString; + use crate::io; + use crate::os::unix::ffi::OsStringExt; + + let mut buf: Vec = Vec::with_capacity(0); + let mut bytes_needed_including_nul = size_hint + .unwrap_or_else(|| { + // Treat "None" as "do an extra call to get the length". In theory + // we could move this into the loop below, but it's hard to do given + // that it isn't 100% clear if it's legal to pass 0 for `len` when + // the buffer isn't null. + unsafe { libc::confstr(key, core::ptr::null_mut(), 0) } + }) + .max(1); + // If the value returned by `confstr` is greater than the len passed into + // it, then the value was truncated, meaning we need to retry. Note that + // while `confstr` results don't seem to change for a process, it's unclear + // if this is guaranteed anywhere, so looping does seem required. + while bytes_needed_including_nul > buf.capacity() { + // We write into the spare capacity of `buf`. This lets us avoid + // changing buf's `len`, which both simplifies `reserve` computation, + // allows working with `Vec` instead of `Vec>`, and + // may avoid a copy, since the Vec knows that none of the bytes are needed + // when reallocating (well, in theory anyway). + buf.reserve(bytes_needed_including_nul); + // `confstr` returns + // - 0 in the case of errors: we break and return an error. + // - The number of bytes written, iff the provided buffer is enough to + // hold the entire value: we break and return the data in `buf`. + // - Otherwise, the number of bytes needed (including nul): we go + // through the loop again. + bytes_needed_including_nul = + unsafe { libc::confstr(key, buf.as_mut_ptr().cast(), buf.capacity()) }; + } + // `confstr` returns 0 in the case of an error. + if bytes_needed_including_nul == 0 { + return Err(io::Error::last_os_error()); + } + // Safety: `confstr(..., buf.as_mut_ptr(), buf.capacity())` returned a + // non-zero value, meaning `bytes_needed_including_nul` bytes were + // initialized. + unsafe { + buf.set_len(bytes_needed_including_nul); + // Remove the NUL-terminator. + let last_byte = buf.pop(); + // ... and smoke-check that it *was* a NUL-terminator. + assert_eq!(last_byte, Some(0), "`confstr` provided a string which wasn't nul-terminated"); + }; + Ok(OsString::from_vec(buf)) +} + +#[cfg(all(target_os = "linux", target_env = "gnu"))] +pub fn glibc_version() -> Option<(usize, usize)> { + use crate::ffi::CStr; + + unsafe extern "C" { + fn gnu_get_libc_version() -> *const libc::c_char; + } + let version_cstr = unsafe { CStr::from_ptr(gnu_get_libc_version()) }; + if let Ok(version_str) = version_cstr.to_str() { + parse_glibc_version(version_str) + } else { + None + } +} + +/// Returns Some((major, minor)) if the string is a valid "x.y" version, +/// ignoring any extra dot-separated parts. Otherwise return None. +#[cfg(all(target_os = "linux", target_env = "gnu"))] +fn parse_glibc_version(version: &str) -> Option<(usize, usize)> { + let mut parsed_ints = version.split('.').map(str::parse::).fuse(); + match (parsed_ints.next(), parsed_ints.next()) { + (Some(Ok(major)), Some(Ok(minor))) => Some((major, minor)), + _ => None, + } +} diff --git a/library/std/src/sys/pal/unix/os/tests.rs b/library/std/src/sys/pal/unix/conf/tests.rs similarity index 100% rename from library/std/src/sys/pal/unix/os/tests.rs rename to library/std/src/sys/pal/unix/conf/tests.rs diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 0fbf37fda7fbf..9931b4de0b9bf 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -2,6 +2,7 @@ use crate::io; +pub mod conf; #[cfg(target_os = "fuchsia")] pub mod fuchsia; pub mod futex; diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index d11282682d08d..f69614c2077cd 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -2,9 +2,6 @@ #![allow(unused_imports)] // lots of cfg code here -#[cfg(test)] -mod tests; - use libc::{c_char, c_int, c_void}; use crate::ffi::{CStr, OsStr, OsString}; @@ -396,75 +393,15 @@ pub fn current_exe() -> io::Result { if !path.is_absolute() { getcwd().map(|cwd| cwd.join(path)) } else { Ok(path) } } -#[cfg(not(target_os = "espidf"))] -pub fn page_size() -> usize { - unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } -} - -// Returns the value for [`confstr(key, ...)`][posix_confstr]. Currently only -// used on Darwin, but should work on any unix (in case we need to get -// `_CS_PATH` or `_CS_V[67]_ENV` in the future). -// -// [posix_confstr]: -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/confstr.html -// -// FIXME: Support `confstr` in Miri. -#[cfg(all(target_vendor = "apple", not(miri)))] -fn confstr(key: c_int, size_hint: Option) -> io::Result { - let mut buf: Vec = Vec::with_capacity(0); - let mut bytes_needed_including_nul = size_hint - .unwrap_or_else(|| { - // Treat "None" as "do an extra call to get the length". In theory - // we could move this into the loop below, but it's hard to do given - // that it isn't 100% clear if it's legal to pass 0 for `len` when - // the buffer isn't null. - unsafe { libc::confstr(key, core::ptr::null_mut(), 0) } - }) - .max(1); - // If the value returned by `confstr` is greater than the len passed into - // it, then the value was truncated, meaning we need to retry. Note that - // while `confstr` results don't seem to change for a process, it's unclear - // if this is guaranteed anywhere, so looping does seem required. - while bytes_needed_including_nul > buf.capacity() { - // We write into the spare capacity of `buf`. This lets us avoid - // changing buf's `len`, which both simplifies `reserve` computation, - // allows working with `Vec` instead of `Vec>`, and - // may avoid a copy, since the Vec knows that none of the bytes are needed - // when reallocating (well, in theory anyway). - buf.reserve(bytes_needed_including_nul); - // `confstr` returns - // - 0 in the case of errors: we break and return an error. - // - The number of bytes written, iff the provided buffer is enough to - // hold the entire value: we break and return the data in `buf`. - // - Otherwise, the number of bytes needed (including nul): we go - // through the loop again. - bytes_needed_including_nul = - unsafe { libc::confstr(key, buf.as_mut_ptr().cast::(), buf.capacity()) }; - } - // `confstr` returns 0 in the case of an error. - if bytes_needed_including_nul == 0 { - return Err(io::Error::last_os_error()); - } - // Safety: `confstr(..., buf.as_mut_ptr(), buf.capacity())` returned a - // non-zero value, meaning `bytes_needed_including_nul` bytes were - // initialized. - unsafe { - buf.set_len(bytes_needed_including_nul); - // Remove the NUL-terminator. - let last_byte = buf.pop(); - // ... and smoke-check that it *was* a NUL-terminator. - assert_eq!(last_byte, Some(0), "`confstr` provided a string which wasn't nul-terminated"); - }; - Ok(OsString::from_vec(buf)) -} - #[cfg(all(target_vendor = "apple", not(miri)))] fn darwin_temp_dir() -> PathBuf { - confstr(libc::_CS_DARWIN_USER_TEMP_DIR, Some(64)).map(PathBuf::from).unwrap_or_else(|_| { - // It failed for whatever reason (there are several possible reasons), - // so return the global one. - PathBuf::from("/tmp") - }) + crate::sys::pal::conf::confstr(libc::_CS_DARWIN_USER_TEMP_DIR, Some(64)) + .map(PathBuf::from) + .unwrap_or_else(|_| { + // It failed for whatever reason (there are several possible reasons), + // so return the global one. + PathBuf::from("/tmp") + }) } pub fn temp_dir() -> PathBuf { @@ -532,27 +469,3 @@ pub fn home_dir() -> Option { } } } - -#[cfg(all(target_os = "linux", target_env = "gnu"))] -pub fn glibc_version() -> Option<(usize, usize)> { - unsafe extern "C" { - fn gnu_get_libc_version() -> *const libc::c_char; - } - let version_cstr = unsafe { CStr::from_ptr(gnu_get_libc_version()) }; - if let Ok(version_str) = version_cstr.to_str() { - parse_glibc_version(version_str) - } else { - None - } -} - -// Returns Some((major, minor)) if the string is a valid "x.y" version, -// ignoring any extra dot-separated parts. Otherwise return None. -#[cfg(all(target_os = "linux", target_env = "gnu"))] -fn parse_glibc_version(version: &str) -> Option<(usize, usize)> { - let mut parsed_ints = version.split('.').map(str::parse::).fuse(); - match (parsed_ints.next(), parsed_ints.next()) { - (Some(Ok(major)), Some(Ok(minor))) => Some((major, minor)), - _ => None, - } -} diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 040fda75a5080..632f619655b37 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -70,7 +70,7 @@ mod imp { use super::thread_info::{delete_current_info, set_current_info, with_current_info}; use crate::ops::Range; use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, AtomicUsize, Ordering}; - use crate::sys::pal::unix::os; + use crate::sys::pal::unix::conf; use crate::{io, mem, ptr}; // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages @@ -150,7 +150,7 @@ mod imp { /// Must be called only once #[forbid(unsafe_op_in_unsafe_fn)] pub unsafe fn init() { - PAGE_SIZE.store(os::page_size(), Ordering::Relaxed); + PAGE_SIZE.store(conf::page_size(), Ordering::Relaxed); let mut guard_page_range = unsafe { install_main_guard() }; diff --git a/library/std/src/sys/pal/wasi/conf.rs b/library/std/src/sys/pal/wasi/conf.rs new file mode 100644 index 0000000000000..cf76ae733358e --- /dev/null +++ b/library/std/src/sys/pal/wasi/conf.rs @@ -0,0 +1,4 @@ +#[allow(dead_code)] +pub fn page_size() -> usize { + unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } +} diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs index 9b49db9af6b05..efe4af6a0c622 100644 --- a/library/std/src/sys/pal/wasi/mod.rs +++ b/library/std/src/sys/pal/wasi/mod.rs @@ -4,6 +4,8 @@ //! OS level functionality for WASI. Currently this includes both WASIp1 and //! WASIp2. +pub mod conf; + #[allow(unused)] #[path = "../wasm/atomics/futex.rs"] pub mod futex; diff --git a/library/std/src/sys/pal/wasi/os.rs b/library/std/src/sys/pal/wasi/os.rs index c8f3ddf692bcc..b0148b265e32e 100644 --- a/library/std/src/sys/pal/wasi/os.rs +++ b/library/std/src/sys/pal/wasi/os.rs @@ -89,11 +89,6 @@ pub fn current_exe() -> io::Result { unsupported() } -#[allow(dead_code)] -pub fn page_size() -> usize { - unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } -} - pub fn temp_dir() -> PathBuf { panic!("no filesystem on wasm") } diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs index 82ff94fb1e030..a68be2543bc23 100644 --- a/library/std/src/sys/process/unix/unix.rs +++ b/library/std/src/sys/process/unix/unix.rs @@ -537,7 +537,7 @@ impl Command { // Only glibc 2.24+ posix_spawn() supports returning ENOENT directly. #[cfg(all(target_os = "linux", target_env = "gnu"))] { - if let Some(version) = sys::os::glibc_version() { + if let Some(version) = sys::pal::conf::glibc_version() { if version < (2, 24) { return Ok(None); } diff --git a/library/std/src/sys/thread/teeos.rs b/library/std/src/sys/thread/teeos.rs index 5e71f757eaa4b..aa679e780c18c 100644 --- a/library/std/src/sys/thread/teeos.rs +++ b/library/std/src/sys/thread/teeos.rs @@ -1,5 +1,5 @@ use crate::mem::{self, ManuallyDrop}; -use crate::sys::os; +use crate::sys::pal::conf; use crate::thread::ThreadInit; use crate::time::Duration; use crate::{cmp, io, ptr}; @@ -52,7 +52,7 @@ impl Thread { // multiple of the system page size. Because it's definitely // >= PTHREAD_STACK_MIN, it must be an alignment issue. // Round up to the nearest page and try again. - let page_size = os::page_size(); + let page_size = conf::page_size(); let stack_size = (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1); assert_eq!(unsafe { libc::pthread_attr_setstacksize(&mut attr, stack_size) }, 0); diff --git a/library/std/src/sys/thread/unix.rs b/library/std/src/sys/thread/unix.rs index 22f9bfef5a383..5d4eabc226ed7 100644 --- a/library/std/src/sys/thread/unix.rs +++ b/library/std/src/sys/thread/unix.rs @@ -76,7 +76,7 @@ impl Thread { // multiple of the system page size. Because it's definitely // >= PTHREAD_STACK_MIN, it must be an alignment issue. // Round up to the nearest page and try again. - let page_size = sys::os::page_size(); + let page_size = sys::pal::conf::page_size(); let stack_size = (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1); From 866975f8a88eea5e8d6d92541e65dcd33dd9c684 Mon Sep 17 00:00:00 2001 From: joboet Date: Mon, 2 Mar 2026 12:58:59 +0100 Subject: [PATCH 6/8] std: move SOLID error converting out of `pal::os` --- library/std/src/sys/pal/solid/error.rs | 8 ++++++++ library/std/src/sys/pal/solid/os.rs | 10 +--------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/library/std/src/sys/pal/solid/error.rs b/library/std/src/sys/pal/solid/error.rs index 3e85cdb3c1d9f..a737abcc04eda 100644 --- a/library/std/src/sys/pal/solid/error.rs +++ b/library/std/src/sys/pal/solid/error.rs @@ -3,6 +3,14 @@ use super::{abi, itron}; use crate::io; use crate::sys::net; +// SOLID directly maps `errno`s to μITRON error codes. +impl SolidError { + #[inline] + pub(crate) fn as_io_error(self) -> crate::io::Error { + crate::io::Error::from_raw_os_error(self.as_raw()) + } +} + /// Describe the specified SOLID error code. Returns `None` if it's an /// undefined error code. /// diff --git a/library/std/src/sys/pal/solid/os.rs b/library/std/src/sys/pal/solid/os.rs index 4a07d240d2e66..79bb91b179969 100644 --- a/library/std/src/sys/pal/solid/os.rs +++ b/library/std/src/sys/pal/solid/os.rs @@ -1,16 +1,8 @@ -use super::{itron, unsupported}; +use super::unsupported; use crate::ffi::{OsStr, OsString}; use crate::path::{self, PathBuf}; use crate::{fmt, io}; -// `solid` directly maps `errno`s to μITRON error codes. -impl itron::error::ItronError { - #[inline] - pub(crate) fn as_io_error(self) -> crate::io::Error { - crate::io::Error::from_raw_os_error(self.as_raw()) - } -} - pub fn getcwd() -> io::Result { unsupported() } From d31aecff6a3a7a5272e8b07a72f8cebc8ec1f517 Mon Sep 17 00:00:00 2001 From: joboet Date: Wed, 4 Mar 2026 16:56:29 +0100 Subject: [PATCH 7/8] std: reorganize some WASI helpers --- library/std/src/sys/env/wasi.rs | 11 +++-- library/std/src/sys/pal/wasi/helpers.rs | 11 ----- library/std/src/sys/pal/wasi/mod.rs | 60 ++++++++++++++++++------- library/std/src/sys/pal/wasi/os.rs | 43 ------------------ 4 files changed, 52 insertions(+), 73 deletions(-) delete mode 100644 library/std/src/sys/pal/wasi/helpers.rs diff --git a/library/std/src/sys/env/wasi.rs b/library/std/src/sys/env/wasi.rs index c970aac182604..6b892376fd0cf 100644 --- a/library/std/src/sys/env/wasi.rs +++ b/library/std/src/sys/env/wasi.rs @@ -1,11 +1,16 @@ use core::slice::memchr; pub use super::common::Env; -use crate::ffi::{CStr, OsStr, OsString}; +use crate::ffi::{CStr, OsStr, OsString, c_char}; use crate::io; use crate::os::wasi::prelude::*; use crate::sys::helpers::run_with_cstr; -use crate::sys::pal::os::{cvt, libc}; +use crate::sys::pal::cvt; + +// This is not available yet in libc. +unsafe extern "C" { + fn __wasilibc_get_environ() -> *mut *mut c_char; +} cfg_select! { target_feature = "atomics" => { @@ -37,7 +42,7 @@ pub fn env() -> Env { // Use `__wasilibc_get_environ` instead of `environ` here so that we // don't require wasi-libc to eagerly initialize the environment // variables. - let mut environ = libc::__wasilibc_get_environ(); + let mut environ = __wasilibc_get_environ(); let mut result = Vec::new(); if !environ.is_null() { diff --git a/library/std/src/sys/pal/wasi/helpers.rs b/library/std/src/sys/pal/wasi/helpers.rs deleted file mode 100644 index 4f2eb1148f0b3..0000000000000 --- a/library/std/src/sys/pal/wasi/helpers.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![forbid(unsafe_op_in_unsafe_fn)] - -pub fn abort_internal() -> ! { - unsafe { libc::abort() } -} - -#[inline] -#[cfg(target_env = "p1")] -pub(crate) fn err2io(err: wasi::Errno) -> crate::io::Error { - crate::io::Error::from_raw_os_error(err.raw().into()) -} diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs index efe4af6a0c622..66d91078a5d54 100644 --- a/library/std/src/sys/pal/wasi/mod.rs +++ b/library/std/src/sys/pal/wasi/mod.rs @@ -4,36 +4,64 @@ //! OS level functionality for WASI. Currently this includes both WASIp1 and //! WASIp2. -pub mod conf; +use crate::io; +pub mod conf; #[allow(unused)] #[path = "../wasm/atomics/futex.rs"] pub mod futex; - pub mod os; pub mod stack_overflow; #[path = "../unix/time.rs"] pub mod time; +#[cfg(not(target_env = "p1"))] +mod cabi_realloc; + #[path = "../unsupported/common.rs"] #[deny(unsafe_op_in_unsafe_fn)] -#[allow(unused)] +#[expect(dead_code)] mod common; +pub use common::{cleanup, init, unsupported}; -pub use common::*; +pub fn abort_internal() -> ! { + unsafe { libc::abort() } +} -mod helpers; +#[inline] +#[cfg(target_env = "p1")] +pub(crate) fn err2io(err: wasi::Errno) -> crate::io::Error { + crate::io::Error::from_raw_os_error(err.raw().into()) +} -// The following exports are listed individually to work around Rust's glob -// import conflict rules. If we glob export `helpers` and `common` together, -// then the compiler complains about conflicts. +#[doc(hidden)] +pub trait IsMinusOne { + fn is_minus_one(&self) -> bool; +} -pub(crate) use helpers::abort_internal; -#[cfg(target_env = "p1")] -pub(crate) use helpers::err2io; -#[cfg(not(target_env = "p1"))] -pub use os::IsMinusOne; -pub use os::{cvt, cvt_r}; +macro_rules! impl_is_minus_one { + ($($t:ident)*) => ($(impl IsMinusOne for $t { + fn is_minus_one(&self) -> bool { + *self == -1 + } + })*) +} -#[cfg(not(target_env = "p1"))] -mod cabi_realloc; +impl_is_minus_one! { i8 i16 i32 i64 isize } + +pub fn cvt(t: T) -> io::Result { + if t.is_minus_one() { Err(io::Error::last_os_error()) } else { Ok(t) } +} + +pub fn cvt_r(mut f: F) -> io::Result +where + T: IsMinusOne, + F: FnMut() -> T, +{ + loop { + match cvt(f()) { + Err(ref e) if e.is_interrupted() => {} + other => return other, + } + } +} diff --git a/library/std/src/sys/pal/wasi/os.rs b/library/std/src/sys/pal/wasi/os.rs index b0148b265e32e..b1c26acc68cb9 100644 --- a/library/std/src/sys/pal/wasi/os.rs +++ b/library/std/src/sys/pal/wasi/os.rs @@ -8,17 +8,6 @@ use crate::sys::helpers::run_path_with_cstr; use crate::sys::unsupported; use crate::{fmt, io}; -// Add a few symbols not in upstream `libc` just yet. -pub mod libc { - pub use libc::*; - - unsafe extern "C" { - pub fn getcwd(buf: *mut c_char, size: size_t) -> *mut c_char; - pub fn chdir(dir: *const c_char) -> c_int; - pub fn __wasilibc_get_environ() -> *mut *mut c_char; - } -} - pub fn getcwd() -> io::Result { let mut buf = Vec::with_capacity(512); loop { @@ -96,35 +85,3 @@ pub fn temp_dir() -> PathBuf { pub fn home_dir() -> Option { None } - -#[doc(hidden)] -pub trait IsMinusOne { - fn is_minus_one(&self) -> bool; -} - -macro_rules! impl_is_minus_one { - ($($t:ident)*) => ($(impl IsMinusOne for $t { - fn is_minus_one(&self) -> bool { - *self == -1 - } - })*) -} - -impl_is_minus_one! { i8 i16 i32 i64 isize } - -pub fn cvt(t: T) -> io::Result { - if t.is_minus_one() { Err(io::Error::last_os_error()) } else { Ok(t) } -} - -pub fn cvt_r(mut f: F) -> io::Result -where - T: IsMinusOne, - F: FnMut() -> T, -{ - loop { - match cvt(f()) { - Err(ref e) if e.is_interrupted() => {} - other => return other, - } - } -} From 85c4af74fab0893eb375f4e4637d91fb390a9881 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 2 Jul 2025 12:49:45 -0300 Subject: [PATCH 8/8] Rustfmt now support use closures on edition 2018 --- rustfmt.toml | 4 ---- tests/codegen-llvm/ergonomic-clones/closure.rs | 1 - 2 files changed, 5 deletions(-) diff --git a/rustfmt.toml b/rustfmt.toml index 910eea1037986..4750b8541457c 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -55,8 +55,4 @@ ignore = [ # Code automatically generated and included. "compiler/rustc_codegen_gcc/src/intrinsic/archs.rs", "compiler/rustc_codegen_gcc/example", - - # Rustfmt doesn't support use closures yet - "tests/mir-opt/ergonomic-clones/closure.rs", - "tests/codegen-llvm/ergonomic-clones/closure.rs", ] diff --git a/tests/codegen-llvm/ergonomic-clones/closure.rs b/tests/codegen-llvm/ergonomic-clones/closure.rs index b6fc81726419a..15a2dd2baebfe 100644 --- a/tests/codegen-llvm/ergonomic-clones/closure.rs +++ b/tests/codegen-llvm/ergonomic-clones/closure.rs @@ -1,7 +1,6 @@ //@ compile-flags: -C no-prepopulate-passes -Copt-level=0 -Zmir-opt-level=0 #![crate_type = "lib"] - #![feature(ergonomic_clones)] #![allow(incomplete_features)]