Auto merge of #136713 - matthiaskrgr:rollup-sy6py39, r=matthiaskrgr
Rollup of 7 pull requests Successful merges: - #135179 (Make sure to use `Receiver` trait when extracting object method candidate) - #136554 (Add `opt_alias_variances` and use it in outlives code) - #136556 ([AIX] Update tests/ui/wait-forked-but-failed-child.rs to accomodate exiting and idle processes.) - #136589 (Enable "jump to def" feature on rustc docs) - #136615 (sys: net: Add UEFI stubs) - #136635 (Remove outdated `base_port` calculation in std net test) - #136682 (Move two windows process tests to tests/ui) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
0148a2be13
23 changed files with 722 additions and 234 deletions
|
@ -284,7 +284,7 @@ where
|
|||
return;
|
||||
}
|
||||
|
||||
match ty.kind() {
|
||||
match *ty.kind() {
|
||||
ty::Closure(_, args) => {
|
||||
// Skip lifetime parameters of the enclosing item(s)
|
||||
|
||||
|
@ -316,10 +316,12 @@ where
|
|||
args.as_coroutine().resume_ty().visit_with(self);
|
||||
}
|
||||
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
|
||||
// Skip lifetime parameters that are not captures.
|
||||
let variances = self.tcx.variances_of(*def_id);
|
||||
|
||||
ty::Alias(kind, ty::AliasTy { def_id, args, .. })
|
||||
if let Some(variances) = self.tcx.opt_alias_variances(kind, def_id) =>
|
||||
{
|
||||
// Skip lifetime parameters that are not captured, since they do
|
||||
// not need member constraints registered for them; we'll erase
|
||||
// them (and hopefully in the future replace them with placeholders).
|
||||
for (v, s) in std::iter::zip(variances, args.iter()) {
|
||||
if *v != ty::Bivariant {
|
||||
s.visit_with(self);
|
||||
|
|
|
@ -347,9 +347,17 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
|
|||
// yield an object-type (e.g., `&Object` or `Box<Object>`
|
||||
// etc).
|
||||
|
||||
// FIXME: this feels, like, super dubious
|
||||
self.fcx
|
||||
.autoderef(self.span, self_ty)
|
||||
let mut autoderef = self.fcx.autoderef(self.span, self_ty);
|
||||
|
||||
// We don't need to gate this behind arbitrary self types
|
||||
// per se, but it does make things a bit more gated.
|
||||
if self.tcx.features().arbitrary_self_types()
|
||||
|| self.tcx.features().arbitrary_self_types_pointers()
|
||||
{
|
||||
autoderef = autoderef.use_receiver_trait();
|
||||
}
|
||||
|
||||
autoderef
|
||||
.include_raw_pointers()
|
||||
.find_map(|(ty, _)| match ty.kind() {
|
||||
ty::Dynamic(data, ..) => Some(closure(
|
||||
|
|
|
@ -48,7 +48,7 @@ where
|
|||
return ty.super_visit_with(self);
|
||||
}
|
||||
|
||||
match ty.kind() {
|
||||
match *ty.kind() {
|
||||
// We can prove that an alias is live two ways:
|
||||
// 1. All the components are live.
|
||||
//
|
||||
|
@ -95,11 +95,9 @@ where
|
|||
assert!(r.type_flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS));
|
||||
r.visit_with(self);
|
||||
} else {
|
||||
// Skip lifetime parameters that are not captures.
|
||||
let variances = match kind {
|
||||
ty::Opaque => Some(self.tcx.variances_of(*def_id)),
|
||||
_ => None,
|
||||
};
|
||||
// Skip lifetime parameters that are not captured, since they do
|
||||
// not need to be live.
|
||||
let variances = tcx.opt_alias_variances(kind, def_id);
|
||||
|
||||
for (idx, s) in args.iter().enumerate() {
|
||||
if variances.map(|variances| variances[idx]) != Some(ty::Bivariant) {
|
||||
|
|
|
@ -392,13 +392,13 @@ where
|
|||
// the problem is to add `T: 'r`, which isn't true. So, if there are no
|
||||
// inference variables, we use a verify constraint instead of adding
|
||||
// edges, which winds up enforcing the same condition.
|
||||
let is_opaque = alias_ty.kind(self.tcx) == ty::Opaque;
|
||||
let kind = alias_ty.kind(self.tcx);
|
||||
if approx_env_bounds.is_empty()
|
||||
&& trait_bounds.is_empty()
|
||||
&& (alias_ty.has_infer_regions() || is_opaque)
|
||||
&& (alias_ty.has_infer_regions() || kind == ty::Opaque)
|
||||
{
|
||||
debug!("no declared bounds");
|
||||
let opt_variances = is_opaque.then(|| self.tcx.variances_of(alias_ty.def_id));
|
||||
let opt_variances = self.tcx.opt_alias_variances(kind, alias_ty.def_id);
|
||||
self.args_must_outlive(alias_ty.args, origin, region, opt_variances);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -102,12 +102,11 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
|
|||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn alias_bound(&self, alias_ty: ty::AliasTy<'tcx>) -> VerifyBound<'tcx> {
|
||||
let alias_ty_as_ty = alias_ty.to_ty(self.tcx);
|
||||
|
||||
// Search the env for where clauses like `P: 'a`.
|
||||
let env_bounds = self.approx_declared_bounds_from_env(alias_ty).into_iter().map(|binder| {
|
||||
if let Some(ty::OutlivesPredicate(ty, r)) = binder.no_bound_vars()
|
||||
&& ty == alias_ty_as_ty
|
||||
&& let ty::Alias(_, alias_ty_from_bound) = *ty.kind()
|
||||
&& alias_ty_from_bound == alias_ty
|
||||
{
|
||||
// Micro-optimize if this is an exact match (this
|
||||
// occurs often when there are no region variables
|
||||
|
@ -127,7 +126,8 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
|
|||
// see the extensive comment in projection_must_outlive
|
||||
let recursive_bound = {
|
||||
let mut components = smallvec![];
|
||||
compute_alias_components_recursive(self.tcx, alias_ty_as_ty, &mut components);
|
||||
let kind = alias_ty.kind(self.tcx);
|
||||
compute_alias_components_recursive(self.tcx, kind, alias_ty, &mut components);
|
||||
self.bound_from_components(&components)
|
||||
};
|
||||
|
||||
|
|
|
@ -827,7 +827,9 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
|
|||
if tcx.sess.opts.unstable_opts.input_stats {
|
||||
rustc_passes::input_stats::print_hir_stats(tcx);
|
||||
}
|
||||
#[cfg(debug_assertions)]
|
||||
// When using rustdoc's "jump to def" feature, it enters this code and `check_crate`
|
||||
// is not defined. So we need to cfg it out.
|
||||
#[cfg(all(not(doc), debug_assertions))]
|
||||
rustc_passes::hir_id_validator::check_crate(tcx);
|
||||
let sess = tcx.sess;
|
||||
sess.time("misc_checking_1", || {
|
||||
|
|
|
@ -194,6 +194,14 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
|
|||
self.variances_of(def_id)
|
||||
}
|
||||
|
||||
fn opt_alias_variances(
|
||||
self,
|
||||
kind: impl Into<ty::AliasTermKind>,
|
||||
def_id: DefId,
|
||||
) -> Option<&'tcx [ty::Variance]> {
|
||||
self.opt_alias_variances(kind, def_id)
|
||||
}
|
||||
|
||||
fn type_of(self, def_id: DefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> {
|
||||
self.type_of(def_id)
|
||||
}
|
||||
|
|
|
@ -948,6 +948,29 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
|
||||
ty
|
||||
}
|
||||
|
||||
// Computes the variances for an alias (opaque or RPITIT) that represent
|
||||
// its (un)captured regions.
|
||||
pub fn opt_alias_variances(
|
||||
self,
|
||||
kind: impl Into<ty::AliasTermKind>,
|
||||
def_id: DefId,
|
||||
) -> Option<&'tcx [ty::Variance]> {
|
||||
match kind.into() {
|
||||
ty::AliasTermKind::ProjectionTy => {
|
||||
if self.is_impl_trait_in_trait(def_id) {
|
||||
Some(self.variances_of(def_id))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
ty::AliasTermKind::OpaqueTy => Some(self.variances_of(def_id)),
|
||||
ty::AliasTermKind::InherentTy
|
||||
| ty::AliasTermKind::WeakTy
|
||||
| ty::AliasTermKind::UnevaluatedConst
|
||||
| ty::AliasTermKind::ProjectionConst => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct OpaqueTypeExpander<'tcx> {
|
||||
|
|
|
@ -141,6 +141,12 @@ pub trait Interner:
|
|||
type VariancesOf: Copy + Debug + SliceLike<Item = ty::Variance>;
|
||||
fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf;
|
||||
|
||||
fn opt_alias_variances(
|
||||
self,
|
||||
kind: impl Into<ty::AliasTermKind>,
|
||||
def_id: Self::DefId,
|
||||
) -> Option<Self::VariancesOf>;
|
||||
|
||||
fn type_of(self, def_id: Self::DefId) -> ty::EarlyBinder<Self, Self::Ty>;
|
||||
|
||||
type AdtDef: AdtDef<Self>;
|
||||
|
|
|
@ -148,7 +148,7 @@ impl<I: Interner> TypeVisitor<I> for OutlivesCollector<'_, I> {
|
|||
// trait-ref. Therefore, if we see any higher-ranked regions,
|
||||
// we simply fallback to the most restrictive rule, which
|
||||
// requires that `Pi: 'a` for all `i`.
|
||||
ty::Alias(_, alias_ty) => {
|
||||
ty::Alias(kind, alias_ty) => {
|
||||
if !alias_ty.has_escaping_bound_vars() {
|
||||
// best case: no escaping regions, so push the
|
||||
// projection and skip the subtree (thus generating no
|
||||
|
@ -162,7 +162,7 @@ impl<I: Interner> TypeVisitor<I> for OutlivesCollector<'_, I> {
|
|||
// OutlivesProjectionComponents. Continue walking
|
||||
// through and constrain Pi.
|
||||
let mut subcomponents = smallvec![];
|
||||
compute_alias_components_recursive(self.cx, ty, &mut subcomponents);
|
||||
compute_alias_components_recursive(self.cx, kind, alias_ty, &mut subcomponents);
|
||||
self.out.push(Component::EscapingAlias(subcomponents.into_iter().collect()));
|
||||
}
|
||||
}
|
||||
|
@ -217,21 +217,17 @@ impl<I: Interner> TypeVisitor<I> for OutlivesCollector<'_, I> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Collect [Component]s for *all* the args of `parent`.
|
||||
/// Collect [Component]s for *all* the args of `alias_ty`.
|
||||
///
|
||||
/// This should not be used to get the components of `parent` itself.
|
||||
/// This should not be used to get the components of `alias_ty` itself.
|
||||
/// Use [push_outlives_components] instead.
|
||||
pub fn compute_alias_components_recursive<I: Interner>(
|
||||
cx: I,
|
||||
alias_ty: I::Ty,
|
||||
kind: ty::AliasTyKind,
|
||||
alias_ty: ty::AliasTy<I>,
|
||||
out: &mut SmallVec<[Component<I>; 4]>,
|
||||
) {
|
||||
let ty::Alias(kind, alias_ty) = alias_ty.kind() else {
|
||||
unreachable!("can only call `compute_alias_components_recursive` on an alias type")
|
||||
};
|
||||
|
||||
let opt_variances =
|
||||
if kind == ty::Opaque { Some(cx.variances_of(alias_ty.def_id)) } else { None };
|
||||
let opt_variances = cx.opt_alias_variances(kind, alias_ty.def_id);
|
||||
|
||||
let mut visitor = OutlivesCollector { cx, out, visited: Default::default() };
|
||||
|
||||
|
|
|
@ -469,6 +469,17 @@ impl AliasTermKind {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<ty::AliasTyKind> for AliasTermKind {
|
||||
fn from(value: ty::AliasTyKind) -> Self {
|
||||
match value {
|
||||
ty::Projection => AliasTermKind::ProjectionTy,
|
||||
ty::Opaque => AliasTermKind::OpaqueTy,
|
||||
ty::Weak => AliasTermKind::WeakTy,
|
||||
ty::Inherent => AliasTermKind::InherentTy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the unprojected term of a projection goal.
|
||||
///
|
||||
/// * For a projection, this would be `<Ty as Trait<...>>::N<...>`.
|
||||
|
|
|
@ -236,28 +236,14 @@ impl<I: Interner> Relate<I> for ty::AliasTy<I> {
|
|||
ExpectedFound::new(a, b)
|
||||
}))
|
||||
} else {
|
||||
let args = match a.kind(relation.cx()) {
|
||||
ty::Opaque => relate_args_with_variances(
|
||||
relation,
|
||||
a.def_id,
|
||||
relation.cx().variances_of(a.def_id),
|
||||
a.args,
|
||||
b.args,
|
||||
let cx = relation.cx();
|
||||
let args = if let Some(variances) = cx.opt_alias_variances(a.kind(cx), a.def_id) {
|
||||
relate_args_with_variances(
|
||||
relation, a.def_id, variances, a.args, b.args,
|
||||
false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle
|
||||
)?,
|
||||
ty::Projection if relation.cx().is_impl_trait_in_trait(a.def_id) => {
|
||||
relate_args_with_variances(
|
||||
relation,
|
||||
a.def_id,
|
||||
relation.cx().variances_of(a.def_id),
|
||||
a.args,
|
||||
b.args,
|
||||
false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle
|
||||
)?
|
||||
}
|
||||
ty::Projection | ty::Weak | ty::Inherent => {
|
||||
relate_args_invariantly(relation, a.args, b.args)?
|
||||
}
|
||||
)?
|
||||
} else {
|
||||
relate_args_invariantly(relation, a.args, b.args)?
|
||||
};
|
||||
Ok(ty::AliasTy::new_from_args(relation.cx(), a.def_id, args))
|
||||
}
|
||||
|
|
|
@ -5,14 +5,15 @@ use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToS
|
|||
use crate::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
static PORT: AtomicUsize = AtomicUsize::new(0);
|
||||
const BASE_PORT: u16 = 19600;
|
||||
|
||||
pub fn next_test_ip4() -> SocketAddr {
|
||||
let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + base_port();
|
||||
let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + BASE_PORT;
|
||||
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), port))
|
||||
}
|
||||
|
||||
pub fn next_test_ip6() -> SocketAddr {
|
||||
let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + base_port();
|
||||
let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + BASE_PORT;
|
||||
SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), port, 0, 0))
|
||||
}
|
||||
|
||||
|
@ -30,31 +31,3 @@ pub fn tsa<A: ToSocketAddrs>(a: A) -> Result<Vec<SocketAddr>, String> {
|
|||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
// The bots run multiple builds at the same time, and these builds
|
||||
// all want to use ports. This function figures out which workspace
|
||||
// it is running in and assigns a port range based on it.
|
||||
fn base_port() -> u16 {
|
||||
let cwd = if cfg!(target_env = "sgx") {
|
||||
String::from("sgx")
|
||||
} else {
|
||||
env::current_dir().unwrap().into_os_string().into_string().unwrap()
|
||||
};
|
||||
let dirs = [
|
||||
"32-opt",
|
||||
"32-nopt",
|
||||
"musl-64-opt",
|
||||
"cross-opt",
|
||||
"64-opt",
|
||||
"64-nopt",
|
||||
"64-opt-vg",
|
||||
"64-debug-opt",
|
||||
"all-opt",
|
||||
"snap3",
|
||||
"dist",
|
||||
"sgx",
|
||||
];
|
||||
dirs.iter().enumerate().find(|&(_, dir)| cwd.contains(dir)).map(|p| p.0).unwrap_or(0) as u16
|
||||
* 1000
|
||||
+ 19600
|
||||
}
|
||||
|
|
|
@ -391,154 +391,6 @@ fn test_interior_nul_in_env_value_is_error() {
|
|||
}
|
||||
}
|
||||
|
||||
/// Tests that process creation flags work by debugging a process.
|
||||
/// Other creation flags make it hard or impossible to detect
|
||||
/// behavioral changes in the process.
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
fn test_creation_flags() {
|
||||
use crate::os::windows::process::CommandExt;
|
||||
use crate::sys::c::{BOOL, INFINITE};
|
||||
#[repr(C)]
|
||||
struct DEBUG_EVENT {
|
||||
pub event_code: u32,
|
||||
pub process_id: u32,
|
||||
pub thread_id: u32,
|
||||
// This is a union in the real struct, but we don't
|
||||
// need this data for the purposes of this test.
|
||||
pub _junk: [u8; 164],
|
||||
}
|
||||
|
||||
extern "system" {
|
||||
fn WaitForDebugEvent(lpDebugEvent: *mut DEBUG_EVENT, dwMilliseconds: u32) -> BOOL;
|
||||
fn ContinueDebugEvent(dwProcessId: u32, dwThreadId: u32, dwContinueStatus: u32) -> BOOL;
|
||||
}
|
||||
|
||||
const DEBUG_PROCESS: u32 = 1;
|
||||
const EXIT_PROCESS_DEBUG_EVENT: u32 = 5;
|
||||
const DBG_EXCEPTION_NOT_HANDLED: u32 = 0x80010001;
|
||||
|
||||
let mut child = Command::new("cmd")
|
||||
.creation_flags(DEBUG_PROCESS)
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.spawn()
|
||||
.unwrap();
|
||||
child.stdin.take().unwrap().write_all(b"exit\r\n").unwrap();
|
||||
let mut events = 0;
|
||||
let mut event = DEBUG_EVENT { event_code: 0, process_id: 0, thread_id: 0, _junk: [0; 164] };
|
||||
loop {
|
||||
if unsafe { WaitForDebugEvent(&mut event as *mut DEBUG_EVENT, INFINITE) } == 0 {
|
||||
panic!("WaitForDebugEvent failed!");
|
||||
}
|
||||
events += 1;
|
||||
|
||||
if event.event_code == EXIT_PROCESS_DEBUG_EVENT {
|
||||
break;
|
||||
}
|
||||
|
||||
if unsafe {
|
||||
ContinueDebugEvent(event.process_id, event.thread_id, DBG_EXCEPTION_NOT_HANDLED)
|
||||
} == 0
|
||||
{
|
||||
panic!("ContinueDebugEvent failed!");
|
||||
}
|
||||
}
|
||||
assert!(events > 0);
|
||||
}
|
||||
|
||||
/// Tests proc thread attributes by spawning a process with a custom parent process,
|
||||
/// then comparing the parent process ID with the expected parent process ID.
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
fn test_proc_thread_attributes() {
|
||||
use crate::mem;
|
||||
use crate::os::windows::io::AsRawHandle;
|
||||
use crate::os::windows::process::{CommandExt, ProcThreadAttributeList};
|
||||
use crate::sys::c::{BOOL, CloseHandle, HANDLE};
|
||||
use crate::sys::cvt;
|
||||
|
||||
#[repr(C)]
|
||||
#[allow(non_snake_case)]
|
||||
struct PROCESSENTRY32W {
|
||||
dwSize: u32,
|
||||
cntUsage: u32,
|
||||
th32ProcessID: u32,
|
||||
th32DefaultHeapID: usize,
|
||||
th32ModuleID: u32,
|
||||
cntThreads: u32,
|
||||
th32ParentProcessID: u32,
|
||||
pcPriClassBase: i32,
|
||||
dwFlags: u32,
|
||||
szExeFile: [u16; 260],
|
||||
}
|
||||
|
||||
extern "system" {
|
||||
fn CreateToolhelp32Snapshot(dwflags: u32, th32processid: u32) -> HANDLE;
|
||||
fn Process32First(hsnapshot: HANDLE, lppe: *mut PROCESSENTRY32W) -> BOOL;
|
||||
fn Process32Next(hsnapshot: HANDLE, lppe: *mut PROCESSENTRY32W) -> BOOL;
|
||||
}
|
||||
|
||||
const PROC_THREAD_ATTRIBUTE_PARENT_PROCESS: usize = 0x00020000;
|
||||
const TH32CS_SNAPPROCESS: u32 = 0x00000002;
|
||||
|
||||
struct ProcessDropGuard(crate::process::Child);
|
||||
|
||||
impl Drop for ProcessDropGuard {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.0.kill();
|
||||
}
|
||||
}
|
||||
|
||||
let mut parent = Command::new("cmd");
|
||||
parent.stdout(Stdio::null()).stderr(Stdio::null());
|
||||
|
||||
let parent = ProcessDropGuard(parent.spawn().unwrap());
|
||||
|
||||
let mut child_cmd = Command::new("cmd");
|
||||
child_cmd.stdout(Stdio::null()).stderr(Stdio::null());
|
||||
|
||||
let parent_process_handle = parent.0.as_raw_handle();
|
||||
|
||||
let mut attribute_list = ProcThreadAttributeList::build()
|
||||
.attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parent_process_handle)
|
||||
.finish()
|
||||
.unwrap();
|
||||
|
||||
let child = ProcessDropGuard(child_cmd.spawn_with_attributes(&mut attribute_list).unwrap());
|
||||
|
||||
let h_snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) };
|
||||
|
||||
let mut process_entry = PROCESSENTRY32W {
|
||||
dwSize: mem::size_of::<PROCESSENTRY32W>() as u32,
|
||||
cntUsage: 0,
|
||||
th32ProcessID: 0,
|
||||
th32DefaultHeapID: 0,
|
||||
th32ModuleID: 0,
|
||||
cntThreads: 0,
|
||||
th32ParentProcessID: 0,
|
||||
pcPriClassBase: 0,
|
||||
dwFlags: 0,
|
||||
szExeFile: [0; 260],
|
||||
};
|
||||
|
||||
unsafe { cvt(Process32First(h_snapshot, &mut process_entry as *mut _)) }.unwrap();
|
||||
|
||||
loop {
|
||||
if child.0.id() == process_entry.th32ProcessID {
|
||||
break;
|
||||
}
|
||||
unsafe { cvt(Process32Next(h_snapshot, &mut process_entry as *mut _)) }.unwrap();
|
||||
}
|
||||
|
||||
unsafe { cvt(CloseHandle(h_snapshot)) }.unwrap();
|
||||
|
||||
assert_eq!(parent.0.id(), process_entry.th32ParentProcessID);
|
||||
|
||||
drop(child)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_command_implements_send_sync() {
|
||||
fn take_send_sync_type<T: Send + Sync>(_: T) {}
|
||||
|
|
369
library/std/src/sys/net/connection/uefi/mod.rs
Normal file
369
library/std/src/sys/net/connection/uefi/mod.rs
Normal file
|
@ -0,0 +1,369 @@
|
|||
use crate::fmt;
|
||||
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
|
||||
use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
|
||||
use crate::sys::unsupported;
|
||||
use crate::time::Duration;
|
||||
|
||||
pub struct TcpStream(!);
|
||||
|
||||
impl TcpStream {
|
||||
pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result<TcpStream> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn read(&self, _: &mut [u8]) -> io::Result<usize> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn is_read_vectored(&self) -> bool {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn write(&self, _: &[u8]) -> io::Result<usize> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn is_write_vectored(&self) -> bool {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn shutdown(&self, _: Shutdown) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn duplicate(&self) -> io::Result<TcpStream> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn linger(&self) -> io::Result<Option<Duration>> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn nodelay(&self) -> io::Result<bool> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn set_ttl(&self, _: u32) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn ttl(&self) -> io::Result<u32> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for TcpStream {
|
||||
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TcpListener(!);
|
||||
|
||||
impl TcpListener {
|
||||
pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn duplicate(&self) -> io::Result<TcpListener> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn set_ttl(&self, _: u32) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn ttl(&self) -> io::Result<u32> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn set_only_v6(&self, _: bool) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn only_v6(&self) -> io::Result<bool> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for TcpListener {
|
||||
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UdpSocket(!);
|
||||
|
||||
impl UdpSocket {
|
||||
pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn duplicate(&self) -> io::Result<UdpSocket> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn broadcast(&self) -> io::Result<bool> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn multicast_loop_v4(&self) -> io::Result<bool> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn multicast_loop_v6(&self) -> io::Result<bool> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn set_ttl(&self, _: u32) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn ttl(&self) -> io::Result<u32> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn send(&self, _: &[u8]) -> io::Result<usize> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for UdpSocket {
|
||||
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LookupHost(!);
|
||||
|
||||
impl LookupHost {
|
||||
pub fn port(&self) -> u16 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for LookupHost {
|
||||
type Item = SocketAddr;
|
||||
fn next(&mut self) -> Option<SocketAddr> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for LookupHost {
|
||||
type Error = io::Error;
|
||||
|
||||
fn try_from(_v: &str) -> io::Result<LookupHost> {
|
||||
unsupported()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<(&'a str, u16)> for LookupHost {
|
||||
type Error = io::Error;
|
||||
|
||||
fn try_from(_v: (&'a str, u16)) -> io::Result<LookupHost> {
|
||||
unsupported()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(nonstandard_style)]
|
||||
pub mod netc {
|
||||
pub const AF_INET: u8 = 0;
|
||||
pub const AF_INET6: u8 = 1;
|
||||
pub type sa_family_t = u8;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct in_addr {
|
||||
pub s_addr: u32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct sockaddr_in {
|
||||
#[allow(dead_code)]
|
||||
pub sin_family: sa_family_t,
|
||||
pub sin_port: u16,
|
||||
pub sin_addr: in_addr,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct in6_addr {
|
||||
pub s6_addr: [u8; 16],
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct sockaddr_in6 {
|
||||
#[allow(dead_code)]
|
||||
pub sin6_family: sa_family_t,
|
||||
pub sin6_port: u16,
|
||||
pub sin6_addr: in6_addr,
|
||||
pub sin6_flowinfo: u32,
|
||||
pub sin6_scope_id: u32,
|
||||
}
|
||||
}
|
|
@ -25,6 +25,11 @@ cfg_if::cfg_if! {
|
|||
mod xous;
|
||||
pub use xous::*;
|
||||
}
|
||||
} else if #[cfg(target_os = "uefi")] {
|
||||
mod connection {
|
||||
mod uefi;
|
||||
pub use uefi::*;
|
||||
}
|
||||
} else {
|
||||
mod connection {
|
||||
mod unsupported;
|
||||
|
|
|
@ -830,7 +830,8 @@ impl Step for Rustc {
|
|||
cargo.rustdocflag("--show-type-layout");
|
||||
// FIXME: `--generate-link-to-definition` tries to resolve cfged out code
|
||||
// see https://github.com/rust-lang/rust/pull/122066#issuecomment-1983049222
|
||||
// cargo.rustdocflag("--generate-link-to-definition");
|
||||
// If there is any bug, please comment out the next line.
|
||||
cargo.rustdocflag("--generate-link-to-definition");
|
||||
|
||||
compile::rustc_cargo(builder, &mut cargo, target, &compiler, &self.crates);
|
||||
cargo.arg("-Zskip-rustdoc-fingerprint");
|
||||
|
|
19
tests/ui/impl-trait/precise-capturing/rpitit-outlives-2.rs
Normal file
19
tests/ui/impl-trait/precise-capturing/rpitit-outlives-2.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
//@ check-pass
|
||||
|
||||
// Ensure that we skip uncaptured args from RPITITs when comptuing outlives.
|
||||
|
||||
#![feature(precise_capturing_in_traits)]
|
||||
|
||||
struct Invariant<T>(*mut T);
|
||||
|
||||
trait Foo {
|
||||
fn hello<'s: 's>(&'s self) -> Invariant<impl Sized + use<Self>>;
|
||||
}
|
||||
|
||||
fn outlives_static(_: impl Sized + 'static) {}
|
||||
|
||||
fn hello<'s, T: Foo + 'static>(x: &'s T) {
|
||||
outlives_static(x.hello());
|
||||
}
|
||||
|
||||
fn main() {}
|
18
tests/ui/impl-trait/precise-capturing/rpitit-outlives.rs
Normal file
18
tests/ui/impl-trait/precise-capturing/rpitit-outlives.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
//@ check-pass
|
||||
|
||||
// Ensure that we skip uncaptured args from RPITITs when collecting the regions
|
||||
// to enforce member constraints in opaque type inference.
|
||||
|
||||
#![feature(precise_capturing_in_traits)]
|
||||
|
||||
struct Invariant<T>(*mut T);
|
||||
|
||||
trait Foo {
|
||||
fn hello<'s: 's>(&'s self) -> Invariant<impl Sized + use<Self>>;
|
||||
}
|
||||
|
||||
fn hello<'s, T: Foo>(x: &'s T) -> Invariant<impl Sized> {
|
||||
x.hello()
|
||||
}
|
||||
|
||||
fn main() {}
|
51
tests/ui/process/win-creation-flags.rs
Normal file
51
tests/ui/process/win-creation-flags.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
// Test that windows `creation_flags` extension to `Command` works.
|
||||
|
||||
//@ run-pass
|
||||
//@ only-windows
|
||||
//@ needs-subprocess
|
||||
|
||||
use std::env;
|
||||
use std::os::windows::process::CommandExt;
|
||||
use std::process::{Command, exit};
|
||||
|
||||
fn main() {
|
||||
if env::args().skip(1).any(|s| s == "--child") {
|
||||
child();
|
||||
} else {
|
||||
parent();
|
||||
}
|
||||
}
|
||||
|
||||
fn parent() {
|
||||
let exe = env::current_exe().unwrap();
|
||||
|
||||
// Use the DETACH_PROCESS to create a subprocess that isn't attached to the console.
|
||||
// The subprocess's exit status will be 0 if it's detached.
|
||||
let status = Command::new(&exe)
|
||||
.arg("--child")
|
||||
.creation_flags(DETACH_PROCESS)
|
||||
.spawn()
|
||||
.unwrap()
|
||||
.wait()
|
||||
.unwrap();
|
||||
assert_eq!(status.code(), Some(0));
|
||||
|
||||
// Try without DETACH_PROCESS to ensure this test works.
|
||||
let status = Command::new(&exe).arg("--child").spawn().unwrap().wait().unwrap();
|
||||
assert_eq!(status.code(), Some(1));
|
||||
}
|
||||
|
||||
// exits with 1 if the console is attached or 0 otherwise
|
||||
fn child() {
|
||||
// Get the attached console's code page.
|
||||
// This will fail (return 0) if no console is attached.
|
||||
let has_console = GetConsoleCP() != 0;
|
||||
exit(has_console as i32);
|
||||
}
|
||||
|
||||
// Windows API definitions.
|
||||
const DETACH_PROCESS: u32 = 0x00000008;
|
||||
#[link(name = "kernel32")]
|
||||
unsafe extern "system" {
|
||||
safe fn GetConsoleCP() -> u32;
|
||||
}
|
118
tests/ui/process/win-proc-thread-attributes.rs
Normal file
118
tests/ui/process/win-proc-thread-attributes.rs
Normal file
|
@ -0,0 +1,118 @@
|
|||
// Tests proc thread attributes by spawning a process with a custom parent process,
|
||||
// then comparing the parent process ID with the expected parent process ID.
|
||||
|
||||
//@ run-pass
|
||||
//@ only-windows
|
||||
//@ needs-subprocess
|
||||
//@ edition: 2021
|
||||
|
||||
#![feature(windows_process_extensions_raw_attribute)]
|
||||
|
||||
use std::os::windows::io::AsRawHandle;
|
||||
use std::os::windows::process::{CommandExt, ProcThreadAttributeList};
|
||||
use std::process::{Child, Command};
|
||||
use std::{env, mem, ptr, thread, time};
|
||||
|
||||
// Make a best effort to ensure child processes always exit.
|
||||
struct ProcessDropGuard(Child);
|
||||
impl Drop for ProcessDropGuard {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.0.kill();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if env::args().skip(1).any(|s| s == "--child") {
|
||||
child();
|
||||
} else {
|
||||
parent();
|
||||
}
|
||||
}
|
||||
|
||||
fn parent() {
|
||||
let exe = env::current_exe().unwrap();
|
||||
|
||||
let (fake_parent_id, child_parent_id) = {
|
||||
// Create a process to be our fake parent process.
|
||||
let fake_parent = Command::new(&exe).arg("--child").spawn().unwrap();
|
||||
let fake_parent = ProcessDropGuard(fake_parent);
|
||||
let parent_handle = fake_parent.0.as_raw_handle();
|
||||
|
||||
// Create another process with the parent process set to the fake.
|
||||
let mut attribute_list = ProcThreadAttributeList::build()
|
||||
.attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parent_handle)
|
||||
.finish()
|
||||
.unwrap();
|
||||
let child =
|
||||
Command::new(&exe).arg("--child").spawn_with_attributes(&mut attribute_list).unwrap();
|
||||
let child = ProcessDropGuard(child);
|
||||
|
||||
// Return the fake's process id and the child's parent's id.
|
||||
(process_info(&fake_parent.0).process_id(), process_info(&child.0).parent_id())
|
||||
};
|
||||
|
||||
assert_eq!(fake_parent_id, child_parent_id);
|
||||
}
|
||||
|
||||
// A process that stays running until killed.
|
||||
fn child() {
|
||||
// Don't wait forever if something goes wrong.
|
||||
thread::sleep(time::Duration::from_secs(60));
|
||||
}
|
||||
|
||||
fn process_info(child: &Child) -> PROCESS_BASIC_INFORMATION {
|
||||
unsafe {
|
||||
let mut info: PROCESS_BASIC_INFORMATION = mem::zeroed();
|
||||
let result = NtQueryInformationProcess(
|
||||
child.as_raw_handle(),
|
||||
ProcessBasicInformation,
|
||||
ptr::from_mut(&mut info).cast(),
|
||||
mem::size_of_val(&info).try_into().unwrap(),
|
||||
ptr::null_mut(),
|
||||
);
|
||||
assert_eq!(result, 0);
|
||||
info
|
||||
}
|
||||
}
|
||||
|
||||
// Windows API
|
||||
mod winapi {
|
||||
#![allow(nonstandard_style)]
|
||||
use std::ffi::c_void;
|
||||
|
||||
pub type HANDLE = *mut c_void;
|
||||
type NTSTATUS = i32;
|
||||
type PROCESSINFOCLASS = i32;
|
||||
|
||||
pub const ProcessBasicInformation: i32 = 0;
|
||||
pub const PROC_THREAD_ATTRIBUTE_PARENT_PROCESS: usize = 0x00020000;
|
||||
#[repr(C)]
|
||||
pub struct PROCESS_BASIC_INFORMATION {
|
||||
pub ExitStatus: NTSTATUS,
|
||||
pub PebBaseAddress: *mut (),
|
||||
pub AffinityMask: usize,
|
||||
pub BasePriority: i32,
|
||||
pub UniqueProcessId: usize,
|
||||
pub InheritedFromUniqueProcessId: usize,
|
||||
}
|
||||
impl PROCESS_BASIC_INFORMATION {
|
||||
pub fn parent_id(&self) -> usize {
|
||||
self.InheritedFromUniqueProcessId
|
||||
}
|
||||
pub fn process_id(&self) -> usize {
|
||||
self.UniqueProcessId
|
||||
}
|
||||
}
|
||||
|
||||
#[link(name = "ntdll")]
|
||||
extern "system" {
|
||||
pub fn NtQueryInformationProcess(
|
||||
ProcessHandle: HANDLE,
|
||||
ProcessInformationClass: PROCESSINFOCLASS,
|
||||
ProcessInformation: *mut c_void,
|
||||
ProcessInformationLength: u32,
|
||||
ReturnLength: *mut u32,
|
||||
) -> NTSTATUS;
|
||||
}
|
||||
}
|
||||
use winapi::*;
|
33
tests/ui/self/arbitrary_self_types_dispatch_to_vtable.rs
Normal file
33
tests/ui/self/arbitrary_self_types_dispatch_to_vtable.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
//@ check-pass
|
||||
|
||||
#![feature(derive_coerce_pointee)]
|
||||
#![feature(arbitrary_self_types)]
|
||||
|
||||
use std::marker::CoercePointee;
|
||||
use std::ops::Receiver;
|
||||
|
||||
// `CoercePointee` isn't needed here, it's just a simpler
|
||||
// (and more conceptual) way of deriving `DispatchFromDyn`.
|
||||
// You could think of `MyDispatcher` as a smart pointer
|
||||
// that just doesn't deref to its target type.
|
||||
#[derive(CoercePointee)]
|
||||
#[repr(transparent)]
|
||||
struct MyDispatcher<T: ?Sized>(*const T);
|
||||
|
||||
impl<T: ?Sized> Receiver for MyDispatcher<T> {
|
||||
type Target = T;
|
||||
}
|
||||
struct Test;
|
||||
|
||||
trait Trait {
|
||||
fn test(self: MyDispatcher<Self>);
|
||||
}
|
||||
|
||||
impl Trait for Test {
|
||||
fn test(self: MyDispatcher<Self>) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
fn main() {
|
||||
MyDispatcher::<dyn Trait>(core::ptr::null_mut::<Test>()).test();
|
||||
}
|
|
@ -31,8 +31,17 @@ fn find_zombies() {
|
|||
// https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html
|
||||
let ps_cmd_output = Command::new("ps").args(&["-A", "-o", "pid,ppid,args"]).output().unwrap();
|
||||
let ps_output = String::from_utf8_lossy(&ps_cmd_output.stdout);
|
||||
// On AIX, the PPID is not always present, such as when a process is blocked
|
||||
// (marked as <exiting>), or if a process is idle. In these situations,
|
||||
// the PPID column contains a "-" for the respective process.
|
||||
// Filter out any lines that have a "-" as the PPID as the PPID is
|
||||
// expected to be an integer.
|
||||
let filtered_ps: Vec<_> = ps_output
|
||||
.lines()
|
||||
.filter(|line| line.split_whitespace().nth(1) != Some("-"))
|
||||
.collect();
|
||||
|
||||
for (line_no, line) in ps_output.split('\n').enumerate() {
|
||||
for (line_no, line) in filtered_ps.into_iter().enumerate() {
|
||||
if 0 < line_no && 0 < line.len() &&
|
||||
my_pid == line.split(' ').filter(|w| 0 < w.len()).nth(1)
|
||||
.expect("1st column should be PPID")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue