diff --git a/Cargo.lock b/Cargo.lock index 816bb1a37859f..470b38d5a91cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6221,9 +6221,9 @@ dependencies = [ [[package]] name = "wasi-preview1-component-adapter-provider" -version = "38.0.4" +version = "40.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ec3ef3783e18f2457796ed91b1e6c2adc46f2905f740d1527ab3053fe8e5682" +checksum = "bb5e2b9858989c3a257de4ca169977f4f79897b64e4f482f188f4fcf8ac557d1" [[package]] name = "wasm-bindgen" @@ -6272,17 +6272,18 @@ dependencies = [ [[package]] name = "wasm-component-ld" -version = "0.5.19" +version = "0.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bfc50dd0b883d841bc1dba5ff7020ca52fa7b2c3bb1266d8bf6a09dd032e115" +checksum = "846d20ed66ae37b7a237e36dfcd2fdc979eae82a46cdb0586f9bba80782fd789" dependencies = [ "anyhow", "clap", + "clap_lex", "lexopt", "libc", "tempfile", "wasi-preview1-component-adapter-provider", - "wasmparser 0.241.2", + "wasmparser 0.243.0", "wat", "windows-sys 0.61.2", "winsplit", @@ -6309,24 +6310,24 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.241.2" +version = "0.243.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e01164c9dda68301e34fdae536c23ed6fe90ce6d97213ccc171eebbd3d02d6b8" +checksum = "c55db9c896d70bd9fa535ce83cd4e1f2ec3726b0edd2142079f594fc3be1cb35" dependencies = [ "leb128fmt", - "wasmparser 0.241.2", + "wasmparser 0.243.0", ] [[package]] name = "wasm-metadata" -version = "0.241.2" +version = "0.243.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876fe286f2fa416386deedebe8407e6f19e0b5aeaef3d03161e77a15fa80f167" +checksum = "eae05bf9579f45a62e8d0a4e3f52eaa8da518883ac5afa482ec8256c329ecd56" dependencies = [ "anyhow", "indexmap", - "wasm-encoder 0.241.2", - "wasmparser 0.241.2", + "wasm-encoder 0.243.0", + "wasmparser 0.243.0", ] [[package]] @@ -6351,9 +6352,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.241.2" +version = "0.243.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46d90019b1afd4b808c263e428de644f3003691f243387d30d673211ee0cb8e8" +checksum = "f6d8db401b0528ec316dfbe579e6ab4152d61739cfe076706d2009127970159d" dependencies = [ "bitflags", "hashbrown 0.15.5", @@ -6364,22 +6365,22 @@ dependencies = [ [[package]] name = "wast" -version = "241.0.2" +version = "243.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63f66e07e2ddf531fef6344dbf94d112df7c2f23ed6ffb10962e711500b8d816" +checksum = "df21d01c2d91e46cb7a221d79e58a2d210ea02020d57c092e79255cc2999ca7f" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width 0.2.2", - "wasm-encoder 0.241.2", + "wasm-encoder 0.243.0", ] [[package]] name = "wat" -version = "1.241.2" +version = "1.243.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45f923705c40830af909c5dec2352ec2821202e4a66008194585e1917458a26d" +checksum = "226a9a91cd80a50449312fef0c75c23478fcecfcc4092bdebe1dc8e760ef521b" dependencies = [ "wast", ] @@ -6775,9 +6776,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.241.2" +version = "0.243.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0c57df25e7ee612d946d3b7646c1ddb2310f8280aa2c17e543b66e0812241" +checksum = "36f9fc53513e461ce51dcf17a3e331752cb829f1d187069e54af5608fc998fe4" dependencies = [ "anyhow", "bitflags", @@ -6786,17 +6787,17 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.241.2", + "wasm-encoder 0.243.0", "wasm-metadata", - "wasmparser 0.241.2", + "wasmparser 0.243.0", "wit-parser", ] [[package]] name = "wit-parser" -version = "0.241.2" +version = "0.243.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ef1c6ad67f35c831abd4039c02894de97034100899614d1c44e2268ad01c91" +checksum = "df983a8608e513d8997f435bb74207bf0933d0e49ca97aa9d8a6157164b9b7fc" dependencies = [ "anyhow", "id-arena", @@ -6807,7 +6808,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.241.2", + "wasmparser 0.243.0", ] [[package]] diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 07b04863af6b7..4b2544b7efdf8 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -211,6 +211,10 @@ pub(crate) unsafe fn create_module<'ll>( // LLVM 22 updated the NVPTX layout to indicate 256-bit vector load/store: https://github.com/llvm/llvm-project/pull/155198 target_data_layout = target_data_layout.replace("-i256:256", ""); } + if sess.target.arch == Arch::PowerPC64 { + // LLVM 22 updated the ABI alignment for double on AIX: https://github.com/llvm/llvm-project/pull/144673 + target_data_layout = target_data_layout.replace("-f64:32:64", ""); + } } // Ensure the data-layout values hardcoded remain the defaults. diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 9b9cfa9622220..932e78958345c 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -17,9 +17,9 @@ use rustc_ast_pretty::pprust::state::MacHeader; use rustc_ast_pretty::pprust::{Comments, PrintState}; use rustc_hir::attrs::{AttributeKind, PrintAttribute}; use rustc_hir::{ - BindingMode, ByRef, ConstArgKind, GenericArg, GenericBound, GenericParam, GenericParamKind, - HirId, ImplicitSelfKind, LifetimeParamKind, Node, PatKind, PreciseCapturingArg, RangeEnd, Term, - TyPatKind, + BindingMode, ByRef, ConstArg, ConstArgExprField, ConstArgKind, GenericArg, GenericBound, + GenericParam, GenericParamKind, HirId, ImplicitSelfKind, LifetimeParamKind, Node, PatKind, + PreciseCapturingArg, RangeEnd, Term, TyPatKind, }; use rustc_span::source_map::SourceMap; use rustc_span::{DUMMY_SP, FileName, Ident, Span, Symbol, kw, sym}; @@ -1141,9 +1141,8 @@ impl<'a> State<'a> { fn print_const_arg(&mut self, const_arg: &hir::ConstArg<'_>) { match &const_arg.kind { - // FIXME(mgca): proper printing for struct exprs - ConstArgKind::Struct(..) => self.word("/* STRUCT EXPR */"), - ConstArgKind::TupleCall(..) => self.word("/* TUPLE CALL */"), + ConstArgKind::Struct(qpath, fields) => self.print_const_struct(qpath, fields), + ConstArgKind::TupleCall(qpath, args) => self.print_const_ctor(qpath, args), ConstArgKind::Path(qpath) => self.print_qpath(qpath, true), ConstArgKind::Anon(anon) => self.print_anon_const(anon), ConstArgKind::Error(_, _) => self.word("/*ERROR*/"), @@ -1151,6 +1150,31 @@ impl<'a> State<'a> { } } + fn print_const_struct(&mut self, qpath: &hir::QPath<'_>, fields: &&[&ConstArgExprField<'_>]) { + self.print_qpath(qpath, true); + self.word(" "); + self.word("{"); + if !fields.is_empty() { + self.nbsp(); + } + self.commasep(Inconsistent, *fields, |s, field| { + s.word(field.field.as_str().to_string()); + s.word(":"); + s.nbsp(); + s.print_const_arg(field.expr); + }); + self.word("}"); + } + + fn print_const_ctor(&mut self, qpath: &hir::QPath<'_>, args: &&[&ConstArg<'_, ()>]) { + self.print_qpath(qpath, true); + self.word("("); + self.commasep(Inconsistent, *args, |s, arg| { + s.print_const_arg(arg); + }); + self.word(")"); + } + fn print_call_post(&mut self, args: &[hir::Expr<'_>]) { self.popen(); self.commasep_exprs(Inconsistent, args); diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 1a25f6a582f22..beb0337d8c591 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -190,8 +190,8 @@ impl PickConstraintsForShadowed { // An item never shadows itself candidate.item.def_id != self.def_id // and we're only concerned about inherent impls doing the shadowing. - // Shadowing can only occur if the shadowed is further along - // the Receiver dereferencing chain than the shadowed. + // Shadowing can only occur if the impl being shadowed is further along + // the Receiver dereferencing chain than the impl doing the shadowing. && match candidate.kind { CandidateKind::InherentImplCandidate { receiver_steps, .. } => match self.receiver_steps { Some(shadowed_receiver_steps) => receiver_steps > shadowed_receiver_steps, diff --git a/compiler/rustc_mir_build/src/thir/cx/block.rs b/compiler/rustc_mir_build/src/thir/cx/block.rs index 57ddb8eddb8e7..7cdd70a7fc272 100644 --- a/compiler/rustc_mir_build/src/thir/cx/block.rs +++ b/compiler/rustc_mir_build/src/thir/cx/block.rs @@ -2,8 +2,6 @@ use rustc_hir as hir; use rustc_index::Idx; use rustc_middle::middle::region; use rustc_middle::thir::*; -use rustc_middle::ty; -use rustc_middle::ty::CanonicalUserTypeAnnotation; use tracing::debug; use crate::thir::cx::ThirBuildCx; @@ -73,29 +71,9 @@ impl<'tcx> ThirBuildCx<'tcx> { let else_block = local.els.map(|els| self.mirror_block(els)); - let mut pattern = self.pattern_from_hir(local.pat); + let pattern = self.pattern_from_hir_with_annotation(local.pat, local.ty); debug!(?pattern); - if let Some(ty) = &local.ty - && let Some(&user_ty) = - self.typeck_results.user_provided_types().get(ty.hir_id) - { - debug!("mirror_stmts: user_ty={:?}", user_ty); - let annotation = CanonicalUserTypeAnnotation { - user_ty: Box::new(user_ty), - span: ty.span, - inferred_ty: self.typeck_results.node_type(ty.hir_id), - }; - pattern = Box::new(Pat { - ty: pattern.ty, - span: pattern.span, - kind: PatKind::AscribeUserType { - ascription: Ascription { annotation, variance: ty::Covariant }, - subpattern: pattern, - }, - }); - } - let span = match local.init { Some(init) => local.span.with_hi(init.span.hi()), None => local.span, diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index d26dfac0c2abd..79e85a243f400 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -12,9 +12,6 @@ use rustc_hir::{self as hir, HirId, find_attr}; use rustc_middle::bug; use rustc_middle::thir::*; use rustc_middle::ty::{self, TyCtxt}; -use tracing::instrument; - -use crate::thir::pattern::pat_from_hir; /// Query implementation for [`TyCtxt::thir_body`]. pub(crate) fn thir_body( @@ -111,9 +108,22 @@ impl<'tcx> ThirBuildCx<'tcx> { } } - #[instrument(level = "debug", skip(self))] - fn pattern_from_hir(&mut self, p: &'tcx hir::Pat<'tcx>) -> Box> { - pat_from_hir(self.tcx, self.typing_env, self.typeck_results, p) + fn pattern_from_hir(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box> { + self.pattern_from_hir_with_annotation(pat, None) + } + + fn pattern_from_hir_with_annotation( + &mut self, + pat: &'tcx hir::Pat<'tcx>, + let_stmt_type: Option<&hir::Ty<'tcx>>, + ) -> Box> { + crate::thir::pattern::pat_from_hir( + self.tcx, + self.typing_env, + self.typeck_results, + pat, + let_stmt_type, + ) } fn closure_env_param(&self, owner_def: LocalDefId, expr_id: HirId) -> Option> { diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 988baae491417..4128508955cb3 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -39,11 +39,14 @@ struct PatCtxt<'tcx> { rust_2024_migration: Option>, } +#[instrument(level = "debug", skip(tcx, typing_env, typeck_results), ret)] pub(super) fn pat_from_hir<'tcx>( tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, typeck_results: &'tcx ty::TypeckResults<'tcx>, pat: &'tcx hir::Pat<'tcx>, + // Present if `pat` came from a let statement with an explicit type annotation + let_stmt_type: Option<&hir::Ty<'tcx>>, ) -> Box> { let mut pcx = PatCtxt { tcx, @@ -54,12 +57,35 @@ pub(super) fn pat_from_hir<'tcx>( .get(pat.hir_id) .map(PatMigration::new), }; - let result = pcx.lower_pattern(pat); - debug!("pat_from_hir({:?}) = {:?}", pat, result); + + let mut thir_pat = pcx.lower_pattern(pat); + + // If this pattern came from a let statement with an explicit type annotation + // (e.g. `let x: Foo = ...`), retain that user type information in the THIR pattern. + if let Some(let_stmt_type) = let_stmt_type + && let Some(&user_ty) = typeck_results.user_provided_types().get(let_stmt_type.hir_id) + { + debug!(?user_ty); + let annotation = CanonicalUserTypeAnnotation { + user_ty: Box::new(user_ty), + span: let_stmt_type.span, + inferred_ty: typeck_results.node_type(let_stmt_type.hir_id), + }; + thir_pat = Box::new(Pat { + ty: thir_pat.ty, + span: thir_pat.span, + kind: PatKind::AscribeUserType { + ascription: Ascription { annotation, variance: ty::Covariant }, + subpattern: thir_pat, + }, + }); + } + if let Some(m) = pcx.rust_2024_migration { m.emit(tcx, pat.hir_id); } - result + + thir_pat } impl<'tcx> PatCtxt<'tcx> { diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 0563e9619419b..3294b6802a719 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -778,6 +778,15 @@ fn maybe_record_as_seed<'tcx>( match tcx.def_kind(parent) { DefKind::Impl { of_trait: false } | DefKind::Trait => {} DefKind::Impl { of_trait: true } => { + if let Some(trait_item_def_id) = + tcx.associated_item(owner_id.def_id).trait_item_def_id() + && let Some(trait_item_local_def_id) = trait_item_def_id.as_local() + && let Some(comes_from_allow) = + has_allow_dead_code_or_lang_attr(tcx, trait_item_local_def_id) + { + worklist.push((owner_id.def_id, comes_from_allow)); + } + // We only care about associated items of traits, // because they cannot be visited directly, // so we later mark them as live if their corresponding traits @@ -791,6 +800,14 @@ fn maybe_record_as_seed<'tcx>( } DefKind::Impl { of_trait: true } => { if allow_dead_code.is_none() { + if let Some(trait_def_id) = + tcx.impl_trait_ref(owner_id.def_id).skip_binder().def_id.as_local() + && let Some(comes_from_allow) = + has_allow_dead_code_or_lang_attr(tcx, trait_def_id) + { + worklist.push((owner_id.def_id, comes_from_allow)); + } + unsolved_items.push(owner_id.def_id); } } diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_ibm_aix.rs b/compiler/rustc_target/src/spec/targets/powerpc64_ibm_aix.rs index b4f394643a9dd..b83f5544a3515 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_ibm_aix.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_ibm_aix.rs @@ -17,7 +17,8 @@ pub(crate) fn target() -> Target { std: None, // ? }, pointer_width: 64, - data_layout: "E-m:a-Fi64-i64:64-i128:128-n32:64-S128-v256:256:256-v512:512:512".into(), + data_layout: "E-m:a-Fi64-i64:64-i128:128-n32:64-f64:32:64-S128-v256:256:256-v512:512:512" + .into(), arch: Arch::PowerPC64, options: base, } diff --git a/library/std/src/os/linux/process.rs b/library/std/src/os/linux/process.rs index fef321436f6a3..60851db831bf9 100644 --- a/library/std/src/os/linux/process.rs +++ b/library/std/src/os/linux/process.rs @@ -67,8 +67,10 @@ impl PidFd { /// Waits for the child to exit completely, returning the status that it exited with. /// /// Unlike [`Child::wait`] it does not ensure that the stdin handle is closed. - /// Additionally it will not return an `ExitStatus` if the child - /// has already been reaped. Instead an error will be returned. + /// + /// Additionally on kernels prior to 6.15 only the first attempt to + /// reap a child will return an ExitStatus, further attempts + /// will return an Error. /// /// [`Child::wait`]: process::Child::wait pub fn wait(&self) -> Result { @@ -77,8 +79,8 @@ impl PidFd { /// Attempts to collect the exit status of the child if it has already exited. /// - /// Unlike [`Child::try_wait`] this method will return an Error - /// if the child has already been reaped. + /// On kernels prior to 6.15, and unlike [`Child::try_wait`], only the first attempt + /// to reap a child will return an ExitStatus, further attempts will return an Error. /// /// [`Child::try_wait`]: process::Child::try_wait pub fn try_wait(&self) -> Result> { diff --git a/library/std/src/os/unix/io/mod.rs b/library/std/src/os/unix/io/mod.rs index 6d4090ee31cfc..938a71c835ec9 100644 --- a/library/std/src/os/unix/io/mod.rs +++ b/library/std/src/os/unix/io/mod.rs @@ -92,9 +92,137 @@ #![stable(feature = "rust1", since = "1.0.0")] +use crate::io::{self, Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, Write}; #[stable(feature = "rust1", since = "1.0.0")] pub use crate::os::fd::*; +#[allow(unused_imports)] // not used on all targets +use crate::sys::cvt; // Tests for this module #[cfg(test)] mod tests; + +#[unstable(feature = "stdio_swap", issue = "150667", reason = "recently added")] +pub trait StdioExt: crate::sealed::Sealed { + /// Redirects the stdio file descriptor to point to the file description underpinning `fd`. + /// + /// Rust std::io write buffers (if any) are flushed, but other runtimes + /// (e.g. C stdio) or libraries that acquire a clone of the file descriptor + /// will not be aware of this change. + /// + /// # Platform-specific behavior + /// + /// This is [currently] implemented using + /// + /// - `fd_renumber` on wasip1 + /// - `dup2` on most unixes + /// + /// [currently]: crate::io#platform-specific-behavior + /// + /// ``` + /// #![feature(stdio_swap)] + /// use std::io::{self, Read, Write}; + /// use std::os::unix::io::StdioExt; + /// + /// fn main() -> io::Result<()> { + /// let (reader, mut writer) = io::pipe()?; + /// let mut stdin = io::stdin(); + /// stdin.set_fd(reader)?; + /// writer.write_all(b"Hello, world!")?; + /// let mut buffer = vec![0; 13]; + /// assert_eq!(stdin.read(&mut buffer)?, 13); + /// assert_eq!(&buffer, b"Hello, world!"); + /// Ok(()) + /// } + /// ``` + fn set_fd>(&mut self, fd: T) -> io::Result<()>; + + /// Redirects the stdio file descriptor and returns a new `OwnedFd` + /// backed by the previous file description. + /// + /// See [`set_fd()`] for details. + /// + /// [`set_fd()`]: StdioExt::set_fd + fn replace_fd>(&mut self, replace_with: T) -> io::Result; + + /// Redirects the stdio file descriptor to the null device (`/dev/null`) + /// and returns a new `OwnedFd` backed by the previous file description. + /// + /// Programs that communicate structured data via stdio can use this early in `main()` to + /// extract the fds, treat them as other IO types (`File`, `UnixStream`, etc), + /// apply custom buffering or avoid interference from stdio use later in the program. + /// + /// See [`set_fd()`] for additional details. + /// + /// [`set_fd()`]: StdioExt::set_fd + fn take_fd(&mut self) -> io::Result; +} + +macro io_ext_impl($stdio_ty:ty, $stdio_lock_ty:ty, $writer:literal) { + #[unstable(feature = "stdio_swap", issue = "150667", reason = "recently added")] + impl StdioExt for $stdio_ty { + fn set_fd>(&mut self, fd: T) -> io::Result<()> { + self.lock().set_fd(fd) + } + + fn take_fd(&mut self) -> io::Result { + self.lock().take_fd() + } + + fn replace_fd>(&mut self, replace_with: T) -> io::Result { + self.lock().replace_fd(replace_with) + } + } + + #[unstable(feature = "stdio_swap", issue = "150667", reason = "recently added")] + impl StdioExt for $stdio_lock_ty { + fn set_fd>(&mut self, fd: T) -> io::Result<()> { + #[cfg($writer)] + self.flush()?; + replace_stdio_fd(self.as_fd(), fd.into()) + } + + fn take_fd(&mut self) -> io::Result { + let null = null_fd()?; + let cloned = self.as_fd().try_clone_to_owned()?; + self.set_fd(null)?; + Ok(cloned) + } + + fn replace_fd>(&mut self, replace_with: T) -> io::Result { + let cloned = self.as_fd().try_clone_to_owned()?; + self.set_fd(replace_with)?; + Ok(cloned) + } + } +} + +io_ext_impl!(Stdout, StdoutLock<'_>, true); +io_ext_impl!(Stdin, StdinLock<'_>, false); +io_ext_impl!(Stderr, StderrLock<'_>, true); + +fn null_fd() -> io::Result { + let null_dev = crate::fs::OpenOptions::new().read(true).write(true).open("/dev/null")?; + Ok(null_dev.into()) +} + +/// Replaces the underlying file descriptor with the one from `other`. +/// Does not set CLOEXEC. +fn replace_stdio_fd(this: BorrowedFd<'_>, other: OwnedFd) -> io::Result<()> { + cfg_select! { + all(target_os = "wasi", target_env = "p1") => { + cvt(unsafe { libc::__wasilibc_fd_renumber(other.as_raw_fd(), this.as_raw_fd()) }).map(|_| ()) + } + not(any( + target_arch = "wasm32", + target_os = "hermit", + target_os = "trusty", + target_os = "motor" + )) => { + cvt(unsafe {libc::dup2(other.as_raw_fd(), this.as_raw_fd())}).map(|_| ()) + } + _ => { + Err(io::Error::UNSUPPORTED_PLATFORM) + } + } +} diff --git a/library/std/src/sys/pal/unix/linux/pidfd.rs b/library/std/src/sys/pal/unix/linux/pidfd.rs index 7859854e96b4b..e9e4831fcc02b 100644 --- a/library/std/src/sys/pal/unix/linux/pidfd.rs +++ b/library/std/src/sys/pal/unix/linux/pidfd.rs @@ -1,5 +1,5 @@ use crate::io; -use crate::os::fd::{AsRawFd, FromRawFd, RawFd}; +use crate::os::fd::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::fd::FileDesc; use crate::sys::process::ExitStatus; use crate::sys::{AsInner, FromInner, IntoInner, cvt}; @@ -15,6 +15,73 @@ impl PidFd { self.send_signal(libc::SIGKILL) } + #[cfg(any(test, target_env = "gnu", target_env = "musl"))] + pub(crate) fn current_process() -> io::Result { + let pid = crate::process::id(); + let pidfd = cvt(unsafe { libc::syscall(libc::SYS_pidfd_open, pid, 0) })?; + Ok(unsafe { PidFd::from_raw_fd(pidfd as RawFd) }) + } + + #[cfg(any(test, target_env = "gnu", target_env = "musl"))] + pub(crate) fn pid(&self) -> io::Result { + use crate::sys::weak::weak; + + // since kernel 6.13 + // https://lore.kernel.org/all/20241010155401.2268522-1-luca.boccassi@gmail.com/ + let mut pidfd_info: libc::pidfd_info = unsafe { crate::mem::zeroed() }; + pidfd_info.mask = libc::PIDFD_INFO_PID as u64; + match cvt(unsafe { libc::ioctl(self.0.as_raw_fd(), libc::PIDFD_GET_INFO, &mut pidfd_info) }) + { + Ok(_) => {} + Err(e) if e.raw_os_error() == Some(libc::EINVAL) => { + // kernel doesn't support that ioctl, try the glibc helper that looks at procfs + weak!( + fn pidfd_getpid(pidfd: RawFd) -> libc::pid_t; + ); + if let Some(pidfd_getpid) = pidfd_getpid.get() { + let pid: libc::c_int = cvt(unsafe { pidfd_getpid(self.0.as_raw_fd()) })?; + return Ok(pid as u32); + } + return Err(e); + } + Err(e) => return Err(e), + } + + Ok(pidfd_info.pid) + } + + fn exit_for_reaped_child(&self) -> io::Result { + // since kernel 6.15 + // https://lore.kernel.org/linux-fsdevel/20250305-work-pidfs-kill_on_last_close-v3-0-c8c3d8361705@kernel.org/T/ + let mut pidfd_info: libc::pidfd_info = unsafe { crate::mem::zeroed() }; + pidfd_info.mask = libc::PIDFD_INFO_EXIT as u64; + cvt(unsafe { libc::ioctl(self.0.as_raw_fd(), libc::PIDFD_GET_INFO, &mut pidfd_info) })?; + Ok(ExitStatus::new(pidfd_info.exit_code)) + } + + fn waitid(&self, options: libc::c_int) -> io::Result> { + let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() }; + let r = cvt(unsafe { + libc::waitid(libc::P_PIDFD, self.0.as_raw_fd() as u32, &mut siginfo, options) + }); + match r { + Err(waitid_err) if waitid_err.raw_os_error() == Some(libc::ECHILD) => { + // already reaped + match self.exit_for_reaped_child() { + Ok(exit_status) => return Ok(Some(exit_status)), + Err(_) => return Err(waitid_err), + } + } + Err(e) => return Err(e), + Ok(_) => {} + } + if unsafe { siginfo.si_pid() } == 0 { + Ok(None) + } else { + Ok(Some(ExitStatus::from_waitid_siginfo(siginfo))) + } + } + pub(crate) fn send_signal(&self, signal: i32) -> io::Result<()> { cvt(unsafe { libc::syscall( @@ -29,29 +96,15 @@ impl PidFd { } pub fn wait(&self) -> io::Result { - let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() }; - cvt(unsafe { - libc::waitid(libc::P_PIDFD, self.0.as_raw_fd() as u32, &mut siginfo, libc::WEXITED) - })?; - Ok(ExitStatus::from_waitid_siginfo(siginfo)) + let r = self.waitid(libc::WEXITED)?; + match r { + Some(exit_status) => Ok(exit_status), + None => unreachable!("waitid with WEXITED should not return None"), + } } pub fn try_wait(&self) -> io::Result> { - let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() }; - - cvt(unsafe { - libc::waitid( - libc::P_PIDFD, - self.0.as_raw_fd() as u32, - &mut siginfo, - libc::WEXITED | libc::WNOHANG, - ) - })?; - if unsafe { siginfo.si_pid() } == 0 { - Ok(None) - } else { - Ok(Some(ExitStatus::from_waitid_siginfo(siginfo))) - } + self.waitid(libc::WEXITED | libc::WNOHANG) } } @@ -78,3 +131,9 @@ impl FromRawFd for PidFd { Self(FileDesc::from_raw_fd(fd)) } } + +impl IntoRawFd for PidFd { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} diff --git a/library/std/src/sys/pal/unix/linux/pidfd/tests.rs b/library/std/src/sys/pal/unix/linux/pidfd/tests.rs index 17b06bea91278..a3bb5d5d64ba5 100644 --- a/library/std/src/sys/pal/unix/linux/pidfd/tests.rs +++ b/library/std/src/sys/pal/unix/linux/pidfd/tests.rs @@ -1,8 +1,11 @@ +use super::PidFd as InternalPidFd; use crate::assert_matches::assert_matches; -use crate::os::fd::{AsRawFd, RawFd}; +use crate::io::ErrorKind; +use crate::os::fd::AsRawFd; use crate::os::linux::process::{ChildExt, CommandExt as _}; use crate::os::unix::process::{CommandExt as _, ExitStatusExt}; use crate::process::Command; +use crate::sys::AsInner; #[test] fn test_command_pidfd() { @@ -48,11 +51,22 @@ fn test_command_pidfd() { let mut cmd = Command::new("false"); let mut child = unsafe { cmd.pre_exec(|| Ok(())) }.create_pidfd(true).spawn().unwrap(); - assert!(child.id() > 0 && child.id() < -1i32 as u32); + let id = child.id(); + + assert!(id > 0 && id < -1i32 as u32, "spawning with pidfd still returns a sane pid"); if pidfd_open_available { assert!(child.pidfd().is_ok()) } + + if let Ok(pidfd) = child.pidfd() { + match pidfd.as_inner().pid() { + Ok(pid) => assert_eq!(pid, id), + Err(e) if e.kind() == ErrorKind::InvalidInput => { /* older kernel */ } + Err(e) => panic!("unexpected error getting pid from pidfd: {}", e), + } + } + child.wait().expect("error waiting on child"); } @@ -77,9 +91,15 @@ fn test_pidfd() { assert_eq!(status.signal(), Some(libc::SIGKILL)); // Trying to wait again for a reaped child is safe since there's no pid-recycling race. - // But doing so will return an error. + // But doing so may return an error. let res = fd.wait(); - assert_matches!(res, Err(e) if e.raw_os_error() == Some(libc::ECHILD)); + match res { + // older kernels + Err(e) if e.raw_os_error() == Some(libc::ECHILD) => {} + // 6.15+ + Ok(exit) if exit.signal() == Some(libc::SIGKILL) => {} + other => panic!("expected ECHILD error, got {:?}", other), + } // Ditto for additional attempts to kill an already-dead child. let res = fd.kill(); @@ -87,13 +107,5 @@ fn test_pidfd() { } fn probe_pidfd_support() -> bool { - // pidfds require the pidfd_open syscall - let our_pid = crate::process::id(); - let pidfd = unsafe { libc::syscall(libc::SYS_pidfd_open, our_pid, 0) }; - if pidfd >= 0 { - unsafe { libc::close(pidfd as RawFd) }; - true - } else { - false - } + InternalPidFd::current_process().is_ok() } diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs index 5ba57e11679cf..df64a1716d523 100644 --- a/library/std/src/sys/process/unix/unix.rs +++ b/library/std/src/sys/process/unix/unix.rs @@ -482,10 +482,6 @@ impl Command { ) -> libc::c_int; ); - weak!( - fn pidfd_getpid(pidfd: libc::c_int) -> libc::c_int; - ); - static PIDFD_SUPPORTED: Atomic = AtomicU8::new(0); const UNKNOWN: u8 = 0; const SPAWN: u8 = 1; @@ -502,24 +498,26 @@ impl Command { } if support == UNKNOWN { support = NO; - let our_pid = crate::process::id(); - let pidfd = cvt(unsafe { libc::syscall(libc::SYS_pidfd_open, our_pid, 0) } as c_int); - match pidfd { + + match PidFd::current_process() { Ok(pidfd) => { + // if pidfd_open works then we at least know the fork path is available. support = FORK_EXEC; - if let Some(Ok(pid)) = pidfd_getpid.get().map(|f| cvt(unsafe { f(pidfd) } as i32)) { - if pidfd_spawnp.get().is_some() && pid as u32 == our_pid { - support = SPAWN - } + // but for the fast path we need both spawnp and the + // pidfd -> pid conversion to work. + if pidfd_spawnp.get().is_some() && let Ok(pid) = pidfd.pid() { + assert_eq!(pid, crate::process::id(), "sanity check"); + support = SPAWN; } - unsafe { libc::close(pidfd) }; } Err(e) if e.raw_os_error() == Some(libc::EMFILE) => { - // We're temporarily(?) out of file descriptors. In this case obtaining a pidfd would also fail + // We're temporarily(?) out of file descriptors. In this case pidfd_spawnp would also fail // Don't update the support flag so we can probe again later. return Err(e) } - _ => {} + _ => { + // pidfd_open not available? likely an old kernel without pidfd support. + } } PIDFD_SUPPORTED.store(support, Ordering::Relaxed); if support == FORK_EXEC { @@ -791,13 +789,17 @@ impl Command { } spawn_res?; - let pid = match cvt(pidfd_getpid.get().unwrap()(pidfd)) { + use crate::os::fd::{FromRawFd, IntoRawFd}; + + let pidfd = PidFd::from_raw_fd(pidfd); + let pid = match pidfd.pid() { Ok(pid) => pid, Err(e) => { // The child has been spawned and we are holding its pidfd. - // But we cannot obtain its pid even though pidfd_getpid support was verified earlier. - // This might happen if libc can't open procfs because the file descriptor limit has been reached. - libc::close(pidfd); + // But we cannot obtain its pid even though pidfd_spawnp and getpid support + // was verified earlier. + // This is quite unlikely, but might happen if the ioctl is not supported, + // glibc tries to use procfs and we're out of file descriptors. return Err(Error::new( e.kind(), "pidfd_spawnp succeeded but the child's PID could not be obtained", @@ -805,7 +807,7 @@ impl Command { } }; - return Ok(Some(Process::new(pid, pidfd))); + return Ok(Some(Process::new(pid as i32, pidfd.into_raw_fd()))); } // Safety: -1 indicates we don't have a pidfd. diff --git a/src/tools/wasm-component-ld/Cargo.toml b/src/tools/wasm-component-ld/Cargo.toml index 744b67c17f65b..2d44358c74334 100644 --- a/src/tools/wasm-component-ld/Cargo.toml +++ b/src/tools/wasm-component-ld/Cargo.toml @@ -10,4 +10,4 @@ name = "wasm-component-ld" path = "src/main.rs" [dependencies] -wasm-component-ld = "0.5.19" +wasm-component-ld = "0.5.20" diff --git a/tests/ui/lint/dead-code/allow-trait-or-impl.rs b/tests/ui/lint/dead-code/allow-trait-or-impl.rs new file mode 100644 index 0000000000000..92817549a91e3 --- /dev/null +++ b/tests/ui/lint/dead-code/allow-trait-or-impl.rs @@ -0,0 +1,38 @@ +#![deny(dead_code)] + +pub mod a { + pub trait Foo { } + impl Foo for u32 { } + + struct PrivateType; //~ ERROR struct `PrivateType` is never constructed + impl Foo for PrivateType { } // <-- warns as dead, even though Foo is public + + struct AnotherPrivateType; //~ ERROR struct `AnotherPrivateType` is never constructed + impl Foo for AnotherPrivateType { } // <-- warns as dead, even though Foo is public +} + +pub mod b { + #[allow(dead_code)] + pub trait Foo { } + impl Foo for u32 { } + + struct PrivateType; + impl Foo for PrivateType { } // <-- no warning, trait is "allowed" + + struct AnotherPrivateType; + impl Foo for AnotherPrivateType { } // <-- no warning, trait is "allowed" +} + +pub mod c { + pub trait Foo { } + impl Foo for u32 { } + + struct PrivateType; + #[allow(dead_code)] + impl Foo for PrivateType { } // <-- no warning, impl is allowed + + struct AnotherPrivateType; //~ ERROR struct `AnotherPrivateType` is never constructed + impl Foo for AnotherPrivateType { } // <-- warns as dead, even though Foo is public +} + +fn main() {} diff --git a/tests/ui/lint/dead-code/allow-trait-or-impl.stderr b/tests/ui/lint/dead-code/allow-trait-or-impl.stderr new file mode 100644 index 0000000000000..130116b6c91d0 --- /dev/null +++ b/tests/ui/lint/dead-code/allow-trait-or-impl.stderr @@ -0,0 +1,26 @@ +error: struct `PrivateType` is never constructed + --> $DIR/allow-trait-or-impl.rs:7:12 + | +LL | struct PrivateType; + | ^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/allow-trait-or-impl.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: struct `AnotherPrivateType` is never constructed + --> $DIR/allow-trait-or-impl.rs:10:12 + | +LL | struct AnotherPrivateType; + | ^^^^^^^^^^^^^^^^^^ + +error: struct `AnotherPrivateType` is never constructed + --> $DIR/allow-trait-or-impl.rs:34:12 + | +LL | struct AnotherPrivateType; + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/lint/dead-code/allow-unused-trait.rs b/tests/ui/lint/dead-code/allow-unused-trait.rs new file mode 100644 index 0000000000000..4eb63bd4d27ab --- /dev/null +++ b/tests/ui/lint/dead-code/allow-unused-trait.rs @@ -0,0 +1,29 @@ +//@ check-pass + +#![deny(dead_code)] + +#[allow(dead_code)] +trait Foo { + const FOO: u32; + type Baz; + fn foobar(); +} + +const fn bar(x: u32) -> u32 { + x +} + +struct Qux; + +struct FooBar; + +impl Foo for u32 { + const FOO: u32 = bar(0); + type Baz = Qux; + + fn foobar() { + let _ = FooBar; + } +} + +fn main() {} diff --git a/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.rs b/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.rs new file mode 100644 index 0000000000000..5f2d6680efac6 --- /dev/null +++ b/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.rs @@ -0,0 +1,33 @@ +//@ compile-flags: -Zunpretty=hir +//@ check-pass + +#![feature(min_generic_const_args, adt_const_params)] +#![expect(incomplete_features)] +#![allow(dead_code)] + +use std::marker::ConstParamTy; + +struct Point(u32, u32); + +struct Point3(); + +struct Point1 { + a: u32, + b: u32, +} + +struct Point2 {} + +fn with_point() {} +fn with_point1() {} +fn with_point2() {} +fn with_point3() {} + +fn test() { + with_point::<{ Point(N, N) }>(); + with_point1::<{ Point1 { a: N, b: N } }>(); + with_point2::<{ Point2 {} }>(); + with_point3::<{ Point3() }>(); +} + +fn main() {} diff --git a/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout b/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout new file mode 100644 index 0000000000000..fc0fbe8ef237a --- /dev/null +++ b/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout @@ -0,0 +1,39 @@ +//@ compile-flags: -Zunpretty=hir +//@ check-pass + +#![feature(min_generic_const_args, adt_const_params)] +#![expect(incomplete_features)] +#![allow(dead_code)] +#[attr = MacroUse {arguments: UseAll}] +extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; + +use std::marker::ConstParamTy; + +struct Point(u32, u32); + +struct Point3(); + +struct Point1 { + a: u32, + b: u32, +} + +struct Point2 { +} + +fn with_point() { } +fn with_point1() { } +fn with_point2() { } +fn with_point3() { } + +fn test() { + with_point::(); + with_point1::(); + with_point2::(); + with_point3::(); +} + +fn main() { }