about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-02-08 02:57:14 +0000
committerbors <bors@rust-lang.org>2025-02-08 02:57:14 +0000
commit0148a2be13fcc929273eb3fc79359068afbe93e1 (patch)
treea95ff3fedc0e38342a6b93bd78588781a93a8f30
parente0607238c95df66e3d25a6c17aebe18c6726fc74 (diff)
parent0a33d7ccd1491b9c751624bae9802e2578e3c067 (diff)
downloadrust-0148a2be13fcc929273eb3fc79359068afbe93e1.tar.gz
rust-0148a2be13fcc929273eb3fc79359068afbe93e1.zip
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
-rw-r--r--compiler/rustc_borrowck/src/type_check/opaque_types.rs12
-rw-r--r--compiler/rustc_hir_typeck/src/method/confirm.rs14
-rw-r--r--compiler/rustc_infer/src/infer/outlives/for_liveness.rs10
-rw-r--r--compiler/rustc_infer/src/infer/outlives/obligations.rs6
-rw-r--r--compiler/rustc_infer/src/infer/outlives/verify.rs8
-rw-r--r--compiler/rustc_interface/src/passes.rs4
-rw-r--r--compiler/rustc_middle/src/ty/context.rs8
-rw-r--r--compiler/rustc_middle/src/ty/util.rs23
-rw-r--r--compiler/rustc_type_ir/src/interner.rs6
-rw-r--r--compiler/rustc_type_ir/src/outlives.rs18
-rw-r--r--compiler/rustc_type_ir/src/predicate.rs11
-rw-r--r--compiler/rustc_type_ir/src/relate.rs28
-rw-r--r--library/std/src/net/test.rs33
-rw-r--r--library/std/src/process/tests.rs148
-rw-r--r--library/std/src/sys/net/connection/uefi/mod.rs369
-rw-r--r--library/std/src/sys/net/mod.rs5
-rw-r--r--src/bootstrap/src/core/build_steps/doc.rs3
-rw-r--r--tests/ui/impl-trait/precise-capturing/rpitit-outlives-2.rs19
-rw-r--r--tests/ui/impl-trait/precise-capturing/rpitit-outlives.rs18
-rw-r--r--tests/ui/process/win-creation-flags.rs51
-rw-r--r--tests/ui/process/win-proc-thread-attributes.rs118
-rw-r--r--tests/ui/self/arbitrary_self_types_dispatch_to_vtable.rs33
-rw-r--r--tests/ui/wait-forked-but-failed-child.rs11
23 files changed, 722 insertions, 234 deletions
diff --git a/compiler/rustc_borrowck/src/type_check/opaque_types.rs b/compiler/rustc_borrowck/src/type_check/opaque_types.rs
index ad4e006c21a..17482cc0cbd 100644
--- a/compiler/rustc_borrowck/src/type_check/opaque_types.rs
+++ b/compiler/rustc_borrowck/src/type_check/opaque_types.rs
@@ -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);
diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs
index 5c1c38aeb95..3e48e8d15c3 100644
--- a/compiler/rustc_hir_typeck/src/method/confirm.rs
+++ b/compiler/rustc_hir_typeck/src/method/confirm.rs
@@ -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(
diff --git a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs
index c02ab98b2ba..379410641fe 100644
--- a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs
+++ b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs
@@ -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) {
diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs
index 84e51b18dc5..86a47426049 100644
--- a/compiler/rustc_infer/src/infer/outlives/obligations.rs
+++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs
@@ -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;
         }
diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs
index d68f3639176..c14c288c6e4 100644
--- a/compiler/rustc_infer/src/infer/outlives/verify.rs
+++ b/compiler/rustc_infer/src/infer/outlives/verify.rs
@@ -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)
         };
 
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index eda9c4e03fe..e5adcdb244f 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -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", || {
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index c6fc5f98f56..82b4fe193f0 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -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)
     }
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 6fd0fb9a0a4..398ed57faeb 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -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> {
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index a6c72da0c56..aae2d2e96b9 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -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>;
diff --git a/compiler/rustc_type_ir/src/outlives.rs b/compiler/rustc_type_ir/src/outlives.rs
index c26e211a794..b09a378d341 100644
--- a/compiler/rustc_type_ir/src/outlives.rs
+++ b/compiler/rustc_type_ir/src/outlives.rs
@@ -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() };
 
diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs
index 9a80f97e274..f75b2927600 100644
--- a/compiler/rustc_type_ir/src/predicate.rs
+++ b/compiler/rustc_type_ir/src/predicate.rs
@@ -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<...>`.
diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs
index 5b696ee5ed4..d065384b58e 100644
--- a/compiler/rustc_type_ir/src/relate.rs
+++ b/compiler/rustc_type_ir/src/relate.rs
@@ -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))
         }
diff --git a/library/std/src/net/test.rs b/library/std/src/net/test.rs
index d318d457f35..a5c3983cd89 100644
--- a/library/std/src/net/test.rs
+++ b/library/std/src/net/test.rs
@@ -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
-}
diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs
index 1323aba38b7..9b87259abef 100644
--- a/library/std/src/process/tests.rs
+++ b/library/std/src/process/tests.rs
@@ -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) {}
diff --git a/library/std/src/sys/net/connection/uefi/mod.rs b/library/std/src/sys/net/connection/uefi/mod.rs
new file mode 100644
index 00000000000..87e6106468f
--- /dev/null
+++ b/library/std/src/sys/net/connection/uefi/mod.rs
@@ -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,
+    }
+}
diff --git a/library/std/src/sys/net/mod.rs b/library/std/src/sys/net/mod.rs
index 5aa197fbc0d..646679a1cc8 100644
--- a/library/std/src/sys/net/mod.rs
+++ b/library/std/src/sys/net/mod.rs
@@ -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;
diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs
index 0eb4080c053..dedcc139ae1 100644
--- a/src/bootstrap/src/core/build_steps/doc.rs
+++ b/src/bootstrap/src/core/build_steps/doc.rs
@@ -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");
diff --git a/tests/ui/impl-trait/precise-capturing/rpitit-outlives-2.rs b/tests/ui/impl-trait/precise-capturing/rpitit-outlives-2.rs
new file mode 100644
index 00000000000..6f7e1a0eaef
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/rpitit-outlives-2.rs
@@ -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() {}
diff --git a/tests/ui/impl-trait/precise-capturing/rpitit-outlives.rs b/tests/ui/impl-trait/precise-capturing/rpitit-outlives.rs
new file mode 100644
index 00000000000..94d81d766f7
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/rpitit-outlives.rs
@@ -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() {}
diff --git a/tests/ui/process/win-creation-flags.rs b/tests/ui/process/win-creation-flags.rs
new file mode 100644
index 00000000000..0c7c6883d68
--- /dev/null
+++ b/tests/ui/process/win-creation-flags.rs
@@ -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;
+}
diff --git a/tests/ui/process/win-proc-thread-attributes.rs b/tests/ui/process/win-proc-thread-attributes.rs
new file mode 100644
index 00000000000..91bb0b17e95
--- /dev/null
+++ b/tests/ui/process/win-proc-thread-attributes.rs
@@ -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::*;
diff --git a/tests/ui/self/arbitrary_self_types_dispatch_to_vtable.rs b/tests/ui/self/arbitrary_self_types_dispatch_to_vtable.rs
new file mode 100644
index 00000000000..f9e346ea11e
--- /dev/null
+++ b/tests/ui/self/arbitrary_self_types_dispatch_to_vtable.rs
@@ -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();
+}
diff --git a/tests/ui/wait-forked-but-failed-child.rs b/tests/ui/wait-forked-but-failed-child.rs
index 04f1c1a65d5..4a7f2bee9d9 100644
--- a/tests/ui/wait-forked-but-failed-child.rs
+++ b/tests/ui/wait-forked-but-failed-child.rs
@@ -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")