From 68abe69f13be3d3864f6fa3136a5a19a776549ea Mon Sep 17 00:00:00 2001 From: bendn Date: Thu, 20 Nov 2025 15:21:27 +0700 Subject: [PATCH 1/6] iter::ArrayChunks::into_remainder ought not return option --- library/core/src/iter/adapters/array_chunks.rs | 6 +++--- library/core/src/iter/traits/iterator.rs | 2 +- library/coretests/tests/iter/adapters/array_chunks.rs | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/library/core/src/iter/adapters/array_chunks.rs b/library/core/src/iter/adapters/array_chunks.rs index 8f1744fc5fbb7..967136288865c 100644 --- a/library/core/src/iter/adapters/array_chunks.rs +++ b/library/core/src/iter/adapters/array_chunks.rs @@ -40,17 +40,17 @@ where /// # // Also serves as a regression test for https://github.com/rust-lang/rust/issues/123333 /// # #![feature(iter_array_chunks)] /// let x = [1,2,3,4,5].into_iter().array_chunks::<2>(); - /// let mut rem = x.into_remainder().unwrap(); + /// let mut rem = x.into_remainder(); /// assert_eq!(rem.next(), Some(5)); /// assert_eq!(rem.next(), None); /// ``` #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] #[inline] - pub fn into_remainder(mut self) -> Option> { + pub fn into_remainder(mut self) -> array::IntoIter { if self.remainder.is_none() { while let Some(_) = self.next() {} } - self.remainder + self.remainder.unwrap_or_default() } } diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 695f8d1e195e9..29230b1665380 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -3523,7 +3523,7 @@ pub trait Iterator { /// assert_eq!(iter.next(), Some(['l', 'o'])); /// assert_eq!(iter.next(), Some(['r', 'e'])); /// assert_eq!(iter.next(), None); - /// assert_eq!(iter.into_remainder().unwrap().as_slice(), &['m']); + /// assert_eq!(iter.into_remainder().as_slice(), &['m']); /// ``` /// /// ``` diff --git a/library/coretests/tests/iter/adapters/array_chunks.rs b/library/coretests/tests/iter/adapters/array_chunks.rs index fb19a519f63b5..e6e279b14e626 100644 --- a/library/coretests/tests/iter/adapters/array_chunks.rs +++ b/library/coretests/tests/iter/adapters/array_chunks.rs @@ -18,10 +18,10 @@ fn test_iterator_array_chunks_clone_and_drop() { assert_eq!(count.get(), 3); let mut it2 = it.clone(); assert_eq!(count.get(), 3); - assert_eq!(it.into_remainder().unwrap().len(), 2); + assert_eq!(it.into_remainder().len(), 2); assert_eq!(count.get(), 5); assert!(it2.next().is_none()); - assert_eq!(it2.into_remainder().unwrap().len(), 2); + assert_eq!(it2.into_remainder().len(), 2); assert_eq!(count.get(), 7); } @@ -31,7 +31,7 @@ fn test_iterator_array_chunks_remainder() { assert_eq!(it.next(), Some([0, 1, 2, 3])); assert_eq!(it.next(), Some([4, 5, 6, 7])); assert_eq!(it.next(), None); - assert_eq!(it.into_remainder().unwrap().as_slice(), &[8, 9, 10]); + assert_eq!(it.into_remainder().as_slice(), &[8, 9, 10]); } #[test] @@ -89,7 +89,7 @@ fn test_iterator_array_chunks_next_and_next_back() { assert_eq!(it.next(), None); assert_eq!(it.next_back(), None); assert_eq!(it.next(), None); - assert_eq!(it.into_remainder().unwrap().as_slice(), &[9, 10]); + assert_eq!(it.into_remainder().as_slice(), &[9, 10]); } #[test] @@ -102,7 +102,7 @@ fn test_iterator_array_chunks_rev_remainder() { assert_eq!(it.next(), None); assert_eq!(it.next(), None); } - assert_eq!(it.into_remainder().unwrap().as_slice(), &[8, 9, 10]); + assert_eq!(it.into_remainder().as_slice(), &[8, 9, 10]); } #[test] From 3f08502242399d10f879a5573c7f6ee1568462c4 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Thu, 20 Nov 2025 15:50:19 +0530 Subject: [PATCH 2/6] std: sys: net: uefi: Implement read_vectored - Basically a copy of write_vectored [0] - Tested on QEMU ovmf. [0]: https://github.com/rust-lang/rust/pull/146301 Signed-off-by: Ayush Singh --- .../std/src/sys/net/connection/uefi/mod.rs | 5 +- .../std/src/sys/net/connection/uefi/tcp.rs | 12 +++- .../std/src/sys/net/connection/uefi/tcp4.rs | 68 +++++++++++++++---- 3 files changed, 69 insertions(+), 16 deletions(-) diff --git a/library/std/src/sys/net/connection/uefi/mod.rs b/library/std/src/sys/net/connection/uefi/mod.rs index d76e3e576f330..db2d18646d026 100644 --- a/library/std/src/sys/net/connection/uefi/mod.rs +++ b/library/std/src/sys/net/connection/uefi/mod.rs @@ -69,12 +69,11 @@ impl TcpStream { } pub fn read_vectored(&self, buf: &mut [IoSliceMut<'_>]) -> io::Result { - // FIXME: UEFI does support vectored read, so implement that. - crate::io::default_read_vectored(|b| self.read(b), buf) + self.inner.read_vectored(buf, self.read_timeout()?) } pub fn is_read_vectored(&self) -> bool { - false + true } pub fn write(&self, buf: &[u8]) -> io::Result { diff --git a/library/std/src/sys/net/connection/uefi/tcp.rs b/library/std/src/sys/net/connection/uefi/tcp.rs index 16283e64fb35a..1e7e829c85f35 100644 --- a/library/std/src/sys/net/connection/uefi/tcp.rs +++ b/library/std/src/sys/net/connection/uefi/tcp.rs @@ -1,5 +1,5 @@ use super::tcp4; -use crate::io::{self, IoSlice}; +use crate::io::{self, IoSlice, IoSliceMut}; use crate::net::SocketAddr; use crate::ptr::NonNull; use crate::sys::{helpers, unsupported}; @@ -44,6 +44,16 @@ impl Tcp { } } + pub(crate) fn read_vectored( + &self, + buf: &mut [IoSliceMut<'_>], + timeout: Option, + ) -> io::Result { + match self { + Self::V4(client) => client.read_vectored(buf, timeout), + } + } + pub(crate) fn ttl(&self) -> io::Result { match self { Self::V4(client) => client.get_mode_data().map(|x| x.time_to_live.into()), diff --git a/library/std/src/sys/net/connection/uefi/tcp4.rs b/library/std/src/sys/net/connection/uefi/tcp4.rs index ba0424454d738..0409997f02721 100644 --- a/library/std/src/sys/net/connection/uefi/tcp4.rs +++ b/library/std/src/sys/net/connection/uefi/tcp4.rs @@ -1,7 +1,7 @@ use r_efi::efi::{self, Status}; use r_efi::protocols::tcp4; -use crate::io::{self, IoSlice}; +use crate::io::{self, IoSlice, IoSliceMut}; use crate::net::SocketAddrV4; use crate::ptr::NonNull; use crate::sync::atomic::{AtomicBool, Ordering}; @@ -193,30 +193,74 @@ impl Tcp4 { } pub(crate) fn read(&self, buf: &mut [u8], timeout: Option) -> io::Result { - let evt = unsafe { self.create_evt() }?; - let completion_token = - tcp4::CompletionToken { event: evt.as_ptr(), status: Status::SUCCESS }; let data_len = u32::try_from(buf.len()).unwrap_or(u32::MAX); let fragment = tcp4::FragmentData { fragment_length: data_len, fragment_buffer: buf.as_mut_ptr().cast::(), }; - let mut tx_data = tcp4::ReceiveData { + let mut rx_data = tcp4::ReceiveData { urgent_flag: r_efi::efi::Boolean::FALSE, data_length: data_len, fragment_count: 1, fragment_table: [fragment], }; - let protocol = self.protocol.as_ptr(); - let mut token = tcp4::IoToken { - completion_token, - packet: tcp4::IoTokenPacket { - rx_data: (&raw mut tx_data).cast::>(), - }, + self.read_inner((&raw mut rx_data).cast(), timeout).map(|_| data_len as usize) + } + + pub(crate) fn read_vectored( + &self, + buf: &[IoSliceMut<'_>], + timeout: Option, + ) -> io::Result { + let mut data_length = 0u32; + let mut fragment_count = 0u32; + + // Calculate how many IoSlice in buf can be transmitted. + for i in buf { + // IoSlice length is always <= u32::MAX in UEFI. + match data_length.checked_add(u32::try_from(i.len()).expect("value is stored as a u32")) + { + Some(x) => data_length = x, + None => break, + } + fragment_count += 1; + } + + let rx_data_size = size_of::>() + + size_of::() * (fragment_count as usize); + let mut rx_data = helpers::UefiBox::::new(rx_data_size)?; + rx_data.write(tcp4::ReceiveData { + urgent_flag: r_efi::efi::Boolean::FALSE, + data_length, + fragment_count, + fragment_table: [], + }); + unsafe { + // SAFETY: IoSlice and FragmentData are guaranteed to have same layout. + crate::ptr::copy_nonoverlapping( + buf.as_ptr().cast(), + (*rx_data.as_mut_ptr()).fragment_table.as_mut_ptr(), + fragment_count as usize, + ); }; + self.read_inner(rx_data.as_mut_ptr(), timeout).map(|_| data_length as usize) + } + + pub(crate) fn read_inner( + &self, + rx_data: *mut tcp4::ReceiveData, + timeout: Option, + ) -> io::Result<()> { + let evt = unsafe { self.create_evt() }?; + let completion_token = + tcp4::CompletionToken { event: evt.as_ptr(), status: Status::SUCCESS }; + + let protocol = self.protocol.as_ptr(); + let mut token = tcp4::IoToken { completion_token, packet: tcp4::IoTokenPacket { rx_data } }; + let r = unsafe { ((*protocol).receive)(protocol, &mut token) }; if r.is_error() { return Err(io::Error::from_raw_os_error(r.as_usize())); @@ -227,7 +271,7 @@ impl Tcp4 { if completion_token.status.is_error() { Err(io::Error::from_raw_os_error(completion_token.status.as_usize())) } else { - Ok(data_len as usize) + Ok(()) } } From 5590d7db51265a3fabbc47a25a679ba8d32a5959 Mon Sep 17 00:00:00 2001 From: nxsaken Date: Thu, 20 Nov 2025 14:33:01 +0400 Subject: [PATCH 3/6] Constify `residual_into_try_type` --- library/core/src/ops/try_trait.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/library/core/src/ops/try_trait.rs b/library/core/src/ops/try_trait.rs index f68782c804cdb..204291886589e 100644 --- a/library/core/src/ops/try_trait.rs +++ b/library/core/src/ops/try_trait.rs @@ -371,11 +371,14 @@ pub const trait Residual: Sized { /// but importantly not on the contextual type the way it would be if /// we called `<_ as FromResidual>::from_residual(r)` directly. #[unstable(feature = "try_trait_v2_residual", issue = "91285")] +#[rustc_const_unstable(feature = "const_try_residual", issue = "91285")] // needs to be `pub` to avoid `private type` errors #[expect(unreachable_pub)] #[inline] // FIXME: force would be nice, but fails -- see #148915 #[lang = "into_try_type"] -pub fn residual_into_try_type, O>(r: R) -> >::TryType { +pub const fn residual_into_try_type, O>( + r: R, +) -> >::TryType { FromResidual::from_residual(r) } From ebf1c38aa67f113c3a638fe467c75b0b154c903c Mon Sep 17 00:00:00 2001 From: Max Dexheimer Date: Thu, 20 Nov 2025 21:25:19 +0100 Subject: [PATCH 4/6] Check for intrinsic to fn ptr casts in unified coercions Ensures that when coercing multiple expressions to a unified type, the same "intrinsic to fn ptr" check is applied as for other coercions. --- compiler/rustc_hir_typeck/src/coercion.rs | 16 +++++++++-- .../intrinsic-in-unifying-coercion-149143.rs | 25 +++++++++++++++++ ...trinsic-in-unifying-coercion-149143.stderr | 27 +++++++++++++++++++ 3 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 tests/ui/coercion/intrinsic-in-unifying-coercion-149143.rs create mode 100644 tests/ui/coercion/intrinsic-in-unifying-coercion-149143.stderr diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 857e4f66489ab..009caad51eacb 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1291,14 +1291,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Closure(..) => { Adjust::Pointer(PointerCoercion::ClosureFnPointer(a_sig.safety())) } - ty::FnDef(..) => Adjust::Pointer(PointerCoercion::ReifyFnPointer), + ty::FnDef(def_id, ..) => { + // Intrinsics are not coercible to function pointers + if self.tcx.intrinsic(def_id).is_some() { + return Err(TypeError::IntrinsicCast); + } + Adjust::Pointer(PointerCoercion::ReifyFnPointer) + } _ => span_bug!(cause.span, "should not try to coerce a {prev_ty} to a fn pointer"), }; let next_adjustment = match new_ty.kind() { ty::Closure(..) => { Adjust::Pointer(PointerCoercion::ClosureFnPointer(b_sig.safety())) } - ty::FnDef(..) => Adjust::Pointer(PointerCoercion::ReifyFnPointer), + ty::FnDef(def_id, ..) => { + // Intrinsics are not coercible to function pointers + if self.tcx.intrinsic(def_id).is_some() { + return Err(TypeError::IntrinsicCast); + } + Adjust::Pointer(PointerCoercion::ReifyFnPointer) + } _ => span_bug!(new.span, "should not try to coerce a {new_ty} to a fn pointer"), }; for expr in exprs.iter().map(|e| e.as_coercion_site()) { diff --git a/tests/ui/coercion/intrinsic-in-unifying-coercion-149143.rs b/tests/ui/coercion/intrinsic-in-unifying-coercion-149143.rs new file mode 100644 index 0000000000000..f20a491916f6d --- /dev/null +++ b/tests/ui/coercion/intrinsic-in-unifying-coercion-149143.rs @@ -0,0 +1,25 @@ +// Regression test for #149143. +// The compiler did not check for a coercion from intrinsics +// to fn ptrs in all possible code paths that could lead to such a coercion. +// This caused an ICE during a later sanity check. + +use std::mem::transmute; + +fn main() { + unsafe { + let f = if true { transmute } else { safe_transmute }; + //~^ ERROR `if` and `else` have incompatible type + + let _: i64 = f(5i64); + } + unsafe { + let f = if true { safe_transmute } else { transmute }; + //~^ ERROR `if` and `else` have incompatible type + + let _: i64 = f(5i64); + } +} + +unsafe fn safe_transmute(x: A) -> B { + panic!() +} diff --git a/tests/ui/coercion/intrinsic-in-unifying-coercion-149143.stderr b/tests/ui/coercion/intrinsic-in-unifying-coercion-149143.stderr new file mode 100644 index 0000000000000..43fc194deea2b --- /dev/null +++ b/tests/ui/coercion/intrinsic-in-unifying-coercion-149143.stderr @@ -0,0 +1,27 @@ +error[E0308]: `if` and `else` have incompatible types + --> $DIR/intrinsic-in-unifying-coercion-149143.rs:10:46 + | +LL | let f = if true { transmute } else { safe_transmute }; + | --------- ^^^^^^^^^^^^^^ cannot coerce intrinsics to function pointers + | | + | expected because of this + | + = note: expected fn item `unsafe fn(_) -> _ {std::intrinsics::transmute::<_, _>}` + found fn item `unsafe fn(_) -> _ {safe_transmute::<_, _>}` + = note: different fn items have unique types, even if their signatures are the same + +error[E0308]: `if` and `else` have incompatible types + --> $DIR/intrinsic-in-unifying-coercion-149143.rs:16:51 + | +LL | let f = if true { safe_transmute } else { transmute }; + | -------------- ^^^^^^^^^ cannot coerce intrinsics to function pointers + | | + | expected because of this + | + = note: expected fn item `unsafe fn(_) -> _ {safe_transmute::<_, _>}` + found fn item `unsafe fn(_) -> _ {std::intrinsics::transmute::<_, _>}` + = note: different fn items have unique types, even if their signatures are the same + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. From c81d22d5066d3e04861bdd6b44c9c6facacc2e93 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 11 Nov 2025 17:44:29 +1100 Subject: [PATCH 5/6] Extract module `rustc_session::config::print_request` --- compiler/rustc_session/src/config.rs | 181 +---------------- .../rustc_session/src/config/print_request.rs | 185 ++++++++++++++++++ 2 files changed, 189 insertions(+), 177 deletions(-) create mode 100644 compiler/rustc_session/src/config/print_request.rs diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index b229db1a4b16d..f97a29e064b65 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -36,6 +36,7 @@ use tracing::debug; pub use crate::config::cfg::{Cfg, CheckCfg, ExpectedValues}; use crate::config::native_libs::parse_native_libs; +pub use crate::config::print_request::{PrintKind, PrintRequest}; use crate::errors::FileWriteFail; pub use crate::options::*; use crate::search_paths::SearchPath; @@ -45,37 +46,9 @@ use crate::{EarlyDiagCtxt, HashStableContext, Session, filesearch, lint}; mod cfg; mod externs; mod native_libs; +mod print_request; pub mod sigpipe; -pub const PRINT_KINDS: &[(&str, PrintKind)] = &[ - // tidy-alphabetical-start - ("all-target-specs-json", PrintKind::AllTargetSpecsJson), - ("calling-conventions", PrintKind::CallingConventions), - ("cfg", PrintKind::Cfg), - ("check-cfg", PrintKind::CheckCfg), - ("code-models", PrintKind::CodeModels), - ("crate-name", PrintKind::CrateName), - ("crate-root-lint-levels", PrintKind::CrateRootLintLevels), - ("deployment-target", PrintKind::DeploymentTarget), - ("file-names", PrintKind::FileNames), - ("host-tuple", PrintKind::HostTuple), - ("link-args", PrintKind::LinkArgs), - ("native-static-libs", PrintKind::NativeStaticLibs), - ("relocation-models", PrintKind::RelocationModels), - ("split-debuginfo", PrintKind::SplitDebuginfo), - ("stack-protector-strategies", PrintKind::StackProtectorStrategies), - ("supported-crate-types", PrintKind::SupportedCrateTypes), - ("sysroot", PrintKind::Sysroot), - ("target-cpus", PrintKind::TargetCPUs), - ("target-features", PrintKind::TargetFeatures), - ("target-libdir", PrintKind::TargetLibdir), - ("target-list", PrintKind::TargetList), - ("target-spec-json", PrintKind::TargetSpecJson), - ("target-spec-json-schema", PrintKind::TargetSpecJsonSchema), - ("tls-models", PrintKind::TlsModels), - // tidy-alphabetical-end -]; - /// The different settings that the `-C strip` flag can have. #[derive(Clone, Copy, PartialEq, Hash, Debug)] pub enum Strip { @@ -1015,42 +988,6 @@ impl ExternEntry { } } -#[derive(Clone, PartialEq, Debug)] -pub struct PrintRequest { - pub kind: PrintKind, - pub out: OutFileName, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum PrintKind { - // tidy-alphabetical-start - AllTargetSpecsJson, - CallingConventions, - Cfg, - CheckCfg, - CodeModels, - CrateName, - CrateRootLintLevels, - DeploymentTarget, - FileNames, - HostTuple, - LinkArgs, - NativeStaticLibs, - RelocationModels, - SplitDebuginfo, - StackProtectorStrategies, - SupportedCrateTypes, - Sysroot, - TargetCPUs, - TargetFeatures, - TargetLibdir, - TargetList, - TargetSpecJson, - TargetSpecJsonSchema, - TlsModels, - // tidy-alphabetical-end -} - #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Default)] pub struct NextSolverConfig { /// Whether the new trait solver should be enabled in coherence. @@ -1798,14 +1735,6 @@ The default is {DEFAULT_EDITION} and the latest stable edition is {LATEST_STABLE ) }); -static PRINT_HELP: LazyLock = LazyLock::new(|| { - format!( - "Compiler information to print on stdout (or to a file)\n\ - INFO may be one of <{}>.", - PRINT_KINDS.iter().map(|(name, _)| format!("{name}")).collect::>().join("|") - ) -}); - static EMIT_HELP: LazyLock = LazyLock::new(|| { let mut result = String::from("Comma separated list of types of output for the compiler to emit.\n"); @@ -1872,7 +1801,7 @@ pub fn rustc_optgroups() -> Vec { opt(Stable, Opt, "", "crate-name", "Specify the name of the crate being built", ""), opt(Stable, Opt, "", "edition", &EDITION_STRING, EDITION_NAME_LIST), opt(Stable, Multi, "", "emit", &EMIT_HELP, "[=]"), - opt(Stable, Multi, "", "print", &PRINT_HELP, "[=]"), + opt(Stable, Multi, "", "print", &print_request::PRINT_HELP, "[=]"), opt(Stable, FlagMulti, "g", "", "Equivalent to -C debuginfo=2", ""), opt(Stable, FlagMulti, "O", "", "Equivalent to -C opt-level=3", ""), opt(Stable, Opt, "o", "", "Write output to FILENAME", ""), @@ -2320,108 +2249,6 @@ fn should_override_cgus_and_disable_thinlto( (disable_local_thinlto, codegen_units) } -fn collect_print_requests( - early_dcx: &EarlyDiagCtxt, - cg: &mut CodegenOptions, - unstable_opts: &UnstableOptions, - matches: &getopts::Matches, -) -> Vec { - let mut prints = Vec::::new(); - if cg.target_cpu.as_deref() == Some("help") { - prints.push(PrintRequest { kind: PrintKind::TargetCPUs, out: OutFileName::Stdout }); - cg.target_cpu = None; - }; - if cg.target_feature == "help" { - prints.push(PrintRequest { kind: PrintKind::TargetFeatures, out: OutFileName::Stdout }); - cg.target_feature = String::new(); - } - - // We disallow reusing the same path in multiple prints, such as `--print - // cfg=output.txt --print link-args=output.txt`, because outputs are printed - // by disparate pieces of the compiler, and keeping track of which files - // need to be overwritten vs appended to is annoying. - let mut printed_paths = FxHashSet::default(); - - prints.extend(matches.opt_strs("print").into_iter().map(|req| { - let (req, out) = split_out_file_name(&req); - - let kind = if let Some((print_name, print_kind)) = - PRINT_KINDS.iter().find(|&&(name, _)| name == req) - { - check_print_request_stability(early_dcx, unstable_opts, (print_name, *print_kind)); - *print_kind - } else { - let is_nightly = nightly_options::match_is_nightly_build(matches); - emit_unknown_print_request_help(early_dcx, req, is_nightly) - }; - - let out = out.unwrap_or(OutFileName::Stdout); - if let OutFileName::Real(path) = &out { - if !printed_paths.insert(path.clone()) { - early_dcx.early_fatal(format!( - "cannot print multiple outputs to the same path: {}", - path.display(), - )); - } - } - - PrintRequest { kind, out } - })); - - prints -} - -fn check_print_request_stability( - early_dcx: &EarlyDiagCtxt, - unstable_opts: &UnstableOptions, - (print_name, print_kind): (&str, PrintKind), -) { - if !is_print_request_stable(print_kind) && !unstable_opts.unstable_options { - early_dcx.early_fatal(format!( - "the `-Z unstable-options` flag must also be passed to enable the `{print_name}` \ - print option" - )); - } -} - -fn is_print_request_stable(print_kind: PrintKind) -> bool { - match print_kind { - PrintKind::AllTargetSpecsJson - | PrintKind::CheckCfg - | PrintKind::CrateRootLintLevels - | PrintKind::SupportedCrateTypes - | PrintKind::TargetSpecJson - | PrintKind::TargetSpecJsonSchema => false, - _ => true, - } -} - -fn emit_unknown_print_request_help(early_dcx: &EarlyDiagCtxt, req: &str, is_nightly: bool) -> ! { - let prints = PRINT_KINDS - .iter() - .filter_map(|(name, kind)| { - // If we're not on nightly, we don't want to print unstable options - if !is_nightly && !is_print_request_stable(*kind) { - None - } else { - Some(format!("`{name}`")) - } - }) - .collect::>(); - let prints = prints.join(", "); - - let mut diag = early_dcx.early_struct_fatal(format!("unknown print request: `{req}`")); - #[allow(rustc::diagnostic_outside_of_impl)] - diag.help(format!("valid print requests are: {prints}")); - - if req == "lints" { - diag.help(format!("use `-Whelp` to print a list of lints")); - } - - diag.help(format!("for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information")); - diag.emit() -} - pub fn parse_target_triple(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> TargetTuple { match matches.opt_str("target") { Some(target) if target.ends_with(".json") => { @@ -2846,7 +2673,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M )); } - let prints = collect_print_requests(early_dcx, &mut cg, &unstable_opts, matches); + let prints = print_request::collect_print_requests(early_dcx, &mut cg, &unstable_opts, matches); // -Zretpoline-external-thunk also requires -Zretpoline if unstable_opts.retpoline_external_thunk { diff --git a/compiler/rustc_session/src/config/print_request.rs b/compiler/rustc_session/src/config/print_request.rs new file mode 100644 index 0000000000000..6226a8aee7d36 --- /dev/null +++ b/compiler/rustc_session/src/config/print_request.rs @@ -0,0 +1,185 @@ +//! Code for dealing with `--print` requests. + +use std::sync::LazyLock; + +use rustc_data_structures::fx::FxHashSet; + +use crate::EarlyDiagCtxt; +use crate::config::{ + CodegenOptions, OutFileName, UnstableOptions, nightly_options, split_out_file_name, +}; + +const PRINT_KINDS: &[(&str, PrintKind)] = &[ + // tidy-alphabetical-start + ("all-target-specs-json", PrintKind::AllTargetSpecsJson), + ("calling-conventions", PrintKind::CallingConventions), + ("cfg", PrintKind::Cfg), + ("check-cfg", PrintKind::CheckCfg), + ("code-models", PrintKind::CodeModels), + ("crate-name", PrintKind::CrateName), + ("crate-root-lint-levels", PrintKind::CrateRootLintLevels), + ("deployment-target", PrintKind::DeploymentTarget), + ("file-names", PrintKind::FileNames), + ("host-tuple", PrintKind::HostTuple), + ("link-args", PrintKind::LinkArgs), + ("native-static-libs", PrintKind::NativeStaticLibs), + ("relocation-models", PrintKind::RelocationModels), + ("split-debuginfo", PrintKind::SplitDebuginfo), + ("stack-protector-strategies", PrintKind::StackProtectorStrategies), + ("supported-crate-types", PrintKind::SupportedCrateTypes), + ("sysroot", PrintKind::Sysroot), + ("target-cpus", PrintKind::TargetCPUs), + ("target-features", PrintKind::TargetFeatures), + ("target-libdir", PrintKind::TargetLibdir), + ("target-list", PrintKind::TargetList), + ("target-spec-json", PrintKind::TargetSpecJson), + ("target-spec-json-schema", PrintKind::TargetSpecJsonSchema), + ("tls-models", PrintKind::TlsModels), + // tidy-alphabetical-end +]; + +#[derive(Clone, PartialEq, Debug)] +pub struct PrintRequest { + pub kind: PrintKind, + pub out: OutFileName, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum PrintKind { + // tidy-alphabetical-start + AllTargetSpecsJson, + CallingConventions, + Cfg, + CheckCfg, + CodeModels, + CrateName, + CrateRootLintLevels, + DeploymentTarget, + FileNames, + HostTuple, + LinkArgs, + NativeStaticLibs, + RelocationModels, + SplitDebuginfo, + StackProtectorStrategies, + SupportedCrateTypes, + Sysroot, + TargetCPUs, + TargetFeatures, + TargetLibdir, + TargetList, + TargetSpecJson, + TargetSpecJsonSchema, + TlsModels, + // tidy-alphabetical-end +} + +pub(crate) static PRINT_HELP: LazyLock = LazyLock::new(|| { + format!( + "Compiler information to print on stdout (or to a file)\n\ + INFO may be one of <{}>.", + PRINT_KINDS.iter().map(|(name, _)| format!("{name}")).collect::>().join("|") + ) +}); + +pub(crate) fn collect_print_requests( + early_dcx: &EarlyDiagCtxt, + cg: &mut CodegenOptions, + unstable_opts: &UnstableOptions, + matches: &getopts::Matches, +) -> Vec { + let mut prints = Vec::::new(); + if cg.target_cpu.as_deref() == Some("help") { + prints.push(PrintRequest { kind: PrintKind::TargetCPUs, out: OutFileName::Stdout }); + cg.target_cpu = None; + }; + if cg.target_feature == "help" { + prints.push(PrintRequest { kind: PrintKind::TargetFeatures, out: OutFileName::Stdout }); + cg.target_feature = String::new(); + } + + // We disallow reusing the same path in multiple prints, such as `--print + // cfg=output.txt --print link-args=output.txt`, because outputs are printed + // by disparate pieces of the compiler, and keeping track of which files + // need to be overwritten vs appended to is annoying. + let mut printed_paths = FxHashSet::default(); + + prints.extend(matches.opt_strs("print").into_iter().map(|req| { + let (req, out) = split_out_file_name(&req); + + let kind = if let Some((print_name, print_kind)) = + PRINT_KINDS.iter().find(|&&(name, _)| name == req) + { + check_print_request_stability(early_dcx, unstable_opts, (print_name, *print_kind)); + *print_kind + } else { + let is_nightly = nightly_options::match_is_nightly_build(matches); + emit_unknown_print_request_help(early_dcx, req, is_nightly) + }; + + let out = out.unwrap_or(OutFileName::Stdout); + if let OutFileName::Real(path) = &out { + if !printed_paths.insert(path.clone()) { + early_dcx.early_fatal(format!( + "cannot print multiple outputs to the same path: {}", + path.display(), + )); + } + } + + PrintRequest { kind, out } + })); + + prints +} + +fn check_print_request_stability( + early_dcx: &EarlyDiagCtxt, + unstable_opts: &UnstableOptions, + (print_name, print_kind): (&str, PrintKind), +) { + if !is_print_request_stable(print_kind) && !unstable_opts.unstable_options { + early_dcx.early_fatal(format!( + "the `-Z unstable-options` flag must also be passed to enable the `{print_name}` \ + print option" + )); + } +} + +fn is_print_request_stable(print_kind: PrintKind) -> bool { + match print_kind { + PrintKind::AllTargetSpecsJson + | PrintKind::CheckCfg + | PrintKind::CrateRootLintLevels + | PrintKind::SupportedCrateTypes + | PrintKind::TargetSpecJson + | PrintKind::TargetSpecJsonSchema => false, + _ => true, + } +} + +fn emit_unknown_print_request_help(early_dcx: &EarlyDiagCtxt, req: &str, is_nightly: bool) -> ! { + let prints = PRINT_KINDS + .iter() + .filter_map(|(name, kind)| { + // If we're not on nightly, we don't want to print unstable options + if !is_nightly && !is_print_request_stable(*kind) { + None + } else { + Some(format!("`{name}`")) + } + }) + .collect::>(); + let prints = prints.join(", "); + + let mut diag = early_dcx.early_struct_fatal(format!("unknown print request: `{req}`")); + #[allow(rustc::diagnostic_outside_of_impl)] + diag.help(format!("valid print requests are: {prints}")); + + if req == "lints" { + diag.help(format!("use `-Whelp` to print a list of lints")); + } + + diag.help(format!("for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information")); + diag.emit() +} From 9c7e7ccd25ec8f89dfe5f64417ce466d2ace2dc4 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 11 Nov 2025 21:20:46 +1100 Subject: [PATCH 6/6] Exhaustively specify names and stability of `--print` values --- .../rustc_session/src/config/print_request.rs | 163 +++++++++++------- compiler/rustc_session/src/lib.rs | 2 + compiler/rustc_session/src/macros.rs | 27 +++ 3 files changed, 129 insertions(+), 63 deletions(-) create mode 100644 compiler/rustc_session/src/macros.rs diff --git a/compiler/rustc_session/src/config/print_request.rs b/compiler/rustc_session/src/config/print_request.rs index 6226a8aee7d36..b8111fbc17f84 100644 --- a/compiler/rustc_session/src/config/print_request.rs +++ b/compiler/rustc_session/src/config/print_request.rs @@ -1,5 +1,6 @@ //! Code for dealing with `--print` requests. +use std::fmt; use std::sync::LazyLock; use rustc_data_structures::fx::FxHashSet; @@ -8,35 +9,7 @@ use crate::EarlyDiagCtxt; use crate::config::{ CodegenOptions, OutFileName, UnstableOptions, nightly_options, split_out_file_name, }; - -const PRINT_KINDS: &[(&str, PrintKind)] = &[ - // tidy-alphabetical-start - ("all-target-specs-json", PrintKind::AllTargetSpecsJson), - ("calling-conventions", PrintKind::CallingConventions), - ("cfg", PrintKind::Cfg), - ("check-cfg", PrintKind::CheckCfg), - ("code-models", PrintKind::CodeModels), - ("crate-name", PrintKind::CrateName), - ("crate-root-lint-levels", PrintKind::CrateRootLintLevels), - ("deployment-target", PrintKind::DeploymentTarget), - ("file-names", PrintKind::FileNames), - ("host-tuple", PrintKind::HostTuple), - ("link-args", PrintKind::LinkArgs), - ("native-static-libs", PrintKind::NativeStaticLibs), - ("relocation-models", PrintKind::RelocationModels), - ("split-debuginfo", PrintKind::SplitDebuginfo), - ("stack-protector-strategies", PrintKind::StackProtectorStrategies), - ("supported-crate-types", PrintKind::SupportedCrateTypes), - ("sysroot", PrintKind::Sysroot), - ("target-cpus", PrintKind::TargetCPUs), - ("target-features", PrintKind::TargetFeatures), - ("target-libdir", PrintKind::TargetLibdir), - ("target-list", PrintKind::TargetList), - ("target-spec-json", PrintKind::TargetSpecJson), - ("target-spec-json-schema", PrintKind::TargetSpecJsonSchema), - ("tls-models", PrintKind::TlsModels), - // tidy-alphabetical-end -]; +use crate::macros::AllVariants; #[derive(Clone, PartialEq, Debug)] pub struct PrintRequest { @@ -45,6 +18,7 @@ pub struct PrintRequest { } #[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[derive(AllVariants)] pub enum PrintKind { // tidy-alphabetical-start AllTargetSpecsJson, @@ -74,11 +48,94 @@ pub enum PrintKind { // tidy-alphabetical-end } +impl PrintKind { + /// FIXME: rust-analyzer doesn't support `#![feature(macro_derive)]` yet + /// (), which breaks autocomplete. + /// Work around that by aliasing the trait constant to a regular constant. + const ALL_VARIANTS: &[Self] = ::ALL_VARIANTS; + + fn name(self) -> &'static str { + use PrintKind::*; + match self { + // tidy-alphabetical-start + AllTargetSpecsJson => "all-target-specs-json", + CallingConventions => "calling-conventions", + Cfg => "cfg", + CheckCfg => "check-cfg", + CodeModels => "code-models", + CrateName => "crate-name", + CrateRootLintLevels => "crate-root-lint-levels", + DeploymentTarget => "deployment-target", + FileNames => "file-names", + HostTuple => "host-tuple", + LinkArgs => "link-args", + NativeStaticLibs => "native-static-libs", + RelocationModels => "relocation-models", + SplitDebuginfo => "split-debuginfo", + StackProtectorStrategies => "stack-protector-strategies", + SupportedCrateTypes => "supported-crate-types", + Sysroot => "sysroot", + TargetCPUs => "target-cpus", + TargetFeatures => "target-features", + TargetLibdir => "target-libdir", + TargetList => "target-list", + TargetSpecJson => "target-spec-json", + TargetSpecJsonSchema => "target-spec-json-schema", + TlsModels => "tls-models", + // tidy-alphabetical-end + } + } + + fn is_stable(self) -> bool { + use PrintKind::*; + match self { + // Stable values: + CallingConventions + | Cfg + | CodeModels + | CrateName + | DeploymentTarget + | FileNames + | HostTuple + | LinkArgs + | NativeStaticLibs + | RelocationModels + | SplitDebuginfo + | StackProtectorStrategies + | Sysroot + | TargetCPUs + | TargetFeatures + | TargetLibdir + | TargetList + | TlsModels => true, + + // Unstable values: + AllTargetSpecsJson => false, + CheckCfg => false, + CrateRootLintLevels => false, + SupportedCrateTypes => false, + TargetSpecJson => false, + TargetSpecJsonSchema => false, + } + } + + fn from_str(s: &str) -> Option { + Self::ALL_VARIANTS.iter().find(|kind| kind.name() == s).copied() + } +} + +impl fmt::Display for PrintKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.name().fmt(f) + } +} + pub(crate) static PRINT_HELP: LazyLock = LazyLock::new(|| { + let print_kinds = + PrintKind::ALL_VARIANTS.iter().map(|kind| kind.name()).collect::>().join("|"); format!( "Compiler information to print on stdout (or to a file)\n\ - INFO may be one of <{}>.", - PRINT_KINDS.iter().map(|(name, _)| format!("{name}")).collect::>().join("|") + INFO may be one of <{print_kinds}>.", ) }); @@ -107,11 +164,9 @@ pub(crate) fn collect_print_requests( prints.extend(matches.opt_strs("print").into_iter().map(|req| { let (req, out) = split_out_file_name(&req); - let kind = if let Some((print_name, print_kind)) = - PRINT_KINDS.iter().find(|&&(name, _)| name == req) - { - check_print_request_stability(early_dcx, unstable_opts, (print_name, *print_kind)); - *print_kind + let kind = if let Some(print_kind) = PrintKind::from_str(req) { + check_print_request_stability(early_dcx, unstable_opts, print_kind); + print_kind } else { let is_nightly = nightly_options::match_is_nightly_build(matches); emit_unknown_print_request_help(early_dcx, req, is_nightly) @@ -136,41 +191,23 @@ pub(crate) fn collect_print_requests( fn check_print_request_stability( early_dcx: &EarlyDiagCtxt, unstable_opts: &UnstableOptions, - (print_name, print_kind): (&str, PrintKind), + print_kind: PrintKind, ) { - if !is_print_request_stable(print_kind) && !unstable_opts.unstable_options { + if !print_kind.is_stable() && !unstable_opts.unstable_options { early_dcx.early_fatal(format!( - "the `-Z unstable-options` flag must also be passed to enable the `{print_name}` \ - print option" + "the `-Z unstable-options` flag must also be passed to enable the `{print_kind}` print option" )); } } -fn is_print_request_stable(print_kind: PrintKind) -> bool { - match print_kind { - PrintKind::AllTargetSpecsJson - | PrintKind::CheckCfg - | PrintKind::CrateRootLintLevels - | PrintKind::SupportedCrateTypes - | PrintKind::TargetSpecJson - | PrintKind::TargetSpecJsonSchema => false, - _ => true, - } -} - fn emit_unknown_print_request_help(early_dcx: &EarlyDiagCtxt, req: &str, is_nightly: bool) -> ! { - let prints = PRINT_KINDS + let prints = PrintKind::ALL_VARIANTS .iter() - .filter_map(|(name, kind)| { - // If we're not on nightly, we don't want to print unstable options - if !is_nightly && !is_print_request_stable(*kind) { - None - } else { - Some(format!("`{name}`")) - } - }) - .collect::>(); - let prints = prints.join(", "); + // If we're not on nightly, we don't want to print unstable options + .filter(|kind| is_nightly || kind.is_stable()) + .map(|kind| format!("`{kind}`")) + .collect::>() + .join(", "); let mut diag = early_dcx.early_struct_fatal(format!("unknown print request: `{req}`")); #[allow(rustc::diagnostic_outside_of_impl)] diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 5e5872ee06815..90108e9110440 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -2,6 +2,7 @@ #![allow(internal_features)] #![feature(default_field_values)] #![feature(iter_intersperse)] +#![feature(macro_derive)] #![feature(rustc_attrs)] // To generate CodegenOptionsTargetModifiers and UnstableOptionsTargetModifiers enums // with macro_rules, it is necessary to use recursive mechanic ("Incremental TT Munchers"). @@ -20,6 +21,7 @@ pub mod code_stats; pub mod config; pub mod cstore; pub mod filesearch; +mod macros; mod options; pub mod search_paths; diff --git a/compiler/rustc_session/src/macros.rs b/compiler/rustc_session/src/macros.rs new file mode 100644 index 0000000000000..8f9a12d00cc7f --- /dev/null +++ b/compiler/rustc_session/src/macros.rs @@ -0,0 +1,27 @@ +/// Derivable trait for enums with no fields (i.e. C-style enums) that want to +/// allow iteration over a list of all variant values. +pub(crate) trait AllVariants: Copy + 'static { + const ALL_VARIANTS: &[Self]; +} + +macro_rules! AllVariantsDerive { + derive() ( + $(#[$meta:meta])* + $vis:vis enum $Type:ident { + $( + $(#[$varmeta:meta])* + $Variant:ident $( = $value:literal )? + ), *$(,)? + } + ) => { + impl $crate::macros::AllVariants for $Type { + const ALL_VARIANTS: &[$Type] = &[ + $( $Type::$Variant, )* + ]; + } + }; +} + +// For some reason the compiler won't allow `pub(crate) use AllVariants` due +// to a conflict with the trait of the same name, but will allow this form. +pub(crate) use AllVariantsDerive as AllVariants;