diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 324912fbe6a63..2775e61902fbb 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -2698,8 +2698,7 @@ impl HumanEmitter { [SubstitutionHighlight { start: 0, end }] if *end == line_to_add.len() => { buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition); } - [] => { - // FIXME: needed? Doesn't get exercised in any test. + [] | [SubstitutionHighlight { start: 0, end: 0 }] => { self.draw_col_separator_no_space(buffer, *row_num, max_line_num_len + 1); } _ => { diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index d1426ff55fbd6..484a7d4217211 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1111,7 +1111,7 @@ impl Input { } } -#[derive(Clone, Hash, Debug, HashStable_Generic, PartialEq, Encodable, Decodable)] +#[derive(Clone, Hash, Debug, HashStable_Generic, PartialEq, Eq, Encodable, Decodable)] pub enum OutFileName { Real(PathBuf), Stdout, diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 87ad5b0ce30e6..cd7e920f76620 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -142,6 +142,7 @@ #![feature(slice_range)] #![feature(std_internals)] #![feature(str_internals)] +#![feature(string_replace_in_place)] #![feature(temporary_niche_types)] #![feature(trusted_fused)] #![feature(trusted_len)] diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index d5ea5dc1e79e4..31743b0e35b24 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -2090,6 +2090,67 @@ impl String { unsafe { self.as_mut_vec() }.splice((start, end), replace_with.bytes()); } + /// Replaces the leftmost occurrence of a pattern with another string, in-place. + /// + /// This method can be preferred over [`string = string.replacen(..., 1);`][replacen], + /// as it can use the `String`'s existing capacity to prevent a reallocation if + /// sufficient space is available. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(string_replace_in_place)] + /// + /// let mut s = String::from("Test Results: ❌❌❌"); + /// + /// // Replace the leftmost ❌ with a ✅ + /// s.replace_first('❌', "✅"); + /// assert_eq!(s, "Test Results: ✅❌❌"); + /// ``` + /// + /// [replacen]: ../../std/primitive.str.html#method.replacen + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "string_replace_in_place", issue = "147949")] + pub fn replace_first(&mut self, from: P, to: &str) { + let range = match self.match_indices(from).next() { + Some((start, match_str)) => start..start + match_str.len(), + None => return, + }; + + self.replace_range(range, to); + } + + /// Replaces the rightmost occurrence of a pattern with another string, in-place. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(string_replace_in_place)] + /// + /// let mut s = String::from("Test Results: ❌❌❌"); + /// + /// // Replace the rightmost ❌ with a ✅ + /// s.replace_last('❌', "✅"); + /// assert_eq!(s, "Test Results: ❌❌✅"); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "string_replace_in_place", issue = "147949")] + pub fn replace_last(&mut self, from: P, to: &str) + where + for<'a> P::Searcher<'a>: core::str::pattern::ReverseSearcher<'a>, + { + let range = match self.rmatch_indices(from).next() { + Some((start, match_str)) => start..start + match_str.len(), + None => return, + }; + + self.replace_range(range, to); + } + /// Converts this `String` into a [Box]<[str]>. /// /// Before doing the conversion, this method discards excess capacity like [`shrink_to_fit`]. diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index 49fb21ef5f3ac..f94f92397bb18 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -36,6 +36,7 @@ #![feature(local_waker)] #![feature(str_as_str)] #![feature(strict_provenance_lints)] +#![feature(string_replace_in_place)] #![feature(vec_deque_pop_if)] #![feature(vec_deque_truncate_front)] #![feature(unique_rc_arc)] diff --git a/library/alloctests/tests/string.rs b/library/alloctests/tests/string.rs index d996c55f94660..ecc5b9dc82ed0 100644 --- a/library/alloctests/tests/string.rs +++ b/library/alloctests/tests/string.rs @@ -719,6 +719,40 @@ fn test_replace_range_evil_end_bound() { assert_eq!(Ok(""), str::from_utf8(s.as_bytes())); } +#[test] +fn test_replace_first() { + let mut s = String::from("~ First ❌ Middle ❌ Last ❌ ~"); + s.replace_first("❌", "✅✅"); + assert_eq!(s, "~ First ✅✅ Middle ❌ Last ❌ ~"); + s.replace_first("🦀", "😳"); + assert_eq!(s, "~ First ✅✅ Middle ❌ Last ❌ ~"); + + let mut s = String::from("❌"); + s.replace_first('❌', "✅✅"); + assert_eq!(s, "✅✅"); + + let mut s = String::from(""); + s.replace_first('🌌', "❌"); + assert_eq!(s, ""); +} + +#[test] +fn test_replace_last() { + let mut s = String::from("~ First ❌ Middle ❌ Last ❌ ~"); + s.replace_last("❌", "✅✅"); + assert_eq!(s, "~ First ❌ Middle ❌ Last ✅✅ ~"); + s.replace_last("🦀", "😳"); + assert_eq!(s, "~ First ❌ Middle ❌ Last ✅✅ ~"); + + let mut s = String::from("❌"); + s.replace_last::('❌', "✅✅"); + assert_eq!(s, "✅✅"); + + let mut s = String::from(""); + s.replace_last::('🌌', "❌"); + assert_eq!(s, ""); +} + #[test] fn test_extend_ref() { let mut a = "foo".to_string(); diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index c223eee95b5f5..f21ed51606f6d 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -365,6 +365,20 @@ pub trait CommandExt: Sealed { /// [1]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa #[unstable(feature = "windows_process_extensions_startupinfo", issue = "141010")] fn startupinfo_force_feedback(&mut self, enabled: Option) -> &mut process::Command; + + /// If this flag is set to `true`, each inheritable handle in the calling + /// process is inherited by the new process. If the flag is `false`, the + /// handles are not inherited. + /// + /// The default value for this flag is `true`. + /// + /// **Note** that inherited handles have the same value and access rights + /// as the original handles. For additional discussion of inheritable handles, + /// see the [Remarks][1] section of the `CreateProcessW` documentation. + /// + /// [1]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw#remarks + #[unstable(feature = "windows_process_extensions_inherit_handles", issue = "146407")] + fn inherit_handles(&mut self, inherit_handles: bool) -> &mut process::Command; } #[stable(feature = "windows_process_extensions", since = "1.16.0")] @@ -421,6 +435,11 @@ impl CommandExt for process::Command { self.as_inner_mut().startupinfo_force_feedback(enabled); self } + + fn inherit_handles(&mut self, inherit_handles: bool) -> &mut process::Command { + self.as_inner_mut().inherit_handles(inherit_handles); + self + } } #[unstable(feature = "windows_process_extensions_main_thread_handle", issue = "96723")] diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index dd1059fe04a2d..9d303b8d65b39 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -2,10 +2,6 @@ use crate::io::ErrorKind; -#[cfg(not(target_os = "espidf"))] -#[macro_use] -pub mod weak; - #[cfg(target_os = "fuchsia")] pub mod fuchsia; pub mod futex; @@ -19,6 +15,7 @@ pub mod stack_overflow; pub mod sync; pub mod thread_parking; pub mod time; +pub mod weak; #[cfg(target_os = "espidf")] pub fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {} diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 28b05d8a68a8a..062f031189fc4 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -69,7 +69,6 @@ mod imp { use super::Handler; use super::thread_info::{delete_current_info, set_current_info, with_current_info}; use crate::ops::Range; - use crate::sync::OnceLock; use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, AtomicUsize, Ordering}; use crate::sys::pal::unix::os; use crate::{io, mem, ptr}; @@ -406,6 +405,10 @@ mod imp { } else if cfg!(all(target_os = "linux", target_env = "musl")) { install_main_guard_linux_musl(page_size) } else if cfg!(target_os = "freebsd") { + #[cfg(not(target_os = "freebsd"))] + return None; + // The FreeBSD code cannot be checked on non-BSDs. + #[cfg(target_os = "freebsd")] install_main_guard_freebsd(page_size) } else if cfg!(any(target_os = "netbsd", target_os = "openbsd")) { install_main_guard_bsds(page_size) @@ -442,6 +445,7 @@ mod imp { } #[forbid(unsafe_op_in_unsafe_fn)] + #[cfg(target_os = "freebsd")] unsafe fn install_main_guard_freebsd(page_size: usize) -> Option> { // FreeBSD's stack autogrows, and optionally includes a guard page // at the bottom. If we try to remap the bottom of the stack @@ -453,38 +457,23 @@ mod imp { // by the security.bsd.stack_guard_page sysctl. // By default it is 1, checking once is enough since it is // a boot time config value. - static PAGES: OnceLock = OnceLock::new(); + static PAGES: crate::sync::OnceLock = crate::sync::OnceLock::new(); let pages = PAGES.get_or_init(|| { - use crate::sys::weak::dlsym; - dlsym!( - fn sysctlbyname( - name: *const libc::c_char, - oldp: *mut libc::c_void, - oldlenp: *mut libc::size_t, - newp: *const libc::c_void, - newlen: libc::size_t, - ) -> libc::c_int; - ); let mut guard: usize = 0; let mut size = size_of_val(&guard); let oid = c"security.bsd.stack_guard_page"; - match sysctlbyname.get() { - Some(fcn) - if unsafe { - fcn( - oid.as_ptr(), - (&raw mut guard).cast(), - &raw mut size, - ptr::null_mut(), - 0, - ) == 0 - } => - { - guard - } - _ => 1, - } + + let r = unsafe { + libc::sysctlbyname( + oid.as_ptr(), + (&raw mut guard).cast(), + &raw mut size, + ptr::null_mut(), + 0, + ) + }; + if r == 0 { guard } else { 1 } }); Some(guardaddr..guardaddr + pages * page_size) } diff --git a/library/std/src/sys/pal/unix/weak.rs b/library/std/src/sys/pal/unix/weak.rs deleted file mode 100644 index a3b980a3f3d85..0000000000000 --- a/library/std/src/sys/pal/unix/weak.rs +++ /dev/null @@ -1,225 +0,0 @@ -//! Support for "weak linkage" to symbols on Unix -//! -//! Some I/O operations we do in std require newer versions of OSes but we need -//! to maintain binary compatibility with older releases for now. In order to -//! use the new functionality when available we use this module for detection. -//! -//! One option to use here is weak linkage, but that is unfortunately only -//! really workable with ELF. Otherwise, use dlsym to get the symbol value at -//! runtime. This is also done for compatibility with older versions of glibc, -//! and to avoid creating dependencies on GLIBC_PRIVATE symbols. It assumes that -//! we've been dynamically linked to the library the symbol comes from, but that -//! is currently always the case for things like libpthread/libc. -//! -//! A long time ago this used weak linkage for the __pthread_get_minstack -//! symbol, but that caused Debian to detect an unnecessarily strict versioned -//! dependency on libc6 (#23628) because it is GLIBC_PRIVATE. We now use `dlsym` -//! for a runtime lookup of that symbol to avoid the ELF versioned dependency. - -// There are a variety of `#[cfg]`s controlling which targets are involved in -// each instance of `weak!` and `syscall!`. Rather than trying to unify all of -// that, we'll just allow that some unix targets don't use this module at all. -#![allow(dead_code, unused_macros)] -#![forbid(unsafe_op_in_unsafe_fn)] - -use crate::ffi::{CStr, c_char, c_void}; -use crate::marker::{FnPtr, PhantomData}; -use crate::sync::atomic::{Atomic, AtomicPtr, Ordering}; -use crate::{mem, ptr}; - -// We currently only test `dlsym!`, but that doesn't work on all platforms, so -// we gate the tests to only the platforms where it is actually used. -// -// FIXME(joboet): add more tests, reorganise the whole module and get rid of -// `#[allow(dead_code, unused_macros)]`. -#[cfg(any( - target_vendor = "apple", - all(target_os = "linux", target_env = "gnu"), - target_os = "freebsd", -))] -#[cfg(test)] -mod tests; - -// We can use true weak linkage on ELF targets. -#[cfg(all(unix, not(target_vendor = "apple")))] -pub(crate) macro weak { - (fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;) => ( - let ref $name: ExternWeak $ret> = { - unsafe extern "C" { - #[linkage = "extern_weak"] - static $name: Option $ret>; - } - #[allow(unused_unsafe)] - ExternWeak::new(unsafe { $name }) - }; - ) -} - -// On non-ELF targets, use the dlsym approximation of weak linkage. -#[cfg(target_vendor = "apple")] -pub(crate) use self::dlsym as weak; - -pub(crate) struct ExternWeak { - weak_ptr: Option, -} - -impl ExternWeak { - #[inline] - pub(crate) fn new(weak_ptr: Option) -> Self { - ExternWeak { weak_ptr } - } - - #[inline] - pub(crate) fn get(&self) -> Option { - self.weak_ptr - } -} - -pub(crate) macro dlsym { - (fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;) => ( - dlsym!( - #[link_name = stringify!($name)] - fn $name($($param : $t),*) -> $ret; - ); - ), - ( - #[link_name = $sym:expr] - fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty; - ) => ( - static DLSYM: DlsymWeak $ret> = { - let Ok(name) = CStr::from_bytes_with_nul(concat!($sym, '\0').as_bytes()) else { - panic!("symbol name may not contain NUL") - }; - - // SAFETY: Whoever calls the function pointer returned by `get()` - // is responsible for ensuring that the signature is correct. Just - // like with extern blocks, this is syntactically enforced by making - // the function pointer be unsafe. - unsafe { DlsymWeak::new(name) } - }; - - let $name = &DLSYM; - ) -} - -pub(crate) struct DlsymWeak { - /// A pointer to the nul-terminated name of the symbol. - // Use a pointer instead of `&'static CStr` to save space. - name: *const c_char, - func: Atomic<*mut libc::c_void>, - _marker: PhantomData, -} - -impl DlsymWeak { - /// # Safety - /// - /// If the signature of `F` does not match the signature of the symbol (if - /// it exists), calling the function pointer returned by `get()` is - /// undefined behaviour. - pub(crate) const unsafe fn new(name: &'static CStr) -> Self { - DlsymWeak { - name: name.as_ptr(), - func: AtomicPtr::new(ptr::without_provenance_mut(1)), - _marker: PhantomData, - } - } - - #[inline] - pub(crate) fn get(&self) -> Option { - // The caller is presumably going to read through this value - // (by calling the function we've dlsymed). This means we'd - // need to have loaded it with at least C11's consume - // ordering in order to be guaranteed that the data we read - // from the pointer isn't from before the pointer was - // stored. Rust has no equivalent to memory_order_consume, - // so we use an acquire load (sorry, ARM). - // - // Now, in practice this likely isn't needed even on CPUs - // where relaxed and consume mean different things. The - // symbols we're loading are probably present (or not) at - // init, and even if they aren't the runtime dynamic loader - // is extremely likely have sufficient barriers internally - // (possibly implicitly, for example the ones provided by - // invoking `mprotect`). - // - // That said, none of that's *guaranteed*, so we use acquire. - match self.func.load(Ordering::Acquire) { - func if func.addr() == 1 => self.initialize(), - func if func.is_null() => None, - // SAFETY: - // `func` is not null and `F` implements `FnPtr`, thus this - // transmutation is well-defined. It is the responsibility of the - // creator of this `DlsymWeak` to ensure that calling the resulting - // function pointer does not result in undefined behaviour (though - // the `dlsym!` macro delegates this responsibility to the caller - // of the function by using `unsafe` function pointers). - // FIXME: use `transmute` once it stops complaining about generics. - func => Some(unsafe { mem::transmute_copy::<*mut c_void, F>(&func) }), - } - } - - // Cold because it should only happen during first-time initialization. - #[cold] - fn initialize(&self) -> Option { - // SAFETY: `self.name` was created from a `&'static CStr` and is - // therefore a valid C string pointer. - let val = unsafe { libc::dlsym(libc::RTLD_DEFAULT, self.name) }; - // This synchronizes with the acquire load in `get`. - self.func.store(val, Ordering::Release); - - if val.is_null() { - None - } else { - // SAFETY: see the comment in `get`. - // FIXME: use `transmute` once it stops complaining about generics. - Some(unsafe { mem::transmute_copy::<*mut libc::c_void, F>(&val) }) - } - } -} - -unsafe impl Send for DlsymWeak {} -unsafe impl Sync for DlsymWeak {} - -#[cfg(not(any(target_os = "linux", target_os = "android")))] -pub(crate) macro syscall { - (fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;) => ( - unsafe fn $name($($param: $t),*) -> $ret { - weak!(fn $name($($param: $t),*) -> $ret;); - - if let Some(fun) = $name.get() { - unsafe { fun($($param),*) } - } else { - super::os::set_errno(libc::ENOSYS); - -1 - } - } - ) -} - -#[cfg(any(target_os = "linux", target_os = "android"))] -pub(crate) macro syscall { - ( - fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty; - ) => ( - unsafe fn $name($($param: $t),*) -> $ret { - weak!(fn $name($($param: $t),*) -> $ret;); - - // Use a weak symbol from libc when possible, allowing `LD_PRELOAD` - // interposition, but if it's not found just use a raw syscall. - if let Some(fun) = $name.get() { - unsafe { fun($($param),*) } - } else { - unsafe { libc::syscall(libc::${concat(SYS_, $name)}, $($param),*) as $ret } - } - } - ) -} - -#[cfg(any(target_os = "linux", target_os = "android"))] -pub(crate) macro raw_syscall { - (fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;) => ( - unsafe fn $name($($param: $t),*) -> $ret { - unsafe { libc::syscall(libc::${concat(SYS_, $name)}, $($param),*) as $ret } - } - ) -} diff --git a/library/std/src/sys/pal/unix/weak/dlsym.rs b/library/std/src/sys/pal/unix/weak/dlsym.rs new file mode 100644 index 0000000000000..4967b93cc52b5 --- /dev/null +++ b/library/std/src/sys/pal/unix/weak/dlsym.rs @@ -0,0 +1,104 @@ +use crate::ffi::{CStr, c_char, c_void}; +use crate::marker::{FnPtr, PhantomData}; +use crate::sync::atomic::{Atomic, AtomicPtr, Ordering}; +use crate::{mem, ptr}; + +#[cfg(test)] +#[path = "./tests.rs"] +mod tests; + +pub(crate) macro weak { + (fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;) => ( + static DLSYM: DlsymWeak $ret> = { + let Ok(name) = CStr::from_bytes_with_nul(concat!(stringify!($name), '\0').as_bytes()) else { + panic!("symbol name may not contain NUL") + }; + + // SAFETY: Whoever calls the function pointer returned by `get()` + // is responsible for ensuring that the signature is correct. Just + // like with extern blocks, this is syntactically enforced by making + // the function pointer be unsafe. + unsafe { DlsymWeak::new(name) } + }; + + let $name = &DLSYM; + ) +} + +pub(crate) struct DlsymWeak { + /// A pointer to the nul-terminated name of the symbol. + // Use a pointer instead of `&'static CStr` to save space. + name: *const c_char, + func: Atomic<*mut libc::c_void>, + _marker: PhantomData, +} + +impl DlsymWeak { + /// # Safety + /// + /// If the signature of `F` does not match the signature of the symbol (if + /// it exists), calling the function pointer returned by `get()` is + /// undefined behaviour. + pub const unsafe fn new(name: &'static CStr) -> Self { + DlsymWeak { + name: name.as_ptr(), + func: AtomicPtr::new(ptr::without_provenance_mut(1)), + _marker: PhantomData, + } + } + + #[inline] + pub fn get(&self) -> Option { + // The caller is presumably going to read through this value + // (by calling the function we've dlsymed). This means we'd + // need to have loaded it with at least C11's consume + // ordering in order to be guaranteed that the data we read + // from the pointer isn't from before the pointer was + // stored. Rust has no equivalent to memory_order_consume, + // so we use an acquire load (sorry, ARM). + // + // Now, in practice this likely isn't needed even on CPUs + // where relaxed and consume mean different things. The + // symbols we're loading are probably present (or not) at + // init, and even if they aren't the runtime dynamic loader + // is extremely likely have sufficient barriers internally + // (possibly implicitly, for example the ones provided by + // invoking `mprotect`). + // + // That said, none of that's *guaranteed*, so we use acquire. + match self.func.load(Ordering::Acquire) { + func if func.addr() == 1 => self.initialize(), + func if func.is_null() => None, + // SAFETY: + // `func` is not null and `F` implements `FnPtr`, thus this + // transmutation is well-defined. It is the responsibility of the + // creator of this `DlsymWeak` to ensure that calling the resulting + // function pointer does not result in undefined behaviour (though + // the `weak!` macro delegates this responsibility to the caller + // of the function by using `unsafe` function pointers). + // FIXME: use `transmute` once it stops complaining about generics. + func => Some(unsafe { mem::transmute_copy::<*mut c_void, F>(&func) }), + } + } + + // Cold because it should only happen during first-time initialization. + #[cold] + fn initialize(&self) -> Option { + // SAFETY: `self.name` was created from a `&'static CStr` and is + // therefore a valid C string pointer. + let val = unsafe { libc::dlsym(libc::RTLD_DEFAULT, self.name) }; + // This synchronizes with the acquire load in `get`. + self.func.store(val, Ordering::Release); + + if val.is_null() { + None + } else { + // SAFETY: see the comment in `get`. + // FIXME: use `transmute` once it stops complaining about generics. + Some(unsafe { mem::transmute_copy::<*mut libc::c_void, F>(&val) }) + } + } +} + +unsafe impl Send for DlsymWeak {} +unsafe impl Sync for DlsymWeak {} diff --git a/library/std/src/sys/pal/unix/weak/mod.rs b/library/std/src/sys/pal/unix/weak/mod.rs new file mode 100644 index 0000000000000..be53f148fc664 --- /dev/null +++ b/library/std/src/sys/pal/unix/weak/mod.rs @@ -0,0 +1,52 @@ +//! Support for "weak linkage" to symbols on Unix +//! +//! Some I/O operations we do in std require newer versions of OSes but we need +//! to maintain binary compatibility with older releases for now. In order to +//! use the new functionality when available we use this module for detection. +//! +//! One option to use here is weak linkage, but that is unfortunately only +//! really workable with ELF. Otherwise, use dlsym to get the symbol value at +//! runtime. This is also done for compatibility with older versions of glibc, +//! and to avoid creating dependencies on GLIBC_PRIVATE symbols. It assumes that +//! we've been dynamically linked to the library the symbol comes from, but that +//! is currently always the case for things like libpthread/libc. +//! +//! A long time ago this used weak linkage for the __pthread_get_minstack +//! symbol, but that caused Debian to detect an unnecessarily strict versioned +//! dependency on libc6 (#23628) because it is GLIBC_PRIVATE. We now use `dlsym` +//! for a runtime lookup of that symbol to avoid the ELF versioned dependency. + +#![forbid(unsafe_op_in_unsafe_fn)] + +cfg_select! { + // On non-ELF targets, use the dlsym approximation of weak linkage. + target_vendor = "apple" => { + mod dlsym; + pub(crate) use dlsym::weak; + } + + // Some targets don't need and support weak linkage at all... + target_os = "espidf" => {} + + // ... but ELF targets support true weak linkage. + _ => { + // There are a variety of `#[cfg]`s controlling which targets are involved in + // each instance of `weak!`. Rather than trying to unify all of + // that, we'll just allow that some unix targets don't use this macro at all. + #[cfg_attr(not(target_os = "linux"), allow(unused_macros, dead_code))] + mod weak_linkage; + #[cfg_attr(not(target_os = "linux"), allow(unused_imports))] + pub(crate) use weak_linkage::weak; + } +} + +// GNU/Linux needs the `dlsym` variant to avoid linking to private glibc symbols. +#[cfg(all(target_os = "linux", target_env = "gnu"))] +mod dlsym; +#[cfg(all(target_os = "linux", target_env = "gnu"))] +pub(crate) use dlsym::weak as dlsym; + +#[cfg(any(target_os = "android", target_os = "linux"))] +mod syscall; +#[cfg(any(target_os = "android", target_os = "linux"))] +pub(crate) use syscall::syscall; diff --git a/library/std/src/sys/pal/unix/weak/syscall.rs b/library/std/src/sys/pal/unix/weak/syscall.rs new file mode 100644 index 0000000000000..f1a60fb01d584 --- /dev/null +++ b/library/std/src/sys/pal/unix/weak/syscall.rs @@ -0,0 +1,19 @@ +use super::weak; + +pub(crate) macro syscall { + ( + fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty; + ) => ( + unsafe fn $name($($param: $t),*) -> $ret { + weak!(fn $name($($param: $t),*) -> $ret;); + + // Use a weak symbol from libc when possible, allowing `LD_PRELOAD` + // interposition, but if it's not found just use a raw syscall. + if let Some(fun) = $name.get() { + unsafe { fun($($param),*) } + } else { + unsafe { libc::syscall(libc::${concat(SYS_, $name)}, $($param),*) as $ret } + } + } + ) +} diff --git a/library/std/src/sys/pal/unix/weak/tests.rs b/library/std/src/sys/pal/unix/weak/tests.rs index d807ba64e3577..90d52095694a5 100644 --- a/library/std/src/sys/pal/unix/weak/tests.rs +++ b/library/std/src/sys/pal/unix/weak/tests.rs @@ -1,30 +1,24 @@ -use super::*; +// This file is included by both implementations of `weak!`. +use super::weak; +use crate::ffi::{CStr, c_char}; #[test] -fn dlsym_existing() { +fn weak_existing() { const TEST_STRING: &'static CStr = c"Ferris!"; // Try to find a symbol that definitely exists. - dlsym! { + weak! { fn strlen(cs: *const c_char) -> usize; } - dlsym! { - #[link_name = "strlen"] - fn custom_name(cs: *const c_char) -> usize; - } - let strlen = strlen.get().unwrap(); assert_eq!(unsafe { strlen(TEST_STRING.as_ptr()) }, TEST_STRING.count_bytes()); - - let custom_name = custom_name.get().unwrap(); - assert_eq!(unsafe { custom_name(TEST_STRING.as_ptr()) }, TEST_STRING.count_bytes()); } #[test] -fn dlsym_missing() { +fn weak_missing() { // Try to find a symbol that definitely does not exist. - dlsym! { + weak! { fn test_symbol_that_does_not_exist() -> i32; } diff --git a/library/std/src/sys/pal/unix/weak/weak_linkage.rs b/library/std/src/sys/pal/unix/weak/weak_linkage.rs new file mode 100644 index 0000000000000..3963f1d89be7c --- /dev/null +++ b/library/std/src/sys/pal/unix/weak/weak_linkage.rs @@ -0,0 +1,32 @@ +#[cfg(test)] +#[path = "./tests.rs"] +mod tests; + +pub(crate) macro weak { + (fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;) => ( + let ref $name: ExternWeak $ret> = { + unsafe extern "C" { + #[linkage = "extern_weak"] + static $name: Option $ret>; + } + #[allow(unused_unsafe)] + ExternWeak::new(unsafe { $name }) + }; + ) +} + +pub(crate) struct ExternWeak { + weak_ptr: Option, +} + +impl ExternWeak { + #[inline] + pub fn new(weak_ptr: Option) -> Self { + ExternWeak { weak_ptr } + } + + #[inline] + pub fn get(&self) -> Option { + self.weak_ptr + } +} diff --git a/library/std/src/sys/process/windows.rs b/library/std/src/sys/process/windows.rs index 1f2001bdc2040..7d58093c54bbf 100644 --- a/library/std/src/sys/process/windows.rs +++ b/library/std/src/sys/process/windows.rs @@ -159,6 +159,7 @@ pub struct Command { startupinfo_fullscreen: bool, startupinfo_untrusted_source: bool, startupinfo_force_feedback: Option, + inherit_handles: bool, } pub enum Stdio { @@ -187,6 +188,7 @@ impl Command { startupinfo_fullscreen: false, startupinfo_untrusted_source: false, startupinfo_force_feedback: None, + inherit_handles: true, } } @@ -252,6 +254,10 @@ impl Command { self.cwd.as_ref().map(Path::new) } + pub fn inherit_handles(&mut self, inherit_handles: bool) { + self.inherit_handles = inherit_handles; + } + pub fn spawn( &mut self, default: Stdio, @@ -310,6 +316,7 @@ impl Command { flags |= c::DETACHED_PROCESS | c::CREATE_NEW_PROCESS_GROUP; } + let inherit_handles = self.inherit_handles as c::BOOL; let (envp, _data) = make_envp(maybe_env)?; let (dirp, _data) = make_dirp(self.cwd.as_ref())?; let mut pi = zeroed_process_information(); @@ -401,7 +408,7 @@ impl Command { cmd_str.as_mut_ptr(), ptr::null_mut(), ptr::null_mut(), - c::TRUE, + inherit_handles, flags, envp, dirp, diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index fada93e1f8304..cf0858810f55f 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -9,8 +9,8 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::DiagCtxtHandle; use rustc_session::config::{ self, CodegenOptions, CrateType, ErrorOutputType, Externs, Input, JsonUnusedExterns, - OptionsTargetModifiers, Sysroot, UnstableOptions, get_cmd_lint_options, nightly_options, - parse_crate_types_from_list, parse_externs, parse_target_triple, + OptionsTargetModifiers, OutFileName, Sysroot, UnstableOptions, get_cmd_lint_options, + nightly_options, parse_crate_types_from_list, parse_externs, parse_target_triple, }; use rustc_session::lint::Level; use rustc_session::search_paths::SearchPath; @@ -314,7 +314,7 @@ pub(crate) enum EmitType { Unversioned, Toolchain, InvocationSpecific, - DepInfo(Option), + DepInfo(Option), } impl FromStr for EmitType { @@ -326,13 +326,11 @@ impl FromStr for EmitType { "toolchain-shared-resources" => Ok(Self::Toolchain), "invocation-specific" => Ok(Self::InvocationSpecific), "dep-info" => Ok(Self::DepInfo(None)), - option => { - if let Some(file) = option.strip_prefix("dep-info=") { - Ok(Self::DepInfo(Some(Path::new(file).into()))) - } else { - Err(()) - } - } + option => match option.strip_prefix("dep-info=") { + Some("-") => Ok(Self::DepInfo(Some(OutFileName::Stdout))), + Some(f) => Ok(Self::DepInfo(Some(OutFileName::Real(f.into())))), + None => Err(()), + }, } } } @@ -342,10 +340,10 @@ impl RenderOptions { self.emit.is_empty() || self.emit.contains(&EmitType::InvocationSpecific) } - pub(crate) fn dep_info(&self) -> Option> { + pub(crate) fn dep_info(&self) -> Option> { for emit in &self.emit { if let EmitType::DepInfo(file) = emit { - return Some(file.as_deref()); + return Some(file.as_ref()); } } None diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 8e10096750be7..44bac8197539b 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -19,7 +19,7 @@ use rustc_lint::{MissingDoc, late_lint_mod}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt}; use rustc_session::config::{ - self, CrateType, ErrorOutputType, Input, OutFileName, OutputType, OutputTypes, ResolveDocLinks, + self, CrateType, ErrorOutputType, Input, OutputType, OutputTypes, ResolveDocLinks, }; pub(crate) use rustc_session::config::{Options, UnstableOptions}; use rustc_session::{Session, lint}; @@ -271,10 +271,7 @@ pub(crate) fn create_config( test, remap_path_prefix, output_types: if let Some(file) = render_options.dep_info() { - OutputTypes::new(&[( - OutputType::DepInfo, - file.map(|f| OutFileName::Real(f.to_path_buf())), - )]) + OutputTypes::new(&[(OutputType::DepInfo, file.cloned())]) } else { OutputTypes::new(&[]) }, diff --git a/src/tools/clippy/tests/ui/derivable_impls.stderr b/src/tools/clippy/tests/ui/derivable_impls.stderr index cd46414cb4a8a..19c7d09512a22 100644 --- a/src/tools/clippy/tests/ui/derivable_impls.stderr +++ b/src/tools/clippy/tests/ui/derivable_impls.stderr @@ -14,7 +14,7 @@ LL | | } help: replace the manual implementation with a derive attribute | LL + #[derive(Default)] -LL ~ struct FooDefault<'a> { +LL | struct FooDefault<'a> { | error: this `impl` can be derived @@ -31,7 +31,7 @@ LL | | } help: replace the manual implementation with a derive attribute | LL + #[derive(Default)] -LL ~ struct TupleDefault(bool, i32, u64); +LL | struct TupleDefault(bool, i32, u64); | error: this `impl` can be derived @@ -48,7 +48,7 @@ LL | | } help: replace the manual implementation with a derive attribute | LL + #[derive(Default)] -LL ~ struct StrDefault<'a>(&'a str); +LL | struct StrDefault<'a>(&'a str); | error: this `impl` can be derived @@ -65,7 +65,7 @@ LL | | } help: replace the manual implementation with a derive attribute | LL + #[derive(Default)] -LL ~ struct Y(u32); +LL | struct Y(u32); | error: this `impl` can be derived @@ -82,7 +82,7 @@ LL | | } help: replace the manual implementation with a derive attribute | LL + #[derive(Default)] -LL ~ struct WithoutSelfCurly { +LL | struct WithoutSelfCurly { | error: this `impl` can be derived @@ -99,7 +99,7 @@ LL | | } help: replace the manual implementation with a derive attribute | LL + #[derive(Default)] -LL ~ struct WithoutSelfParan(bool); +LL | struct WithoutSelfParan(bool); | error: this `impl` can be derived @@ -115,7 +115,7 @@ LL | | } help: replace the manual implementation with a derive attribute | LL + #[derive(Default)] -LL ~ pub struct DirectDefaultDefaultCall { +LL | pub struct DirectDefaultDefaultCall { | error: this `impl` can be derived @@ -131,7 +131,7 @@ LL | | } help: replace the manual implementation with a derive attribute | LL + #[derive(Default)] -LL ~ pub struct EquivalentToDefaultDefaultCallVec { +LL | pub struct EquivalentToDefaultDefaultCallVec { | error: this `impl` can be derived @@ -147,7 +147,7 @@ LL | | } help: replace the manual implementation with a derive attribute | LL + #[derive(Default)] -LL ~ pub struct EquivalentToDefaultDefaultCallLocal { +LL | pub struct EquivalentToDefaultDefaultCallLocal { | error: this `impl` can be derived @@ -164,7 +164,7 @@ LL | | } help: replace the manual implementation with a derive attribute | LL + #[derive(Default)] -LL ~ pub struct RepeatDefault1 { +LL | pub struct RepeatDefault1 { | error: this `impl` can be derived @@ -181,7 +181,7 @@ LL | | } help: replace the manual implementation with a derive attribute and mark the default variant | LL + #[derive(Default)] -LL ~ pub enum SimpleEnum { +LL | pub enum SimpleEnum { LL | Foo, LL ~ #[default] LL ~ Bar, diff --git a/tests/run-make/rustdoc-dep-info/rmake.rs b/tests/run-make/rustdoc-dep-info/rmake.rs index 166e8d5702fc3..5d6176b18e886 100644 --- a/tests/run-make/rustdoc-dep-info/rmake.rs +++ b/tests/run-make/rustdoc-dep-info/rmake.rs @@ -45,4 +45,15 @@ fn main() { assert!(!path("precedence1.d").exists()); assert!(!path("precedence2.d").exists()); assert!(path("precedence3.d").exists()); + + // stdout (-) also wins if being the last. + let result = rustdoc() + .input("lib.rs") + .arg("-Zunstable-options") + .emit("dep-info=precedence1.d") + .emit("dep-info=-") + .run(); + assert!(!path("precedence1.d").exists()); + assert!(!path("-").exists()); // `-` shouldn't be treated as a file path + assert!(!result.stdout().is_empty()); // Something emitted to stdout } diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr b/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr index 5cd98faa8abb2..d1b672c14904a 100644 --- a/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr +++ b/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr @@ -8,7 +8,7 @@ LL | 0..5+1 => errors_only.push(x), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 5+1; -LL ~ match x as i32 { +LL | match x as i32 { LL ~ 0..VAL => errors_only.push(x), | diff --git a/tests/ui/parser/issues/issue-24375.stderr b/tests/ui/parser/issues/issue-24375.stderr index e96c004fb3516..40916ce321e37 100644 --- a/tests/ui/parser/issues/issue-24375.stderr +++ b/tests/ui/parser/issues/issue-24375.stderr @@ -13,7 +13,7 @@ LL + val if val == tmp[0] => {} help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = tmp[0]; -LL ~ match z { +LL | match z { LL ~ VAL => {} | diff --git a/tests/ui/parser/recover/recover-pat-exprs.stderr b/tests/ui/parser/recover/recover-pat-exprs.stderr index a99f7e16fca83..60e8386bcba78 100644 --- a/tests/ui/parser/recover/recover-pat-exprs.stderr +++ b/tests/ui/parser/recover/recover-pat-exprs.stderr @@ -13,7 +13,7 @@ LL + val if val == x.y => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x.y; -LL ~ match 0 { +LL | match 0 { LL | x => (), LL ~ VAL => (), | @@ -33,7 +33,7 @@ LL + val if val == x.0 => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x.0; -LL ~ match 0 { +LL | match 0 { LL | x => (), LL | x.y => (), LL ~ VAL => (), @@ -54,7 +54,7 @@ LL + val if val == x._0 => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x._0; -LL ~ match 0 { +LL | match 0 { LL | x => (), LL | x.y => (), LL | x.0 => (), @@ -76,7 +76,7 @@ LL + val if val == x.0.1 => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x.0.1; -LL ~ match 0 { +LL | match 0 { LL | x => (), ... LL | x._0 => (), @@ -98,7 +98,7 @@ LL + val if val == x.4.y.17.__z => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x.4.y.17.__z; -LL ~ match 0 { +LL | match 0 { LL | x => (), ... LL | x.0.1 => (), @@ -150,7 +150,7 @@ LL + val if val == x[0] => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x[0]; -LL ~ match 0 { +LL | match 0 { LL ~ VAL => (), | @@ -169,7 +169,7 @@ LL + val if val == x[..] => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x[..]; -LL ~ match 0 { +LL | match 0 { LL | x[0] => (), LL ~ VAL => (), | @@ -216,7 +216,7 @@ LL + val if val == x.f() => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x.f(); -LL ~ match 0 { +LL | match 0 { LL ~ VAL => (), | @@ -235,7 +235,7 @@ LL + val if val == x._f() => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x._f(); -LL ~ match 0 { +LL | match 0 { LL | x.f() => (), LL ~ VAL => (), | @@ -255,7 +255,7 @@ LL + val if val == x? => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x?; -LL ~ match 0 { +LL | match 0 { LL | x.f() => (), LL | x._f() => (), LL ~ VAL => (), @@ -276,7 +276,7 @@ LL + val if val == ().f() => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = ().f(); -LL ~ match 0 { +LL | match 0 { LL | x.f() => (), LL | x._f() => (), LL | x? => (), @@ -298,7 +298,7 @@ LL + val if val == (0, x)?.f() => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = (0, x)?.f(); -LL ~ match 0 { +LL | match 0 { LL | x.f() => (), ... LL | ().f() => (), @@ -320,7 +320,7 @@ LL + val if val == x.f().g() => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x.f().g(); -LL ~ match 0 { +LL | match 0 { LL | x.f() => (), ... LL | (0, x)?.f() => (), @@ -342,7 +342,7 @@ LL + val if val == 0.f()?.g()?? => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 0.f()?.g()??; -LL ~ match 0 { +LL | match 0 { LL | x.f() => (), ... LL | x.f().g() => (), @@ -364,7 +364,7 @@ LL + val if val == x as usize => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x as usize; -LL ~ match 0 { +LL | match 0 { LL ~ VAL => (), | @@ -383,7 +383,7 @@ LL + val if val == 0 as usize => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 0 as usize; -LL ~ match 0 { +LL | match 0 { LL | x as usize => (), LL ~ VAL => (), | @@ -403,7 +403,7 @@ LL + val if val == x.f().0.4 as f32 => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x.f().0.4 as f32; -LL ~ match 0 { +LL | match 0 { LL | x as usize => (), LL | 0 as usize => (), LL ~ VAL => (), @@ -424,7 +424,7 @@ LL + val if val == 1 + 1 => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 1 + 1; -LL ~ match 0 { +LL | match 0 { LL ~ VAL => (), | @@ -443,7 +443,7 @@ LL + val if val == (1 + 2) * 3 => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = (1 + 2) * 3; -LL ~ match 0 { +LL | match 0 { LL | 1 + 1 => (), LL ~ VAL => (), | @@ -463,7 +463,7 @@ LL + val if val == (x.0 > 2) => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x.0 > 2; -LL ~ match 0 { +LL | match 0 { LL | 1 + 1 => (), ... LL | @@ -485,7 +485,7 @@ LL + val if val == (x.0 == 2) => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x.0 == 2; -LL ~ match 0 { +LL | match 0 { LL | 1 + 1 => (), ... LL | x.0 > 2 => (), @@ -507,7 +507,7 @@ LL + (x, val) if x != 0 && val == (y.0 > 2) => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = y.0 > 2; -LL ~ match (0, 0) { +LL | match (0, 0) { LL ~ (x, VAL) if x != 0 => (), | @@ -526,7 +526,7 @@ LL + (x, val) if (x != 0 || x != 1) && val == (y.0 > 2) => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = y.0 > 2; -LL ~ match (0, 0) { +LL | match (0, 0) { LL | (x, y.0 > 2) if x != 0 => (), LL ~ (x, VAL) if x != 0 || x != 1 => (), | @@ -563,7 +563,7 @@ LL + val if val == u8::MAX.abs() => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = u8::MAX.abs(); -LL ~ match u8::MAX { +LL | match u8::MAX { LL ~ VAL => (), | @@ -582,7 +582,7 @@ LL + z @ w @ val if val == v.u() => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = v.u(); -LL ~ match u8::MAX { +LL | match u8::MAX { LL | u8::MAX.abs() => (), ... LL | @@ -604,7 +604,7 @@ LL + val if val == y.ilog(3) => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = y.ilog(3); -LL ~ match u8::MAX { +LL | match u8::MAX { LL | u8::MAX.abs() => (), ... LL | @@ -626,7 +626,7 @@ LL + val if val == n + 1 => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = n + 1; -LL ~ match u8::MAX { +LL | match u8::MAX { LL | u8::MAX.abs() => (), ... LL | @@ -648,7 +648,7 @@ LL + (val) if val == "".f() + 14 * 8 => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = "".f() + 14 * 8; -LL ~ match u8::MAX { +LL | match u8::MAX { LL | u8::MAX.abs() => (), ... LL | @@ -670,7 +670,7 @@ LL + val if val == f?() => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = f?(); -LL ~ match u8::MAX { +LL | match u8::MAX { LL | u8::MAX.abs() => (), ... LL | 0 | ((1) | 2) | 3 => (), diff --git a/tests/ui/parser/recover/recover-pat-issues.stderr b/tests/ui/parser/recover/recover-pat-issues.stderr index ec7fcda3497f4..27754289f534f 100644 --- a/tests/ui/parser/recover/recover-pat-issues.stderr +++ b/tests/ui/parser/recover/recover-pat-issues.stderr @@ -13,7 +13,7 @@ LL + Foo(val) if val == "hi".to_owned() => true, help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = "hi".to_owned(); -LL ~ match foo { +LL | match foo { LL ~ Foo(VAL) => true, | @@ -32,7 +32,7 @@ LL + Bar { baz } if baz == "hi".to_owned() => true, help: consider extracting the expression into a `const` | LL + const BAZ: /* Type */ = "hi".to_owned(); -LL ~ match bar { +LL | match bar { LL ~ Bar { baz: BAZ } => true, | @@ -51,7 +51,7 @@ LL + &[val] if val == "foo".to_string() => {} help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = "foo".to_string(); -LL ~ match foo.as_slice() { +LL | match foo.as_slice() { LL ~ &[VAL] => {} | diff --git a/tests/ui/parser/recover/recover-pat-ranges.stderr b/tests/ui/parser/recover/recover-pat-ranges.stderr index afa7f25405435..edda9a8e7570a 100644 --- a/tests/ui/parser/recover/recover-pat-ranges.stderr +++ b/tests/ui/parser/recover/recover-pat-ranges.stderr @@ -92,7 +92,7 @@ LL | ..=1 + 2 => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 1 + 2; -LL ~ match -1 { +LL | match -1 { LL | 0..=1 => (), ... LL | @@ -109,7 +109,7 @@ LL | (-4 + 0).. => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = -4 + 0; -LL ~ match -1 { +LL | match -1 { LL | 0..=1 => (), ... LL | @@ -126,7 +126,7 @@ LL | (1 + 4)...1 * 2 => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 1 + 4; -LL ~ match -1 { +LL | match -1 { LL | 0..=1 => (), ... LL | @@ -143,7 +143,7 @@ LL | (1 + 4)...1 * 2 => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 1 * 2; -LL ~ match -1 { +LL | match -1 { LL | 0..=1 => (), ... LL | @@ -160,7 +160,7 @@ LL | 0.x()..="y".z() => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 0.x(); -LL ~ match -1 { +LL | match -1 { LL | 0..=1 => (), ... LL | @@ -177,7 +177,7 @@ LL | 0.x()..="y".z() => (), help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = "y".z(); -LL ~ match -1 { +LL | match -1 { LL | 0..=1 => (), ... LL | diff --git a/tests/ui/parser/recover/recover-pat-wildcards.stderr b/tests/ui/parser/recover/recover-pat-wildcards.stderr index ebc1cbf7d591e..97dc842b85919 100644 --- a/tests/ui/parser/recover/recover-pat-wildcards.stderr +++ b/tests/ui/parser/recover/recover-pat-wildcards.stderr @@ -81,7 +81,7 @@ LL | 4..=(2 + _) => () help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 2 + _; -LL ~ match 9 { +LL | match 9 { LL ~ 4..=(VAL) => () | diff --git a/tests/ui/process/win-inherit-handles.rs b/tests/ui/process/win-inherit-handles.rs new file mode 100644 index 0000000000000..5750a4b40f055 --- /dev/null +++ b/tests/ui/process/win-inherit-handles.rs @@ -0,0 +1,81 @@ +// Tests `inherit_handles` by spawning a child process and checking its handle +// count to be greater than when not setting the option. + +//@ run-pass +//@ only-windows +//@ needs-subprocess +//@ edition: 2024 + +#![feature(windows_process_extensions_inherit_handles)] + +use std::os::windows::io::AsRawHandle; +use std::os::windows::process::CommandExt; +use std::process::{Command, Stdio}; +use std::time::Duration; +use std::{env, io, thread}; + +fn main() { + if std::env::args().skip(1).any(|s| s == "--child") { + child(); + } else { + parent(); + } +} + +fn parent() { + let with_inherit_count = child_handle_count(true); + let without_inherit_count = child_handle_count(false); + // Only compare the two values instead of only expecting a hard 1 for + // robustness, although only 1 has ever been observed here. + assert!( + with_inherit_count > without_inherit_count, + "Child process handle count unexpectedly smaller when inheriting handles compared to when \ + not: {} <= {}", + with_inherit_count, + without_inherit_count, + ); +} + +/// Spawns the current program as a child process and returns its handle count. +fn child_handle_count(inherit_handles: bool) -> u32 { + let mut child_proc = Command::new(&env::current_exe().unwrap()) + .arg("--child") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .inherit_handles(inherit_handles) + .spawn() + .unwrap(); + + let mut handle_count = 0; + let ret = unsafe { GetProcessHandleCount(child_proc.as_raw_handle(), &raw mut handle_count) }; + assert_ne!( + ret, + 0, + "GetProcessHandleCount failed: {:?}", + io::Error::last_os_error(), + ); + + // Cleanup. + child_proc.kill().unwrap(); + child_proc.wait().unwrap(); + + handle_count +} + +/// A process that stays running until killed. +fn child() { + // Don't wait forever if something goes wrong. + thread::sleep(Duration::from_secs(10)); +} + +// Windows API +mod winapi { + use std::os::windows::raw::HANDLE; + + #[link(name = "kernel32")] + unsafe extern "system" { + pub fn GetProcessHandleCount(hprocess: HANDLE, pdwhandlecount: *mut u32) -> i32; + } +} +use winapi::*;