Auto merge of #148012 - Zalathar:rollup-ed6cnn8, r=Zalathar
Rollup of 4 pull requests Successful merges: - rust-lang/rust#115501 (Add new inherit_handles flag to CommandExt trait) - rust-lang/rust#146629 (std: reorganize the UNIX-internal `weak` module) - rust-lang/rust#147762 (feat(rustdoc): `--emit=depinfo` output to stdout via `-`) - rust-lang/rust#148001 (fix: Don't add diff symbol to unchanged lines) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
7838ce1a3a
23 changed files with 417 additions and 343 deletions
|
|
@ -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);
|
||||
}
|
||||
_ => {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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<bool>) -> &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")]
|
||||
|
|
|
|||
|
|
@ -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) {}
|
||||
|
|
|
|||
|
|
@ -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<Range<usize>> {
|
||||
// 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<usize> = OnceLock::new();
|
||||
static PAGES: crate::sync::OnceLock<usize> = 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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<unsafe extern "C" fn($($t),*) -> $ret> = {
|
||||
unsafe extern "C" {
|
||||
#[linkage = "extern_weak"]
|
||||
static $name: Option<unsafe extern "C" fn($($t),*) -> $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<F: Copy> {
|
||||
weak_ptr: Option<F>,
|
||||
}
|
||||
|
||||
impl<F: Copy> ExternWeak<F> {
|
||||
#[inline]
|
||||
pub(crate) fn new(weak_ptr: Option<F>) -> Self {
|
||||
ExternWeak { weak_ptr }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn get(&self) -> Option<F> {
|
||||
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<unsafe extern "C" fn($($t),*) -> $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<F> {
|
||||
/// 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<F>,
|
||||
}
|
||||
|
||||
impl<F: FnPtr> DlsymWeak<F> {
|
||||
/// # 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<F> {
|
||||
// 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<F> {
|
||||
// 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<F> Send for DlsymWeak<F> {}
|
||||
unsafe impl<F> Sync for DlsymWeak<F> {}
|
||||
|
||||
#[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 }
|
||||
}
|
||||
)
|
||||
}
|
||||
104
library/std/src/sys/pal/unix/weak/dlsym.rs
Normal file
104
library/std/src/sys/pal/unix/weak/dlsym.rs
Normal file
|
|
@ -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<unsafe extern "C" fn($($t),*) -> $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<F> {
|
||||
/// 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<F>,
|
||||
}
|
||||
|
||||
impl<F: FnPtr> DlsymWeak<F> {
|
||||
/// # 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<F> {
|
||||
// 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<F> {
|
||||
// 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<F> Send for DlsymWeak<F> {}
|
||||
unsafe impl<F> Sync for DlsymWeak<F> {}
|
||||
52
library/std/src/sys/pal/unix/weak/mod.rs
Normal file
52
library/std/src/sys/pal/unix/weak/mod.rs
Normal file
|
|
@ -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;
|
||||
19
library/std/src/sys/pal/unix/weak/syscall.rs
Normal file
19
library/std/src/sys/pal/unix/weak/syscall.rs
Normal file
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
32
library/std/src/sys/pal/unix/weak/weak_linkage.rs
Normal file
32
library/std/src/sys/pal/unix/weak/weak_linkage.rs
Normal file
|
|
@ -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<unsafe extern "C" fn($($t),*) -> $ret> = {
|
||||
unsafe extern "C" {
|
||||
#[linkage = "extern_weak"]
|
||||
static $name: Option<unsafe extern "C" fn($($t),*) -> $ret>;
|
||||
}
|
||||
#[allow(unused_unsafe)]
|
||||
ExternWeak::new(unsafe { $name })
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) struct ExternWeak<F: Copy> {
|
||||
weak_ptr: Option<F>,
|
||||
}
|
||||
|
||||
impl<F: Copy> ExternWeak<F> {
|
||||
#[inline]
|
||||
pub fn new(weak_ptr: Option<F>) -> Self {
|
||||
ExternWeak { weak_ptr }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&self) -> Option<F> {
|
||||
self.weak_ptr
|
||||
}
|
||||
}
|
||||
|
|
@ -159,6 +159,7 @@ pub struct Command {
|
|||
startupinfo_fullscreen: bool,
|
||||
startupinfo_untrusted_source: bool,
|
||||
startupinfo_force_feedback: Option<bool>,
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -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<PathBuf>),
|
||||
DepInfo(Option<OutFileName>),
|
||||
}
|
||||
|
||||
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<Option<&Path>> {
|
||||
pub(crate) fn dep_info(&self) -> Option<Option<&OutFileName>> {
|
||||
for emit in &self.emit {
|
||||
if let EmitType::DepInfo(file) = emit {
|
||||
return Some(file.as_deref());
|
||||
return Some(file.as_ref());
|
||||
}
|
||||
}
|
||||
None
|
||||
|
|
|
|||
|
|
@ -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(&[])
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
||||
|
||||
|
|
|
|||
|
|
@ -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 => {}
|
||||
|
|
||||
|
||||
|
|
|
|||
|
|
@ -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 => (),
|
||||
|
|
|
|||
|
|
@ -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] => {}
|
||||
|
|
||||
|
||||
|
|
|
|||
|
|
@ -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 |
|
||||
|
|
|
|||
|
|
@ -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) => ()
|
||||
|
|
||||
|
||||
|
|
|
|||
81
tests/ui/process/win-inherit-handles.rs
Normal file
81
tests/ui/process/win-inherit-handles.rs
Normal file
|
|
@ -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::*;
|
||||
Loading…
Add table
Add a link
Reference in a new issue