From 2a870404fd3e2292955478875336217a27939202 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 12 Mar 2026 12:50:16 +1100 Subject: [PATCH 01/41] Add a suite of ChunkedBitSet union/subtract/intersect test scenarios --- compiler/rustc_index/src/bit_set/tests.rs | 110 ++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/compiler/rustc_index/src/bit_set/tests.rs b/compiler/rustc_index/src/bit_set/tests.rs index 341e0622df75..08704863ec3d 100644 --- a/compiler/rustc_index/src/bit_set/tests.rs +++ b/compiler/rustc_index/src/bit_set/tests.rs @@ -298,6 +298,116 @@ fn chunked_bitset() { b10000b.assert_valid(); } +/// Additional helper methods for testing. +impl ChunkedBitSet { + /// Creates a new `ChunkedBitSet` containing all `i` for which `fill_fn(i)` is true. + fn fill_with(domain_size: usize, fill_fn: impl Fn(usize) -> bool) -> Self { + let mut this = ChunkedBitSet::new_empty(domain_size); + for i in 0..domain_size { + if fill_fn(i) { + this.insert(i); + } + } + this + } + + /// Asserts that for each `i` in `0..self.domain_size()`, `self.contains(i) == expected_fn(i)`. + #[track_caller] + fn assert_filled_with(&self, expected_fn: impl Fn(usize) -> bool) { + for i in 0..self.domain_size() { + let expected = expected_fn(i); + assert_eq!(self.contains(i), expected, "i = {i}"); + } + } +} + +#[test] +fn chunked_bulk_ops() { + struct ChunkedBulkOp { + name: &'static str, + op_fn: fn(&mut ChunkedBitSet, &ChunkedBitSet) -> bool, + spec_fn: fn(fn(usize) -> bool, fn(usize) -> bool, usize) -> bool, + } + let ops = &[ + ChunkedBulkOp { + name: "union", + op_fn: ChunkedBitSet::union, + spec_fn: |fizz, buzz, i| fizz(i) || buzz(i), + }, + ChunkedBulkOp { + name: "subtract", + op_fn: ChunkedBitSet::subtract, + spec_fn: |fizz, buzz, i| fizz(i) && !buzz(i), + }, + ChunkedBulkOp { + name: "intersect", + op_fn: ChunkedBitSet::intersect, + spec_fn: |fizz, buzz, i| fizz(i) && buzz(i), + }, + ]; + + let domain_sizes = [ + CHUNK_BITS / 7, // Smaller than a full chunk. + CHUNK_BITS, + (CHUNK_BITS + CHUNK_BITS / 7), // Larger than a full chunk. + ]; + + for ChunkedBulkOp { name, op_fn, spec_fn } in ops { + for domain_size in domain_sizes { + // If false, use different values for LHS and RHS, to test "fizz op buzz". + // If true, use identical values, to test "fizz op fizz". + for identical in [false, true] { + // If false, make a clone of LHS before doing the op. + // This covers optimizations that depend on whether chunk words are shared or not. + for unique in [false, true] { + // Print the current test case, so that we can see which one failed. + println!( + "Testing op={name}, domain_size={domain_size}, identical={identical}, unique={unique} ..." + ); + + let fizz_fn = |i| i % 3 == 0; + let buzz_fn = if identical { fizz_fn } else { |i| i % 5 == 0 }; + + // Check that `fizz op buzz` gives the expected results. + chunked_bulk_ops_test_inner( + domain_size, + unique, + fizz_fn, + buzz_fn, + op_fn, + |i| spec_fn(fizz_fn, buzz_fn, i), + ); + } + } + } + } +} + +fn chunked_bulk_ops_test_inner( + domain_size: usize, + unique: bool, + fizz_fn: impl Fn(usize) -> bool + Copy, + buzz_fn: impl Fn(usize) -> bool + Copy, + op_fn: impl Fn(&mut ChunkedBitSet, &ChunkedBitSet) -> bool, + expected_fn: impl Fn(usize) -> bool + Copy, +) { + // Create two bitsets, "fizz" (LHS) and "buzz" (RHS). + let mut fizz = ChunkedBitSet::fill_with(domain_size, fizz_fn); + let buzz = ChunkedBitSet::fill_with(domain_size, buzz_fn); + + // If requested, clone `fizz` so that its word Rcs are not uniquely-owned. + let _cloned = (!unique).then(|| fizz.clone()); + + // Perform the op (e.g. union/subtract/intersect), and verify that the + // mutated LHS contains exactly the expected values. + let changed = op_fn(&mut fizz, &buzz); + fizz.assert_filled_with(expected_fn); + + // Verify that the "changed" return value is correct. + let should_change = (0..domain_size).any(|i| fizz_fn(i) != expected_fn(i)); + assert_eq!(changed, should_change); +} + fn with_elements_chunked(elements: &[usize], domain_size: usize) -> ChunkedBitSet { let mut s = ChunkedBitSet::new_empty(domain_size); for &e in elements { From 30db41dc08248ccd0187c5b1efc2825860919088 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Mon, 13 Apr 2026 09:50:34 +0800 Subject: [PATCH 02/41] cg_clif: don't show verbose run-make cmd output for passing tests --- compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index 71ff4eef071c..8870962f1c46 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -198,5 +198,5 @@ index 073116933bd..c3e4578204d 100644 EOF echo "[TEST] rustc test suite" -./x.py test --stage 0 --test-args=--no-capture tests/{codegen-units,run-make,run-make-cargo,ui,incremental} +./x.py test --stage 0 --no-capture --verbose-run-make-subprocess-output=false tests/{codegen-units,run-make,run-make-cargo,ui,incremental} popd From 653a8f90200aaf8740d175e3ae98a9fc052e7f9b Mon Sep 17 00:00:00 2001 From: Gent Semaj Date: Tue, 17 Mar 2026 16:22:11 -0700 Subject: [PATCH 03/41] Add `keepalive`, `set_keepalive` to `TcpStream` implementations --- library/std/src/net/tcp.rs | 51 +++++++++++++++++++ library/std/src/net/tcp/tests.rs | 15 ++++++ library/std/src/sys/net/connection/motor.rs | 10 +++- library/std/src/sys/net/connection/sgx.rs | 8 +++ .../src/sys/net/connection/socket/hermit.rs | 9 ++++ .../std/src/sys/net/connection/socket/mod.rs | 8 +++ .../src/sys/net/connection/socket/solid.rs | 9 ++++ .../std/src/sys/net/connection/socket/unix.rs | 9 ++++ .../src/sys/net/connection/socket/windows.rs | 9 ++++ .../std/src/sys/net/connection/uefi/mod.rs | 8 +++ .../std/src/sys/net/connection/unsupported.rs | 8 +++ library/std/src/sys/net/connection/wasip1.rs | 8 +++ .../src/sys/net/connection/xous/tcpstream.rs | 8 +++ .../std/src/sys/pal/windows/c/bindings.txt | 1 + .../std/src/sys/pal/windows/c/windows_sys.rs | 1 + 15 files changed, 161 insertions(+), 1 deletion(-) diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index a8046a5541c5..6001869dbf28 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -474,6 +474,57 @@ pub fn linger(&self) -> io::Result> { self.0.linger() } + /// Sets the value of the `SO_KEEPALIVE` option on this socket. + /// + /// If set to `true`, the operating system will periodically send keepalive + /// probes on an idle connection to verify that the remote peer is still + /// reachable. If the peer fails to respond after a system-determined number + /// of probes, the connection is considered broken and subsequent I/O calls + /// will return an error. + /// + /// This is useful for detecting dead peers on long-lived connections where + /// no application-level traffic is exchanged, such as database or SSH + /// connections. + /// + /// The timing and frequency of keepalive probes are controlled by + /// system-level settings and are not configured by this method alone. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(tcp_keepalive)] + /// + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_keepalive(true).expect("set_keepalive call failed"); + #[unstable(feature = "tcp_keepalive", issue = "155889")] + pub fn set_keepalive(&self, keepalive: bool) -> io::Result<()> { + self.0.set_keepalive(keepalive) + } + + /// Gets the value of the `SO_KEEPALIVE` option on this socket. + /// + /// For more information about this option, see [`TcpStream::set_keepalive`]. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(tcp_keepalive)] + /// + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_keepalive(true).expect("set_keepalive call failed"); + /// assert_eq!(stream.keepalive().unwrap_or(false), true); + /// ``` + #[unstable(feature = "tcp_keepalive", issue = "155889")] + pub fn keepalive(&self) -> io::Result { + self.0.keepalive() + } + /// Sets the value of the `TCP_NODELAY` option on this socket. /// /// If set, this option disables the Nagle algorithm. This means that diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index af15009e665e..f6fe8e1b2353 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -844,6 +844,21 @@ fn linger() { assert_eq!(None, t!(stream.linger())); } +#[test] +#[cfg_attr(target_env = "sgx", ignore)] +#[cfg_attr(target_os = "wasi", ignore)] +fn keepalive() { + let addr = next_test_ip4(); + let _listener = t!(TcpListener::bind(&addr)); + let stream = t!(TcpStream::connect(&addr)); + + assert_eq!(false, t!(stream.keepalive())); + t!(stream.set_keepalive(true)); + assert_eq!(true, t!(stream.keepalive())); + t!(stream.set_keepalive(false)); + assert_eq!(false, t!(stream.keepalive())); +} + #[test] #[cfg_attr(target_env = "sgx", ignore)] fn nodelay() { diff --git a/library/std/src/sys/net/connection/motor.rs b/library/std/src/sys/net/connection/motor.rs index 79a528792106..be879f43b0bb 100644 --- a/library/std/src/sys/net/connection/motor.rs +++ b/library/std/src/sys/net/connection/motor.rs @@ -5,7 +5,7 @@ use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::fd::FileDesc; -use crate::sys::{AsInner, FromInner, IntoInner, map_motor_error}; +use crate::sys::{AsInner, FromInner, IntoInner, map_motor_error, unsupported}; use crate::time::Duration; // We want to re-use as much of Rust's stdlib code as possible, @@ -127,6 +127,14 @@ pub fn linger(&self) -> io::Result> { moto_rt::net::linger(self.inner.as_raw_fd()).map_err(map_motor_error) } + pub fn set_keepalive(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn keepalive(&self) -> io::Result { + unsupported() + } + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { moto_rt::net::set_nodelay(self.inner.as_raw_fd(), nodelay).map_err(map_motor_error) } diff --git a/library/std/src/sys/net/connection/sgx.rs b/library/std/src/sys/net/connection/sgx.rs index 5735a5db488f..c72581cf69f0 100644 --- a/library/std/src/sys/net/connection/sgx.rs +++ b/library/std/src/sys/net/connection/sgx.rs @@ -219,6 +219,14 @@ pub fn linger(&self) -> io::Result> { sgx_ineffective(None) } + pub fn set_keepalive(&self, _: bool) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn keepalive(&self) -> io::Result { + sgx_ineffective(false) + } + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { sgx_ineffective(()) } diff --git a/library/std/src/sys/net/connection/socket/hermit.rs b/library/std/src/sys/net/connection/socket/hermit.rs index 7bbb3fba9ad4..09953cc515af 100644 --- a/library/std/src/sys/net/connection/socket/hermit.rs +++ b/library/std/src/sys/net/connection/socket/hermit.rs @@ -272,6 +272,15 @@ pub fn linger(&self) -> io::Result> { Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) } + pub fn set_keepalive(&self, keepalive: bool) -> io::Result<()> { + unsafe { setsockopt(self, netc::SOL_SOCKET, netc::SO_KEEPALIVE, keepalive as c_int) } + } + + pub fn keepalive(&self) -> io::Result { + let raw: c_int = unsafe { getsockopt(self, netc::SOL_SOCKET, netc::SO_KEEPALIVE)? }; + Ok(raw != 0) + } + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { let value: i32 = if nodelay { 1 } else { 0 }; unsafe { setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, value) } diff --git a/library/std/src/sys/net/connection/socket/mod.rs b/library/std/src/sys/net/connection/socket/mod.rs index 256b99dfa987..8efe8bd78514 100644 --- a/library/std/src/sys/net/connection/socket/mod.rs +++ b/library/std/src/sys/net/connection/socket/mod.rs @@ -468,6 +468,14 @@ pub fn linger(&self) -> io::Result> { self.inner.linger() } + pub fn set_keepalive(&self, keepalive: bool) -> io::Result<()> { + self.inner.set_keepalive(keepalive) + } + + pub fn keepalive(&self) -> io::Result { + self.inner.keepalive() + } + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { self.inner.set_nodelay(nodelay) } diff --git a/library/std/src/sys/net/connection/socket/solid.rs b/library/std/src/sys/net/connection/socket/solid.rs index 7e3cd9b6a5e0..79e6c12d6ab3 100644 --- a/library/std/src/sys/net/connection/socket/solid.rs +++ b/library/std/src/sys/net/connection/socket/solid.rs @@ -6,6 +6,7 @@ use crate::io::{self, BorrowedBuf, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; use crate::os::solid::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd}; +use crate::sys::pal::unsupported; use crate::sys::{FromInner, IntoInner, abi}; use crate::time::Duration; use crate::{cmp, mem, ptr, str}; @@ -332,6 +333,14 @@ pub fn linger(&self) -> io::Result> { Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) } + pub fn set_keepalive(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn keepalive(&self) -> io::Result { + unsupported() + } + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { unsafe { setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int) } } diff --git a/library/std/src/sys/net/connection/socket/unix.rs b/library/std/src/sys/net/connection/socket/unix.rs index 04714048e3c5..936dd4c93233 100644 --- a/library/std/src/sys/net/connection/socket/unix.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -456,6 +456,15 @@ pub fn linger(&self) -> io::Result> { Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) } + pub fn set_keepalive(&self, keepalive: bool) -> io::Result<()> { + unsafe { setsockopt(self, libc::SOL_SOCKET, libc::SO_KEEPALIVE, keepalive as c_int) } + } + + pub fn keepalive(&self) -> io::Result { + let raw: c_int = unsafe { getsockopt(self, libc::SOL_SOCKET, libc::SO_KEEPALIVE)? }; + Ok(raw != 0) + } + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { unsafe { setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int) } } diff --git a/library/std/src/sys/net/connection/socket/windows.rs b/library/std/src/sys/net/connection/socket/windows.rs index ca4fa343cb9a..0ea139cad6a0 100644 --- a/library/std/src/sys/net/connection/socket/windows.rs +++ b/library/std/src/sys/net/connection/socket/windows.rs @@ -426,6 +426,15 @@ pub fn linger(&self) -> io::Result> { Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) } + pub fn set_keepalive(&self, keepalive: bool) -> io::Result<()> { + unsafe { setsockopt(self, c::SOL_SOCKET, c::SO_KEEPALIVE, keepalive as c::BOOL) } + } + + pub fn keepalive(&self) -> io::Result { + let raw: c::BOOL = unsafe { getsockopt(self, c::SOL_SOCKET, c::SO_KEEPALIVE)? }; + Ok(raw != 0) + } + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { unsafe { setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BOOL) } } diff --git a/library/std/src/sys/net/connection/uefi/mod.rs b/library/std/src/sys/net/connection/uefi/mod.rs index 107a3e23733d..b32316a70990 100644 --- a/library/std/src/sys/net/connection/uefi/mod.rs +++ b/library/std/src/sys/net/connection/uefi/mod.rs @@ -112,6 +112,14 @@ pub fn linger(&self) -> io::Result> { unsupported() } + pub fn set_keepalive(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn keepalive(&self) -> io::Result { + unsupported() + } + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { unsupported() } diff --git a/library/std/src/sys/net/connection/unsupported.rs b/library/std/src/sys/net/connection/unsupported.rs index fb18e8dec557..c019ec1c9e52 100644 --- a/library/std/src/sys/net/connection/unsupported.rs +++ b/library/std/src/sys/net/connection/unsupported.rs @@ -87,6 +87,14 @@ pub fn linger(&self) -> io::Result> { self.0 } + pub fn set_keepalive(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn keepalive(&self) -> io::Result { + self.0 + } + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { self.0 } diff --git a/library/std/src/sys/net/connection/wasip1.rs b/library/std/src/sys/net/connection/wasip1.rs index d6c7e023e865..93cb777a7084 100644 --- a/library/std/src/sys/net/connection/wasip1.rs +++ b/library/std/src/sys/net/connection/wasip1.rs @@ -145,6 +145,14 @@ pub fn linger(&self) -> io::Result> { unsupported() } + pub fn set_keepalive(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn keepalive(&self) -> io::Result { + unsupported() + } + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { unsupported() } diff --git a/library/std/src/sys/net/connection/xous/tcpstream.rs b/library/std/src/sys/net/connection/xous/tcpstream.rs index 4df75453d1f4..521d24e3c496 100644 --- a/library/std/src/sys/net/connection/xous/tcpstream.rs +++ b/library/std/src/sys/net/connection/xous/tcpstream.rs @@ -353,6 +353,14 @@ pub fn linger(&self) -> io::Result> { unimpl!(); } + pub fn set_keepalive(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + + pub fn keepalive(&self) -> io::Result { + unimpl!(); + } + pub fn set_nodelay(&self, enabled: bool) -> io::Result<()> { crate::os::xous::ffi::blocking_scalar( services::net_server(), diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index c21d1de81341..7b5abbc36378 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -2388,6 +2388,7 @@ SleepConditionVariableSRW SleepEx SO_BROADCAST SO_ERROR +SO_KEEPALIVE SO_LINGER SO_RCVTIMEO SO_SNDTIMEO diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index eb54efd1c1fe..7cef71097a78 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -3189,6 +3189,7 @@ fn default() -> Self { pub const SOL_SOCKET: i32 = 65535i32; pub const SO_BROADCAST: i32 = 32i32; pub const SO_ERROR: i32 = 4103i32; +pub const SO_KEEPALIVE: i32 = 8i32; pub const SO_LINGER: i32 = 128i32; pub const SO_RCVTIMEO: i32 = 4102i32; pub const SO_SNDTIMEO: i32 = 4101i32; From 0d0aaa1a54d8136524d0cfd4bd6346f8b431afb7 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 20 Apr 2026 19:56:55 +1000 Subject: [PATCH 04/41] Some arena macro tweaks. - Improve a comment. - Remove unused `[decode]` annotations on some arenas. - Improve `impl_arena_allocatable_decoder`: - Make the first rule more like the second rule. - Remove unnecessary brackets. - Remove unused support for attributes other than `decode`. --- compiler/rustc_middle/src/arena.rs | 11 +++++------ compiler/rustc_middle/src/ty/codec.rs | 7 +++---- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index de6a105ee2b7..e12583f38fe7 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -1,7 +1,7 @@ /// This higher-order macro declares a list of types which can be allocated by `Arena`. /// /// Specifying the `decode` modifier will add decode impls for `&T` and `&[T]` where `T` is the type -/// listed. These impls will appear in the implement_ty_decoder! macro. +/// listed. See the `impl_arena_allocatable_decoder!` macro for more. #[macro_export] macro_rules! arena_types { ($macro:path) => ( @@ -9,8 +9,7 @@ macro_rules! arena_types { [] layout: rustc_abi::LayoutData, [] proxy_coroutine_layout: rustc_middle::mir::CoroutineLayout<'tcx>, [] fn_abi: rustc_target::callconv::FnAbi<'tcx, rustc_middle::ty::Ty<'tcx>>, - // AdtDef are interned and compared by address - [decode] adt_def: rustc_middle::ty::AdtDefData, + [] adt_def: rustc_middle::ty::AdtDefData, [] steal_thir: rustc_data_structures::steal::Steal>, [] steal_mir: rustc_data_structures::steal::Steal>, [decode] mir: rustc_middle::mir::Body<'tcx>, @@ -27,7 +26,7 @@ macro_rules! arena_types { rustc_middle::mir::Body<'tcx> >, [decode] typeck_results: rustc_middle::ty::TypeckResults<'tcx>, - [decode] borrowck_result: rustc_data_structures::fx::FxIndexMap< + [] borrowck_result: rustc_data_structures::fx::FxIndexMap< rustc_hir::def_id::LocalDefId, rustc_middle::ty::DefinitionSiteHiddenType<'tcx>, >, @@ -100,7 +99,7 @@ macro_rules! arena_types { // (during lowering) and the `rustc_middle` arena (for decoding MIR) [decode] asm_template: rustc_ast::InlineAsmTemplatePiece, [decode] used_trait_imports: rustc_data_structures::unord::UnordSet, - [decode] is_late_bound_map: rustc_data_structures::fx::FxIndexSet, + [] is_late_bound_map: rustc_data_structures::fx::FxIndexSet, [decode] impl_source: rustc_middle::traits::ImplSource<'tcx, ()>, [] dep_kind_vtable: rustc_middle::dep_graph::DepKindVTable<'tcx>, @@ -111,7 +110,7 @@ macro_rules! arena_types { rustc_middle::ty::EarlyBinder<'tcx, rustc_middle::ty::Ty<'tcx>> >, [] external_constraints: rustc_middle::traits::solve::ExternalConstraintsData>, - [decode] doc_link_resolutions: rustc_hir::def::DocLinkResMap, + [] doc_link_resolutions: rustc_hir::def::DocLinkResMap, [] stripped_cfg_items: rustc_hir::attrs::StrippedCfgItem, [] mod_child: rustc_middle::metadata::ModChild, [] features: rustc_feature::Features, diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 28bdeabf34dc..8b1e81258278 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -513,9 +513,8 @@ fn $name(&mut self) -> $ty { } macro_rules! impl_arena_allocatable_decoder { - ([]$args:tt) => {}; - ([decode $(, $attrs:ident)*] - [$name:ident: $ty:ty]) => { + ([] $name:ident: $ty:ty) => {}; + ([decode] $name:ident: $ty:ty) => { impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for $ty { #[inline] fn decode(decoder: &mut D) -> &'tcx Self { @@ -535,7 +534,7 @@ fn decode(decoder: &mut D) -> &'tcx Self { macro_rules! impl_arena_allocatable_decoders { ([$($a:tt $name:ident: $ty:ty,)*]) => { $( - impl_arena_allocatable_decoder!($a [$name: $ty]); + impl_arena_allocatable_decoder!($a $name: $ty); )* } } From cdefdd054cd04e43aec96d99042f14d01a8b8bb8 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 23 Apr 2026 08:54:38 +1000 Subject: [PATCH 05/41] Fix a metadata table name. The table names usually match the name of the corresponding query. The `trait_impl_trait_tys` table is an exception; this commit renames it `collect_return_position_impl_trait_in_trait_tys` to match the query. --- compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs | 8 +++++--- compiler/rustc_metadata/src/rmeta/encoder.rs | 2 +- compiler/rustc_metadata/src/rmeta/mod.rs | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index a00fb59963ac..cbd6afd68473 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -261,7 +261,8 @@ fn into_args(self) -> (DefId, SimplifiedType) { .coerce_unsized_info .get(cdata, def_id.index) .map(|lazy| lazy.decode((cdata, tcx))) - .process_decoded(tcx, || panic!("{def_id:?} does not have coerce_unsized_info"))) } + .process_decoded(tcx, || panic!("{def_id:?} does not have coerce_unsized_info"))) + } mir_const_qualif => { table } rendered_const => { table } rendered_precise_capturing_args => { table } @@ -300,10 +301,10 @@ fn into_args(self) -> (DefId, SimplifiedType) { Ok(cdata .root .tables - .trait_impl_trait_tys + .collect_return_position_impl_trait_in_trait_tys .get(cdata, def_id.index) .map(|lazy| lazy.decode((cdata, tcx))) - .process_decoded(tcx, || panic!("{def_id:?} does not have trait_impl_trait_tys"))) + .process_decoded(tcx, || panic!("{def_id:?} does not have collect_return_position_impl_trait_in_trait_tys"))) } associated_types_for_impl_traits_in_trait_or_impl => { table } @@ -695,6 +696,7 @@ impl CrateStore for CStore { fn as_any(&self) -> &dyn Any { self } + fn untracked_as_any(&mut self) -> &mut dyn Any { self } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 729a0dda7cf3..57fe9210c627 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1628,7 +1628,7 @@ fn encode_def_ids(&mut self) { if tcx.impl_method_has_trait_impl_trait_tys(def_id) && let Ok(table) = self.tcx.collect_return_position_impl_trait_in_trait_tys(def_id) { - record!(self.tables.trait_impl_trait_tys[def_id] <- table); + record!(self.tables.collect_return_position_impl_trait_in_trait_tys[def_id] <- table); } if let DefKind::Impl { .. } | DefKind::Trait = def_kind { let table = tcx.associated_types_for_impl_traits_in_trait_or_impl(def_id); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index c7b2eaa15ebf..a3645a5556bf 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -466,7 +466,7 @@ fn encode(&self, buf: &mut FileEncoder) -> LazyTables { macro_definition: Table>, proc_macro: Table, deduced_param_attrs: Table>, - trait_impl_trait_tys: Table>>>>, + collect_return_position_impl_trait_in_trait_tys: Table>>>>, doc_link_resolutions: Table>, doc_link_traits_in_scope: Table>, assumed_wf_types_for_rpitit: Table, Span)>>, From 25e018de2da463f1977ac7510f86e0a2c303a06b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Sun, 5 Apr 2026 12:38:18 +0200 Subject: [PATCH 06/41] Add better default spans for the `Ty` impl of `QueryKey` --- compiler/rustc_middle/src/query/keys.rs | 60 ++++++++++++++++++- src/tools/miri/tests/fail/layout_cycle.rs | 3 +- src/tools/miri/tests/fail/layout_cycle.stderr | 10 +++- tests/ui/consts/const-size_of-cycle.stderr | 6 +- tests/ui/consts/issue-44415.stderr | 6 +- tests/ui/layout/layout-cycle.rs | 2 +- tests/ui/layout/layout-cycle.stderr | 10 +++- tests/ui/layout/post-mono-layout-cycle.rs | 2 +- tests/ui/layout/post-mono-layout-cycle.stderr | 13 +++- ...on-structural-match-types-cycle-err.stderr | 9 ++- .../issue-26548-recursion-via-normalize.rs | 12 ++-- ...issue-26548-recursion-via-normalize.stderr | 13 +++- tests/ui/sized/recursive-type-binding.rs | 6 +- tests/ui/sized/recursive-type-binding.stderr | 12 +++- .../recursive-type-coercion-from-never.rs | 6 +- .../recursive-type-coercion-from-never.stderr | 12 +++- .../sized/stack-overflow-trait-infer-98842.rs | 2 +- .../stack-overflow-trait-infer-98842.stderr | 7 ++- ...cursive-enum-and-array-impl.current.stderr | 16 ++++- ...-recursive-enum-and-array-impl.next.stderr | 16 ++++- .../129541-recursive-enum-and-array-impl.rs | 3 +- .../self-in-enum-definition.stderr | 6 +- 22 files changed, 192 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index 5c92f126e116..71bd08861b8d 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -5,6 +5,7 @@ use std::hash::Hash; use rustc_ast::tokenstream::TokenStream; +use rustc_data_structures::sso::SsoHashSet; use rustc_data_structures::stable_hasher::StableHash; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId}; use rustc_hir::hir_id::OwnerId; @@ -256,8 +257,8 @@ fn default_span(&self, _: TyCtxt<'_>) -> Span { } impl<'tcx> QueryKey for Ty<'tcx> { - fn default_span(&self, _: TyCtxt<'_>) -> Span { - DUMMY_SP + fn default_span(&self, tcx: TyCtxt<'_>) -> Span { + def_id_of_type(*self).map(|def_id| tcx.def_span(def_id)).unwrap_or(DUMMY_SP) } } @@ -360,3 +361,58 @@ fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.0.default_span(tcx) } } + +/// Gets a `DefId` associated with a type +/// +/// Visited set is needed to avoid full iteration over +/// deeply nested tuples that have no DefId. +fn def_id_of_type_cached<'a>(ty: Ty<'a>, visited: &mut SsoHashSet>) -> Option { + match *ty.kind() { + ty::Adt(adt_def, _) => Some(adt_def.did()), + + ty::Dynamic(data, ..) => data.principal_def_id(), + + ty::Pat(subty, _) | ty::Array(subty, _) | ty::Slice(subty) => { + def_id_of_type_cached(subty, visited) + } + + ty::RawPtr(ty, _) => def_id_of_type_cached(ty, visited), + + ty::Ref(_, ty, _) => def_id_of_type_cached(ty, visited), + + ty::Tuple(tys) => tys.iter().find_map(|ty| { + if visited.insert(ty) { + return def_id_of_type_cached(ty, visited); + } + return None; + }), + + ty::FnDef(def_id, _) + | ty::Closure(def_id, _) + | ty::CoroutineClosure(def_id, _) + | ty::Coroutine(def_id, _) + | ty::CoroutineWitness(def_id, _) + | ty::Foreign(def_id) => Some(def_id), + + ty::Alias(alias) => Some(alias.kind.def_id()), + + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Str + | ty::FnPtr(..) + | ty::UnsafeBinder(_) + | ty::Placeholder(..) + | ty::Param(_) + | ty::Infer(_) + | ty::Bound(..) + | ty::Error(_) + | ty::Never + | ty::Float(_) => None, + } +} + +fn def_id_of_type(ty: Ty<'_>) -> Option { + def_id_of_type_cached(ty, &mut SsoHashSet::new()) +} diff --git a/src/tools/miri/tests/fail/layout_cycle.rs b/src/tools/miri/tests/fail/layout_cycle.rs index 8d5f1914d0c3..9e93761871d6 100644 --- a/src/tools/miri/tests/fail/layout_cycle.rs +++ b/src/tools/miri/tests/fail/layout_cycle.rs @@ -1,8 +1,7 @@ -//~ ERROR: cycle detected when computing layout of - use std::mem; pub struct S { + //~^ ERROR: cycle detected when computing layout of pub f: ::I, } diff --git a/src/tools/miri/tests/fail/layout_cycle.stderr b/src/tools/miri/tests/fail/layout_cycle.stderr index f8d555e5a10b..1efbe239016e 100644 --- a/src/tools/miri/tests/fail/layout_cycle.stderr +++ b/src/tools/miri/tests/fail/layout_cycle.stderr @@ -1,6 +1,14 @@ error[E0391]: cycle detected when computing layout of `S>` + --> tests/fail/layout_cycle.rs:LL:CC | - = note: ...which requires computing layout of ` as Tr>::I`... +LL | pub struct S { + | ^^^^^^^^^^^^^^^^^^^ + | +note: ...which requires computing layout of ` as Tr>::I`... + --> tests/fail/layout_cycle.rs:LL:CC + | +LL | type I: Tr; + | ^^^^^^^^^^ = note: ...which again requires computing layout of `S>`, completing the cycle note: cycle used when const-evaluating + checking `core::mem::SizedTypeProperties::SIZE` --> RUSTLIB/core/src/mem/mod.rs:LL:CC diff --git a/tests/ui/consts/const-size_of-cycle.stderr b/tests/ui/consts/const-size_of-cycle.stderr index 01aa5e726b45..6a7f44ed3589 100644 --- a/tests/ui/consts/const-size_of-cycle.stderr +++ b/tests/ui/consts/const-size_of-cycle.stderr @@ -10,7 +10,11 @@ note: ...which requires simplifying constant for the type system `core::mem::Siz --> $SRC_DIR/core/src/mem/mod.rs:LL:COL note: ...which requires const-evaluating + checking `core::mem::SizedTypeProperties::SIZE`... --> $SRC_DIR/core/src/mem/mod.rs:LL:COL - = note: ...which requires computing layout of `Foo`... +note: ...which requires computing layout of `Foo`... + --> $DIR/const-size_of-cycle.rs:1:1 + | +LL | struct Foo { + | ^^^^^^^^^^ = note: ...which requires computing layout of `[u8; std::mem::size_of::()]`... note: ...which requires normalizing `[u8; std::mem::size_of::()]`... --> $DIR/const-size_of-cycle.rs:2:17 diff --git a/tests/ui/consts/issue-44415.stderr b/tests/ui/consts/issue-44415.stderr index 0e3f2e6199f7..28f681371733 100644 --- a/tests/ui/consts/issue-44415.stderr +++ b/tests/ui/consts/issue-44415.stderr @@ -9,7 +9,11 @@ note: ...which requires const-evaluating + checking `Foo::bytes::{constant#0}`.. | LL | bytes: [u8; unsafe { intrinsics::size_of::() }], | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: ...which requires computing layout of `Foo`... +note: ...which requires computing layout of `Foo`... + --> $DIR/issue-44415.rs:5:1 + | +LL | struct Foo { + | ^^^^^^^^^^ = note: ...which requires computing layout of `[u8; unsafe { intrinsics::size_of::() }]`... note: ...which requires normalizing `[u8; unsafe { intrinsics::size_of::() }]`... --> $DIR/issue-44415.rs:6:17 diff --git a/tests/ui/layout/layout-cycle.rs b/tests/ui/layout/layout-cycle.rs index 846ce0882cad..e873fcdaa97a 100644 --- a/tests/ui/layout/layout-cycle.rs +++ b/tests/ui/layout/layout-cycle.rs @@ -1,11 +1,11 @@ //@ build-fail -//~^ ERROR: cycle detected when computing layout of // Issue #111176 -- ensure that we do not emit ICE on layout cycles use std::mem; pub struct S { + //~^ ERROR: cycle detected when computing layout of pub f: ::I, } diff --git a/tests/ui/layout/layout-cycle.stderr b/tests/ui/layout/layout-cycle.stderr index 28c35d431226..0652f032c9db 100644 --- a/tests/ui/layout/layout-cycle.stderr +++ b/tests/ui/layout/layout-cycle.stderr @@ -1,6 +1,14 @@ error[E0391]: cycle detected when computing layout of `S>` + --> $DIR/layout-cycle.rs:7:1 | - = note: ...which requires computing layout of ` as Tr>::I`... +LL | pub struct S { + | ^^^^^^^^^^^^^^^^^^^ + | +note: ...which requires computing layout of ` as Tr>::I`... + --> $DIR/layout-cycle.rs:13:5 + | +LL | type I: Tr; + | ^^^^^^^^^^ = note: ...which again requires computing layout of `S>`, completing the cycle note: cycle used when const-evaluating + checking `core::mem::SizedTypeProperties::SIZE` --> $SRC_DIR/core/src/mem/mod.rs:LL:COL diff --git a/tests/ui/layout/post-mono-layout-cycle.rs b/tests/ui/layout/post-mono-layout-cycle.rs index 841fc30a50bc..71ccc8de94da 100644 --- a/tests/ui/layout/post-mono-layout-cycle.rs +++ b/tests/ui/layout/post-mono-layout-cycle.rs @@ -1,5 +1,4 @@ //@ build-fail -//~^ ERROR cycle detected when computing layout of `Wrapper<()>` trait Trait { type Assoc; @@ -10,6 +9,7 @@ impl Trait for () { } struct Wrapper { + //~^ ERROR cycle detected when computing layout of `Wrapper<()>` _x: ::Assoc, } diff --git a/tests/ui/layout/post-mono-layout-cycle.stderr b/tests/ui/layout/post-mono-layout-cycle.stderr index b9b1b988499e..05c57d20b8fe 100644 --- a/tests/ui/layout/post-mono-layout-cycle.stderr +++ b/tests/ui/layout/post-mono-layout-cycle.stderr @@ -1,8 +1,17 @@ error[E0391]: cycle detected when computing layout of `Wrapper<()>` + --> $DIR/post-mono-layout-cycle.rs:11:1 | - = note: ...which requires computing layout of `<() as Trait>::Assoc`... +LL | struct Wrapper { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: ...which requires computing layout of `<() as Trait>::Assoc`... + --> $DIR/post-mono-layout-cycle.rs:4:5 + | +LL | type Assoc; + | ^^^^^^^^^^ = note: ...which again requires computing layout of `Wrapper<()>`, completing the cycle - = note: cycle used when computing layout of `core::option::Option>` +note: cycle used when computing layout of `core::option::Option>` + --> $SRC_DIR/core/src/option.rs:LL:COL = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: aborting due to 1 previous error diff --git a/tests/ui/pattern/non-structural-match-types-cycle-err.stderr b/tests/ui/pattern/non-structural-match-types-cycle-err.stderr index 2f4ac63fc570..a7dd597d9614 100644 --- a/tests/ui/pattern/non-structural-match-types-cycle-err.stderr +++ b/tests/ui/pattern/non-structural-match-types-cycle-err.stderr @@ -14,8 +14,13 @@ note: ...which requires const-evaluating + checking ` = None; | ^^^^^^^^^^^^^^^^^^^^^ - = note: ...which requires computing layout of `core::option::Option<{async block@$DIR/non-structural-match-types-cycle-err.rs:18:16: 18:21}>`... - = note: ...which requires computing layout of `{async block@$DIR/non-structural-match-types-cycle-err.rs:18:16: 18:21}`... +note: ...which requires computing layout of `core::option::Option<{async block@$DIR/non-structural-match-types-cycle-err.rs:18:16: 18:21}>`... + --> $SRC_DIR/core/src/option.rs:LL:COL +note: ...which requires computing layout of `{async block@$DIR/non-structural-match-types-cycle-err.rs:18:16: 18:21}`... + --> $DIR/non-structural-match-types-cycle-err.rs:18:16 + | +LL | match Some(async {}) { + | ^^^^^ note: ...which requires optimizing MIR for `defines::{closure#0}`... --> $DIR/non-structural-match-types-cycle-err.rs:18:16 | diff --git a/tests/ui/recursion/issue-26548-recursion-via-normalize.rs b/tests/ui/recursion/issue-26548-recursion-via-normalize.rs index 6c7fc4beb543..a7eeccbfbd54 100644 --- a/tests/ui/recursion/issue-26548-recursion-via-normalize.rs +++ b/tests/ui/recursion/issue-26548-recursion-via-normalize.rs @@ -1,9 +1,8 @@ -//~ ERROR cycle detected when computing layout of `core::option::Option` -//~| NOTE see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -//~| NOTE ...which requires computing layout of `S`... -//~| NOTE ...which requires computing layout of `core::option::Option<::It>`... -//~| NOTE ...which again requires computing layout of `core::option::Option`, completing the cycle -//~| NOTE cycle used when computing layout of `core::option::Option<::It>` +//~? ERROR cycle detected when computing layout of `core::option::Option` +//~? NOTE see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information +//~? NOTE ...which requires computing layout of `core::option::Option<::It>`... +//~? NOTE ...which again requires computing layout of `core::option::Option`, completing the cycle +//~? NOTE cycle used when computing layout of `core::option::Option<::It>` trait Mirror { type It: ?Sized; @@ -12,6 +11,7 @@ impl Mirror for T { type It = Self; } struct S(Option<::It>); +//~^ NOTE ...which requires computing layout of `S`... fn main() { let _s = S(None); diff --git a/tests/ui/recursion/issue-26548-recursion-via-normalize.stderr b/tests/ui/recursion/issue-26548-recursion-via-normalize.stderr index e77fb025bcf1..daf9240ec5c9 100644 --- a/tests/ui/recursion/issue-26548-recursion-via-normalize.stderr +++ b/tests/ui/recursion/issue-26548-recursion-via-normalize.stderr @@ -1,9 +1,16 @@ error[E0391]: cycle detected when computing layout of `core::option::Option` + --> $SRC_DIR/core/src/option.rs:LL:COL | - = note: ...which requires computing layout of `S`... - = note: ...which requires computing layout of `core::option::Option<::It>`... +note: ...which requires computing layout of `S`... + --> $DIR/issue-26548-recursion-via-normalize.rs:13:1 + | +LL | struct S(Option<::It>); + | ^^^^^^^^ +note: ...which requires computing layout of `core::option::Option<::It>`... + --> $SRC_DIR/core/src/option.rs:LL:COL = note: ...which again requires computing layout of `core::option::Option`, completing the cycle - = note: cycle used when computing layout of `core::option::Option<::It>` +note: cycle used when computing layout of `core::option::Option<::It>` + --> $SRC_DIR/core/src/option.rs:LL:COL = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: aborting due to 1 previous error diff --git a/tests/ui/sized/recursive-type-binding.rs b/tests/ui/sized/recursive-type-binding.rs index 52de04afd66d..c67e8369a3b7 100644 --- a/tests/ui/sized/recursive-type-binding.rs +++ b/tests/ui/sized/recursive-type-binding.rs @@ -1,12 +1,14 @@ //@ build-fail -//~^ ERROR cycle detected when computing layout of `Foo<()>` -trait A { type Assoc: ?Sized; } +trait A { + type Assoc: ?Sized; +} impl A for () { type Assoc = Foo<()>; } struct Foo(T::Assoc); +//~^ ERROR cycle detected when computing layout of `Foo<()>` fn main() { let x: Foo<()>; diff --git a/tests/ui/sized/recursive-type-binding.stderr b/tests/ui/sized/recursive-type-binding.stderr index d9c2efa4d53b..290262c16a07 100644 --- a/tests/ui/sized/recursive-type-binding.stderr +++ b/tests/ui/sized/recursive-type-binding.stderr @@ -1,9 +1,17 @@ error[E0391]: cycle detected when computing layout of `Foo<()>` + --> $DIR/recursive-type-binding.rs:10:1 | - = note: ...which requires computing layout of `<() as A>::Assoc`... +LL | struct Foo(T::Assoc); + | ^^^^^^^^^^^^^^^^ + | +note: ...which requires computing layout of `<() as A>::Assoc`... + --> $DIR/recursive-type-binding.rs:4:5 + | +LL | type Assoc: ?Sized; + | ^^^^^^^^^^^^^^^^^^ = note: ...which again requires computing layout of `Foo<()>`, completing the cycle note: cycle used when elaborating drops for `main` - --> $DIR/recursive-type-binding.rs:11:1 + --> $DIR/recursive-type-binding.rs:13:1 | LL | fn main() { | ^^^^^^^^^ diff --git a/tests/ui/sized/recursive-type-coercion-from-never.rs b/tests/ui/sized/recursive-type-coercion-from-never.rs index 7bd87ae06c5e..7bb535bb20a7 100644 --- a/tests/ui/sized/recursive-type-coercion-from-never.rs +++ b/tests/ui/sized/recursive-type-coercion-from-never.rs @@ -1,15 +1,17 @@ //@ build-fail -//~^ ERROR cycle detected when computing layout of `Foo<()>` // Regression test for a stack overflow: https://github.com/rust-lang/rust/issues/113197 -trait A { type Assoc; } +trait A { + type Assoc; +} impl A for () { type Assoc = Foo<()>; } struct Foo(T::Assoc); +//~^ ERROR cycle detected when computing layout of `Foo<()>` fn main() { Foo::<()>(todo!()); diff --git a/tests/ui/sized/recursive-type-coercion-from-never.stderr b/tests/ui/sized/recursive-type-coercion-from-never.stderr index 7580e780dda5..1e04c2ab396f 100644 --- a/tests/ui/sized/recursive-type-coercion-from-never.stderr +++ b/tests/ui/sized/recursive-type-coercion-from-never.stderr @@ -1,9 +1,17 @@ error[E0391]: cycle detected when computing layout of `Foo<()>` + --> $DIR/recursive-type-coercion-from-never.rs:13:1 | - = note: ...which requires computing layout of `<() as A>::Assoc`... +LL | struct Foo(T::Assoc); + | ^^^^^^^^^^^^^^^^ + | +note: ...which requires computing layout of `<() as A>::Assoc`... + --> $DIR/recursive-type-coercion-from-never.rs:6:5 + | +LL | type Assoc; + | ^^^^^^^^^^ = note: ...which again requires computing layout of `Foo<()>`, completing the cycle note: cycle used when elaborating drops for `main` - --> $DIR/recursive-type-coercion-from-never.rs:14:1 + --> $DIR/recursive-type-coercion-from-never.rs:16:1 | LL | fn main() { | ^^^^^^^^^ diff --git a/tests/ui/sized/stack-overflow-trait-infer-98842.rs b/tests/ui/sized/stack-overflow-trait-infer-98842.rs index d6522e3cfb64..ff080eb19d03 100644 --- a/tests/ui/sized/stack-overflow-trait-infer-98842.rs +++ b/tests/ui/sized/stack-overflow-trait-infer-98842.rs @@ -2,11 +2,11 @@ // issue: rust-lang/rust#98842 //@ check-fail //@ edition:2021 -//~^^^^ ERROR cycle detected when computing layout of `Foo` // If the inner `Foo` is named through an associated type, // the "infinite size" error does not occur. struct Foo(<&'static Foo as ::core::ops::Deref>::Target); +//~^ ERROR cycle detected when computing layout of `Foo` // But Rust will be unable to know whether `Foo` is sized or not, // and it will infinitely recurse somewhere trying to figure out the // size of this pointer (is my guess): diff --git a/tests/ui/sized/stack-overflow-trait-infer-98842.stderr b/tests/ui/sized/stack-overflow-trait-infer-98842.stderr index 5557a6fc45b8..f15d95ea9480 100644 --- a/tests/ui/sized/stack-overflow-trait-infer-98842.stderr +++ b/tests/ui/sized/stack-overflow-trait-infer-98842.stderr @@ -1,6 +1,11 @@ error[E0391]: cycle detected when computing layout of `Foo` + --> $DIR/stack-overflow-trait-infer-98842.rs:8:1 | - = note: ...which requires computing layout of `<&'static Foo as core::ops::deref::Deref>::Target`... +LL | struct Foo(<&'static Foo as ::core::ops::Deref>::Target); + | ^^^^^^^^^^ + | +note: ...which requires computing layout of `<&'static Foo as core::ops::deref::Deref>::Target`... + --> $SRC_DIR/core/src/ops/deref.rs:LL:COL = note: ...which again requires computing layout of `Foo`, completing the cycle note: cycle used when const-evaluating + checking `_` --> $DIR/stack-overflow-trait-infer-98842.rs:13:1 diff --git a/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.current.stderr b/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.current.stderr index 50dcea0bfac6..d429b08c8c98 100644 --- a/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.current.stderr +++ b/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.current.stderr @@ -1,8 +1,20 @@ error[E0391]: cycle detected when computing layout of `<[Hello] as Normalize>::Assoc` + --> $DIR/129541-recursive-enum-and-array-impl.rs:8:5 | - = note: ...which requires computing layout of `Hello`... +LL | type Assoc; + | ^^^^^^^^^^ + | +note: ...which requires computing layout of `Hello`... + --> $DIR/129541-recursive-enum-and-array-impl.rs:21:1 + | +LL | enum Hello { + | ^^^^^^^^^^ = note: ...which again requires computing layout of `<[Hello] as Normalize>::Assoc`, completing the cycle - = note: cycle used when computing layout of `Hello` +note: cycle used when computing layout of `Hello` + --> $DIR/129541-recursive-enum-and-array-impl.rs:21:1 + | +LL | enum Hello { + | ^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: aborting due to 1 previous error diff --git a/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.next.stderr b/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.next.stderr index 50dcea0bfac6..d429b08c8c98 100644 --- a/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.next.stderr +++ b/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.next.stderr @@ -1,8 +1,20 @@ error[E0391]: cycle detected when computing layout of `<[Hello] as Normalize>::Assoc` + --> $DIR/129541-recursive-enum-and-array-impl.rs:8:5 | - = note: ...which requires computing layout of `Hello`... +LL | type Assoc; + | ^^^^^^^^^^ + | +note: ...which requires computing layout of `Hello`... + --> $DIR/129541-recursive-enum-and-array-impl.rs:21:1 + | +LL | enum Hello { + | ^^^^^^^^^^ = note: ...which again requires computing layout of `<[Hello] as Normalize>::Assoc`, completing the cycle - = note: cycle used when computing layout of `Hello` +note: cycle used when computing layout of `Hello` + --> $DIR/129541-recursive-enum-and-array-impl.rs:21:1 + | +LL | enum Hello { + | ^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: aborting due to 1 previous error diff --git a/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.rs b/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.rs index 5b7bf5f3404c..6c0411135dad 100644 --- a/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.rs +++ b/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.rs @@ -1,6 +1,4 @@ // Regression test for #129541 -//~^ ERROR cycle detected when computing layout of `<[Hello] as Normalize>::Assoc` [E0391] - //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver @@ -8,6 +6,7 @@ trait Bound {} trait Normalize { type Assoc; + //~^ ERROR cycle detected when computing layout of `<[Hello] as Normalize>::Assoc` [E0391] } impl Normalize for T { diff --git a/tests/ui/type-alias-enum-variants/self-in-enum-definition.stderr b/tests/ui/type-alias-enum-variants/self-in-enum-definition.stderr index 13ae6dfcaa35..8b6d11b8df85 100644 --- a/tests/ui/type-alias-enum-variants/self-in-enum-definition.stderr +++ b/tests/ui/type-alias-enum-variants/self-in-enum-definition.stderr @@ -9,7 +9,11 @@ note: ...which requires const-evaluating + checking `Alpha::V3::{constant#0}`... | LL | V3 = Self::V1 {} as u8 + 2, | ^^^^^^^^^^^^^^^^^^^^^ - = note: ...which requires computing layout of `Alpha`... +note: ...which requires computing layout of `Alpha`... + --> $DIR/self-in-enum-definition.rs:2:1 + | +LL | enum Alpha { + | ^^^^^^^^^^ = note: ...which again requires simplifying constant for the type system `Alpha::V3::{constant#0}`, completing the cycle note: cycle used when checking that `Alpha` is well-formed --> $DIR/self-in-enum-definition.rs:2:1 From 3b5dd18d9a6b9e738daf4b3918e2eb2414b7e304 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 24 Apr 2026 14:57:56 +1000 Subject: [PATCH 07/41] Fix some comments. - Remove comment about blanket implementation of `FallibleTypeFolder` -- it does not exist. (It probably used to.) - Fix some incorrect name references. - Fix minor grammatical errors. - Fix stale comment on `visit_region` -- it was a no-op once, but no longer. LLM disclosure: Claude Code identified these problems with comments when I asked it to review `fold.rs` and `visit.rs`. I verified the correctness of the problems and made the changes by hand. --- compiler/rustc_type_ir/src/flags.rs | 2 +- compiler/rustc_type_ir/src/fold.rs | 12 ++++-------- compiler/rustc_type_ir/src/visit.rs | 16 ++++++++-------- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 50c30f425270..3debef168cff 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -139,7 +139,7 @@ pub struct TypeFlags: u32 { /// Does this type have any coroutines in it? const HAS_TY_CORO = 1 << 25; - /// Does this have have a `Bound(BoundVarIndexKind::Canonical, _)`? + /// Does this have a `Bound(BoundVarIndexKind::Canonical, _)`? const HAS_CANONICAL_BOUND = 1 << 26; } } diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index d1a50599e8b9..94d984559462 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -121,10 +121,6 @@ fn try_super_fold_with>( /// default that does an "identity" fold. Implementations of these methods /// often fall back to a `super_fold_with` method if the primary argument /// doesn't satisfy a particular condition. -/// -/// A blanket implementation of [`FallibleTypeFolder`] will defer to -/// the infallible methods of this trait to ensure that the two APIs -/// are coherent. pub trait TypeFolder: Sized { fn cx(&self) -> I; @@ -477,10 +473,10 @@ pub fn fold_regions( /// Folds over the substructure of a type, visiting its component /// types and all regions that occur *free* within it. /// -/// That is, function pointer types and trait object can introduce -/// new bound regions which are not visited by this visitors as +/// That is, function pointer types and trait objects can introduce +/// new bound regions which are not visited by this visitor as /// they are not free; only regions that occur free will be -/// visited by `fld_r`. +/// visited by `fold_region_fn`. pub struct RegionFolder { cx: I, @@ -489,7 +485,7 @@ pub struct RegionFolder { /// binder, it is incremented (via `shift_in`). current_index: ty::DebruijnIndex, - /// Callback invokes for each free region. The `DebruijnIndex` + /// Callback invoked for each free region. The `DebruijnIndex` /// points to the binder *just outside* the ones we have passed /// through. fold_region_fn: F, diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index a078b860be77..6d0b307f013e 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -67,7 +67,7 @@ pub trait TypeVisitable: fmt::Debug { /// each field/element. /// /// For types of interest (such as `Ty`), the implementation of this method - /// that calls a visitor method specifically for that type (such as + /// calls a visitor method specifically for that type (such as /// `V::visit_ty`). This is where control transfers from `TypeVisitable` to /// `TypeVisitor`. fn visit_with>(&self, visitor: &mut V) -> V::Result; @@ -102,8 +102,8 @@ fn visit_ty(&mut self, t: I::Ty) -> Self::Result { t.super_visit_with(self) } - // The default region visitor is a no-op because `Region` is non-recursive - // and has no `super_visit_with` method to call. + // `Region` is non-recursive so the default region visitor has no + // `super_visit_with` method to call. fn visit_region(&mut self, r: I::Region) -> Self::Result { if let ty::ReError(guar) = r.kind() { self.visit_error(guar) @@ -251,10 +251,10 @@ fn has_vars_bound_above(&self, binder: ty::DebruijnIndex) -> bool { self.has_vars_bound_at_or_above(binder.shifted_in(1)) } - /// Return `true` if this type has regions that are not a part of the type. - /// For example, `for<'a> fn(&'a i32)` return `false`, while `fn(&'a i32)` - /// would return `true`. The latter can occur when traversing through the - /// former. + /// Returns `true` if this type has regions that are not a part of the + /// type. For example, given a `for<'a> fn(&'a i32)` this function returns + /// `false`, while given a `fn(&'a i32)` it returns `true`. The latter can + /// occur when traversing through the former. /// /// See [`HasEscapingVarsVisitor`] for more information. fn has_escaping_bound_vars(&self) -> bool { @@ -571,7 +571,7 @@ fn visit_const(&mut self, ct: I::Const) -> Self::Result { // `outer_index`, that means that `ct` contains some content // bound at `outer_index` or above (because // `outer_exclusive_binder` is always 1 higher than the - // content in `t`). Therefore, `t` has some escaping vars. + // content in `ct`). Therefore, `ct` has some escaping vars. if ct.outer_exclusive_binder() > self.outer_index { ControlFlow::Break(FoundEscapingVars) } else { From a07a3b1bf15be2d344fca2ea6ad62071ed5e15b6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 5 May 2026 08:39:24 +0200 Subject: [PATCH 08/41] windows/time: avoid being too close to 0 --- library/std/src/sys/time/windows.rs | 7 +++++++ library/std/tests/time.rs | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/time/windows.rs b/library/std/src/sys/time/windows.rs index 2e38dbf5cf28..b2abee4219fe 100644 --- a/library/std/src/sys/time/windows.rs +++ b/library/std/src/sys/time/windows.rs @@ -35,7 +35,14 @@ pub fn now() -> Instant { let freq = perf_counter::frequency() as u64; let now = perf_counter::now(); + + // We convert now to `u64` to be able to use `Duration`. let instant_nsec = mul_div_u64(now as u64, NANOS_PER_SEC, freq); + // We can add an arbitrary offset to shift the epoch of this clock. We do that to avoid + // being too close to 0 which would lead to underflow when computing times in the past. Also + // see . + let instant_nsec = instant_nsec + (u64::MAX / 4); + Self { t: Duration::from_nanos(instant_nsec) } } diff --git a/library/std/tests/time.rs b/library/std/tests/time.rs index b73e7bc3962e..d6736e25ace1 100644 --- a/library/std/tests/time.rs +++ b/library/std/tests/time.rs @@ -1,4 +1,5 @@ #![feature(duration_constants)] +#![feature(duration_constructors)] #![feature(time_systemtime_limits)] #![feature(time_saturating_systemtime)] @@ -104,7 +105,8 @@ fn instant_math_is_associative() { #[test] fn instant_duration_since_saturates() { let a = Instant::now(); - assert_eq!((a - Duration::SECOND).duration_since(a), Duration::ZERO); + // This also checks that computing an instant before program startup works. + assert_eq!((a - Duration::from_days(1)).duration_since(a), Duration::ZERO); } #[test] From 3c83d3adc91b253d1e4c500a94244d7ff1f12579 Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Tue, 5 May 2026 19:11:16 +0000 Subject: [PATCH 09/41] llvm: Use correct type for splat mask After llvm/llvm-project#195486 , LLVM has explicit handling for null pointers in simd operations instead of using special handling based on zeroes. This causes LLVM with asserts enabled to detect an improperly typed mask passed to splat (if the output vector does not have i32 elements), and can cause SIGSEGV in more complex cases with asserts disabled. --- compiler/rustc_codegen_llvm/src/intrinsic.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index dd9ebf298b22..84c1e8e6f3d4 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -2086,7 +2086,7 @@ fn vector_mask_to_bitmask<'a, 'll, 'tcx>( } if name == sym::simd_splat { - let (_out_len, out_ty) = require_simd!(ret_ty, SimdReturn); + let (out_len, out_ty) = require_simd!(ret_ty, SimdReturn); require!( args[0].layout.ty == out_ty, @@ -2105,7 +2105,8 @@ fn vector_mask_to_bitmask<'a, 'll, 'tcx>( // `shufflevector v0, poison, zeroinitializer` // The masks is all zeros, so this splats lane 0 (which has our element in it). - let splat = bx.shuffle_vector(v0, poison_vec, bx.const_null(llret_ty)); + let mask_ty = bx.type_vector(bx.type_i32(), out_len); + let splat = bx.shuffle_vector(v0, poison_vec, bx.const_null(mask_ty)); return Ok(splat); } From 09b38e43f67cb11c81680735092151231e47d25a Mon Sep 17 00:00:00 2001 From: Mahdi Ali-Raihan Date: Tue, 5 May 2026 15:34:25 -0400 Subject: [PATCH 10/41] Implemented PathBuf::into_string, which chains PathBuf::into_os_string and OsString::into_string for you --- library/std/src/path.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index da3d9c2454a7..222bf77996c7 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1796,6 +1796,25 @@ pub fn into_os_string(self) -> OsString { self.inner } + /// Converts the `PathBuf` into a `String` if it contains valid Unicode data. + /// + /// On failure, ownership of the original `PathBuf` is returned. + /// + /// # Examples + /// + /// ``` + /// #![feature(pathbuf_into_string)] + /// use std::path::PathBuf; + /// + /// let path_buf = PathBuf::from("foo"); + /// let string = path_buf.into_string(); + /// assert_eq!(string, Ok(String::from("foo"))); + /// ``` + #[unstable(feature = "pathbuf_into_string", issue = "156203")] + pub fn into_string(self) -> Result { + self.into_os_string().into_string().map_err(PathBuf::from) + } + /// Converts this `PathBuf` into a [boxed](Box) [`Path`]. #[stable(feature = "into_boxed_path", since = "1.20.0")] #[must_use = "`self` will be dropped if the result is not used"] From 411a932b382c783b5ac8f110996c44682a030f85 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Wed, 10 Sep 2025 21:58:56 +0200 Subject: [PATCH 11/41] ImproperCTypes: merge outer_ty information into VisitorState Another user-transparent change, unifying outer-type information and the existing VisitorState flags. --- .../rustc_lint/src/types/improper_ctypes.rs | 223 +++++++++++------- 1 file changed, 132 insertions(+), 91 deletions(-) diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index eddc3f628ae6..2d58066a96fc 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -257,12 +257,22 @@ fn check_struct_for_power_alignment<'tcx>( } } +/// Annotates whether we are in the context of an item *defined* in rust +/// and exposed to an FFI boundary, +/// or the context of an item from elsewhere, whose interface is re-*declared* in rust. #[derive(Clone, Copy)] enum CItemKind { Declaration, Definition, } +/// Annotates whether we are in the context of a function's argument types or return type. +#[derive(Clone, Copy)] +enum FnPos { + Arg, + Ret, +} + enum FfiResult<'tcx> { FfiSafe, FfiPhantom(Ty<'tcx>), @@ -286,8 +296,10 @@ enum IndirectionKind { } bitflags! { + /// VisitorState flags that are linked with the root type's use. + /// (These are the permanent part of the state, kept when visiting new Ty.) #[derive(Clone, Copy, Debug, PartialEq, Eq)] - struct VisitorState: u8 { + struct RootUseFlags: u8 { /// For use in (externally-linked) static variables. const STATIC = 0b000001; /// For use in functions in general. @@ -302,7 +314,45 @@ struct VisitorState: u8 { } } -impl VisitorState { +/// Description of the relationship between current Ty and +/// the type (or lack thereof) immediately containing it +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum OuterTyKind { + None, + /// A variant that should not exist, + /// but is needed because we don't change the lint's behavior yet + NoneThroughFnPtr, + /// Placeholder for properties that will be used eventually + Other, +} + +impl OuterTyKind { + /// Computes the relationship by providing the containing Ty itself + fn from_ty<'tcx>(ty: Ty<'tcx>) -> Self { + match ty.kind() { + ty::FnPtr(..) => Self::NoneThroughFnPtr, + ty::RawPtr(..) + | ty::Ref(..) + | ty::Adt(..) + | ty::Tuple(..) + | ty::Array(..) + | ty::Slice(_) => OuterTyKind::Other, + _ => bug!("Unexpected outer type {ty:?}"), + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +struct VisitorState { + /// Flags describing both the overall context in which the current Ty is, + /// linked to how the Visitor's original Ty was used. + root_use_flags: RootUseFlags, + /// Flags describing both the immediate context in which the current Ty is, + /// linked to how it relates to its parent Ty (or lack thereof). + outer_ty_kind: OuterTyKind, +} + +impl RootUseFlags { // The values that can be set. const STATIC_TY: Self = Self::STATIC; const ARGUMENT_TY_IN_DEFINITION: Self = @@ -317,86 +367,85 @@ impl VisitorState { const RETURN_TY_IN_FNPTR: Self = Self::from_bits(Self::FUNC.bits() | Self::THEORETICAL.bits() | Self::FN_RETURN.bits()) .unwrap(); +} - /// Get the proper visitor state for a given function's arguments. - fn argument_from_fnmode(fn_mode: CItemKind) -> Self { - match fn_mode { - CItemKind::Definition => VisitorState::ARGUMENT_TY_IN_DEFINITION, - CItemKind::Declaration => VisitorState::ARGUMENT_TY_IN_DECLARATION, +impl VisitorState { + /// From an existing state, compute the state of any subtype of the current type. + /// (General case. For the case where the current type is a function pointer, see `next_in_fnptr`.) + fn next(&self, current_ty: Ty<'_>) -> Self { + assert!(!matches!(current_ty.kind(), ty::FnPtr(..))); + VisitorState { + root_use_flags: self.root_use_flags, + outer_ty_kind: OuterTyKind::from_ty(current_ty), } } - /// Get the proper visitor state for a given function's return type. - fn return_from_fnmode(fn_mode: CItemKind) -> Self { - match fn_mode { - CItemKind::Definition => VisitorState::RETURN_TY_IN_DEFINITION, - CItemKind::Declaration => VisitorState::RETURN_TY_IN_DECLARATION, + /// From an existing state, compute the state of any subtype of the current type. + /// (Case where the current type is a function pointer, + /// meaning we need to specify if the subtype is an argument or the return.) + fn next_in_fnptr(&self, current_ty: Ty<'_>, fn_pos: FnPos) -> Self { + assert!(matches!(current_ty.kind(), ty::FnPtr(..))); + VisitorState { + root_use_flags: match fn_pos { + FnPos::Ret => RootUseFlags::RETURN_TY_IN_FNPTR, + FnPos::Arg => RootUseFlags::ARGUMENT_TY_IN_FNPTR, + }, + outer_ty_kind: OuterTyKind::from_ty(current_ty), } } + /// Get the proper visitor state for a given function's arguments or return type. + fn fn_entry_point(fn_mode: CItemKind, fn_pos: FnPos) -> Self { + let p_flags = match (fn_mode, fn_pos) { + (CItemKind::Definition, FnPos::Ret) => RootUseFlags::RETURN_TY_IN_DEFINITION, + (CItemKind::Declaration, FnPos::Ret) => RootUseFlags::RETURN_TY_IN_DECLARATION, + (CItemKind::Definition, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DEFINITION, + (CItemKind::Declaration, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DECLARATION, + }; + VisitorState { root_use_flags: p_flags, outer_ty_kind: OuterTyKind::None } + } + + /// Get the proper visitor state for a static variable's type + fn static_entry_point() -> Self { + VisitorState { root_use_flags: RootUseFlags::STATIC_TY, outer_ty_kind: OuterTyKind::None } + } + /// Whether the type is used in a function. - fn is_in_function(self) -> bool { - let ret = self.contains(Self::FUNC); + fn is_in_function(&self) -> bool { + let ret = self.root_use_flags.contains(RootUseFlags::FUNC); if ret { - debug_assert!(!self.contains(Self::STATIC)); + debug_assert!(!self.root_use_flags.contains(RootUseFlags::STATIC)); } ret } + /// Whether the type is used (directly or not) in a function, in return position. - fn is_in_function_return(self) -> bool { - let ret = self.contains(Self::FN_RETURN); + fn is_in_function_return(&self) -> bool { + let ret = self.root_use_flags.contains(RootUseFlags::FN_RETURN); if ret { debug_assert!(self.is_in_function()); } ret } + /// Whether the type is used (directly or not) in a defined function. /// In other words, whether or not we allow non-FFI-safe types behind a C pointer, /// to be treated as an opaque type on the other side of the FFI boundary. - fn is_in_defined_function(self) -> bool { - self.contains(Self::DEFINED) && self.is_in_function() + fn is_in_defined_function(&self) -> bool { + self.root_use_flags.contains(RootUseFlags::DEFINED) && self.is_in_function() } /// Whether the type is used (directly or not) in a function pointer type. /// Here, we also allow non-FFI-safe types behind a C pointer, /// to be treated as an opaque type on the other side of the FFI boundary. - fn is_in_fnptr(self) -> bool { - self.contains(Self::THEORETICAL) && self.is_in_function() + fn is_in_fnptr(&self) -> bool { + self.root_use_flags.contains(RootUseFlags::THEORETICAL) && self.is_in_function() } /// Whether we can expect type parameters and co in a given type. - fn can_expect_ty_params(self) -> bool { + fn can_expect_ty_params(&self) -> bool { // rust-defined functions, as well as FnPtrs - self.contains(Self::THEORETICAL) || self.is_in_defined_function() - } -} - -bitflags! { - /// Data that summarises how an "outer type" surrounds its inner type(s) - #[derive(Clone, Copy, Debug, PartialEq, Eq)] - struct OuterTyData: u8 { - /// To show that there is no outer type, the current type is directly used by a `static` - /// variable or a function/FnPtr - const NO_OUTER_TY = 0b01; - /// For NO_OUTER_TY cases, show that we are being directly used by a FnPtr specifically - /// FIXME(ctypes): this is only used for "bad behaviour" reproduced for compatibility's sake - const NO_OUTER_TY_FNPTR = 0b10; - } -} - -impl OuterTyData { - /// Get the proper data for a given outer type. - fn from_ty<'tcx>(ty: Ty<'tcx>) -> Self { - match ty.kind() { - ty::FnPtr(..) => Self::NO_OUTER_TY | Self::NO_OUTER_TY_FNPTR, - ty::RawPtr(..) - | ty::Ref(..) - | ty::Adt(..) - | ty::Tuple(..) - | ty::Array(..) - | ty::Slice(_) => Self::empty(), - k @ _ => bug!("unexpected outer type {:?} of kind {:?}", ty, k), - } + self.root_use_flags.contains(RootUseFlags::THEORETICAL) || self.is_in_defined_function() } } @@ -416,7 +465,7 @@ struct ImproperCTypesVisitor<'a, 'tcx> { impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { fn new(cx: &'a LateContext<'tcx>, base_ty: Ty<'tcx>, base_fn_mode: CItemKind) -> Self { - Self { cx, base_ty, base_fn_mode, cache: FxHashSet::default() } + ImproperCTypesVisitor { cx, base_ty, base_fn_mode, cache: FxHashSet::default() } } /// Checks if the given indirection (box,ref,pointer) is "ffi-safe". @@ -485,7 +534,7 @@ fn visit_indirection( { FfiSafe } else { - self.visit_type(state, OuterTyData::from_ty(ty), inner_ty) + self.visit_type(state.next(ty), inner_ty) } } } @@ -506,7 +555,7 @@ fn visit_variant_fields( if let Some(field) = super::transparent_newtype_field(self.cx.tcx, variant) { // Transparent newtypes have at most one non-ZST field which needs to be checked.. let field_ty = get_type_from_field(self.cx, field, args); - match self.visit_type(state, OuterTyData::from_ty(ty), field_ty) { + match self.visit_type(state.next(ty), field_ty) { FfiUnsafe { ty, .. } if ty.is_unit() => (), r => return r, } @@ -525,7 +574,7 @@ fn visit_variant_fields( let mut all_phantom = !variant.fields.is_empty(); for field in &variant.fields { let field_ty = get_type_from_field(self.cx, field, args); - all_phantom &= match self.visit_type(state, OuterTyData::from_ty(ty), field_ty) { + all_phantom &= match self.visit_type(state.next(ty), field_ty) { FfiSafe => false, // `()` fields are FFI-safe! FfiUnsafe { ty, .. } if ty.is_unit() => false, @@ -570,7 +619,7 @@ fn visit_struct_or_union( "consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct" )) } else { - // FIXME(ctypes): confirm that this makes sense for unions once #60405 / RFC2645 stabilises + // FIXME(#60405): confirm that this makes sense for unions once #60405 / RFC2645 stabilises Some(msg!( "consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this union" )) @@ -628,7 +677,7 @@ fn visit_enum( if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none() { // Special-case types like `Option` and `Result` if let Some(inner_ty) = repr_nullable_ptr(self.cx.tcx, self.cx.typing_env(), ty) { - return self.visit_type(state, OuterTyData::from_ty(ty), inner_ty); + return self.visit_type(state.next(ty), inner_ty); } return FfiUnsafe { @@ -660,12 +709,7 @@ fn visit_enum( /// Checks if the given type is "ffi-safe" (has a stable, well-defined /// representation which can be exported to C code). - fn visit_type( - &mut self, - state: VisitorState, - outer_ty: OuterTyData, - ty: Ty<'tcx>, - ) -> FfiResult<'tcx> { + fn visit_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> { use FfiResult::*; let tcx = self.cx.tcx; @@ -709,7 +753,8 @@ fn visit_type( // Pattern types are just extra invariants on the type that you need to uphold, // but only the base type is relevant for being representable in FFI. // (note: this lint was written when pattern types could only be integers constrained to ranges) - ty::Pat(pat_ty, _) => self.visit_type(state, outer_ty, pat_ty), + // (also note: the lack of ".next(ty)" on the state is on purpose) + ty::Pat(pat_ty, _) => self.visit_type(state, pat_ty), // types which likely have a stable representation, if the target architecture defines those // note: before rust 1.77, 128-bit ints were not FFI-safe on x86_64 @@ -740,12 +785,14 @@ fn visit_type( }, ty::Tuple(tuple) => { - // C functions can return void - let empty_and_safe = tuple.is_empty() - && outer_ty.contains(OuterTyData::NO_OUTER_TY) - && state.is_in_function_return(); - - if empty_and_safe { + if tuple.is_empty() + && state.is_in_function_return() + && matches!( + state.outer_ty_kind, + OuterTyKind::None | OuterTyKind::NoneThroughFnPtr + ) + { + // C functions can return void FfiSafe } else { FfiUnsafe { @@ -774,9 +821,8 @@ fn visit_type( ty::Array(inner_ty, _) => { if state.is_in_function() - && outer_ty.contains(OuterTyData::NO_OUTER_TY) - // FIXME(ctypes): VVV-this-VVV shouldn't be the case - && !outer_ty.contains(OuterTyData::NO_OUTER_TY_FNPTR) + // FIXME(ctypes): VVV-this-VVV shouldn't make a difference between ::None and ::NoneThroughFnPtr + && matches!(state.outer_ty_kind, OuterTyKind::None) { // C doesn't really support passing arrays by value - the only way to pass an array by value // is through a struct. @@ -788,7 +834,7 @@ fn visit_type( } else { // let's allow phantoms to go through, // since an array of 1-ZSTs is also a 1-ZST - self.visit_type(state, OuterTyData::from_ty(ty), inner_ty) + self.visit_type(state.next(ty), inner_ty) } } @@ -806,19 +852,14 @@ fn visit_type( let sig = tcx.instantiate_bound_regions_with_erased(sig); for arg in sig.inputs() { - match self.visit_type( - VisitorState::ARGUMENT_TY_IN_FNPTR, - OuterTyData::from_ty(ty), - *arg, - ) { + match self.visit_type(state.next_in_fnptr(ty, FnPos::Arg), *arg) { FfiSafe => {} r => return r, } } let ret_ty = sig.output(); - - self.visit_type(VisitorState::RETURN_TY_IN_FNPTR, OuterTyData::from_ty(ty), ret_ty) + self.visit_type(state.next_in_fnptr(ty, FnPos::Ret), ret_ty) } ty::Foreign(..) => FfiSafe, @@ -896,7 +937,7 @@ fn check_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> { return res; } - self.visit_type(state, OuterTyData::NO_OUTER_TY, ty) + self.visit_type(state, ty) } } @@ -925,7 +966,7 @@ fn visit_ty(&mut self, ty: &'_ hir::Ty<'_, AmbigArg>) { self.spans.push(ty.span); } - hir::intravisit::walk_ty(self, ty) + hir::intravisit::walk_ty(self, ty); } } @@ -970,12 +1011,12 @@ fn check_fn_for_external_abi_fnptr( let sig = cx.tcx.instantiate_bound_regions_with_erased(sig); for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { - let state = VisitorState::argument_from_fnmode(fn_mode); + let state = VisitorState::fn_entry_point(fn_mode, FnPos::Arg); self.check_type_for_external_abi_fnptr(cx, state, input_hir, *input_ty, fn_mode); } if let hir::FnRetTy::Return(ret_hir) = decl.output { - let state = VisitorState::return_from_fnmode(fn_mode); + let state = VisitorState::fn_entry_point(fn_mode, FnPos::Ret); self.check_type_for_external_abi_fnptr(cx, state, ret_hir, sig.output(), fn_mode); } } @@ -1000,7 +1041,7 @@ fn check_reprc_adt( fn check_foreign_static(&mut self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) { let ty = cx.tcx.type_of(id).instantiate_identity().skip_norm_wip(); let mut visitor = ImproperCTypesVisitor::new(cx, ty, CItemKind::Declaration); - let ffi_res = visitor.check_type(VisitorState::STATIC_TY, ty); + let ffi_res = visitor.check_type(VisitorState::static_entry_point(), ty); self.process_ffi_result(cx, span, ffi_res, CItemKind::Declaration); } @@ -1016,14 +1057,14 @@ fn check_foreign_fn( let sig = cx.tcx.instantiate_bound_regions_with_erased(sig); for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { - let state = VisitorState::argument_from_fnmode(fn_mode); + let state = VisitorState::fn_entry_point(fn_mode, FnPos::Arg); let mut visitor = ImproperCTypesVisitor::new(cx, *input_ty, fn_mode); let ffi_res = visitor.check_type(state, *input_ty); self.process_ffi_result(cx, input_hir.span, ffi_res, fn_mode); } if let hir::FnRetTy::Return(ret_hir) = decl.output { - let state = VisitorState::return_from_fnmode(fn_mode); + let state = VisitorState::fn_entry_point(fn_mode, FnPos::Ret); let mut visitor = ImproperCTypesVisitor::new(cx, sig.output(), fn_mode); let ffi_res = visitor.check_type(state, sig.output()); self.process_ffi_result(cx, ret_hir.span, ffi_res, fn_mode); @@ -1124,7 +1165,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { | hir::ItemKind::TyAlias(_, _, ty) => { self.check_type_for_external_abi_fnptr( cx, - VisitorState::STATIC_TY, + VisitorState::static_entry_point(), ty, cx.tcx.type_of(item.owner_id).instantiate_identity().skip_norm_wip(), CItemKind::Definition, @@ -1158,7 +1199,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) { self.check_type_for_external_abi_fnptr( cx, - VisitorState::STATIC_TY, + VisitorState::static_entry_point(), field.ty, cx.tcx.type_of(field.def_id).instantiate_identity().skip_norm_wip(), CItemKind::Definition, From 6839797f18f4e8b014932c19df77e3e0e2874481 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Sat, 25 Apr 2026 00:00:01 +0200 Subject: [PATCH 12/41] ImproperCTypes: Use more `Unnormalized` type wrappers In order to follow along with the efforts to properly distinguish already-normalised and unnormalized types, we separate the internal interfaces of this lint that rely on normalized types from those that do not. We do that by adding the `Unnormalized` wrapper to some interfaces. --- .../rustc_lint/src/types/improper_ctypes.rs | 65 ++++++++++++------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index 2d58066a96fc..03928c78cfb6 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -138,17 +138,16 @@ USES_POWER_ALIGNMENT ]); -/// Getting the (normalized) type out of a field (for, e.g., an enum variant or a tuple). -#[inline] -fn get_type_from_field<'tcx>( +/// A common pattern in this lint is to attempt normalize_erasing_regions, +/// but keep the original type if it were to fail. +/// This may or may not be supported in the logic behind the `Unnormalized` wrapper, +/// (FIXME?) +/// but it should be enough for non-wrapped types to be as normalised as this lint needs them to be. +fn maybe_normalize_erasing_regions<'tcx>( cx: &LateContext<'tcx>, - field: &ty::FieldDef, - args: GenericArgsRef<'tcx>, + value: Unnormalized<'tcx, Ty<'tcx>>, ) -> Ty<'tcx> { - let field_ty = field.ty(cx.tcx, args); - cx.tcx - .try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(field_ty)) - .unwrap_or(field_ty) + cx.tcx.try_normalize_erasing_regions(cx.typing_env(), value).unwrap_or(value.skip_norm_wip()) } /// Check a variant of a non-exhaustive enum for improper ctypes @@ -464,8 +463,17 @@ struct ImproperCTypesVisitor<'a, 'tcx> { } impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'tcx>, base_ty: Ty<'tcx>, base_fn_mode: CItemKind) -> Self { - ImproperCTypesVisitor { cx, base_ty, base_fn_mode, cache: FxHashSet::default() } + fn new( + cx: &'a LateContext<'tcx>, + base_ty: Unnormalized<'tcx, Ty<'tcx>>, + base_fn_mode: CItemKind, + ) -> Self { + ImproperCTypesVisitor { + cx, + base_ty: maybe_normalize_erasing_regions(cx, base_ty), + base_fn_mode, + cache: FxHashSet::default(), + } } /// Checks if the given indirection (box,ref,pointer) is "ffi-safe". @@ -554,7 +562,10 @@ fn visit_variant_fields( let transparent_with_all_zst_fields = if def.repr().transparent() { if let Some(field) = super::transparent_newtype_field(self.cx.tcx, variant) { // Transparent newtypes have at most one non-ZST field which needs to be checked.. - let field_ty = get_type_from_field(self.cx, field, args); + let field_ty = maybe_normalize_erasing_regions( + self.cx, + Unnormalized::new_wip(field.ty(self.cx.tcx, args)), + ); match self.visit_type(state.next(ty), field_ty) { FfiUnsafe { ty, .. } if ty.is_unit() => (), r => return r, @@ -573,7 +584,10 @@ fn visit_variant_fields( // We can't completely trust `repr(C)` markings, so make sure the fields are actually safe. let mut all_phantom = !variant.fields.is_empty(); for field in &variant.fields { - let field_ty = get_type_from_field(self.cx, field, args); + let field_ty = maybe_normalize_erasing_regions( + self.cx, + Unnormalized::new_wip(field.ty(self.cx.tcx, args)), + ); all_phantom &= match self.visit_type(state.next(ty), field_ty) { FfiSafe => false, // `()` fields are FFI-safe! @@ -927,12 +941,12 @@ fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { }) } - fn check_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> { - let ty = self - .cx - .tcx - .try_normalize_erasing_regions(self.cx.typing_env(), Unnormalized::new_wip(ty)) - .unwrap_or(ty); + fn check_type( + &mut self, + state: VisitorState, + ty: Unnormalized<'tcx, Ty<'tcx>>, + ) -> FfiResult<'tcx> { + let ty = maybe_normalize_erasing_regions(self.cx, ty); if let Some(res) = self.visit_for_opaque_ty(ty) { return res; } @@ -990,6 +1004,7 @@ fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { let all_types = iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)); for (fn_ptr_ty, span) in all_types { + let fn_ptr_ty = Unnormalized::new_wip(fn_ptr_ty); let mut visitor = ImproperCTypesVisitor::new(cx, fn_ptr_ty, fn_mode); // FIXME(ctypes): make a check_for_fnptr let ffi_res = visitor.check_type(state, fn_ptr_ty); @@ -1039,7 +1054,7 @@ fn check_reprc_adt( } fn check_foreign_static(&mut self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) { - let ty = cx.tcx.type_of(id).instantiate_identity().skip_norm_wip(); + let ty = cx.tcx.type_of(id).instantiate_identity(); let mut visitor = ImproperCTypesVisitor::new(cx, ty, CItemKind::Declaration); let ffi_res = visitor.check_type(VisitorState::static_entry_point(), ty); self.process_ffi_result(cx, span, ffi_res, CItemKind::Declaration); @@ -1057,16 +1072,18 @@ fn check_foreign_fn( let sig = cx.tcx.instantiate_bound_regions_with_erased(sig); for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { + let input_ty = Unnormalized::new_wip(*input_ty); let state = VisitorState::fn_entry_point(fn_mode, FnPos::Arg); - let mut visitor = ImproperCTypesVisitor::new(cx, *input_ty, fn_mode); - let ffi_res = visitor.check_type(state, *input_ty); + let mut visitor = ImproperCTypesVisitor::new(cx, input_ty, fn_mode); + let ffi_res = visitor.check_type(state, input_ty); self.process_ffi_result(cx, input_hir.span, ffi_res, fn_mode); } if let hir::FnRetTy::Return(ret_hir) = decl.output { + let output_ty = Unnormalized::new_wip(sig.output()); let state = VisitorState::fn_entry_point(fn_mode, FnPos::Ret); - let mut visitor = ImproperCTypesVisitor::new(cx, sig.output(), fn_mode); - let ffi_res = visitor.check_type(state, sig.output()); + let mut visitor = ImproperCTypesVisitor::new(cx, output_ty, fn_mode); + let ffi_res = visitor.check_type(state, output_ty); self.process_ffi_result(cx, ret_hir.span, ffi_res, fn_mode); } } From 803c7ba50bc1dfd27bd4241edc3619126c9b7350 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 24 Apr 2026 15:08:32 +1000 Subject: [PATCH 13/41] Some fold/visit tweaks. - Introduce `HAS_REGIONS`/`has_regions`, which is true if any regions are present. - Add `fold_clauses` to `Shifter` and `RegionFolder` for consistency. - Simplify `has_type_flags`. - Remove unnecessary local variables in `HasTypeFlagsVisitor` methods. - Use `|=` operator in one place. - Avoid `is_break` in one place for consistency with nearby code. LLM disclosure: Claude Code suggested these changes when I asked it to review `fold.rs` and `visit.rs`. I verified the correctness of the suggestions and made the changes by hand. --- compiler/rustc_type_ir/src/flags.rs | 7 +++++- compiler/rustc_type_ir/src/fold.rs | 34 ++++++++++------------------- compiler/rustc_type_ir/src/visit.rs | 17 ++++++++------- 3 files changed, 27 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 3debef168cff..19c59df0604c 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -118,6 +118,11 @@ pub struct TypeFlags: u32 { /// Does this have any `ReErased` regions? const HAS_RE_ERASED = 1 << 21; + /// Does this have any regions of any kind? + const HAS_REGIONS = TypeFlags::HAS_FREE_REGIONS.bits() + | TypeFlags::HAS_RE_BOUND.bits() + | TypeFlags::HAS_RE_ERASED.bits(); + /// Does this value have parameters/placeholders/inference variables which could be /// replaced later, in a way that would change the results of `impl` specialization? const STILL_FURTHER_SPECIALIZABLE = TypeFlags::HAS_TY_PARAM.bits() @@ -192,7 +197,7 @@ pub fn for_clauses(clauses: &[I::Clause]) -> FlagComputation { } fn add_flags(&mut self, flags: TypeFlags) { - self.flags = self.flags | flags; + self.flags |= flags; } /// indicates that `self` refers to something at binding level `binder` diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index 94d984559462..0fe68b525669 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -55,7 +55,7 @@ use crate::inherent::*; use crate::visit::{TypeVisitable, TypeVisitableExt as _}; -use crate::{self as ty, BoundVarIndexKind, Interner, TypeFlags}; +use crate::{self as ty, BoundVarIndexKind, Interner}; /// This trait is implemented for every type that can be folded, /// providing the skeleton of the traversal. @@ -433,6 +433,10 @@ fn fold_const(&mut self, ct: I::Const) -> I::Const { fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate { if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } } + + fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses { + if c.has_vars_bound_at_or_above(self.current_index) { c.super_fold_with(self) } else { c } + } } pub fn shift_region(cx: I, region: I::Region, amount: u32) -> I::Region { @@ -535,32 +539,18 @@ fn fold_region(&mut self, r: I::Region) -> I::Region { } fn fold_ty(&mut self, t: I::Ty) -> I::Ty { - if t.has_type_flags( - TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_RE_BOUND | TypeFlags::HAS_RE_ERASED, - ) { - t.super_fold_with(self) - } else { - t - } + if t.has_regions() { t.super_fold_with(self) } else { t } } fn fold_const(&mut self, ct: I::Const) -> I::Const { - if ct.has_type_flags( - TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_RE_BOUND | TypeFlags::HAS_RE_ERASED, - ) { - ct.super_fold_with(self) - } else { - ct - } + if ct.has_regions() { ct.super_fold_with(self) } else { ct } } fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate { - if p.has_type_flags( - TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_RE_BOUND | TypeFlags::HAS_RE_ERASED, - ) { - p.super_fold_with(self) - } else { - p - } + if p.has_regions() { p.super_fold_with(self) } else { p } + } + + fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses { + if c.has_regions() { c.super_fold_with(self) } else { c } } } diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 6d0b307f013e..492c37481298 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -285,6 +285,10 @@ fn has_non_region_param(&self) -> bool { self.has_type_flags(TypeFlags::HAS_PARAM - TypeFlags::HAS_RE_PARAM) } + fn has_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_REGIONS) + } + fn has_infer_regions(&self) -> bool { self.has_type_flags(TypeFlags::HAS_RE_INFER) } @@ -363,13 +367,12 @@ fn has_non_region_error(&self) -> bool { impl> TypeVisitableExt for T { fn has_type_flags(&self, flags: TypeFlags) -> bool { - let res = - self.visit_with(&mut HasTypeFlagsVisitor { flags }) == ControlFlow::Break(FoundFlags); - res + self.visit_with(&mut HasTypeFlagsVisitor { flags }) == ControlFlow::Break(FoundFlags) } fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool { - self.visit_with(&mut HasEscapingVarsVisitor { outer_index: binder }).is_break() + self.visit_with(&mut HasEscapingVarsVisitor { outer_index: binder }) + == ControlFlow::Break(FoundEscapingVars) } fn error_reported(&self) -> Result<(), I::ErrorGuaranteed> { @@ -438,8 +441,7 @@ fn visit_binder>(&mut self, t: &ty::Binder) -> Self::R #[inline] fn visit_ty(&mut self, t: I::Ty) -> Self::Result { // Note: no `super_visit_with` call. - let flags = t.flags(); - if flags.intersects(self.flags) { + if t.flags().intersects(self.flags) { ControlFlow::Break(FoundFlags) } else { ControlFlow::Continue(()) @@ -449,8 +451,7 @@ fn visit_ty(&mut self, t: I::Ty) -> Self::Result { #[inline] fn visit_region(&mut self, r: I::Region) -> Self::Result { // Note: no `super_visit_with` call, as usual for `Region`. - let flags = r.flags(); - if flags.intersects(self.flags) { + if r.flags().intersects(self.flags) { ControlFlow::Break(FoundFlags) } else { ControlFlow::Continue(()) From 4fe8268ff9724a7a881712f0fc6f7be0f74dc3c0 Mon Sep 17 00:00:00 2001 From: Asuna Date: Wed, 6 May 2026 05:10:26 +0000 Subject: [PATCH 14/41] Refactor `Type::size` field to `TypeId::size` method for `type_info` --- .../src/const_eval/machine.rs | 21 ++++- .../src/const_eval/type_info.rs | 19 +---- .../rustc_hir_analysis/src/check/intrinsic.rs | 24 ++---- compiler/rustc_span/src/symbol.rs | 1 + library/core/src/any.rs | 2 +- library/core/src/intrinsics/mod.rs | 9 ++ library/core/src/mem/type_info.rs | 23 ++++- library/coretests/tests/mem/type_info.rs | 84 +++++++++++-------- .../vtable-try-as-dyn.full-debuginfo.stderr | 2 +- .../vtable-try-as-dyn.no-debuginfo.stderr | 2 +- .../reflection/trait_info_of_too_big.stderr | 4 +- 11 files changed, 113 insertions(+), 78 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 50d58e020601..c7ee14b4b5dd 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -2,7 +2,7 @@ use std::hash::Hash; use std::{fmt, mem}; -use rustc_abi::{Align, FIRST_VARIANT, Size}; +use rustc_abi::{Align, FIRST_VARIANT, FieldIdx, Size}; use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry}; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -11,7 +11,7 @@ use rustc_middle::mir::interpret::ReportedErrorInfo; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout, ValidityRequirement}; -use rustc_middle::ty::{self, FieldInfo, Ty, TyCtxt}; +use rustc_middle::ty::{self, FieldInfo, ScalarInt, Ty, TyCtxt}; use rustc_middle::{bug, mir, span_bug}; use rustc_span::{Span, Symbol, sym}; use rustc_target::callconv::FnAbi; @@ -605,6 +605,23 @@ fn call_intrinsic( ecx.write_type_info(ty, dest)?; } + sym::size_of_type_id => { + let ty = ecx.read_type_id(&args[0])?; + let layout = ecx.layout_of(ty)?; + let variant_index = if layout.is_sized() { + let (variant, variant_place) = ecx.downcast(dest, sym::Some)?; + let size_field_place = ecx.project_field(&variant_place, FieldIdx::ZERO)?; + ecx.write_scalar( + ScalarInt::try_from_target_usize(layout.size.bytes(), ecx.tcx.tcx).unwrap(), + &size_field_place, + )?; + variant + } else { + ecx.downcast(dest, sym::None)?.0 + }; + ecx.write_discriminant(variant_index, dest)?; + } + sym::field_offset => { let frt_ty = instance.args.type_at(0); ensure_monomorphic_enough(ecx.tcx.tcx, frt_ty)?; diff --git a/compiler/rustc_const_eval/src/const_eval/type_info.rs b/compiler/rustc_const_eval/src/const_eval/type_info.rs index 7b63ab5bb02e..7a90ac828796 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info.rs @@ -18,7 +18,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { /// Equivalent to `project_downcast`, but identifies the variant by name instead of index. - fn downcast<'a>( + pub fn downcast<'a>( &self, place: &(impl Writeable<'tcx, CtfeProvenance> + 'a), name: Symbol, @@ -218,23 +218,6 @@ pub(crate) fn write_type_info( }; self.write_discriminant(variant_index, &field_dest)? } - sym::size => { - let layout = self.layout_of(ty)?; - let variant_index = if layout.is_sized() { - let (variant, variant_place) = self.downcast(&field_dest, sym::Some)?; - let size_field_place = - self.project_field(&variant_place, FieldIdx::ZERO)?; - self.write_scalar( - ScalarInt::try_from_target_usize(layout.size.bytes(), self.tcx.tcx) - .unwrap(), - &size_field_place, - )?; - variant - } else { - self.downcast(&field_dest, sym::None)?.0 - }; - self.write_discriminant(variant_index, &field_dest)?; - } other => span_bug!(self.tcx.span, "unknown `Type` field {other}"), } } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 7d606439cedc..d0bb68c5bc33 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -199,6 +199,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::sinf64 | sym::sinf128 | sym::size_of + | sym::size_of_type_id | sym::sqrtf16 | sym::sqrtf32 | sym::sqrtf64 @@ -281,6 +282,7 @@ pub(crate) fn check_intrinsic_type( let va_list_ty = tcx.type_of(did).instantiate(tcx, &[region.into()]).skip_norm_wip(); (Ty::new_ref(tcx, env_region, va_list_ty, mutbl), va_list_ty) }; + let type_id_ty = || tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap(); let safety = intrinsic_operation_unsafety(tcx, intrinsic_id); let n_lts = 0; @@ -294,6 +296,7 @@ pub(crate) fn check_intrinsic_type( sym::size_of_val | sym::align_of_val => { (1, 0, vec![Ty::new_imm_ptr(tcx, param(0))], tcx.types.usize) } + sym::size_of_type_id => (0, 0, vec![type_id_ty()], Ty::new_option(tcx, tcx.types.usize)), sym::offset_of => (1, 0, vec![tcx.types.u32, tcx.types.u32], tcx.types.usize), sym::field_offset => (1, 0, vec![], tcx.types.usize), sym::rustc_peek => (1, 0, vec![param(0)], param(0)), @@ -313,16 +316,8 @@ pub(crate) fn check_intrinsic_type( sym::needs_drop => (1, 0, vec![], tcx.types.bool), sym::type_name => (1, 0, vec![], Ty::new_static_str(tcx)), - sym::type_id => ( - 1, - 0, - vec![], - tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap(), - ), - sym::type_id_eq => { - let type_id = tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap(); - (0, 0, vec![type_id, type_id], tcx.types.bool) - } + sym::type_id => (1, 0, vec![], type_id_ty()), + sym::type_id_eq => (0, 0, vec![type_id_ty(), type_id_ty()], tcx.types.bool), sym::type_id_vtable => { let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, span); let dyn_metadata_adt_ref = tcx.adt_def(dyn_metadata); @@ -335,17 +330,12 @@ pub(crate) fn check_intrinsic_type( let option_args = tcx.mk_args(&[dyn_ty.into()]); let ret_ty = Ty::new_adt(tcx, option_adt_ref, option_args); - ( - 0, - 0, - vec![tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap(); 2], - ret_ty, - ) + (0, 0, vec![type_id_ty(); 2], ret_ty) } sym::type_of => ( 0, 0, - vec![tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap()], + vec![type_id_ty()], tcx.type_of(tcx.lang_items().type_struct().unwrap()).no_bound_vars().unwrap(), ), sym::offload => ( diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 695103a249b4..728482bf5741 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1927,6 +1927,7 @@ sinf128, size, size_of, + size_of_type_id, size_of_val, sized, sized_hierarchy, diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 7d820403ccb7..62300d8b70a9 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -839,7 +839,7 @@ pub const fn trait_info_of_trait_type_id( self, trait_represented_by_type_id: TypeId, ) -> Option> { - if self.info().size.is_none() { + if self.size().is_none() { return None; } diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index c71085ef9f97..8eaf91dab8df 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2937,6 +2937,15 @@ pub const fn type_id_eq(a: crate::any::TypeId, b: crate::any::TypeId) -> bool { a.data == b.data } +/// Gets the size of the type represented by this `TypeId`. +/// +/// The stabilized version of this intrinsic is [`core::any::TypeId::size`]. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +pub const fn size_of_type_id(_id: crate::any::TypeId) -> Option { + panic!("`Type::size` can only be called at compile-time") +} + /// Lowers in MIR to `Rvalue::Aggregate` with `AggregateKind::RawPtr`. /// /// This is used to implement functions like `slice::from_raw_parts_mut` and diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index e4d47dedb860..17c51ccaad85 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -2,7 +2,7 @@ //! runtime or const-eval processable way. use crate::any::TypeId; -use crate::intrinsics::{type_id, type_of}; +use crate::intrinsics::{self, type_id, type_of}; use crate::marker::PointeeSized; use crate::ptr::DynMetadata; @@ -14,8 +14,6 @@ pub struct Type { /// Per-type information pub kind: TypeKind, - /// Size of the type. `None` if it is unsized - pub size: Option, } /// Info of a trait implementation, you can retrieve the vtable with [Self::get_vtable] @@ -360,3 +358,22 @@ pub enum Abi { /// C-calling convention ExternC, } + +impl TypeId { + /// Returns the size of the type represented by this `TypeId`. `None` if it is unsized. + /// + /// # Examples + /// + /// ``` + /// #![feature(type_info)] + /// use std::any::TypeId; + /// + /// assert_eq!(const { TypeId::of::().size() }, Some(4)); + /// assert_eq!(const { TypeId::of::<[u8; 16]>().size() }, Some(16)); + /// ``` + #[unstable(feature = "type_info", issue = "146922")] + #[rustc_const_unstable(feature = "type_info", issue = "146922")] + pub const fn size(self) -> Option { + intrinsics::size_of_type_id(self) + } +} diff --git a/library/coretests/tests/mem/type_info.rs b/library/coretests/tests/mem/type_info.rs index 2483b4c2aacd..e02077b96d35 100644 --- a/library/coretests/tests/mem/type_info.rs +++ b/library/coretests/tests/mem/type_info.rs @@ -80,8 +80,7 @@ struct TestStruct { reference: &'static u16, } - let Type { kind: Struct(ty), size, .. } = Type::of::() else { panic!() }; - assert!(size == Some(size_of::())); + let Type { kind: Struct(ty), .. } = Type::of::() else { panic!() }; assert!(!ty.non_exhaustive); assert!(ty.fields.len() == 3); assert!(ty.fields[0].name == "first"); @@ -93,6 +92,9 @@ struct TestStruct { assert!(ty.fields[2].name == "reference"); assert!(ty.fields[2].ty == TypeId::of::<&'static u16>()); assert!(ty.fields[2].offset == offset_of!(TestStruct, reference)); + + let ty_id = TypeId::of::(); + assert!(ty_id.size() == Some(size_of::())); } const { @@ -145,13 +147,15 @@ union TestUnion { second: u16, } - let Type { kind: Union(ty), size, .. } = Type::of::() else { panic!() }; - assert!(size == Some(size_of::())); + let Type { kind: Union(ty), .. } = Type::of::() else { panic!() }; assert!(ty.fields.len() == 2); assert!(ty.fields[0].name == "first"); assert!(ty.fields[0].offset == offset_of!(TestUnion, first)); assert!(ty.fields[1].name == "second"); assert!(ty.fields[1].offset == offset_of!(TestUnion, second)); + + let ty_id = TypeId::of::(); + assert!(ty_id.size() == Some(size_of::())); } const { @@ -191,8 +195,7 @@ enum E { }, } - let Type { kind: Enum(ty), size, .. } = Type::of::() else { panic!() }; - assert!(size == Some(size_of::())); + let Type { kind: Enum(ty), .. } = Type::of::() else { panic!() }; assert!(ty.variants.len() == 3); assert!(ty.variants[0].name == "Some"); @@ -206,15 +209,20 @@ enum E { assert!(ty.variants[2].name == "Foomp"); assert!(ty.variants[2].non_exhaustive); assert!(ty.variants[2].fields.len() == 2); + + let ty_id = TypeId::of::(); + assert!(ty_id.size() == Some(size_of::())); } const { - let Type { kind: Enum(ty), size, .. } = Type::of::>() else { panic!() }; - assert!(size == Some(size_of::>())); + let Type { kind: Enum(ty), .. } = Type::of::>() else { panic!() }; assert!(ty.variants.len() == 2); assert!(ty.generics.len() == 1); let Generic::Type(GenericType { ty: generic_ty, .. }) = ty.generics[0] else { panic!() }; assert!(generic_ty == TypeId::of::()); + + let ty_id = TypeId::of::>(); + assert!(ty_id.size() == Some(size_of::>())); } } @@ -222,38 +230,48 @@ enum E { fn test_primitives() { use TypeKind::*; - let Type { kind: Bool(_ty), size, .. } = (const { Type::of::() }) else { panic!() }; - assert_eq!(size, Some(1)); + const { + let Type { kind: Bool(_ty), .. } = (const { Type::of::() }) else { panic!() }; + let ty_id = TypeId::of::(); + assert!(ty_id.size() == Some(size_of::())); - let Type { kind: Char(_ty), size, .. } = (const { Type::of::() }) else { panic!() }; - assert_eq!(size, Some(4)); + let Type { kind: Char(_ty), .. } = (const { Type::of::() }) else { panic!() }; + let ty_id = TypeId::of::(); + assert!(ty_id.size() == Some(size_of::())); - let Type { kind: Int(ty), size, .. } = (const { Type::of::() }) else { panic!() }; - assert_eq!(size, Some(4)); - assert_eq!(ty.bits, 32); - assert!(ty.signed); + let Type { kind: Int(ty), .. } = (const { Type::of::() }) else { panic!() }; + assert!(ty.bits == 32); + assert!(ty.signed); + let ty_id = TypeId::of::(); + assert!(ty_id.size() == Some(size_of::())); - let Type { kind: Int(ty), size, .. } = (const { Type::of::() }) else { panic!() }; - assert_eq!(size, Some(size_of::())); - assert_eq!(ty.bits as usize, size_of::() * 8); - assert!(ty.signed); + let Type { kind: Int(ty), .. } = (const { Type::of::() }) else { panic!() }; + assert!(ty.bits as usize == size_of::() * 8); + assert!(ty.signed); + let ty_id = TypeId::of::(); + assert!(ty_id.size() == Some(size_of::())); - let Type { kind: Int(ty), size, .. } = (const { Type::of::() }) else { panic!() }; - assert_eq!(size, Some(4)); - assert_eq!(ty.bits, 32); - assert!(!ty.signed); + let Type { kind: Int(ty), .. } = (const { Type::of::() }) else { panic!() }; + assert!(ty.bits == 32); + assert!(!ty.signed); + let ty_id = TypeId::of::(); + assert!(ty_id.size() == Some(size_of::())); - let Type { kind: Int(ty), size, .. } = (const { Type::of::() }) else { panic!() }; - assert_eq!(size, Some(size_of::())); - assert_eq!(ty.bits as usize, size_of::() * 8); - assert!(!ty.signed); + let Type { kind: Int(ty), .. } = (const { Type::of::() }) else { panic!() }; + assert!(ty.bits as usize == size_of::() * 8); + assert!(!ty.signed); + let ty_id = TypeId::of::(); + assert!(ty_id.size() == Some(size_of::())); - let Type { kind: Float(ty), size, .. } = (const { Type::of::() }) else { panic!() }; - assert_eq!(size, Some(4)); - assert_eq!(ty.bits, 32); + let Type { kind: Float(ty), .. } = (const { Type::of::() }) else { panic!() }; + assert!(ty.bits == 32); + let ty_id = TypeId::of::(); + assert!(ty_id.size() == Some(size_of::())); - let Type { kind: Str(_ty), size, .. } = (const { Type::of::() }) else { panic!() }; - assert_eq!(size, None); + let Type { kind: Str(_ty), .. } = (const { Type::of::() }) else { panic!() }; + let ty_id = TypeId::of::(); + assert!(ty_id.size() == None); + } } #[test] diff --git a/tests/ui/limits/vtable-try-as-dyn.full-debuginfo.stderr b/tests/ui/limits/vtable-try-as-dyn.full-debuginfo.stderr index 700083069cb0..c9c15e2d62c9 100644 --- a/tests/ui/limits/vtable-try-as-dyn.full-debuginfo.stderr +++ b/tests/ui/limits/vtable-try-as-dyn.full-debuginfo.stderr @@ -6,7 +6,7 @@ note: inside `TypeId::trait_info_of::` --> $SRC_DIR/core/src/any.rs:LL:COL note: inside `TypeId::trait_info_of_trait_type_id` --> $SRC_DIR/core/src/any.rs:LL:COL -note: inside `type_info::::info` +note: inside `type_info::::size` --> $SRC_DIR/core/src/mem/type_info.rs:LL:COL note: the above error was encountered while instantiating `fn try_as_dyn::<[u8; usize::MAX], dyn Trait>` diff --git a/tests/ui/limits/vtable-try-as-dyn.no-debuginfo.stderr b/tests/ui/limits/vtable-try-as-dyn.no-debuginfo.stderr index 700083069cb0..c9c15e2d62c9 100644 --- a/tests/ui/limits/vtable-try-as-dyn.no-debuginfo.stderr +++ b/tests/ui/limits/vtable-try-as-dyn.no-debuginfo.stderr @@ -6,7 +6,7 @@ note: inside `TypeId::trait_info_of::` --> $SRC_DIR/core/src/any.rs:LL:COL note: inside `TypeId::trait_info_of_trait_type_id` --> $SRC_DIR/core/src/any.rs:LL:COL -note: inside `type_info::::info` +note: inside `type_info::::size` --> $SRC_DIR/core/src/mem/type_info.rs:LL:COL note: the above error was encountered while instantiating `fn try_as_dyn::<[u8; usize::MAX], dyn Trait>` diff --git a/tests/ui/reflection/trait_info_of_too_big.stderr b/tests/ui/reflection/trait_info_of_too_big.stderr index fa41d3c9ec90..b5dc1d3cdb44 100644 --- a/tests/ui/reflection/trait_info_of_too_big.stderr +++ b/tests/ui/reflection/trait_info_of_too_big.stderr @@ -6,7 +6,7 @@ LL | TypeId::of::<[u8; usize::MAX]>().trait_info_of_trait_type_id(TypeId::of | note: inside `TypeId::trait_info_of_trait_type_id` --> $SRC_DIR/core/src/any.rs:LL:COL -note: inside `type_info::::info` +note: inside `type_info::::size` --> $SRC_DIR/core/src/mem/type_info.rs:LL:COL error[E0080]: values of the type `[u8; usize::MAX]` are too big for the target architecture @@ -19,7 +19,7 @@ note: inside `TypeId::trait_info_of::` --> $SRC_DIR/core/src/any.rs:LL:COL note: inside `TypeId::trait_info_of_trait_type_id` --> $SRC_DIR/core/src/any.rs:LL:COL -note: inside `type_info::::info` +note: inside `type_info::::size` --> $SRC_DIR/core/src/mem/type_info.rs:LL:COL error: aborting due to 2 previous errors From 814fc17cb6c5860b04fde34abc12fa92151d9505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Wed, 29 Apr 2026 10:53:45 +0200 Subject: [PATCH 15/41] Deny warnings in rustc crates on stable --- tests/run-make-cargo/rustc-crates-on-stable/rmake.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run-make-cargo/rustc-crates-on-stable/rmake.rs b/tests/run-make-cargo/rustc-crates-on-stable/rmake.rs index cbc1f24b8c16..de29abfd7c3a 100644 --- a/tests/run-make-cargo/rustc-crates-on-stable/rmake.rs +++ b/tests/run-make-cargo/rustc-crates-on-stable/rmake.rs @@ -10,7 +10,7 @@ fn main() { .env("RUSTC_STAGE", "0") .env("RUSTC", rustc_path()) // We want to disallow all nightly features to simulate a stable build - .env("RUSTFLAGS", "-Zallow-features=") + .env("RUSTFLAGS", "-D warnings -Zallow-features=") .arg("build") .arg("--manifest-path") .arg(source_root().join("Cargo.toml")) From 0afe083fd1f0e9fa2c627b8c3155f221675f3d68 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 5 May 2026 06:31:19 +0200 Subject: [PATCH 16/41] Avoid some global `node_id_to_def_id` lookups in import resolution --- .../rustc_resolve/src/build_reduced_graph.rs | 11 +++++- compiler/rustc_resolve/src/imports.rs | 39 +++++++++++-------- compiler/rustc_resolve/src/lib.rs | 4 +- 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 524c9ce8ad5d..642a618e1867 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -720,13 +720,15 @@ fn build_reduced_graph_for_use_tree( decls: Default::default(), nested, id, + def_id: feed.def_id(), }; self.add_import(module_path, kind, use_tree.span(), item, root_span, item.id, vis); } ast::UseTreeKind::Glob(_) => { if !ast::attr::contains_name(&item.attrs, sym::prelude_import) { - let kind = ImportKind::Glob { max_vis: CmCell::new(None), id }; + let kind = + ImportKind::Glob { max_vis: CmCell::new(None), id, def_id: feed.def_id() }; self.add_import(prefix, kind, use_tree.span(), item, root_span, item.id, vis); } else { // Resolve the prelude import early. @@ -1018,7 +1020,12 @@ fn build_reduced_graph_for_extern_crate( }) .unwrap_or((true, None, self.r.dummy_decl)); let import = self.r.arenas.alloc_import(ImportData { - kind: ImportKind::ExternCrate { source: orig_name, target: orig_ident, id: item.id }, + kind: ImportKind::ExternCrate { + source: orig_name, + target: orig_ident, + id: item.id, + def_id: local_def_id, + }, root_id: item.id, parent_scope, imported_module: CmCell::new(module), diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index e63ab9f21a6a..4de7a89ba290 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -16,7 +16,7 @@ use rustc_hir::attrs::AttributeKind; use rustc_hir::attrs::diagnostic::{CustomDiagnostic, Directive, FormatArgs}; use rustc_hir::def::{self, DefKind, PartialRes}; -use rustc_hir::def_id::{DefId, LocalDefIdMap}; +use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdMap}; use rustc_middle::metadata::{AmbigModChild, ModChild, Reexport}; use rustc_middle::span_bug; use rustc_middle::ty::{TyCtxt, Visibility}; @@ -90,17 +90,20 @@ pub(crate) enum ImportKind<'ra> { /// If this is the import for `foo::bar::a`, we would have the ID of the `UseTree` /// for `a` in this field. id: NodeId, + def_id: LocalDefId, }, Glob { // The visibility of the greatest re-export. // n.b. `max_vis` is only used in `finalize_import` to check for re-export errors. max_vis: CmCell>, id: NodeId, + def_id: LocalDefId, }, ExternCrate { source: Option, target: Ident, id: NodeId, + def_id: LocalDefId, }, MacroUse { /// A field has been added indicating whether it should be reported as a lint, @@ -116,7 +119,7 @@ impl<'ra> std::fmt::Debug for ImportKind<'ra> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use ImportKind::*; match self { - Single { source, target, decls, nested, id, .. } => f + Single { source, target, decls, nested, id, def_id } => f .debug_struct("Single") .field("source", source) .field("target", target) @@ -127,15 +130,20 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { ) .field("nested", nested) .field("id", id) + .field("def_id", def_id) .finish(), - Glob { max_vis, id } => { - f.debug_struct("Glob").field("max_vis", max_vis).field("id", id).finish() - } - ExternCrate { source, target, id } => f + Glob { max_vis, id, def_id } => f + .debug_struct("Glob") + .field("max_vis", max_vis) + .field("id", id) + .field("def_id", def_id) + .finish(), + ExternCrate { source, target, id, def_id } => f .debug_struct("ExternCrate") .field("source", source) .field("target", target) .field("id", id) + .field("def_id", def_id) .finish(), MacroUse { warn_private } => { f.debug_struct("MacroUse").field("warn_private", warn_private).finish() @@ -260,12 +268,11 @@ pub(crate) fn id(&self) -> Option { } } - pub(crate) fn simplify(&self, r: &Resolver<'_, '_>) -> Reexport { - let to_def_id = |id| r.local_def_id(id).to_def_id(); + pub(crate) fn simplify(&self) -> Reexport { match self.kind { - ImportKind::Single { id, .. } => Reexport::Single(to_def_id(id)), - ImportKind::Glob { id, .. } => Reexport::Glob(to_def_id(id)), - ImportKind::ExternCrate { id, .. } => Reexport::ExternCrate(to_def_id(id)), + ImportKind::Single { def_id, .. } => Reexport::Single(def_id.to_def_id()), + ImportKind::Glob { def_id, .. } => Reexport::Glob(def_id.to_def_id()), + ImportKind::ExternCrate { def_id, .. } => Reexport::ExternCrate(def_id.to_def_id()), ImportKind::MacroUse { .. } => Reexport::MacroUse, ImportKind::MacroExport => Reexport::MacroExport, } @@ -1267,7 +1274,7 @@ fn finalize_import(&mut self, import: Import<'ra>) -> Option (source, target, decls, id), - ImportKind::Glob { ref max_vis, id } => { + ImportKind::Glob { ref max_vis, id, def_id: _ } => { if import.module_path.len() <= 1 { // HACK(eddyb) `lint_if_path_starts_with_module` needs at least // 2 segments, so the `resolve_path` above won't trigger it. @@ -1829,23 +1836,23 @@ fn finalize_resolutions_in( let mut children = Vec::new(); let mut ambig_children = Vec::new(); - module.to_module().for_each_child(self, |this, ident, orig_ident_span, _, binding| { + module.to_module().for_each_child(self, |_this, ident, orig_ident_span, _, binding| { let res = binding.res().expect_non_local(); if res != def::Res::Err { let ident = ident.orig(orig_ident_span); let child = |reexport_chain| ModChild { ident, res, vis: binding.vis(), reexport_chain }; if let Some((ambig_binding1, ambig_binding2)) = binding.descent_to_ambiguity() { - let main = child(ambig_binding1.reexport_chain(this)); + let main = child(ambig_binding1.reexport_chain()); let second = ModChild { ident, res: ambig_binding2.res().expect_non_local(), vis: ambig_binding2.vis(), - reexport_chain: ambig_binding2.reexport_chain(this), + reexport_chain: ambig_binding2.reexport_chain(), }; ambig_children.push(AmbigModChild { main, second }) } else { - children.push(child(binding.reexport_chain(this))); + children.push(child(binding.reexport_chain())); } } }); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 1141ac75ce4c..7f8ff91d3ad6 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1171,11 +1171,11 @@ fn macro_kinds(&self) -> Option { self.res().macro_kinds() } - fn reexport_chain(self: Decl<'ra>, r: &Resolver<'_, '_>) -> SmallVec<[Reexport; 2]> { + fn reexport_chain(self: Decl<'ra>) -> SmallVec<[Reexport; 2]> { let mut reexport_chain = SmallVec::new(); let mut next_binding = self; while let DeclKind::Import { source_decl, import, .. } = next_binding.kind { - reexport_chain.push(import.simplify(r)); + reexport_chain.push(import.simplify()); next_binding = source_decl; } reexport_chain From 191cda55e4e2612e47d1ce06980cb371aa96e776 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 5 May 2026 06:36:36 +0200 Subject: [PATCH 17/41] Avoid using `id` followed by `local_def_id` if we can just call `def_id` instead --- .../rustc_resolve/src/effective_visibilities.rs | 9 +++------ compiler/rustc_resolve/src/imports.rs | 14 +++++++++++--- compiler/rustc_resolve/src/lib.rs | 3 +-- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index b5614106fe99..693e49995c1c 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -45,10 +45,7 @@ fn nearest_normal_mod(&self, def_id: LocalDefId) -> LocalDefId { fn private_vis_import(&self, decl: Decl<'_>) -> Visibility { let DeclKind::Import { import, .. } = decl.kind else { unreachable!() }; Visibility::Restricted( - import - .id() - .map(|id| self.nearest_normal_mod(self.local_def_id(id))) - .unwrap_or(CRATE_DEF_ID), + import.def_id().map(|id| self.nearest_normal_mod(id)).unwrap_or(CRATE_DEF_ID), ) } @@ -96,8 +93,8 @@ pub(crate) fn compute_effective_visibilities<'c>( // is the maximum value among visibilities of declarations corresponding to that def id. for (decl, eff_vis) in visitor.import_effective_visibilities.iter() { let DeclKind::Import { import, .. } = decl.kind else { unreachable!() }; - if let Some(node_id) = import.id() { - r.effective_visibilities.update_eff_vis(r.local_def_id(node_id), eff_vis, r.tcx) + if let Some(def_id) = import.def_id() { + r.effective_visibilities.update_eff_vis(def_id, eff_vis, r.tcx) } if decl.ambiguity.get().is_some() && eff_vis.is_public_at_level(Level::Reexported) { exported_ambiguities.insert(*decl); diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 4de7a89ba290..c778df19818f 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -268,6 +268,15 @@ pub(crate) fn id(&self) -> Option { } } + pub(crate) fn def_id(&self) -> Option { + match self.kind { + ImportKind::Single { def_id, .. } + | ImportKind::Glob { def_id, .. } + | ImportKind::ExternCrate { def_id, .. } => Some(def_id), + ImportKind::MacroUse { .. } | ImportKind::MacroExport => None, + } + } + pub(crate) fn simplify(&self) -> Reexport { match self.kind { ImportKind::Single { def_id, .. } => Reexport::Single(def_id.to_def_id()), @@ -852,8 +861,7 @@ pub(crate) fn lint_reexports(&mut self, exported_ambiguities: FxHashSet &'tcx [LocalDefId] { let mut import_ids: SmallVec<[LocalDefId; 1]> = smallvec![]; while let DeclKind::Import { import, source_decl, .. } = kind { - if let Some(node_id) = import.id() { - let def_id = self.local_def_id(node_id); + if let Some(def_id) = import.def_id() { self.maybe_unused_trait_imports.insert(def_id); import_ids.push(def_id); } From 400240adb4dc4f5fe0bad053d42215583a76e155 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 5 May 2026 06:15:58 +0200 Subject: [PATCH 18/41] Avoid hitting the global node_id_to_def_id table for unused macros --- compiler/rustc_resolve/src/build_reduced_graph.rs | 2 +- compiler/rustc_resolve/src/lib.rs | 4 ++-- compiler/rustc_resolve/src/macros.rs | 5 ++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 642a618e1867..da75238bacd5 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -1277,7 +1277,7 @@ fn insert_unused_macro(&mut self, ident: Ident, def_id: LocalDefId, node_id: Nod if !ident.as_str().starts_with('_') { self.r.unused_macros.insert(def_id, (node_id, ident)); let nrules = self.r.local_macro_map[&def_id].nrules; - self.r.unused_macro_rules.insert(node_id, DenseBitSet::new_filled(nrules)); + self.r.unused_macro_rules.insert(node_id, (def_id, DenseBitSet::new_filled(nrules))); } } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 25cb4659758d..6f777984e6c0 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1405,8 +1405,8 @@ pub struct Resolver<'ra, 'tcx> { local_macro_def_scopes: FxHashMap> = default::fx_hash_map(), ast_transform_scopes: FxHashMap> = default::fx_hash_map(), unused_macros: FxIndexMap, - /// A map from the macro to all its potentially unused arms. - unused_macro_rules: FxIndexMap>, + /// A map from the macro to all its potentially unused arms and the `LocalDefId` of the macro itself. + unused_macro_rules: FxIndexMap)>, proc_macro_stubs: FxHashSet = default::fx_hash_set(), /// Traces collected during macro resolution and validated when it's complete. single_segment_macro_resolutions: diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index d4349b9d73a7..27390c0394d2 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -339,7 +339,7 @@ fn resolve_macro_invocation( } fn record_macro_rule_usage(&mut self, id: NodeId, rule_i: usize) { - if let Some(rules) = self.unused_macro_rules.get_mut(&id) { + if let Some((_, rules)) = self.unused_macro_rules.get_mut(&id) { rules.remove(rule_i); } } @@ -356,11 +356,10 @@ fn check_unused_macros(&mut self) { self.unused_macro_rules.swap_remove(&node_id); } - for (&node_id, unused_arms) in self.unused_macro_rules.iter() { + for (&node_id, (def_id, unused_arms)) in self.unused_macro_rules.iter() { if unused_arms.is_empty() { continue; } - let def_id = self.local_def_id(node_id); let m = &self.local_macro_map[&def_id]; let SyntaxExtensionKind::MacroRules(ref m) = m.ext.kind else { continue; From a082567e7b77a4ce6416a4445c9a1e915319c4e9 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 5 May 2026 07:23:24 +0200 Subject: [PATCH 19/41] Follow-up cleanups reusing the now-available `LocalDefId`s --- compiler/rustc_resolve/src/check_unused.rs | 3 +-- compiler/rustc_resolve/src/imports.rs | 18 ++++++++++-------- compiler/rustc_resolve/src/lib.rs | 3 +-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 7e1b1bce3ff7..8ab0087ae615 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -431,8 +431,7 @@ pub(crate) fn check_unused(&mut self, krate: &ast::Crate) { } } } - ImportKind::ExternCrate { id, .. } => { - let def_id = self.local_def_id(id); + ImportKind::ExternCrate { id, def_id, .. } => { if self.extern_crate_map.get(&def_id).is_none_or(|&cnum| { !tcx.is_compiler_builtins(cnum) && !tcx.is_panic_runtime(cnum) diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index c778df19818f..1b9e5d2a2daa 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -356,13 +356,16 @@ struct UnresolvedImportError { // Reexports of the form `pub use foo as bar;` where `foo` is `extern crate foo;` // are permitted for backward-compatibility under a deprecation lint. -fn pub_use_of_private_extern_crate_hack(import: ImportSummary, decl: Decl<'_>) -> Option { +fn pub_use_of_private_extern_crate_hack( + import: ImportSummary, + decl: Decl<'_>, +) -> Option { match (import.is_single, decl.kind) { (true, DeclKind::Import { import: decl_import, .. }) - if let ImportKind::ExternCrate { id, .. } = decl_import.kind + if let ImportKind::ExternCrate { def_id, .. } = decl_import.kind && import.vis.is_public() => { - Some(id) + Some(def_id) } _ => None, } @@ -1282,7 +1285,7 @@ fn finalize_import(&mut self, import: Import<'ra>) -> Option (source, target, decls, id), - ImportKind::Glob { ref max_vis, id, def_id: _ } => { + ImportKind::Glob { ref max_vis, id, def_id } => { if import.module_path.len() <= 1 { // HACK(eddyb) `lint_if_path_starts_with_module` needs at least // 2 segments, so the `resolve_path` above won't trigger it. @@ -1309,7 +1312,6 @@ fn finalize_import(&mut self, import: Import<'ra>) -> Option) -> bool { // This function is only called for single imports. - let ImportKind::Single { source, target, ref decls, id, .. } = import.kind else { + let ImportKind::Single { source, target, ref decls, id, def_id, .. } = import.kind else { unreachable!() }; @@ -1690,7 +1692,7 @@ pub(crate) fn check_for_redundant_imports(&mut self, import: Import<'ra>) -> boo // Skip if the import is public or was used through non scope-based resolution, // e.g. through a module-relative path. if self.import_use_map.get(&import) == Some(&Used::Other) - || self.effective_visibilities.is_exported(self.local_def_id(id)) + || self.effective_visibilities.is_exported(def_id) { return false; } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 6f777984e6c0..68e1dd9291df 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -2291,8 +2291,7 @@ fn record_use_inner( #[inline] fn add_to_glob_map(&mut self, import: Import<'_>, name: Symbol) { - if let ImportKind::Glob { id, .. } = import.kind { - let def_id = self.local_def_id(id); + if let ImportKind::Glob { def_id, .. } = import.kind { self.glob_map.entry(def_id).or_default().insert(name); } } From b8e547a8dc5caeee4d3ab8751a2ecb84ff7f244c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Wed, 29 Apr 2026 10:53:45 +0200 Subject: [PATCH 20/41] fix warnings in rustc_type_ir --- compiler/rustc_type_ir/src/binder.rs | 12 ++++++++---- compiler/rustc_type_ir/src/ir_print.rs | 9 +++++---- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index b55cbc3164d9..de3e04626f82 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -375,13 +375,17 @@ pub struct EarlyBinder { impl Eq for EarlyBinder {} -/// For early binders, you should first call `instantiate` before using any visitors. +// FIXME(154045): Recommended as per https://github.com/rust-lang/rust/issues/154045, this is so sad :(( #[cfg(feature = "nightly")] -impl !TypeFoldable for ty::EarlyBinder {} +macro_rules! generate { ($( $tt:tt )*) => { $( $tt )* } } -/// For early binders, you should first call `instantiate` before using any visitors. #[cfg(feature = "nightly")] -impl !TypeVisitable for ty::EarlyBinder {} +generate!( + /// For early binders, you should first call `instantiate` before using any visitors. + impl !TypeFoldable for ty::EarlyBinder {} + /// For early binders, you should first call `instantiate` before using any visitors. + impl !TypeVisitable for ty::EarlyBinder {} +); impl EarlyBinder { pub fn bind(value: T) -> EarlyBinder { diff --git a/compiler/rustc_type_ir/src/ir_print.rs b/compiler/rustc_type_ir/src/ir_print.rs index 5af2bd811bab..c6ab804d81c9 100644 --- a/compiler/rustc_type_ir/src/ir_print.rs +++ b/compiler/rustc_type_ir/src/ir_print.rs @@ -1,11 +1,12 @@ use std::fmt; use crate::{ - AliasTerm, AliasTy, Binder, ClosureKind, CoercePredicate, ExistentialProjection, - ExistentialTraitRef, FnSig, HostEffectPredicate, Interner, NormalizesTo, OutlivesPredicate, - PatternKind, Placeholder, ProjectionPredicate, SubtypePredicate, TraitPredicate, TraitRef, - UnevaluatedConst, + AliasTerm, AliasTy, Binder, CoercePredicate, ExistentialProjection, ExistentialTraitRef, FnSig, + HostEffectPredicate, Interner, NormalizesTo, OutlivesPredicate, PatternKind, Placeholder, + ProjectionPredicate, SubtypePredicate, TraitPredicate, TraitRef, }; +#[cfg(feature = "nightly")] +use crate::{ClosureKind, UnevaluatedConst}; pub trait IrPrint { fn print(t: &T, fmt: &mut fmt::Formatter<'_>) -> fmt::Result; From 3f7bf606f0864fb817abe2c4450696fcbedb060e Mon Sep 17 00:00:00 2001 From: Donnie Pollitz Date: Wed, 6 May 2026 10:57:12 +0200 Subject: [PATCH 21/41] Add Trusty OS to the generic error implementation. Background: * During this refactor, the Trusty OS was not targeted in the cfg_select https://github.com/rust-lang/rust/commit/e259373ce9eca5a10201d74a016a4a2e7ab42ed7 --- library/std/src/sys/io/error/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/std/src/sys/io/error/mod.rs b/library/std/src/sys/io/error/mod.rs index 4fca658a7dca..cc8cda9aec04 100644 --- a/library/std/src/sys/io/error/mod.rs +++ b/library/std/src/sys/io/error/mod.rs @@ -43,6 +43,7 @@ target_os = "vexos", target_family = "wasm", target_os = "zkvm", + target_os = "trusty", ) => { mod generic; pub use generic::*; From 7ccd895c0cb52ea8c85cb21990f3cfa87e1c945b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Wed, 6 May 2026 13:27:51 +0200 Subject: [PATCH 22/41] add passing test that should fail --- src/tools/compiletest/src/directives/tests.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs index 56d52982a821..ad369f9f1b60 100644 --- a/src/tools/compiletest/src/directives/tests.rs +++ b/src/tools/compiletest/src/directives/tests.rs @@ -632,6 +632,16 @@ fn test_miropt_mode_forbidden_revisions() { parse_early_props(&config, "//@ revisions: CHECK"); } +#[test] +fn test_multiple_revisions_in_directive() { + let directive = "//@ [foo,bar] compile-flags: -Z hello"; + + // The problem: this is seen as a single revision. + let line_directive = line_directive(Utf8Path::new("foo.txt"), LineNumber::ZERO, directive); + assert!(line_directive.is_some()); + assert_eq!(Some("foo,bar"), line_directive.unwrap().revision); +} + #[test] fn test_forbidden_revisions_allowed_in_non_filecheck_dir() { let revisions = ["CHECK", "COM", "NEXT", "SAME", "EMPTY", "NOT", "COUNT", "DAG", "LABEL"]; From 325b0135b8b9bccaa3241c8c5a488bedc96feecb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Wed, 6 May 2026 13:34:07 +0200 Subject: [PATCH 23/41] prevent directives from having multiple revisions also update the test to make sure there's an error --- src/tools/compiletest/src/directives/line.rs | 14 ++++++++++++++ src/tools/compiletest/src/directives/tests.rs | 5 +++++ 2 files changed, 19 insertions(+) diff --git a/src/tools/compiletest/src/directives/line.rs b/src/tools/compiletest/src/directives/line.rs index 9cc24c98a859..87e803921a85 100644 --- a/src/tools/compiletest/src/directives/line.rs +++ b/src/tools/compiletest/src/directives/line.rs @@ -30,6 +30,20 @@ pub(crate) fn line_directive<'a>( revision = Some(line_revision); raw_directive = after_close_bracket.trim_start(); + + if line_revision.contains(",") { + let suggestion: Vec<_> = line_revision + .split(",") + .map(|revision| { + format!("{COMPILETEST_DIRECTIVE_PREFIX} [{revision}]: {raw_directive}") + }) + .collect(); + panic!( + "malformed condition directive: multiple revisions aren't supported yet in `{}`, split them like\n{}", + original_line, + suggestion.join("\n"), + ); + } } else { revision = None; raw_directive = after_comment; diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs index ad369f9f1b60..c2d73f95c544 100644 --- a/src/tools/compiletest/src/directives/tests.rs +++ b/src/tools/compiletest/src/directives/tests.rs @@ -633,6 +633,7 @@ fn test_miropt_mode_forbidden_revisions() { } #[test] +#[should_panic(expected = "malformed condition directive: multiple revisions aren't supported yet")] fn test_multiple_revisions_in_directive() { let directive = "//@ [foo,bar] compile-flags: -Z hello"; @@ -640,6 +641,10 @@ fn test_multiple_revisions_in_directive() { let line_directive = line_directive(Utf8Path::new("foo.txt"), LineNumber::ZERO, directive); assert!(line_directive.is_some()); assert_eq!(Some("foo,bar"), line_directive.unwrap().revision); + + // The solution for now: forbid directives from having multiple revisions. + let config: Config = cfg().build(); + parse_early_props(&config, directive); } #[test] From ccd70c194e5045f841eb669c41bc8eb6623f0c07 Mon Sep 17 00:00:00 2001 From: danieljofficial Date: Wed, 6 May 2026 13:41:52 +0100 Subject: [PATCH 24/41] move coercion tests out of ui/issues into its folder --- .../coerce-bare-fn-returning-zst-to-closure.rs} | 0 .../coerce-mut-ref-to-raw-ptr-borrow-expires.rs} | 0 .../coerce-trait-object-removes-send-bound.rs} | 0 .../variance-coerce-unsized-cycle.rs} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/{issues/issue-18539.rs => coercion/coerce-bare-fn-returning-zst-to-closure.rs} (100%) rename tests/ui/{issues/issue-47722.rs => coercion/coerce-mut-ref-to-raw-ptr-borrow-expires.rs} (100%) rename tests/ui/{issues/issue-33387.rs => coercion/coerce-trait-object-removes-send-bound.rs} (100%) rename tests/ui/{issues/issue-41936-variance-coerce-unsized-cycle.rs => coercion/variance-coerce-unsized-cycle.rs} (100%) diff --git a/tests/ui/issues/issue-18539.rs b/tests/ui/coercion/coerce-bare-fn-returning-zst-to-closure.rs similarity index 100% rename from tests/ui/issues/issue-18539.rs rename to tests/ui/coercion/coerce-bare-fn-returning-zst-to-closure.rs diff --git a/tests/ui/issues/issue-47722.rs b/tests/ui/coercion/coerce-mut-ref-to-raw-ptr-borrow-expires.rs similarity index 100% rename from tests/ui/issues/issue-47722.rs rename to tests/ui/coercion/coerce-mut-ref-to-raw-ptr-borrow-expires.rs diff --git a/tests/ui/issues/issue-33387.rs b/tests/ui/coercion/coerce-trait-object-removes-send-bound.rs similarity index 100% rename from tests/ui/issues/issue-33387.rs rename to tests/ui/coercion/coerce-trait-object-removes-send-bound.rs diff --git a/tests/ui/issues/issue-41936-variance-coerce-unsized-cycle.rs b/tests/ui/coercion/variance-coerce-unsized-cycle.rs similarity index 100% rename from tests/ui/issues/issue-41936-variance-coerce-unsized-cycle.rs rename to tests/ui/coercion/variance-coerce-unsized-cycle.rs From c5cf60839d8274a638556e9973d1e26260e924ce Mon Sep 17 00:00:00 2001 From: danieljofficial Date: Wed, 6 May 2026 14:22:01 +0100 Subject: [PATCH 25/41] add issue links and bless --- tests/ui/coercion/coerce-bare-fn-returning-zst-to-closure.rs | 2 ++ tests/ui/coercion/coerce-mut-ref-to-raw-ptr-borrow-expires.rs | 2 ++ tests/ui/coercion/coerce-trait-object-removes-send-bound.rs | 2 ++ tests/ui/coercion/variance-coerce-unsized-cycle.rs | 2 +- 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/ui/coercion/coerce-bare-fn-returning-zst-to-closure.rs b/tests/ui/coercion/coerce-bare-fn-returning-zst-to-closure.rs index 66f0dabb73a2..39d4fa845714 100644 --- a/tests/ui/coercion/coerce-bare-fn-returning-zst-to-closure.rs +++ b/tests/ui/coercion/coerce-bare-fn-returning-zst-to-closure.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/18539 + //@ run-pass // Test that coercing bare fn's that return a zero sized type to // a closure doesn't cause an LLVM ERROR diff --git a/tests/ui/coercion/coerce-mut-ref-to-raw-ptr-borrow-expires.rs b/tests/ui/coercion/coerce-mut-ref-to-raw-ptr-borrow-expires.rs index da08b8addda9..c9089b970c83 100644 --- a/tests/ui/coercion/coerce-mut-ref-to-raw-ptr-borrow-expires.rs +++ b/tests/ui/coercion/coerce-mut-ref-to-raw-ptr-borrow-expires.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/47722 + //@ check-pass // Tests that automatic coercions from &mut T to *mut T diff --git a/tests/ui/coercion/coerce-trait-object-removes-send-bound.rs b/tests/ui/coercion/coerce-trait-object-removes-send-bound.rs index 5d323612e411..e7186d045a15 100644 --- a/tests/ui/coercion/coerce-trait-object-removes-send-bound.rs +++ b/tests/ui/coercion/coerce-trait-object-removes-send-bound.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/33387 + //@ run-pass #![feature(rustc_attrs)] diff --git a/tests/ui/coercion/variance-coerce-unsized-cycle.rs b/tests/ui/coercion/variance-coerce-unsized-cycle.rs index 2a2b88410959..91e16edd645b 100644 --- a/tests/ui/coercion/variance-coerce-unsized-cycle.rs +++ b/tests/ui/coercion/variance-coerce-unsized-cycle.rs @@ -1,6 +1,6 @@ //@ check-pass #![allow(dead_code)] -// Regression test for #41936. The coerce-unsized trait check in +// Regression test for https://github.com/rust-lang/rust/issues/41936. The coerce-unsized trait check in // coherence was using subtyping, which triggered variance // computation, which failed because it required type info for fields // that had not (yet) been computed. From da6e4d4890ca04ba43ac00d126079be0ff469e2a Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 4 Mar 2026 11:23:52 +0000 Subject: [PATCH 26/41] Move CrateInfo computation after codegen_crate CrateInfo is only necessary during linking and non-local LTO. --- .../rustc_codegen_cranelift/src/driver/jit.rs | 5 +- compiler/rustc_codegen_cranelift/src/lib.rs | 5 +- compiler/rustc_codegen_gcc/src/lib.rs | 7 +- compiler/rustc_codegen_llvm/src/lib.rs | 7 +- compiler/rustc_codegen_ssa/src/back/lto.rs | 2 +- compiler/rustc_codegen_ssa/src/back/write.rs | 86 +++++-------------- compiler/rustc_codegen_ssa/src/base.rs | 29 +++++-- compiler/rustc_codegen_ssa/src/lib.rs | 2 + .../rustc_codegen_ssa/src/traits/backend.rs | 3 +- compiler/rustc_interface/src/passes.rs | 6 +- compiler/rustc_interface/src/queries.rs | 9 +- compiler/rustc_interface/src/util.rs | 3 +- .../codegen-backend/auxiliary/the_backend.rs | 3 +- tests/ui/rmeta/no_optimized_mir.rs | 1 - tests/ui/rmeta/no_optimized_mir.stderr | 4 +- 15 files changed, 78 insertions(+), 94 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index 3903e6ea3b1d..33b88d70d6f8 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -33,14 +33,15 @@ fn create_jit_module( (jit_module, cx) } -pub(crate) fn run_jit(tcx: TyCtxt<'_>, crate_info: &CrateInfo, jit_args: Vec) -> ! { +pub(crate) fn run_jit(tcx: TyCtxt<'_>, target_cpu: String, jit_args: Vec) -> ! { if !tcx.crate_types().contains(&rustc_session::config::CrateType::Executable) { tcx.dcx().fatal("can't jit non-executable crate"); } let output_filenames = tcx.output_filenames(()); + let crate_info = CrateInfo::new(tcx, target_cpu); let should_write_ir = crate::pretty_clif::should_write_ir(tcx.sess); - let (mut jit_module, mut debug_context) = create_jit_module(tcx, crate_info); + let (mut jit_module, mut debug_context) = create_jit_module(tcx, &crate_info); let mut cached_context = Context::new(); let cgus = tcx.collect_and_partition_mono_items(()).codegen_units; diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index cbbb0ccbbc21..361f143d9991 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -209,12 +209,12 @@ fn target_cpu(&self, sess: &Session) -> String { .to_owned() } - fn codegen_crate(&self, tcx: TyCtxt<'_>, _crate_info: &CrateInfo) -> Box { + fn codegen_crate(&self, tcx: TyCtxt<'_>) -> Box { info!("codegen crate {}", tcx.crate_name(LOCAL_CRATE)); let config = self.config.get().unwrap(); if config.jit_mode { #[cfg(feature = "jit")] - driver::jit::run_jit(tcx, _crate_info, config.jit_args.clone()); + driver::jit::run_jit(tcx, self.target_cpu(tcx.sess), config.jit_args.clone()); #[cfg(not(feature = "jit"))] tcx.dcx().fatal("jit support was disabled when compiling rustc_codegen_cranelift"); @@ -228,6 +228,7 @@ fn join_codegen( ongoing_codegen: Box, sess: &Session, outputs: &OutputFilenames, + _crate_info: &CrateInfo, ) -> (CompiledModules, FxIndexMap) { ongoing_codegen.downcast::().unwrap().join(sess, outputs) } diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 4be25b3fb093..6ca2ef88ef29 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -291,8 +291,8 @@ fn target_cpu(&self, sess: &Session) -> String { target_cpu(sess).to_owned() } - fn codegen_crate(&self, tcx: TyCtxt<'_>, crate_info: &CrateInfo) -> Box { - Box::new(codegen_crate(self.clone(), tcx, crate_info)) + fn codegen_crate(&self, tcx: TyCtxt<'_>) -> Box { + Box::new(codegen_crate(self.clone(), tcx)) } fn join_codegen( @@ -300,11 +300,12 @@ fn join_codegen( ongoing_codegen: Box, sess: &Session, _outputs: &OutputFilenames, + crate_info: &CrateInfo, ) -> (CompiledModules, FxIndexMap) { ongoing_codegen .downcast::>() .expect("Expected GccCodegenBackend's OngoingCodegen, found Box") - .join(sess) + .join(sess, crate_info) } fn target_config(&self, sess: &Session) -> TargetConfig { diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 575e37d0b171..b273047b98fc 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -333,8 +333,8 @@ fn target_cpu(&self, sess: &Session) -> String { crate::llvm_util::target_cpu(sess).to_string() } - fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>, crate_info: &CrateInfo) -> Box { - Box::new(rustc_codegen_ssa::base::codegen_crate(LlvmCodegenBackend(()), tcx, crate_info)) + fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box { + Box::new(rustc_codegen_ssa::base::codegen_crate(LlvmCodegenBackend(()), tcx)) } fn join_codegen( @@ -342,11 +342,12 @@ fn join_codegen( ongoing_codegen: Box, sess: &Session, outputs: &OutputFilenames, + crate_info: &CrateInfo, ) -> (CompiledModules, FxIndexMap) { let (compiled_modules, work_products) = ongoing_codegen .downcast::>() .expect("Expected LlvmCodegenBackend's OngoingCodegen, found Box") - .join(sess); + .join(sess, crate_info); if sess.opts.unstable_opts.llvm_time_trace { sess.time("llvm_dump_timing_file", || { diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index ddfcd8a85f6b..ed36fae3cdc4 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -85,7 +85,7 @@ fn crate_type_allows_lto(crate_type: CrateType) -> bool { } } -pub(super) fn exported_symbols_for_lto( +pub(crate) fn exported_symbols_for_lto( tcx: TyCtxt<'_>, each_linked_rlib_for_lto: &[CrateNum], ) -> Vec { diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index c48e8a58b696..01c60d843d05 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -34,7 +34,7 @@ use rustc_target::spec::{MergeFunctions, SanitizerSet}; use tracing::debug; -use crate::back::link::{self, ensure_removed}; +use crate::back::link::ensure_removed; use crate::back::lto::{self, SerializedModule, check_lto_allowed}; use crate::errors::ErrorCreatingRemarkDir; use crate::traits::*; @@ -389,18 +389,8 @@ fn generate_thin_lto_work( enum MaybeLtoModules { NoLto(CompiledModules), - FatLto { - cgcx: CodegenContext, - exported_symbols_for_lto: Arc>, - each_linked_rlib_file_for_lto: Vec, - needs_fat_lto: Vec>, - }, - ThinLto { - cgcx: CodegenContext, - exported_symbols_for_lto: Arc>, - each_linked_rlib_file_for_lto: Vec, - needs_thin_lto: Vec>, - }, + FatLto { cgcx: CodegenContext, needs_fat_lto: Vec> }, + ThinLto { cgcx: CodegenContext, needs_thin_lto: Vec> }, } fn need_bitcode_in_object(tcx: TyCtxt<'_>) -> bool { @@ -424,7 +414,6 @@ fn need_pre_lto_bitcode_for_incr_comp(sess: &Session) -> bool { pub(crate) fn start_async_codegen( backend: B, tcx: TyCtxt<'_>, - crate_info: &CrateInfo, allocator_module: Option>, ) -> OngoingCodegen { let (coordinator_send, coordinator_receive) = channel(); @@ -440,7 +429,6 @@ pub(crate) fn start_async_codegen( let coordinator_thread = start_executing_work( backend.clone(), tcx, - crate_info, shared_emitter, codegen_worker_send, coordinator_receive, @@ -992,8 +980,8 @@ fn do_thin_lto( prof: &SelfProfilerRef, shared_emitter: SharedEmitter, tm_factory: TargetMachineFactoryFn, - exported_symbols_for_lto: Arc>, - each_linked_rlib_for_lto: Vec, + exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], needs_thin_lto: Vec>, ) -> Vec { let _timer = prof.verbose_generic_activity("LLVM_thinlto"); @@ -1231,7 +1219,6 @@ enum MainThreadState { fn start_executing_work( backend: B, tcx: TyCtxt<'_>, - crate_info: &CrateInfo, shared_emitter: SharedEmitter, codegen_worker_send: Sender, coordinator_receive: Receiver>, @@ -1243,22 +1230,9 @@ fn start_executing_work( let sess = tcx.sess; let prof = sess.prof.clone(); - let mut each_linked_rlib_for_lto = Vec::new(); - let mut each_linked_rlib_file_for_lto = Vec::new(); - if sess.lto() != Lto::No && sess.lto() != Lto::ThinLocal { - drop(link::each_linked_rlib(crate_info, None, &mut |cnum, path| { - if link::ignored_for_lto(sess, crate_info, cnum) { - return; - } - - each_linked_rlib_for_lto.push(cnum); - each_linked_rlib_file_for_lto.push(path.to_path_buf()); - })); - } - - // Compute the set of symbols we need to retain when doing LTO (if we need to) + // Compute the set of symbols we need to retain when doing thin local LTO (if we need to) let exported_symbols_for_lto = - Arc::new(lto::exported_symbols_for_lto(tcx, &each_linked_rlib_for_lto)); + if sess.lto() == Lto::ThinLocal { lto::exported_symbols_for_lto(tcx, &[]) } else { vec![] }; // First up, convert our jobserver into a helper thread so we can use normal // mpsc channels to manage our messages and such. @@ -1757,12 +1731,7 @@ enum CodegenState { needs_fat_lto.push(FatLtoInput::Serialized { name: wp.cgu_name, bitcode_path }) } - return Ok(MaybeLtoModules::FatLto { - cgcx, - exported_symbols_for_lto, - each_linked_rlib_file_for_lto, - needs_fat_lto, - }); + return Ok(MaybeLtoModules::FatLto { cgcx, needs_fat_lto }); } else if !needs_thin_lto.is_empty() || !lto_import_only_modules.is_empty() { assert!(compiled_modules.is_empty()); assert!(needs_fat_lto.is_empty()); @@ -1777,8 +1746,8 @@ enum CodegenState { &prof, shared_emitter.clone(), tm_factory, - exported_symbols_for_lto, - each_linked_rlib_file_for_lto, + &exported_symbols_for_lto, + &[], needs_thin_lto, )); } else { @@ -1790,12 +1759,7 @@ enum CodegenState { }); } - return Ok(MaybeLtoModules::ThinLto { - cgcx, - exported_symbols_for_lto, - each_linked_rlib_file_for_lto, - needs_thin_lto, - }); + return Ok(MaybeLtoModules::ThinLto { cgcx, needs_thin_lto }); } } @@ -2139,7 +2103,11 @@ pub struct OngoingCodegen { } impl OngoingCodegen { - pub fn join(self, sess: &Session) -> (CompiledModules, FxIndexMap) { + pub fn join( + self, + sess: &Session, + crate_info: &CrateInfo, + ) -> (CompiledModules, FxIndexMap) { self.shared_emitter_main.check(sess, true); let maybe_lto_modules = sess.time("join_worker_thread", || match self.coordinator.join() { @@ -2163,12 +2131,7 @@ pub fn join(self, sess: &Session) -> (CompiledModules, FxIndexMap { + MaybeLtoModules::FatLto { cgcx, needs_fat_lto } => { let tm_factory = self.backend.target_machine_factory( sess, cgcx.opt_level, @@ -2181,19 +2144,14 @@ pub fn join(self, sess: &Session) -> (CompiledModules, FxIndexMap { + MaybeLtoModules::ThinLto { cgcx, needs_thin_lto } => { let tm_factory = self.backend.target_machine_factory( sess, cgcx.opt_level, @@ -2206,8 +2164,8 @@ pub fn join(self, sess: &Session) -> (CompiledModules, FxIndexMap, kind: AllocatorKind) -> Vec( - backend: B, - tcx: TyCtxt<'_>, - crate_info: &CrateInfo, -) -> OngoingCodegen { +pub fn codegen_crate(backend: B, tcx: TyCtxt<'_>) -> OngoingCodegen { if tcx.sess.target.need_explicit_cpu && tcx.sess.opts.cg.target_cpu.is_none() { // The target has no default cpu, but none is set explicitly tcx.dcx().emit_fatal(errors::CpuRequired); @@ -734,7 +730,7 @@ pub fn codegen_crate( None }; - let ongoing_codegen = start_async_codegen(backend.clone(), tcx, crate_info, allocator_module); + let ongoing_codegen = start_async_codegen(backend.clone(), tcx, allocator_module); // For better throughput during parallel processing by LLVM, we used to sort // CGUs largest to smallest. This would lead to better thread utilization @@ -959,6 +955,8 @@ pub fn new(tcx: TyCtxt<'_>, target_cpu: String) -> CrateInfo { natvis_debugger_visualizers: Default::default(), lint_levels: CodegenLintLevels::from_tcx(tcx), metadata_symbol: exported_symbols::metadata_symbol_name(tcx), + each_linked_rlib_file_for_lto: Default::default(), + exported_symbols_for_lto: Default::default(), }; info.native_libraries.reserve(n_crates); @@ -1044,6 +1042,25 @@ pub fn new(tcx: TyCtxt<'_>, target_cpu: String) -> CrateInfo { }); } + let mut each_linked_rlib_for_lto = Vec::new(); + let mut each_linked_rlib_file_for_lto = Vec::new(); + if tcx.sess.lto() != config::Lto::No && tcx.sess.lto() != config::Lto::ThinLocal { + drop(crate::back::link::each_linked_rlib(&info, None, &mut |cnum, path| { + if crate::back::link::ignored_for_lto(tcx.sess, &info, cnum) { + return; + } + + each_linked_rlib_for_lto.push(cnum); + each_linked_rlib_file_for_lto.push(path.to_path_buf()); + })); + } + info.each_linked_rlib_file_for_lto = each_linked_rlib_file_for_lto; + + // FIXME move to -Zlink-only half such that each_linked_rlib_file_for_lto can be moved there too + // Compute the set of symbols we need to retain when doing LTO (if we need to) + info.exported_symbols_for_lto = + crate::back::lto::exported_symbols_for_lto(tcx, &each_linked_rlib_for_lto); + let embed_visualizers = tcx.crate_types().iter().any(|&crate_type| match crate_type { CrateType::Executable | CrateType::Dylib | CrateType::Cdylib | CrateType::Sdylib => { // These are crate types for which we invoke the linker and can embed diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 1c266382d027..a112c26684ff 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -230,6 +230,8 @@ pub struct CrateInfo { pub natvis_debugger_visualizers: BTreeSet, pub lint_levels: CodegenLintLevels, pub metadata_symbol: String, + pub each_linked_rlib_file_for_lto: Vec, + pub exported_symbols_for_lto: Vec, } /// Target-specific options that get set in `cfg(...)`. diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 7b95562ddda3..9898b67b91f7 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -104,7 +104,7 @@ fn provide(&self, _providers: &mut Providers) {} fn target_cpu(&self, sess: &Session) -> String; - fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>, crate_info: &CrateInfo) -> Box; + fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box; /// This is called on the returned `Box` from [`codegen_crate`](Self::codegen_crate) /// @@ -116,6 +116,7 @@ fn join_codegen( ongoing_codegen: Box, sess: &Session, outputs: &OutputFilenames, + crate_info: &CrateInfo, ) -> (CompiledModules, FxIndexMap); fn print_pass_timings(&self) {} diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index cb41974af41b..bcd1a52ce9dc 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1305,8 +1305,6 @@ pub(crate) fn start_codegen<'tcx>( let metadata = rustc_metadata::fs::encode_and_write_metadata(tcx); - let crate_info = CrateInfo::new(tcx, codegen_backend.target_cpu(tcx.sess)); - let codegen = tcx.sess.time("codegen_crate", || { if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() { // Skip crate items and just output metadata in -Z no-codegen mode. @@ -1315,7 +1313,7 @@ pub(crate) fn start_codegen<'tcx>( // Linker::link will skip join_codegen in case of a CodegenResults Any value. Box::new(CompiledModules { modules: vec![], allocator_module: None }) } else { - codegen_backend.codegen_crate(tcx, &crate_info) + codegen_backend.codegen_crate(tcx) } }); @@ -1327,6 +1325,8 @@ pub(crate) fn start_codegen<'tcx>( tcx.sess.code_stats.print_type_sizes(); } + let crate_info = CrateInfo::new(tcx, codegen_backend.target_cpu(tcx.sess)); + (codegen, crate_info, metadata) } diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 63c7332893b8..fd1b5104fc6d 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -53,9 +53,12 @@ pub fn link(self, sess: &Session, codegen_backend: &dyn CodegenBackend) { // This was a check only build Ok(compiled_modules) => (*compiled_modules, IndexMap::default()), - Err(ongoing_codegen) => { - codegen_backend.join_codegen(ongoing_codegen, sess, &self.output_filenames) - } + Err(ongoing_codegen) => codegen_backend.join_codegen( + ongoing_codegen, + sess, + &self.output_filenames, + &self.crate_info, + ), } }); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 24b23cc4199e..3ff427a425b7 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -406,7 +406,7 @@ fn target_cpu(&self, _sess: &Session) -> String { String::new() } - fn codegen_crate<'tcx>(&self, _tcx: TyCtxt<'tcx>, _crate_info: &CrateInfo) -> Box { + fn codegen_crate<'tcx>(&self, _tcx: TyCtxt<'tcx>) -> Box { Box::new(CompiledModules { modules: vec![], allocator_module: None }) } @@ -415,6 +415,7 @@ fn join_codegen( ongoing_codegen: Box, _sess: &Session, _outputs: &OutputFilenames, + _crate_info: &CrateInfo, ) -> (CompiledModules, FxIndexMap) { (*ongoing_codegen.downcast().unwrap(), FxIndexMap::default()) } diff --git a/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs b/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs index 5dd11b0a016e..8a7cacf20e2e 100644 --- a/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs +++ b/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs @@ -37,7 +37,7 @@ fn target_cpu(&self, _sess: &Session) -> String { "fake_target_cpu".to_owned() } - fn codegen_crate(&self, _tcx: TyCtxt<'_>, _crate_info: &CrateInfo) -> Box { + fn codegen_crate(&self, _tcx: TyCtxt<'_>) -> Box { Box::new(CompiledModules { modules: vec![], allocator_module: None }) } @@ -46,6 +46,7 @@ fn join_codegen( ongoing_codegen: Box, _sess: &Session, _outputs: &OutputFilenames, + _crate_info: &CrateInfo, ) -> (CompiledModules, FxIndexMap) { let codegen_results = ongoing_codegen .downcast::() diff --git a/tests/ui/rmeta/no_optimized_mir.rs b/tests/ui/rmeta/no_optimized_mir.rs index dbf612cd03cc..c8ed00b039b2 100644 --- a/tests/ui/rmeta/no_optimized_mir.rs +++ b/tests/ui/rmeta/no_optimized_mir.rs @@ -10,5 +10,4 @@ fn main() { rmeta_meta::missing_optimized_mir(); } -//~? ERROR crate `rmeta_meta` required to be available in rlib format, but was not found in this form //~? ERROR missing optimized MIR for `missing_optimized_mir` in the crate `rmeta_meta` diff --git a/tests/ui/rmeta/no_optimized_mir.stderr b/tests/ui/rmeta/no_optimized_mir.stderr index 91aa98172fe5..254f100aa7b5 100644 --- a/tests/ui/rmeta/no_optimized_mir.stderr +++ b/tests/ui/rmeta/no_optimized_mir.stderr @@ -1,5 +1,3 @@ -error: crate `rmeta_meta` required to be available in rlib format, but was not found in this form - error: missing optimized MIR for `missing_optimized_mir` in the crate `rmeta_meta` | note: missing optimized MIR for this item (was the crate `rmeta_meta` compiled with `--emit=metadata`?) @@ -8,5 +6,5 @@ note: missing optimized MIR for this item (was the crate `rmeta_meta` compiled w LL | pub fn missing_optimized_mir() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 1 previous error From 9297586cb9d1c13f2a7b5174713f2830e3542857 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 6 May 2026 14:18:13 +0000 Subject: [PATCH 27/41] Move invocation_temp into OutputFilenames While it was previously defined in Session, it is only ever used with OutputFilenames methods. --- Cargo.lock | 2 +- .../rustc_codegen_cranelift/src/driver/aot.rs | 22 ++------ .../rustc_codegen_cranelift/src/global_asm.rs | 3 +- compiler/rustc_codegen_gcc/src/back/write.rs | 26 ++-------- compiler/rustc_codegen_llvm/src/back/write.rs | 51 ++++--------------- .../src/debuginfo/metadata.rs | 1 - compiler/rustc_codegen_ssa/src/back/link.rs | 7 +-- compiler/rustc_codegen_ssa/src/back/write.rs | 29 ++--------- compiler/rustc_codegen_ssa/src/lib.rs | 19 +++---- compiler/rustc_interface/Cargo.toml | 1 + compiler/rustc_interface/src/util.rs | 10 ++++ compiler/rustc_session/Cargo.toml | 1 - compiler/rustc_session/src/config.rs | 49 ++++++++---------- compiler/rustc_session/src/session.rs | 16 ------ 14 files changed, 68 insertions(+), 169 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4648a12da487..ee211049c711 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4100,6 +4100,7 @@ dependencies = [ name = "rustc_interface" version = "0.0.0" dependencies = [ + "rand 0.9.2", "rustc_abi", "rustc_ast", "rustc_ast_lowering", @@ -4601,7 +4602,6 @@ version = "0.0.0" dependencies = [ "getopts", "libc", - "rand 0.9.2", "rustc_abi", "rustc_ast", "rustc_data_structures", diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index bea254b7b3a6..a9acd0ae34ca 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -150,7 +150,6 @@ fn make_module(sess: &Session, name: String) -> UnwindModule { fn emit_cgu( output_filenames: &OutputFilenames, - invocation_temp: Option<&str>, prof: &SelfProfilerRef, name: String, module: UnwindModule, @@ -166,7 +165,6 @@ fn emit_cgu( let module_regular = emit_module( output_filenames, - invocation_temp, prof, product.object, ModuleKind::Regular, @@ -192,7 +190,6 @@ fn emit_cgu( fn emit_module( output_filenames: &OutputFilenames, - invocation_temp: Option<&str>, prof: &SelfProfilerRef, mut object: cranelift_object::object::write::Object<'_>, kind: ModuleKind, @@ -211,7 +208,7 @@ fn emit_module( object.set_section_data(comment_section, producer, 1); } - let tmp_file = output_filenames.temp_path_for_cgu(OutputType::Object, &name, invocation_temp); + let tmp_file = output_filenames.temp_path_for_cgu(OutputType::Object, &name); let file = match File::create(&tmp_file) { Ok(file) => file, Err(err) => return Err(format!("error creating object file: {}", err)), @@ -251,11 +248,8 @@ fn reuse_workproduct_for_cgu( cgu: &CodegenUnit<'_>, ) -> Result { let work_product = cgu.previous_work_product(tcx); - let obj_out_regular = tcx.output_filenames(()).temp_path_for_cgu( - OutputType::Object, - cgu.name().as_str(), - tcx.sess.invocation_temp.as_deref(), - ); + let obj_out_regular = + tcx.output_filenames(()).temp_path_for_cgu(OutputType::Object, cgu.name().as_str()); let source_file_regular = rustc_incremental::in_incr_comp_dir_sess( tcx.sess, work_product.saved_files.get("o").expect("no saved object file in work product"), @@ -394,7 +388,6 @@ fn module_codegen( let producer = crate::debuginfo::producer(tcx.sess); let profiler = tcx.prof.clone(); - let invocation_temp = tcx.sess.invocation_temp.clone(); let output_filenames = tcx.output_filenames(()).clone(); let should_write_ir = crate::pretty_clif::should_write_ir(tcx.sess); @@ -421,19 +414,13 @@ fn module_codegen( let global_asm_object_file = profiler.generic_activity_with_arg("compile assembly", &*cgu_name).run(|| { - crate::global_asm::compile_global_asm( - &global_asm_config, - &cgu_name, - global_asm, - invocation_temp.as_deref(), - ) + crate::global_asm::compile_global_asm(&global_asm_config, &cgu_name, global_asm) })?; let codegen_result = profiler.generic_activity_with_arg("write object file", &*cgu_name).run(|| { emit_cgu( &global_asm_config.output_filenames, - invocation_temp.as_deref(), &profiler, cgu_name, module, @@ -456,7 +443,6 @@ fn emit_allocator_module(tcx: TyCtxt<'_>) -> Option { match emit_module( tcx.output_filenames(()), - tcx.sess.invocation_temp.as_deref(), &tcx.sess.prof, product.object, ModuleKind::Allocator, diff --git a/compiler/rustc_codegen_cranelift/src/global_asm.rs b/compiler/rustc_codegen_cranelift/src/global_asm.rs index 5765601763e4..0c5f4136a32d 100644 --- a/compiler/rustc_codegen_cranelift/src/global_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/global_asm.rs @@ -185,7 +185,6 @@ pub(crate) fn compile_global_asm( config: &GlobalAsmConfig, cgu_name: &str, global_asm: String, - invocation_temp: Option<&str>, ) -> Result, String> { if global_asm.is_empty() { return Ok(None); @@ -200,7 +199,7 @@ pub(crate) fn compile_global_asm( global_asm.push('\n'); let global_asm_object_file = add_file_stem_postfix( - config.output_filenames.temp_path_for_cgu(OutputType::Object, cgu_name, invocation_temp), + config.output_filenames.temp_path_for_cgu(OutputType::Object, cgu_name), ".asm", ); diff --git a/compiler/rustc_codegen_gcc/src/back/write.rs b/compiler/rustc_codegen_gcc/src/back/write.rs index 64674423de2c..8fd38a2efd60 100644 --- a/compiler/rustc_codegen_gcc/src/back/write.rs +++ b/compiler/rustc_codegen_gcc/src/back/write.rs @@ -29,16 +29,8 @@ pub(crate) fn codegen( let lto_mode = module.module_llvm.lto_mode; let lto_supported = module.module_llvm.lto_supported; - let bc_out = cgcx.output_filenames.temp_path_for_cgu( - OutputType::Bitcode, - &module.name, - cgcx.invocation_temp.as_deref(), - ); - let obj_out = cgcx.output_filenames.temp_path_for_cgu( - OutputType::Object, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let bc_out = cgcx.output_filenames.temp_path_for_cgu(OutputType::Bitcode, &module.name); + let obj_out = cgcx.output_filenames.temp_path_for_cgu(OutputType::Object, &module.name); if config.bitcode_needed() { let _timer = @@ -82,22 +74,15 @@ pub(crate) fn codegen( } if config.emit_ir { - let out = cgcx.output_filenames.temp_path_for_cgu( - OutputType::LlvmAssembly, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let out = + cgcx.output_filenames.temp_path_for_cgu(OutputType::LlvmAssembly, &module.name); std::fs::write(out, "").expect("write file"); } if config.emit_asm { let _timer = prof.generic_activity_with_arg("GCC_module_codegen_emit_asm", &*module.name); - let path = cgcx.output_filenames.temp_path_for_cgu( - OutputType::Assembly, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let path = cgcx.output_filenames.temp_path_for_cgu(OutputType::Assembly, &module.name); context.compile_to_file(OutputKind::Assembler, path.to_str().expect("path to str")); } @@ -215,7 +200,6 @@ pub(crate) fn codegen( config.emit_asm, config.emit_ir, &cgcx.output_filenames, - cgcx.invocation_temp.as_deref(), ) } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 515b571a9f4b..417f321a6b4a 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -117,17 +117,13 @@ pub(crate) fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> OwnedTar tcx.sess.split_debuginfo(), tcx.sess.opts.unstable_opts.split_dwarf_kind, mod_name, - tcx.sess.invocation_temp.as_deref(), ) } else { None }; - let output_obj_file = Some(tcx.output_filenames(()).temp_path_for_cgu( - OutputType::Object, - mod_name, - tcx.sess.invocation_temp.as_deref(), - )); + let output_obj_file = + Some(tcx.output_filenames(()).temp_path_for_cgu(OutputType::Object, mod_name)); let config = TargetMachineFactoryConfig { split_dwarf_file, output_obj_file }; target_machine_factory( @@ -322,11 +318,7 @@ pub(crate) fn save_temp_bitcode( return; } let ext = format!("{name}.bc"); - let path = cgcx.output_filenames.temp_path_ext_for_cgu( - &ext, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let path = cgcx.output_filenames.temp_path_ext_for_cgu(&ext, &module.name); write_bitcode_to_file(&module.module_llvm, &path) } @@ -949,11 +941,8 @@ pub(crate) fn optimize( if let Some(thin_lto_buffer) = thin_lto_buffer { let thin_lto_buffer = thin_lto_buffer.unwrap(); module.thin_lto_buffer = Some(thin_lto_buffer.data().to_vec()); - let bc_summary_out = cgcx.output_filenames.temp_path_for_cgu( - OutputType::ThinLinkBitcode, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let bc_summary_out = + cgcx.output_filenames.temp_path_for_cgu(OutputType::ThinLinkBitcode, &module.name); if let Some(thin_lto_summary_buffer) = thin_lto_summary_buffer && let Some(thin_link_bitcode_filename) = bc_summary_out.file_name() { @@ -1008,16 +997,8 @@ pub(crate) fn codegen( // copy it to the .o file, and delete the bitcode if it wasn't // otherwise requested. - let bc_out = cgcx.output_filenames.temp_path_for_cgu( - OutputType::Bitcode, - &module.name, - cgcx.invocation_temp.as_deref(), - ); - let obj_out = cgcx.output_filenames.temp_path_for_cgu( - OutputType::Object, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let bc_out = cgcx.output_filenames.temp_path_for_cgu(OutputType::Bitcode, &module.name); + let obj_out = cgcx.output_filenames.temp_path_for_cgu(OutputType::Object, &module.name); if config.bitcode_needed() { if config.emit_bc || config.emit_obj == EmitObj::Bitcode { @@ -1055,11 +1036,8 @@ pub(crate) fn codegen( if config.emit_ir { let _timer = prof.generic_activity_with_arg("LLVM_module_codegen_emit_ir", &*module.name); - let out = cgcx.output_filenames.temp_path_for_cgu( - OutputType::LlvmAssembly, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let out = + cgcx.output_filenames.temp_path_for_cgu(OutputType::LlvmAssembly, &module.name); let out_c = path_to_c_string(&out); extern "C" fn demangle_callback( @@ -1103,11 +1081,7 @@ extern "C" fn demangle_callback( if config.emit_asm { let _timer = prof.generic_activity_with_arg("LLVM_module_codegen_emit_asm", &*module.name); - let path = cgcx.output_filenames.temp_path_for_cgu( - OutputType::Assembly, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let path = cgcx.output_filenames.temp_path_for_cgu(OutputType::Assembly, &module.name); // We can't use the same module for asm and object code output, // because that triggers various errors like invalid IR or broken @@ -1136,9 +1110,7 @@ extern "C" fn demangle_callback( let _timer = prof.generic_activity_with_arg("LLVM_module_codegen_emit_obj", &*module.name); - let dwo_out = cgcx - .output_filenames - .temp_path_dwo_for_cgu(&module.name, cgcx.invocation_temp.as_deref()); + let dwo_out = cgcx.output_filenames.temp_path_dwo_for_cgu(&module.name); let dwo_out = match (cgcx.split_debuginfo, cgcx.split_dwarf_kind) { // Don't change how DWARF is emitted when disabled. (SplitDebuginfo::Off, _) => None, @@ -1203,7 +1175,6 @@ extern "C" fn demangle_callback( config.emit_asm, config.emit_ir, &cgcx.output_filenames, - cgcx.invocation_temp.as_deref(), ) } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 9a9e8287e787..19dbdc6946b7 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -903,7 +903,6 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>( tcx.sess.split_debuginfo(), tcx.sess.opts.unstable_opts.split_dwarf_kind, codegen_unit_name, - tcx.sess.invocation_temp.as_deref(), ) { // We get a path relative to the working directory from split_dwarf_path Some(tcx.sess.source_map().path_mapping().to_real_filename(work_dir, f)) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index b3857cc34e42..9be2fa973af9 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -115,12 +115,7 @@ pub fn link_binary( let path = MaybeTempDir::new(tmpdir, sess.opts.cg.save_temps); let crate_name = format!("{}", crate_info.local_crate_name); - let out_filename = output.file_for_writing( - outputs, - OutputType::Exe, - &crate_name, - sess.invocation_temp.as_deref(), - ); + let out_filename = output.file_for_writing(outputs, OutputType::Exe, &crate_name); match crate_type { CrateType::Rlib => { let _timer = sess.timer("link_rlib"); diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index c48e8a58b696..5352b7035f70 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -285,17 +285,13 @@ pub fn new(cgcx: &CodegenContext, module_name: &str) -> TargetMachineFactoryConf cgcx.split_debuginfo, cgcx.split_dwarf_kind, module_name, - cgcx.invocation_temp.as_deref(), ) } else { None }; - let output_obj_file = Some(cgcx.output_filenames.temp_path_for_cgu( - OutputType::Object, - module_name, - cgcx.invocation_temp.as_deref(), - )); + let output_obj_file = + Some(cgcx.output_filenames.temp_path_for_cgu(OutputType::Object, module_name)); TargetMachineFactoryConfig { split_dwarf_file, output_obj_file } } } @@ -322,7 +318,6 @@ pub struct CodegenContext { pub time_trace: bool, pub crate_types: Vec, pub output_filenames: Arc, - pub invocation_temp: Option, pub module_config: Arc, pub opt_level: OptLevel, pub backend_features: Vec, @@ -529,11 +524,7 @@ pub fn produce_final_output_artifacts( if let [module] = &compiled_modules.modules[..] { // 1) Only one codegen unit. In this case it's no difficulty // to copy `foo.0.x` to `foo.x`. - let path = crate_output.temp_path_for_cgu( - output_type, - &module.name, - sess.invocation_temp.as_deref(), - ); + let path = crate_output.temp_path_for_cgu(output_type, &module.name); let output = crate_output.path(output_type); if !output_type.is_text_output() && output.is_tty() { sess.dcx() @@ -912,12 +903,7 @@ fn execute_copy_from_cache_work_item( module.source.saved_files.get("dwo").as_ref().and_then(|saved_dwarf_object_file| { let dwarf_obj_out = cgcx .output_filenames - .split_dwarf_path( - cgcx.split_debuginfo, - cgcx.split_dwarf_kind, - &module.name, - cgcx.invocation_temp.as_deref(), - ) + .split_dwarf_path(cgcx.split_debuginfo, cgcx.split_dwarf_kind, &module.name) .expect( "saved dwarf object in work product but `split_dwarf_path` returned `None`", ); @@ -927,11 +913,7 @@ fn execute_copy_from_cache_work_item( let mut load_from_incr_cache = |perform, output_type: OutputType| { if perform { let saved_file = module.source.saved_files.get(output_type.extension())?; - let output_path = cgcx.output_filenames.temp_path_for_cgu( - output_type, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let output_path = cgcx.output_filenames.temp_path_for_cgu(output_type, &module.name); load_from_incr_comp_dir(output_path, &saved_file) } else { None @@ -1313,7 +1295,6 @@ fn start_executing_work( split_dwarf_kind: tcx.sess.opts.unstable_opts.split_dwarf_kind, parallel: backend.supports_parallel() && !sess.opts.unstable_opts.no_parallel_backend, pointer_size: tcx.data_layout.pointer_size(), - invocation_temp: sess.invocation_temp.clone(), }; // This is the "main loop" of parallel work happening for parallel codegen. diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 1c266382d027..4d0771b1882a 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -95,19 +95,14 @@ pub fn into_compiled_module( emit_asm: bool, emit_ir: bool, outputs: &OutputFilenames, - invocation_temp: Option<&str>, ) -> CompiledModule { - let object = emit_obj - .then(|| outputs.temp_path_for_cgu(OutputType::Object, &self.name, invocation_temp)); - let dwarf_object = - emit_dwarf_obj.then(|| outputs.temp_path_dwo_for_cgu(&self.name, invocation_temp)); - let bytecode = emit_bc - .then(|| outputs.temp_path_for_cgu(OutputType::Bitcode, &self.name, invocation_temp)); - let assembly = emit_asm - .then(|| outputs.temp_path_for_cgu(OutputType::Assembly, &self.name, invocation_temp)); - let llvm_ir = emit_ir.then(|| { - outputs.temp_path_for_cgu(OutputType::LlvmAssembly, &self.name, invocation_temp) - }); + let object = emit_obj.then(|| outputs.temp_path_for_cgu(OutputType::Object, &self.name)); + let dwarf_object = emit_dwarf_obj.then(|| outputs.temp_path_dwo_for_cgu(&self.name)); + let bytecode = emit_bc.then(|| outputs.temp_path_for_cgu(OutputType::Bitcode, &self.name)); + let assembly = + emit_asm.then(|| outputs.temp_path_for_cgu(OutputType::Assembly, &self.name)); + let llvm_ir = + emit_ir.then(|| outputs.temp_path_for_cgu(OutputType::LlvmAssembly, &self.name)); CompiledModule { name: self.name, diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index c234e21b9254..9c115736a3d4 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -5,6 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start +rand = "0.9.0" rustc_ast = { path = "../rustc_ast" } rustc_ast_lowering = { path = "../rustc_ast_lowering" } rustc_ast_passes = { path = "../rustc_ast_passes" } diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 24b23cc4199e..b4fa4c42c093 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -5,6 +5,7 @@ use std::sync::{Arc, OnceLock}; use std::{env, thread}; +use rand::{RngCore, rng}; use rustc_ast as ast; use rustc_attr_parsing::ShouldEmit; use rustc_codegen_ssa::back::archive::{ArArchiveBuilderBuilder, ArchiveBuilderBuilder}; @@ -12,6 +13,7 @@ use rustc_codegen_ssa::target_features::cfg_target_feature; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::{CompiledModules, CrateInfo, TargetConfig}; +use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::jobserver::Proxy; use rustc_data_structures::sync; @@ -615,6 +617,12 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu parse_crate_name(sess, attrs, ShouldEmit::Nothing).map(|i| i.0.to_string()) }); + let invocation_temp = sess + .opts + .incremental + .as_ref() + .map(|_| rng().next_u32().to_base_fixed_len(CASE_INSENSITIVE).to_string()); + match sess.io.output_file { None => { // "-" as input file will cause the parser to read from stdin so we @@ -631,6 +639,7 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu stem, None, sess.io.temps_dir.clone(), + invocation_temp, sess.opts.unstable_opts.split_dwarf_out_dir.clone(), sess.opts.cg.extra_filename.clone(), sess.opts.output_types.clone(), @@ -661,6 +670,7 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu out_filestem, ofile, sess.io.temps_dir.clone(), + invocation_temp, sess.opts.unstable_opts.split_dwarf_out_dir.clone(), sess.opts.cg.extra_filename.clone(), sess.opts.output_types.clone(), diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml index d66e04f58106..79d05e2d20a6 100644 --- a/compiler/rustc_session/Cargo.toml +++ b/compiler/rustc_session/Cargo.toml @@ -6,7 +6,6 @@ edition = "2024" [dependencies] # tidy-alphabetical-start getopts = "0.2" -rand = "0.9.0" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index f8da18632a99..27cc76cf83b9 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1107,13 +1107,10 @@ pub fn file_for_writing( outputs: &OutputFilenames, flavor: OutputType, codegen_unit_name: &str, - invocation_temp: Option<&str>, ) -> PathBuf { match *self { OutFileName::Real(ref path) => path.clone(), - OutFileName::Stdout => { - outputs.temp_path_for_cgu(flavor, codegen_unit_name, invocation_temp) - } + OutFileName::Stdout => outputs.temp_path_for_cgu(flavor, codegen_unit_name), } } @@ -1138,6 +1135,17 @@ pub struct OutputFilenames { filestem: String, pub single_output_file: Option, temps_directory: Option, + + /// A random string generated per invocation of rustc. + /// + /// This is prepended to all temporary files so that they do not collide + /// during concurrent invocations of rustc, or past invocations that were + /// preserved with a flag like `-C save-temps`, since these files may be + /// hard linked. + // This does not affect incr comp outputs, only where temp files are stored. + #[stable_hasher(ignore)] + invocation_temp: Option, + explicit_dwo_out_directory: Option, pub outputs: OutputTypes, } @@ -1180,6 +1188,7 @@ pub fn new( out_filestem: String, single_output_file: Option, temps_directory: Option, + invocation_temp: Option, explicit_dwo_out_directory: Option, extra: String, outputs: OutputTypes, @@ -1188,6 +1197,7 @@ pub fn new( out_directory, single_output_file, temps_directory, + invocation_temp, explicit_dwo_out_directory, outputs, crate_stem: format!("{out_crate_name}{extra}"), @@ -1224,23 +1234,14 @@ fn output_path(&self, flavor: OutputType) -> PathBuf { /// Gets the path where a compilation artifact of the given type for the /// given codegen unit should be placed on disk. If codegen_unit_name is /// None, a path distinct from those of any codegen unit will be generated. - pub fn temp_path_for_cgu( - &self, - flavor: OutputType, - codegen_unit_name: &str, - invocation_temp: Option<&str>, - ) -> PathBuf { + pub fn temp_path_for_cgu(&self, flavor: OutputType, codegen_unit_name: &str) -> PathBuf { let extension = flavor.extension(); - self.temp_path_ext_for_cgu(extension, codegen_unit_name, invocation_temp) + self.temp_path_ext_for_cgu(extension, codegen_unit_name) } /// Like `temp_path`, but specifically for dwarf objects. - pub fn temp_path_dwo_for_cgu( - &self, - codegen_unit_name: &str, - invocation_temp: Option<&str>, - ) -> PathBuf { - let p = self.temp_path_ext_for_cgu(DWARF_OBJECT_EXT, codegen_unit_name, invocation_temp); + pub fn temp_path_dwo_for_cgu(&self, codegen_unit_name: &str) -> PathBuf { + let p = self.temp_path_ext_for_cgu(DWARF_OBJECT_EXT, codegen_unit_name); if let Some(dwo_out) = &self.explicit_dwo_out_directory { let mut o = dwo_out.clone(); o.push(p.file_name().unwrap()); @@ -1252,16 +1253,11 @@ pub fn temp_path_dwo_for_cgu( /// Like `temp_path`, but also supports things where there is no corresponding /// OutputType, like noopt-bitcode or lto-bitcode. - pub fn temp_path_ext_for_cgu( - &self, - ext: &str, - codegen_unit_name: &str, - invocation_temp: Option<&str>, - ) -> PathBuf { + pub fn temp_path_ext_for_cgu(&self, ext: &str, codegen_unit_name: &str) -> PathBuf { let mut extension = codegen_unit_name.to_string(); // Append `.{invocation_temp}` to ensure temporary files are unique. - if let Some(rng) = invocation_temp { + if let Some(rng) = &self.invocation_temp { extension.push('.'); extension.push_str(rng); } @@ -1302,10 +1298,9 @@ pub fn split_dwarf_path( split_debuginfo_kind: SplitDebuginfo, split_dwarf_kind: SplitDwarfKind, cgu_name: &str, - invocation_temp: Option<&str>, ) -> Option { - let obj_out = self.temp_path_for_cgu(OutputType::Object, cgu_name, invocation_temp); - let dwo_out = self.temp_path_dwo_for_cgu(cgu_name, invocation_temp); + let obj_out = self.temp_path_for_cgu(OutputType::Object, cgu_name); + let dwo_out = self.temp_path_dwo_for_cgu(cgu_name); match (split_debuginfo_kind, split_dwarf_kind) { (SplitDebuginfo::Off, SplitDwarfKind::Single | SplitDwarfKind::Split) => None, // Single mode doesn't change how DWARF is emitted, but does add Split DWARF attributes diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 3b2fc53381a9..9bd5d434266e 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -5,8 +5,6 @@ use std::sync::atomic::{AtomicBool, AtomicUsize}; use std::{env, io}; -use rand::{RngCore, rng}; -use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN}; use rustc_data_structures::flock; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef}; @@ -163,14 +161,6 @@ pub struct Session { target_filesearch: FileSearch, host_filesearch: FileSearch, - /// A random string generated per invocation of rustc. - /// - /// This is prepended to all temporary files so that they do not collide - /// during concurrent invocations of rustc, or past invocations that were - /// preserved with a flag like `-C save-temps`, since these files may be - /// hard linked. - pub invocation_temp: Option, - /// The names of intrinsics that the current codegen backend replaces /// with its own implementations. pub replaced_intrinsics: FxHashSet, @@ -1097,11 +1087,6 @@ pub fn build_session( filesearch::FileSearch::new(&sopts.search_paths, &target_tlib_path, &target); let host_filesearch = filesearch::FileSearch::new(&sopts.search_paths, &host_tlib_path, &host); - let invocation_temp = sopts - .incremental - .as_ref() - .map(|_| rng().next_u32().to_base_fixed_len(CASE_INSENSITIVE).to_string()); - let timings = TimingSectionHandler::new(sopts.json_timings); let sess = Session { @@ -1132,7 +1117,6 @@ pub fn build_session( file_depinfo: Default::default(), target_filesearch, host_filesearch, - invocation_temp, replaced_intrinsics: FxHashSet::default(), // filled by `run_compiler` thin_lto_supported: true, // filled by `run_compiler` mir_opt_bisect_eval_count: AtomicUsize::new(0), From ff25d8acf3978326f5662c072880b5566477fefc Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 9 Apr 2026 06:26:24 +0300 Subject: [PATCH 28/41] Use special DefIds for aliases Like we do for other things for better experience in rust-analyzer. It's possible now that the `AliasTyKind` and `AliasTermKind` contains the DefId. It does require a few `try_into().unwrap()`s since in the solver's `consider_X_candidate()` only get an untyped `DefId`. It's possible to reduce that considerably if we'd pass them the typed def id as a parameter, but I don't know what will be the impact on perf. --- .../src/ty/context/impl_interner.rs | 37 ++- .../rustc_next_trait_solver/src/delegate.rs | 4 +- .../src/solve/assembly/structural_traits.rs | 20 +- .../src/solve/eval_ctxt/mod.rs | 6 +- .../rustc_next_trait_solver/src/solve/mod.rs | 4 +- .../src/solve/normalizes_to/anon_const.rs | 1 + .../src/solve/normalizes_to/free_alias.rs | 10 +- .../src/solve/normalizes_to/inherent.rs | 19 +- .../src/solve/normalizes_to/mod.rs | 234 +++++++++--------- .../src/solve/normalizes_to/opaque_types.rs | 10 +- .../src/solve/trait_goals.rs | 2 +- compiler/rustc_type_ir/src/generic_visit.rs | 1 + compiler/rustc_type_ir/src/inherent.rs | 25 +- compiler/rustc_type_ir/src/interner.rs | 67 ++++- compiler/rustc_type_ir/src/lang_items.rs | 5 +- compiler/rustc_type_ir/src/opaque_ty.rs | 2 +- compiler/rustc_type_ir/src/predicate.rs | 120 ++++----- compiler/rustc_type_ir/src/relate.rs | 5 +- compiler/rustc_type_ir/src/solve/mod.rs | 2 +- compiler/rustc_type_ir/src/ty_kind.rs | 26 +- 20 files changed, 357 insertions(+), 243 deletions(-) diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index bacddb680829..1ab8aa702752 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -8,7 +8,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; use rustc_span::{DUMMY_SP, Span, Symbol}; -use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; +use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverProjectionLangItem, SolverTraitLangItem}; use rustc_type_ir::{CollectAndApply, Interner, TypeFoldable, Unnormalized, search_graph}; use crate::dep_graph::{DepKind, DepNodeIndex}; @@ -39,6 +39,20 @@ fn next_trait_solver_globally(self) -> bool { type AdtId = DefId; type ImplId = DefId; type UnevaluatedConstId = DefId; + type TraitAssocTyId = DefId; + type TraitAssocConstId = DefId; + type TraitAssocTermId = DefId; + type OpaqueTyId = DefId; + type LocalOpaqueTyId = LocalDefId; + type FreeTyAliasId = DefId; + type FreeConstAliasId = DefId; + type FreeTermAliasId = DefId; + type ImplOrTraitAssocTyId = DefId; + type ImplOrTraitAssocConstId = DefId; + type ImplOrTraitAssocTermId = DefId; + type InherentAssocTyId = DefId; + type InherentAssocConstId = DefId; + type InherentAssocTermId = DefId; type Span = Span; type GenericArgs = ty::GenericArgsRef<'tcx>; @@ -288,7 +302,15 @@ fn mk_type_list_from_iter(self, args: I) -> T::Output self.mk_type_list_from_iter(args) } - fn parent(self, def_id: DefId) -> DefId { + fn projection_parent(self, def_id: Self::TraitAssocTermId) -> Self::TraitId { + self.parent(def_id) + } + + fn impl_or_trait_assoc_term_parent(self, def_id: Self::ImplOrTraitAssocTyId) -> DefId { + self.parent(def_id) + } + + fn inherent_alias_term_parent(self, def_id: Self::InherentAssocTermId) -> Self::ImplId { self.parent(def_id) } @@ -446,7 +468,7 @@ fn has_target_features(self, def_id: DefId) -> bool { !self.codegen_fn_attrs(def_id).target_features.is_empty() } - fn require_lang_item(self, lang_item: SolverLangItem) -> DefId { + fn require_projection_lang_item(self, lang_item: SolverProjectionLangItem) -> DefId { self.require_lang_item(solver_lang_item_to_lang_item(lang_item), DUMMY_SP) } @@ -458,7 +480,7 @@ fn require_adt_lang_item(self, lang_item: SolverAdtLangItem) -> DefId { self.require_lang_item(solver_adt_lang_item_to_lang_item(lang_item), DUMMY_SP) } - fn is_lang_item(self, def_id: DefId, lang_item: SolverLangItem) -> bool { + fn is_projection_lang_item(self, def_id: DefId, lang_item: SolverProjectionLangItem) -> bool { self.is_lang_item(def_id, solver_lang_item_to_lang_item(lang_item)) } @@ -478,7 +500,7 @@ fn is_sizedness_trait(self, def_id: DefId) -> bool { self.is_sizedness_trait(def_id) } - fn as_lang_item(self, def_id: DefId) -> Option { + fn as_projection_lang_item(self, def_id: DefId) -> Option { lang_item_to_solver_lang_item(self.lang_items().from_def_id(def_id)?) } @@ -757,7 +779,7 @@ fn $to_solver(lang_item: LangItem) -> Option<$solver_ty> { } bidirectional_lang_item_map! { - SolverLangItem, fn lang_item_to_solver_lang_item, fn solver_lang_item_to_lang_item; + SolverProjectionLangItem, fn lang_item_to_solver_lang_item, fn solver_lang_item_to_lang_item; // tidy-alphabetical-start AsyncFnKindUpvars, @@ -766,7 +788,6 @@ fn $to_solver(lang_item: LangItem) -> Option<$solver_ty> { CallRefFuture, CoroutineReturn, CoroutineYield, - DynMetadata, FieldBase, FieldType, FutureOutput, @@ -778,6 +799,7 @@ fn $to_solver(lang_item: LangItem) -> Option<$solver_ty> { SolverAdtLangItem, fn lang_item_to_solver_adt_lang_item, fn solver_adt_lang_item_to_lang_item; // tidy-alphabetical-start + DynMetadata, Option, Poll, // tidy-alphabetical-end @@ -791,7 +813,6 @@ fn $to_solver(lang_item: LangItem) -> Option<$solver_ty> { AsyncFnKindHelper, AsyncFnMut, AsyncFnOnce, - AsyncFnOnceOutput, AsyncIterator, BikeshedGuaranteedNoDrop, Clone, diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 429206a93dfe..8fd7d6d0471c 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -69,7 +69,7 @@ fn instantiate_canonical_var( fn add_item_bounds_for_hidden_type( &self, - def_id: ::DefId, + def_id: ::OpaqueTyId, args: ::GenericArgs, param_env: ::ParamEnv, hidden_ty: ::Ty, @@ -79,7 +79,7 @@ fn add_item_bounds_for_hidden_type( fn fetch_eligible_assoc_item( &self, goal_trait_ref: ty::TraitRef, - trait_assoc_def_id: ::DefId, + trait_assoc_def_id: ::TraitAssocTermId, impl_def_id: ::ImplId, ) -> FetchEligibleAssocItemResponse; diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 0bbc6f483104..0053e25e6365 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -4,7 +4,7 @@ use derive_where::derive_where; use rustc_type_ir::data_structures::HashMap; use rustc_type_ir::inherent::*; -use rustc_type_ir::lang_items::{SolverLangItem, SolverTraitLangItem}; +use rustc_type_ir::lang_items::{SolverProjectionLangItem, SolverTraitLangItem}; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::solve::inspect::ProbeKind; use rustc_type_ir::{ @@ -106,7 +106,9 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait( // We can resolve the `impl Trait` to its concrete type, // which enforces a DAG between the functions requiring // the auto trait bounds in question. - Ok(ty::Binder::dummy(vec![cx.type_of(def_id).instantiate(cx, args).skip_norm_wip()])) + Ok(ty::Binder::dummy(vec![ + cx.type_of(def_id.into()).instantiate(cx, args).skip_norm_wip(), + ])) } } } @@ -541,7 +543,8 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable( let nested = vec![ bound_sig.rebind(ty::TraitRef::new(cx, future_trait_def_id, [sig.output()])).upcast(cx), ]; - let future_output_def_id = cx.require_lang_item(SolverLangItem::FutureOutput); + let future_output_def_id = + cx.require_projection_lang_item(SolverProjectionLangItem::FutureOutput); let future_output_ty = Ty::new_projection(cx, future_output_def_id, [sig.output()]); Ok(( bound_sig.rebind(AsyncCallableRelevantTypes { @@ -642,7 +646,8 @@ fn coroutine_closure_to_ambiguous_coroutine( args: ty::CoroutineClosureArgs, sig: ty::CoroutineClosureSignature, ) -> I::Ty { - let upvars_projection_def_id = cx.require_lang_item(SolverLangItem::AsyncFnKindUpvars); + let upvars_projection_def_id = + cx.require_projection_lang_item(SolverProjectionLangItem::AsyncFnKindUpvars); let tupled_upvars_ty = Ty::new_projection( cx, upvars_projection_def_id, @@ -920,7 +925,10 @@ pub(in crate::solve) fn predicates_for_object_candidate( // show up in the bounds, but just ones that come from substituting // `Self` with the dyn type. let proj = proj.with_self_ty(cx, trait_ref.self_ty()); - replace_projection_with.entry(proj.def_id()).or_default().push(bound.rebind(proj)); + replace_projection_with + .entry(proj.def_id().into()) + .or_default() + .push(bound.rebind(proj)); } } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index e4d3e881f6af..a03d3182b9fe 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -702,7 +702,7 @@ fn should_rerun_after_erased_canonicalization( original_typing_mode: TypingMode, parent_opaque_types: &[(OpaqueTypeKey, I::Ty)], ) -> RerunDecision { - let parent_opaque_defids = parent_opaque_types.iter().map(|(key, _)| key.def_id); + let parent_opaque_defids = parent_opaque_types.iter().map(|(key, _)| key.def_id.into()); let opaque_in_storage = |opaques: I::LocalDefIds, defids: SmallCopyList<_>| { if defids.as_ref().is_empty() { RerunDecision::No @@ -1357,7 +1357,7 @@ pub(super) fn trait_ref_is_knowable( pub(super) fn fetch_eligible_assoc_item( &self, goal_trait_ref: ty::TraitRef, - trait_assoc_def_id: I::DefId, + trait_assoc_def_id: I::TraitAssocTermId, impl_def_id: I::ImplId, ) -> FetchEligibleAssocItemResponse { self.delegate.fetch_eligible_assoc_item(goal_trait_ref, trait_assoc_def_id, impl_def_id) @@ -1374,7 +1374,7 @@ pub(super) fn register_hidden_type_in_storage( pub(super) fn add_item_bounds_for_hidden_type( &mut self, - opaque_def_id: I::DefId, + opaque_def_id: I::OpaqueTyId, opaque_args: I::GenericArgs, param_env: I::ParamEnv, hidden_ty: I::Ty, diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 178a192a6630..5dc3162fbd2a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -357,7 +357,7 @@ fn structurally_normalize_term( } } - fn opaque_type_is_rigid(&self, def_id: I::DefId) -> bool { + fn opaque_type_is_rigid(&self, def_id: I::OpaqueTyId) -> bool { match self .typing_mode() // Caller should handle erased mode @@ -370,7 +370,7 @@ fn opaque_type_is_rigid(&self, def_id: I::DefId) -> bool { TypingMode::Analysis { defining_opaque_types_and_generators: non_rigid_opaques } | TypingMode::Borrowck { defining_opaque_types: non_rigid_opaques } | TypingMode::PostBorrowckAnalysis { defined_opaque_types: non_rigid_opaques } => { - !def_id.as_local().is_some_and(|def_id| non_rigid_opaques.contains(&def_id)) + !def_id.as_local().is_some_and(|def_id| non_rigid_opaques.contains(&def_id.into())) } } } diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs index 72e8d1be5915..44b8929c45f4 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs @@ -13,6 +13,7 @@ impl EvalCtxt<'_, D> pub(super) fn normalize_anon_const( &mut self, goal: Goal>, + def_id: I::UnevaluatedConstId, ) -> QueryResult { let uv = goal.predicate.alias.expect_ct(self.cx()); self.evaluate_const_and_instantiate_normalizes_to_term(goal, uv) diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs index 44fe2913b73d..f70f4bede33d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs @@ -32,11 +32,13 @@ pub(super) fn normalize_free_alias( let actual = match free_alias.kind(cx) { ty::AliasTermKind::FreeTy { def_id } => { - cx.type_of(def_id).instantiate(cx, free_alias.args).skip_norm_wip().into() - } - ty::AliasTermKind::FreeConst { def_id } if cx.is_type_const(def_id) => { - cx.const_of_item(def_id).instantiate(cx, free_alias.args).skip_norm_wip().into() + cx.type_of(def_id.into()).instantiate(cx, free_alias.args).skip_norm_wip().into() } + ty::AliasTermKind::FreeConst { def_id } if cx.is_type_const(def_id.into()) => cx + .const_of_item(def_id.into()) + .instantiate(cx, free_alias.args) + .skip_norm_wip() + .into(), ty::AliasTermKind::FreeConst { .. } => { return self.evaluate_const_and_instantiate_normalizes_to_term( goal, diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs index 00b5fd7fdcbc..d9eeb3e90f95 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs @@ -18,18 +18,19 @@ impl EvalCtxt<'_, D> pub(super) fn normalize_inherent_associated_term( &mut self, goal: Goal>, + def_id: I::InherentAssocTermId, ) -> QueryResult { let cx = self.cx(); let inherent = goal.predicate.alias; - let impl_def_id = cx.parent(inherent.def_id()); - let impl_args = self.fresh_args_for_item(impl_def_id); + let impl_def_id = cx.inherent_alias_term_parent(def_id); + let impl_args = self.fresh_args_for_item(impl_def_id.into()); // Equate impl header and add impl where clauses self.eq( goal.param_env, inherent.self_ty(), - cx.type_of(impl_def_id).instantiate(cx, impl_args).skip_norm_wip(), + cx.type_of(impl_def_id.into()).instantiate(cx, impl_args).skip_norm_wip(), )?; // Equate IAT with the RHS of the project goal @@ -46,7 +47,7 @@ pub(super) fn normalize_inherent_associated_term( // to be very careful when changing the impl where-clauses to be productive. self.add_goals( GoalSource::Misc, - cx.predicates_of(inherent.def_id()) + cx.predicates_of(def_id.into()) .iter_instantiated(cx, inherent_args) .map(Unnormalized::skip_norm_wip) .map(|pred| goal.with(cx, pred)), @@ -54,11 +55,13 @@ pub(super) fn normalize_inherent_associated_term( let normalized = match inherent.kind(cx) { ty::AliasTermKind::InherentTy { def_id } => { - cx.type_of(def_id).instantiate(cx, inherent_args).skip_norm_wip().into() - } - ty::AliasTermKind::InherentConst { def_id } if cx.is_type_const(def_id) => { - cx.const_of_item(def_id).instantiate(cx, inherent_args).skip_norm_wip().into() + cx.type_of(def_id.into()).instantiate(cx, inherent_args).skip_norm_wip().into() } + ty::AliasTermKind::InherentConst { def_id } if cx.is_type_const(def_id.into()) => cx + .const_of_item(def_id.into()) + .instantiate(cx, inherent_args) + .skip_norm_wip() + .into(), ty::AliasTermKind::InherentConst { .. } => { // FIXME(gca): This is dead code at the moment. It should eventually call // self.evaluate_const like projected consts do in consider_impl_candidate in diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 3b2e13fbb22d..ce9663666ed9 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -5,7 +5,7 @@ use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; -use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; +use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverProjectionLangItem, SolverTraitLangItem}; use rustc_type_ir::solve::{FetchEligibleAssocItemResponse, RerunReason}; use rustc_type_ir::{ self as ty, FieldInfo, Interner, NormalizesTo, PredicateKind, Unnormalized, Upcast as _, @@ -32,74 +32,79 @@ pub(super) fn compute_normalizes_to_goal( goal: Goal>, ) -> QueryResult { debug_assert!(self.term_is_fully_unconstrained(goal)); - let cx = self.cx(); - match goal.predicate.alias.kind(cx) { + match goal.predicate.alias.kind { ty::AliasTermKind::ProjectionTy { .. } | ty::AliasTermKind::ProjectionConst { .. } => { - let trait_ref = goal.predicate.alias.trait_ref(cx); - let (_, proven_via) = - self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| { - let trait_goal: Goal> = goal.with(cx, trait_ref); - ecx.compute_trait_goal(trait_goal) - })?; - self.assemble_and_merge_candidates( - proven_via, - goal, - |ecx| { - // FIXME(generic_associated_types): Addresses aggressive inference in #92917. - // - // If this type is a GAT with currently unconstrained arguments, we do not - // want to normalize it via a candidate which only applies for a specific - // instantiation. We could otherwise keep the GAT as rigid and succeed this way. - // See tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs. - // - // This only avoids normalization if a GAT argument is fully unconstrained. - // This is quite arbitrary but fixing it causes some ambiguity, see #125196. - for arg in goal.predicate.alias.own_args(cx).iter() { - let Some(term) = arg.as_term() else { - continue; - }; - match ecx.structurally_normalize_term(goal.param_env, term) { - Ok(term) => { - if term.is_infer() { - return Some( - ecx.evaluate_added_goals_and_make_canonical_response( - Certainty::AMBIGUOUS, - ), - ); - } - } - Err(NoSolution) => return Some(Err(NoSolution)), - } - } - - None - }, - |ecx| { - ecx.probe(|&result| ProbeKind::RigidAlias { result }) - .enter(|this| { - this.structurally_instantiate_normalizes_to_term( - goal, - goal.predicate.alias, - ); - this.evaluate_added_goals_and_make_canonical_response( - Certainty::Yes, - ) - }) - .map_err(Into::into) - }, - ) + self.normalize_associated_term(goal) } - ty::AliasTermKind::InherentTy { .. } | ty::AliasTermKind::InherentConst { .. } => { - self.normalize_inherent_associated_term(goal) + ty::AliasTermKind::InherentTy { def_id } => { + self.normalize_inherent_associated_term(goal, def_id.into()) } - ty::AliasTermKind::OpaqueTy { .. } => self.normalize_opaque_type(goal), + ty::AliasTermKind::InherentConst { def_id } => { + self.normalize_inherent_associated_term(goal, def_id.into()) + } + ty::AliasTermKind::OpaqueTy { def_id } => self.normalize_opaque_type(goal, def_id), ty::AliasTermKind::FreeTy { .. } | ty::AliasTermKind::FreeConst { .. } => { self.normalize_free_alias(goal) } - ty::AliasTermKind::UnevaluatedConst { .. } => self.normalize_anon_const(goal), + ty::AliasTermKind::UnevaluatedConst { def_id } => { + self.normalize_anon_const(goal, def_id) + } } } + fn normalize_associated_term(&mut self, goal: Goal>) -> QueryResult { + let cx = self.cx(); + + let trait_ref = goal.predicate.alias.trait_ref(cx); + let (_, proven_via) = self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| { + let trait_goal: Goal> = goal.with(cx, trait_ref); + ecx.compute_trait_goal(trait_goal) + })?; + self.assemble_and_merge_candidates( + proven_via, + goal, + |ecx| { + // FIXME(generic_associated_types): Addresses aggressive inference in #92917. + // + // If this type is a GAT with currently unconstrained arguments, we do not + // want to normalize it via a candidate which only applies for a specific + // instantiation. We could otherwise keep the GAT as rigid and succeed this way. + // See tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs. + // + // This only avoids normalization if a GAT argument is fully unconstrained. + // This is quite arbitrary but fixing it causes some ambiguity, see #125196. + for arg in goal.predicate.alias.own_args(cx).iter() { + let Some(term) = arg.as_term() else { + continue; + }; + match ecx.structurally_normalize_term(goal.param_env, term) { + Ok(term) => { + if term.is_infer() { + return Some(ecx.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + )); + } + } + Err(NoSolution) => return Some(Err(NoSolution)), + } + } + + None + }, + |ecx| { + ecx.probe(|&result| ProbeKind::RigidAlias { result }) + .enter(|this| { + this.structurally_instantiate_normalizes_to_term( + goal, + goal.predicate.alias, + ); + this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + .map_err(Into::into) + }, + ) + } + /// When normalizing an associated item, constrain the expected term to `term`. /// /// We know `term` to always be a fully unconstrained inference variable, so @@ -282,7 +287,7 @@ fn consider_impl_candidate( let target_item_def_id = match ecx.fetch_eligible_assoc_item( goal_trait_ref, - goal.predicate.def_id(), + goal.predicate.def_id().try_into().unwrap(), impl_def_id, ) { FetchEligibleAssocItemResponse::Found(target_item_def_id) => target_item_def_id, @@ -354,7 +359,7 @@ fn consider_impl_candidate( } } - let target_container_def_id = cx.parent(target_item_def_id); + let target_container_def_id = cx.impl_or_trait_assoc_term_parent(target_item_def_id); // Getting the right args here is complex, e.g. given: // - a goal ` as Trait>::Assoc` @@ -371,10 +376,10 @@ fn consider_impl_candidate( impl_def_id, impl_args, impl_trait_ref, - target_container_def_id, + target_container_def_id.into(), )?; - if !cx.check_args_compatible(target_item_def_id, target_args) { + if !cx.check_args_compatible(target_item_def_id.into(), target_args) { return error_response( ecx, cx.delay_bug("associated item has mismatched arguments"), @@ -384,21 +389,21 @@ fn consider_impl_candidate( // Finally we construct the actual value of the associated type. let term = match goal.predicate.alias.kind(cx) { ty::AliasTermKind::ProjectionTy { .. } => cx - .type_of(target_item_def_id) + .type_of(target_item_def_id.into()) .instantiate(cx, target_args) .skip_norm_wip() .into(), ty::AliasTermKind::ProjectionConst { .. } - if cx.is_type_const(target_item_def_id) => + if cx.is_type_const(target_item_def_id.into()) => { - cx.const_of_item(target_item_def_id) + cx.const_of_item(target_item_def_id.into()) .instantiate(cx, target_args) .skip_norm_wip() .into() } ty::AliasTermKind::ProjectionConst { .. } => { let uv = ty::UnevaluatedConst::new( - target_item_def_id.try_into().unwrap(), + target_item_def_id.into().try_into().unwrap(), target_args, ); return ecx.evaluate_const_and_instantiate_normalizes_to_term(goal, uv); @@ -504,6 +509,7 @@ fn consider_builtin_async_fn_trait_candidates( goal_kind: ty::ClosureKind, ) -> Result, NoSolution> { let cx = ecx.cx(); + let def_id = goal.predicate.def_id().try_into().unwrap(); let env_region = match goal_kind { ty::ClosureKind::Fn | ty::ClosureKind::FnMut => goal.predicate.alias.args.region_at(2), @@ -531,41 +537,42 @@ fn consider_builtin_async_fn_trait_candidates( [output_coroutine_ty], ); - let (projection_term, term) = - if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::CallOnceFuture) { - ( - ty::AliasTerm::new( - cx, - cx.alias_term_kind_from_def_id(goal.predicate.def_id()), - [goal.predicate.self_ty(), tupled_inputs_ty], - ), - output_coroutine_ty.into(), - ) - } else if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::CallRefFuture) { - ( - ty::AliasTerm::new( - cx, - cx.alias_term_kind_from_def_id(goal.predicate.def_id()), - [ - I::GenericArg::from(goal.predicate.self_ty()), - tupled_inputs_ty.into(), - env_region.into(), - ], - ), - output_coroutine_ty.into(), - ) - } else if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::AsyncFnOnceOutput) { - ( - ty::AliasTerm::new( - cx, - cx.alias_term_kind_from_def_id(goal.predicate.def_id()), - [goal.predicate.self_ty(), tupled_inputs_ty], - ), - coroutine_return_ty.into(), - ) - } else { - panic!("no such associated type in `AsyncFn*`: {:?}", goal.predicate.def_id()) - }; + let (projection_term, term) = if cx + .is_projection_lang_item(def_id, SolverProjectionLangItem::CallOnceFuture) + { + ( + ty::AliasTerm::new( + cx, + cx.alias_term_kind_from_def_id(goal.predicate.def_id()), + [goal.predicate.self_ty(), tupled_inputs_ty], + ), + output_coroutine_ty.into(), + ) + } else if cx.is_projection_lang_item(def_id, SolverProjectionLangItem::CallRefFuture) { + ( + ty::AliasTerm::new( + cx, + cx.alias_term_kind_from_def_id(goal.predicate.def_id()), + [ + I::GenericArg::from(goal.predicate.self_ty()), + tupled_inputs_ty.into(), + env_region.into(), + ], + ), + output_coroutine_ty.into(), + ) + } else if cx.is_projection_lang_item(def_id, SolverProjectionLangItem::AsyncFnOnceOutput) { + ( + ty::AliasTerm::new( + cx, + cx.alias_term_kind_from_def_id(goal.predicate.def_id()), + [goal.predicate.self_ty(), tupled_inputs_ty], + ), + coroutine_return_ty.into(), + ) + } else { + panic!("no such associated type in `AsyncFn*`: {:?}", goal.predicate.def_id()) + }; let pred = ty::ProjectionPredicate { projection_term, term }.upcast(cx); Self::probe_and_consider_implied_clause( @@ -639,8 +646,8 @@ fn consider_builtin_pointee_candidate( goal: Goal, ) -> Result, NoSolution> { let cx = ecx.cx(); - let metadata_def_id = cx.require_lang_item(SolverLangItem::Metadata); - assert_eq!(metadata_def_id, goal.predicate.def_id()); + let metadata_def_id = cx.require_projection_lang_item(SolverProjectionLangItem::Metadata); + assert_eq!(Into::::into(metadata_def_id), goal.predicate.def_id()); let metadata_ty = match goal.predicate.self_ty().kind() { ty::Bool | ty::Char @@ -666,8 +673,8 @@ fn consider_builtin_pointee_candidate( ty::Str | ty::Slice(_) => Ty::new_usize(cx), ty::Dynamic(_, _) => { - let dyn_metadata = cx.require_lang_item(SolverLangItem::DynMetadata); - cx.type_of(dyn_metadata) + let dyn_metadata = cx.require_adt_lang_item(SolverAdtLangItem::DynMetadata); + cx.type_of(dyn_metadata.into()) .instantiate(cx, &[I::GenericArg::from(goal.predicate.self_ty())]) .skip_norm_wip() } @@ -865,10 +872,12 @@ fn consider_builtin_coroutine_candidate( } let coroutine = args.as_coroutine(); + let def_id = goal.predicate.def_id().try_into().unwrap(); - let term = if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::CoroutineReturn) { + let term = if cx.is_projection_lang_item(def_id, SolverProjectionLangItem::CoroutineReturn) + { coroutine.return_ty().into() - } else if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::CoroutineYield) { + } else if cx.is_projection_lang_item(def_id, SolverProjectionLangItem::CoroutineYield) { coroutine.yield_ty().into() } else { panic!("unexpected associated item `{:?}` for `{self_ty:?}`", goal.predicate.def_id()) @@ -992,9 +1001,10 @@ fn consider_builtin_field_candidate( else { return Err(NoSolution); }; - let ty = match ecx.cx().as_lang_item(goal.predicate.def_id()) { - Some(SolverLangItem::FieldBase) => base, - Some(SolverLangItem::FieldType) => ty, + let ty = match ecx.cx().as_projection_lang_item(goal.predicate.def_id().try_into().unwrap()) + { + Some(SolverProjectionLangItem::FieldBase) => base, + Some(SolverProjectionLangItem::FieldType) => ty, _ => panic!("unexpected associated type {:?} in `Field`", goal.predicate), }; ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs index 45dd2f25abd7..f25ac21f4a62 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs @@ -17,6 +17,7 @@ impl EvalCtxt<'_, D> pub(super) fn normalize_opaque_type( &mut self, goal: Goal>, + def_id: I::OpaqueTyId, ) -> QueryResult { let cx = self.cx(); let opaque_ty = goal.predicate.alias; @@ -27,7 +28,7 @@ pub(super) fn normalize_opaque_type( // An impossible opaque type bound is the only way this goal will fail // e.g. assigning `impl Copy := NotCopy` self.add_item_bounds_for_hidden_type( - opaque_ty.def_id(), + def_id, opaque_ty.args, goal.param_env, expected, @@ -43,10 +44,9 @@ pub(super) fn normalize_opaque_type( defining_opaque_types_and_generators: defining_opaque_types, } | TypingMode::Borrowck { defining_opaque_types } => { - let Some(def_id) = opaque_ty - .def_id() + let Some(def_id) = def_id .as_local() - .filter(|&def_id| defining_opaque_types.contains(&def_id)) + .filter(|&def_id| defining_opaque_types.contains(&def_id.into())) else { // If we're not in the defining scope, treat the alias as rigid. self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); @@ -134,7 +134,7 @@ pub(super) fn normalize_opaque_type( TypingMode::PostAnalysis => { // FIXME: Add an assertion that opaque type storage is empty. let actual = - cx.type_of(opaque_ty.def_id()).instantiate(cx, opaque_ty.args).skip_norm_wip(); + cx.type_of(def_id.into()).instantiate(cx, opaque_ty.args).skip_norm_wip(); self.eq(goal.param_env, expected, actual)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 4d5fe3bf6950..a4f35f763e8e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -242,7 +242,7 @@ fn consider_auto_trait_candidate( } debug_assert!(ecx.opaque_type_is_rigid(def_id)); - for item_bound in cx.item_self_bounds(def_id).skip_binder() { + for item_bound in cx.item_self_bounds(def_id.into()).skip_binder() { if item_bound .as_trait_clause() .is_some_and(|b| b.def_id() == goal.predicate.def_id()) diff --git a/compiler/rustc_type_ir/src/generic_visit.rs b/compiler/rustc_type_ir/src/generic_visit.rs index 6669168443a4..6adaf4d158f4 100644 --- a/compiler/rustc_type_ir/src/generic_visit.rs +++ b/compiler/rustc_type_ir/src/generic_visit.rs @@ -212,6 +212,7 @@ fn generic_visit_with(&self, _visitor: &mut V) {} rustc_hash::FxBuildHasher, crate::TypeFlags, crate::solve::GoalSource, + crate::solve::VisibleForLeakCheck, rustc_abi::ExternAbi, ); diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index f63361f5968d..7ff447a81a28 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -55,7 +55,11 @@ pub trait Ty>: fn new_alias(interner: I, alias_ty: ty::AliasTy) -> Self; - fn new_projection_from_args(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self { + fn new_projection_from_args( + interner: I, + def_id: I::TraitAssocTyId, + args: I::GenericArgs, + ) -> Self { Self::new_alias( interner, ty::AliasTy::new_from_args(interner, ty::AliasTyKind::Projection { def_id }, args), @@ -64,7 +68,7 @@ fn new_projection_from_args(interner: I, def_id: I::DefId, args: I::GenericArgs) fn new_projection( interner: I, - def_id: I::DefId, + def_id: I::TraitAssocTyId, args: impl IntoIterator>, ) -> Self { Self::new_alias( @@ -637,19 +641,24 @@ pub trait Features: Copy { } #[rust_analyzer::prefer_underscore_import] -pub trait DefId: Copy + Debug + Hash + Eq + TypeFoldable { +pub trait DefId::LocalDefId>: + Copy + Debug + Hash + Eq + TypeFoldable +{ fn is_local(self) -> bool; - fn as_local(self) -> Option; + fn as_local(self) -> Option; } -pub trait SpecificDefId: - DefId + Into + TryFrom +pub trait SpecificDefId::LocalDefId>: + DefId + Into + TryFrom { } -impl + Into + TryFrom> - SpecificDefId for T +impl< + I: Interner, + T: DefId + Into + TryFrom, + Local, +> SpecificDefId for T { } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index e13c4279a68a..f25bc5fc4e3c 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -9,7 +9,7 @@ use crate::fold::TypeFoldable; use crate::inherent::*; use crate::ir_print::IrPrint; -use crate::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; +use crate::lang_items::{SolverAdtLangItem, SolverProjectionLangItem, SolverTraitLangItem}; use crate::relate::Relate; use crate::solve::{ AccessedOpaques, CanonicalInput, Certainty, ExternalConstraintsData, QueryResult, inspect, @@ -56,6 +56,38 @@ fn next_trait_solver_globally(self) -> bool { type AdtId: SpecificDefId; type ImplId: SpecificDefId; type UnevaluatedConstId: SpecificDefId; + type TraitAssocTyId: SpecificDefId + + Into + + TryFrom; + type TraitAssocConstId: SpecificDefId + + Into + + Into + + TryFrom; + type TraitAssocTermId: SpecificDefId; + type OpaqueTyId: SpecificDefId; + type LocalOpaqueTyId: Copy + + Debug + + Hash + + Eq + + Into + + Into + + Into + + TypeFoldable; + type FreeTyAliasId: SpecificDefId + Into; + type FreeConstAliasId: SpecificDefId + + Into + + Into; + type FreeTermAliasId: SpecificDefId; + type ImplOrTraitAssocTyId: SpecificDefId + Into; + type ImplOrTraitAssocConstId: SpecificDefId + + Into + + Into; + type ImplOrTraitAssocTermId: SpecificDefId; + type InherentAssocTyId: SpecificDefId + Into; + type InherentAssocConstId: SpecificDefId + + Into + + Into; + type InherentAssocTermId: SpecificDefId; type Span: Span; type GenericArgs: GenericArgs; @@ -203,8 +235,10 @@ fn opt_alias_variances( ) -> Option; fn type_of(self, def_id: Self::DefId) -> ty::EarlyBinder; - fn type_of_opaque_hir_typeck(self, def_id: Self::LocalDefId) - -> ty::EarlyBinder; + fn type_of_opaque_hir_typeck( + self, + def_id: Self::LocalOpaqueTyId, + ) -> ty::EarlyBinder; fn is_type_const(self, def_id: Self::DefId) -> bool; fn const_of_item(self, def_id: Self::DefId) -> ty::EarlyBinder; fn anon_const_kind(self, def_id: Self::DefId) -> ty::AnonConstKind; @@ -219,7 +253,7 @@ fn type_of_opaque_hir_typeck(self, def_id: Self::LocalDefId) fn trait_ref_and_own_args_for_alias( self, - def_id: Self::DefId, + def_id: Self::TraitAssocTermId, args: Self::GenericArgs, ) -> (ty::TraitRef, Self::GenericArgsSlice); @@ -243,7 +277,12 @@ fn mk_type_list_from_iter(self, args: I) -> T::Output I: Iterator, T: CollectAndApply; - fn parent(self, def_id: Self::DefId) -> Self::DefId; + fn projection_parent(self, def_id: Self::TraitAssocTermId) -> Self::TraitId; + + /// This can be an impl, or a trait if this is a defaulted term. + fn impl_or_trait_assoc_term_parent(self, def_id: Self::ImplOrTraitAssocTermId) -> Self::DefId; + + fn inherent_alias_term_parent(self, def_id: Self::InherentAssocTermId) -> Self::ImplId; fn recursion_limit(self) -> usize; @@ -325,13 +364,20 @@ fn explicit_implied_const_bounds( fn has_target_features(self, def_id: Self::FunctionId) -> bool; - fn require_lang_item(self, lang_item: SolverLangItem) -> Self::DefId; + fn require_projection_lang_item( + self, + lang_item: SolverProjectionLangItem, + ) -> Self::TraitAssocTyId; fn require_trait_lang_item(self, lang_item: SolverTraitLangItem) -> Self::TraitId; fn require_adt_lang_item(self, lang_item: SolverAdtLangItem) -> Self::AdtId; - fn is_lang_item(self, def_id: Self::DefId, lang_item: SolverLangItem) -> bool; + fn is_projection_lang_item( + self, + def_id: Self::TraitAssocTyId, + lang_item: SolverProjectionLangItem, + ) -> bool; fn is_trait_lang_item(self, def_id: Self::TraitId, lang_item: SolverTraitLangItem) -> bool; @@ -341,7 +387,10 @@ fn explicit_implied_const_bounds( fn is_sizedness_trait(self, def_id: Self::TraitId) -> bool; - fn as_lang_item(self, def_id: Self::DefId) -> Option; + fn as_projection_lang_item( + self, + def_id: Self::TraitAssocTyId, + ) -> Option; fn as_trait_lang_item(self, def_id: Self::TraitId) -> Option; @@ -360,7 +409,7 @@ fn for_each_relevant_impl( ); fn for_each_blanket_impl(self, trait_def_id: Self::TraitId, f: impl FnMut(Self::ImplId)); - fn has_item_definition(self, def_id: Self::DefId) -> bool; + fn has_item_definition(self, def_id: Self::ImplOrTraitAssocTermId) -> bool; fn impl_specializes(self, impl_def_id: Self::ImplId, victim_def_id: Self::ImplId) -> bool; diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs index f1c45a4d98b5..4ed574a55ece 100644 --- a/compiler/rustc_type_ir/src/lang_items.rs +++ b/compiler/rustc_type_ir/src/lang_items.rs @@ -1,6 +1,6 @@ /// Lang items used by the new trait solver. This can be mapped to whatever internal /// representation of `LangItem`s used in the underlying compiler implementation. -pub enum SolverLangItem { +pub enum SolverProjectionLangItem { // tidy-alphabetical-start AsyncFnKindUpvars, AsyncFnOnceOutput, @@ -8,7 +8,6 @@ pub enum SolverLangItem { CallRefFuture, CoroutineReturn, CoroutineYield, - DynMetadata, FieldBase, FieldType, FutureOutput, @@ -18,6 +17,7 @@ pub enum SolverLangItem { pub enum SolverAdtLangItem { // tidy-alphabetical-start + DynMetadata, Option, Poll, // tidy-alphabetical-end @@ -29,7 +29,6 @@ pub enum SolverTraitLangItem { AsyncFnKindHelper, AsyncFnMut, AsyncFnOnce, - AsyncFnOnceOutput, AsyncIterator, BikeshedGuaranteedNoDrop, Clone, diff --git a/compiler/rustc_type_ir/src/opaque_ty.rs b/compiler/rustc_type_ir/src/opaque_ty.rs index 782a7d30b675..d050cc2fe05a 100644 --- a/compiler/rustc_type_ir/src/opaque_ty.rs +++ b/compiler/rustc_type_ir/src/opaque_ty.rs @@ -13,7 +13,7 @@ derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext) )] pub struct OpaqueTypeKey { - pub def_id: I::LocalDefId, + pub def_id: I::LocalOpaqueTyId, pub args: I::GenericArgs, } diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 7b815e61cf09..301cf7dbf108 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -472,7 +472,7 @@ pub fn with_self_ty(&self, cx: I, self_ty: I::Ty) -> ty::Binder> derive(Decodable_NoContext, Encodable_NoContext, StableHash_NoContext) )] pub struct ExistentialProjection { - pub def_id: I::DefId, + pub def_id: I::TraitAssocTermId, pub args: I::GenericArgs, pub term: I::Term, @@ -487,17 +487,17 @@ impl Eq for ExistentialProjection {} impl ExistentialProjection { pub fn new_from_args( interner: I, - def_id: I::DefId, + def_id: I::TraitAssocTermId, args: I::GenericArgs, term: I::Term, ) -> ExistentialProjection { - interner.debug_assert_existential_args_compatible(def_id, args); + interner.debug_assert_existential_args_compatible(def_id.into(), args); Self { def_id, args, term, use_existential_projection_new_instead: () } } pub fn new( interner: I, - def_id: I::DefId, + def_id: I::TraitAssocTermId, args: impl IntoIterator>, term: I::Term, ) -> ExistentialProjection { @@ -511,10 +511,10 @@ pub fn new( /// then this function would return an `exists T. T: Iterator` existential trait /// reference. pub fn trait_ref(&self, interner: I) -> ExistentialTraitRef { - let def_id = interner.parent(self.def_id); - let args_count = interner.generics_of(def_id).count() - 1; + let def_id = interner.projection_parent(self.def_id); + let args_count = interner.generics_of(def_id.into()).count() - 1; let args = interner.mk_args(&self.args.as_slice()[..args_count]); - ExistentialTraitRef::new_from_args(interner, def_id.try_into().unwrap(), args) + ExistentialTraitRef::new_from_args(interner, def_id, args) } pub fn with_self_ty(&self, interner: I, self_ty: I::Ty) -> ProjectionPredicate { @@ -524,7 +524,7 @@ pub fn with_self_ty(&self, interner: I, self_ty: I::Ty) -> ProjectionPredicate) projection_predicate.projection_term.args.type_at(0); Self { - def_id: projection_predicate.projection_term.def_id(), + def_id: projection_predicate.def_id(), args: interner.mk_args(&projection_predicate.projection_term.args.as_slice()[1..]), term: projection_predicate.term, use_existential_projection_new_instead: (), @@ -549,13 +549,13 @@ pub fn with_self_ty(&self, cx: I, self_ty: I::Ty) -> ty::Binder I::DefId { + pub fn item_def_id(&self) -> I::TraitAssocTermId { self.skip_binder().def_id } } #[derive_where(Clone, Copy, PartialEq, Eq, Hash, Debug; I: Interner)] -#[derive(Lift_Generic)] +#[derive(Lift_Generic, GenericTypeVisitable)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext) @@ -570,12 +570,12 @@ pub enum AliasTermKind { /// Note that the `def_id` is not the `DefId` of the `TraitRef` containing this /// associated type, which is in `interner.associated_item(def_id).container`, /// aka. `interner.parent(def_id)`. - ProjectionTy { def_id: I::DefId }, + ProjectionTy { def_id: I::TraitAssocTyId }, /// An associated type in an inherent `impl` /// /// The `def_id` is the `DefId` of the `ImplItem` for the associated type. - InherentTy { def_id: I::DefId }, + InherentTy { def_id: I::InherentAssocTyId }, /// An opaque type (usually from `impl Trait` in type aliases or function return types) /// @@ -585,22 +585,22 @@ pub enum AliasTermKind { /// /// During codegen, `interner.type_of(def_id)` can be used to get the type of the /// underlying type if the type is an opaque. - OpaqueTy { def_id: I::DefId }, + OpaqueTy { def_id: I::OpaqueTyId }, /// A type alias that actually checks its trait bounds. /// /// Currently only used if the type alias references opaque types. /// Can always be normalized away. - FreeTy { def_id: I::DefId }, + FreeTy { def_id: I::FreeTyAliasId }, /// An unevaluated anonymous constants. - UnevaluatedConst { def_id: I::DefId }, + UnevaluatedConst { def_id: I::UnevaluatedConstId }, /// An unevaluated const coming from an associated const. - ProjectionConst { def_id: I::DefId }, + ProjectionConst { def_id: I::TraitAssocConstId }, /// A top level const item not part of a trait or impl. - FreeConst { def_id: I::DefId }, + FreeConst { def_id: I::FreeConstAliasId }, /// An associated const in an inherent `impl` - InherentConst { def_id: I::DefId }, + InherentConst { def_id: I::InherentAssocConstId }, } impl AliasTermKind { @@ -631,17 +631,18 @@ pub fn is_type(self) -> bool { } } - // FIXME: replace with explicit matches + // FIXME(#156181): replace with explicit matches pub fn def_id(self) -> I::DefId { - let (AliasTermKind::ProjectionTy { def_id } - | AliasTermKind::InherentTy { def_id } - | AliasTermKind::OpaqueTy { def_id } - | AliasTermKind::FreeTy { def_id } - | AliasTermKind::UnevaluatedConst { def_id } - | AliasTermKind::ProjectionConst { def_id } - | AliasTermKind::FreeConst { def_id } - | AliasTermKind::InherentConst { def_id }) = self; - def_id + match self { + AliasTermKind::ProjectionTy { def_id } => def_id.into(), + AliasTermKind::InherentTy { def_id } => def_id.into(), + AliasTermKind::OpaqueTy { def_id } => def_id.into(), + AliasTermKind::FreeTy { def_id } => def_id.into(), + AliasTermKind::UnevaluatedConst { def_id } => def_id.into(), + AliasTermKind::ProjectionConst { def_id } => def_id.into(), + AliasTermKind::FreeConst { def_id } => def_id.into(), + AliasTermKind::InherentConst { def_id } => def_id.into(), + } } } @@ -737,11 +738,11 @@ pub fn expect_ty(self, interner: I) -> ty::AliasTy { } pub fn expect_ct(self, interner: I) -> ty::UnevaluatedConst { - let def_id = match self.kind(interner) { - AliasTermKind::InherentConst { def_id } - | AliasTermKind::FreeConst { def_id } - | AliasTermKind::UnevaluatedConst { def_id } - | AliasTermKind::ProjectionConst { def_id } => def_id, + let def = match self.kind(interner) { + AliasTermKind::InherentConst { def_id } => def_id.into(), + AliasTermKind::FreeConst { def_id } => def_id.into(), + AliasTermKind::UnevaluatedConst { def_id } => def_id, + AliasTermKind::ProjectionConst { def_id } => def_id.into(), kind @ (AliasTermKind::ProjectionTy { .. } | AliasTermKind::InherentTy { .. } | AliasTermKind::OpaqueTy { .. } @@ -749,7 +750,7 @@ pub fn expect_ct(self, interner: I) -> ty::UnevaluatedConst { panic!("Cannot turn `{}` into `UnevaluatedConst`", kind.descr()) } }; - ty::UnevaluatedConst { def: def_id.try_into().unwrap(), args: self.args } + ty::UnevaluatedConst { def, args: self.args } } // FIXME: remove this function (access the field instead) @@ -763,17 +764,14 @@ pub fn def_id(self) -> I::DefId { } pub fn to_term(self, interner: I) -> I::Term { + let unevaluated_const = |def_id| { + I::Const::new_unevaluated(interner, ty::UnevaluatedConst::new(def_id, self.args)).into() + }; let alias_ty_kind = match self.kind(interner) { - AliasTermKind::FreeConst { def_id } - | AliasTermKind::InherentConst { def_id } - | AliasTermKind::UnevaluatedConst { def_id } - | AliasTermKind::ProjectionConst { def_id } => { - return I::Const::new_unevaluated( - interner, - ty::UnevaluatedConst::new(def_id.try_into().unwrap(), self.args), - ) - .into(); - } + AliasTermKind::FreeConst { def_id } => return unevaluated_const(def_id.into()), + AliasTermKind::InherentConst { def_id } => return unevaluated_const(def_id.into()), + AliasTermKind::UnevaluatedConst { def_id } => return unevaluated_const(def_id), + AliasTermKind::ProjectionConst { def_id } => return unevaluated_const(def_id.into()), AliasTermKind::ProjectionTy { def_id } => ty::Projection { def_id }, AliasTermKind::InherentTy { def_id } => ty::Inherent { def_id }, @@ -804,15 +802,25 @@ pub fn with_replaced_self_ty(self, interner: I, self_ty: I::Ty) -> Self { ) } + fn projection_def_id(self) -> Option { + match self.kind { + AliasTermKind::ProjectionTy { def_id } => Some(def_id.into()), + AliasTermKind::ProjectionConst { def_id } => Some(def_id.into()), + AliasTermKind::InherentTy { .. } + | AliasTermKind::OpaqueTy { .. } + | AliasTermKind::FreeTy { .. } + | AliasTermKind::UnevaluatedConst { .. } + | AliasTermKind::FreeConst { .. } + | AliasTermKind::InherentConst { .. } => None, + } + } + + fn expect_projection_def_id(self) -> I::TraitAssocTermId { + self.projection_def_id().expect("expected a projection") + } + pub fn trait_def_id(self, interner: I) -> I::TraitId { - assert!( - matches!( - self.kind(interner), - AliasTermKind::ProjectionTy { .. } | AliasTermKind::ProjectionConst { .. } - ), - "expected a projection" - ); - interner.parent(self.def_id()).try_into().unwrap() + interner.projection_parent(self.expect_projection_def_id()) } /// Extracts the underlying trait reference and own args from this projection. @@ -820,7 +828,7 @@ pub fn trait_def_id(self, interner: I) -> I::TraitId { /// then this function would return a `T: StreamingIterator` trait reference and /// `['a]` as the own args. pub fn trait_ref_and_own_args(self, interner: I) -> (TraitRef, I::GenericArgsSlice) { - interner.trait_ref_and_own_args_for_alias(self.def_id(), self.args) + interner.trait_ref_and_own_args_for_alias(self.expect_projection_def_id(), self.args) } /// Extracts the underlying trait reference from this projection. @@ -918,8 +926,8 @@ pub fn trait_def_id(self, interner: I) -> I::TraitId { self.projection_term.trait_def_id(interner) } - pub fn def_id(self) -> I::DefId { - self.projection_term.def_id() + pub fn def_id(self) -> I::TraitAssocTermId { + self.projection_term.expect_projection_def_id() } } diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index 425436dabafb..51de99e92fca 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -268,7 +268,10 @@ fn relate>( b: ty::ExistentialProjection, ) -> RelateResult> { if a.def_id != b.def_id { - Err(TypeError::ProjectionMismatched(ExpectedFound::new(a.def_id, b.def_id))) + Err(TypeError::ProjectionMismatched(ExpectedFound::new( + a.def_id.into(), + b.def_id.into(), + ))) } else { let term = relation.relate_with_variance( ty::Invariant, diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index 0634c9b741d3..cf9530378dc7 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -535,7 +535,7 @@ pub enum BuiltinImplSource { #[derive_where(Copy, Clone, Debug; I: Interner)] pub enum FetchEligibleAssocItemResponse { Err(I::ErrorGuaranteed), - Found(I::DefId), + Found(I::ImplOrTraitAssocTermId), NotFound(TypingMode), NotFoundBecauseErased, } diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index a08bd00eeed6..0fc39660015d 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -39,12 +39,12 @@ pub enum AliasTyKind { /// Note that the `def_id` is not the `DefId` of the `TraitRef` containing this /// associated type, which is in `interner.associated_item(def_id).container`, /// aka. `interner.parent(def_id)`. - Projection { def_id: I::DefId }, + Projection { def_id: I::TraitAssocTyId }, /// An associated type in an inherent `impl` /// /// The `def_id` is the `DefId` of the `ImplItem` for the associated type. - Inherent { def_id: I::DefId }, + Inherent { def_id: I::InherentAssocTyId }, /// An opaque type (usually from `impl Trait` in type aliases or function return types) /// @@ -55,13 +55,13 @@ pub enum AliasTyKind { /// /// During codegen, `interner.type_of(def_id)` can be used to get the type of the /// underlying type if the type is an opaque. - Opaque { def_id: I::DefId }, + Opaque { def_id: I::OpaqueTyId }, /// A type alias that actually checks its trait bounds. /// /// Currently only used if the type alias references opaque types. /// Can always be normalized away. - Free { def_id: I::DefId }, + Free { def_id: I::FreeTyAliasId }, } impl AliasTyKind { @@ -79,12 +79,12 @@ pub fn descr(self) -> &'static str { } pub fn def_id(self) -> I::DefId { - let (AliasTyKind::Projection { def_id } - | AliasTyKind::Inherent { def_id } - | AliasTyKind::Opaque { def_id } - | AliasTyKind::Free { def_id }) = self; - - def_id + match self { + AliasTyKind::Projection { def_id } => def_id.into(), + AliasTyKind::Inherent { def_id } => def_id.into(), + AliasTyKind::Opaque { def_id } => def_id.into(), + AliasTyKind::Free { def_id } => def_id.into(), + } } } @@ -506,10 +506,10 @@ pub fn with_replaced_self_ty(self, interner: I, self_ty: I::Ty) -> Self { ) } - pub fn trait_def_id(self, interner: I) -> I::DefId { + pub fn trait_def_id(self, interner: I) -> I::TraitId { let AliasTyKind::Projection { def_id } = self.kind else { panic!("expected a projection") }; - interner.parent(def_id) + interner.projection_parent(def_id.into()) } /// Extracts the underlying trait reference and own args from this projection. @@ -520,7 +520,7 @@ pub fn trait_def_id(self, interner: I) -> I::DefId { pub fn trait_ref_and_own_args(self, interner: I) -> (ty::TraitRef, I::GenericArgsSlice) { let AliasTyKind::Projection { def_id } = self.kind else { panic!("expected a projection") }; - interner.trait_ref_and_own_args_for_alias(def_id, self.args) + interner.trait_ref_and_own_args_for_alias(def_id.into(), self.args) } /// Extracts the underlying trait reference from this projection. From acc33fe5dd418586393397dcc77bc36a0d331a8c Mon Sep 17 00:00:00 2001 From: Ramon de C Valle Date: Thu, 30 Apr 2026 19:37:04 -0700 Subject: [PATCH 29/41] CFI: Fix LTO for `#![no_builtins]` crates with CFI Fixes LTO for `#![no_builtins]` crates with CFI enabled by using rustc's `EmitObj::Bitcode` path (and emitting LLVM bitcode in the `.o` for linker-based LTO). --- compiler/rustc_codegen_ssa/src/back/write.rs | 28 ++-- .../sanitizer-cfi-build-std-clang/Cargo.lock | 45 ++++++ .../sanitizer-cfi-build-std-clang/Cargo.toml | 13 ++ .../Cargo.toml | 7 + .../cross-lang-cfi-types-crate-abort/build.rs | 5 + .../src/foo.c | 5 + .../src/main.rs | 36 +++++ .../Cargo.toml | 7 + .../build.rs | 5 + .../src/foo.c | 23 +++ .../src/main.rs | 67 ++++++++ .../Cargo.lock | 7 + .../Cargo.toml | 10 ++ .../build.rs | 5 + .../src/foo.c | 5 + .../src/main.rs | 34 ++++ .../Cargo.lock | 7 + .../Cargo.toml | 10 ++ .../build.rs | 5 + .../src/foo.c | 23 +++ .../src/main.rs | 89 +++++++++++ .../indirect-arity-mismatch-abort/Cargo.toml | 4 + .../indirect-arity-mismatch-abort/src/main.rs | 30 ++++ .../Cargo.toml | 4 + .../src/main.rs | 32 ++++ .../Cargo.toml | 4 + .../src/main.rs | 30 ++++ .../indirect-type-mismatch-abort/Cargo.toml | 4 + .../indirect-type-mismatch-abort/src/main.rs | 30 ++++ .../Cargo.toml | 4 + .../src/main.rs | 31 ++++ .../invalid-branch-target-abort/Cargo.toml | 4 + .../invalid-branch-target-abort/src/main.rs | 50 ++++++ .../sanitizer-cfi-build-std-clang/rmake.rs | 151 ++++++++++++++++++ .../shared_build_rs.rs | 73 +++++++++ .../vendor/cfi-types/Cargo.toml | 13 ++ .../vendor/cfi-types/src/lib.rs | 96 +++++++++++ 37 files changed, 987 insertions(+), 9 deletions(-) create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/Cargo.lock create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/Cargo.toml create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/Cargo.toml create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/build.rs create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/src/foo.c create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/src/main.rs create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/Cargo.toml create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/build.rs create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/src/foo.c create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/src/main.rs create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/Cargo.lock create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/Cargo.toml create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/build.rs create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/src/foo.c create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/src/main.rs create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/Cargo.lock create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/Cargo.toml create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/build.rs create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/src/foo.c create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/src/main.rs create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-arity-mismatch-abort/Cargo.toml create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-arity-mismatch-abort/src/main.rs create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-pointee-type-mismatch-abort/Cargo.toml create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-pointee-type-mismatch-abort/src/main.rs create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-return-type-mismatch-abort/Cargo.toml create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-return-type-mismatch-abort/src/main.rs create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-mismatch-abort/Cargo.toml create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-mismatch-abort/src/main.rs create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-qualifier-mismatch-abort/Cargo.toml create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-qualifier-mismatch-abort/src/main.rs create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/invalid-branch-target-abort/Cargo.toml create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/invalid-branch-target-abort/src/main.rs create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/rmake.rs create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/shared_build_rs.rs create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/vendor/cfi-types/Cargo.toml create mode 100644 tests/run-make-cargo/sanitizer-cfi-build-std-clang/vendor/cfi-types/src/lib.rs diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index ff91a08de4de..265a487c6878 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -136,7 +136,8 @@ macro_rules! if_regular { let emit_obj = if !should_emit_obj { EmitObj::None } else if sess.target.obj_is_bitcode - || (sess.opts.cg.linker_plugin_lto.enabled() && !no_builtins) + || (sess.opts.cg.linker_plugin_lto.enabled() + && (!no_builtins || tcx.sess.is_sanitizer_cfi_enabled())) { // This case is selected if the target uses objects as bitcode, or // if linker plugin LTO is enabled. In the linker plugin LTO case @@ -144,14 +145,23 @@ macro_rules! if_regular { // and convert it to object code. This may be done by either the // native linker or rustc itself. // - // Note, however, that the linker-plugin-lto requested here is - // explicitly ignored for `#![no_builtins]` crates. These crates are - // specifically ignored by rustc's LTO passes and wouldn't work if - // loaded into the linker. These crates define symbols that LLVM - // lowers intrinsics to, and these symbol dependencies aren't known - // until after codegen. As a result any crate marked - // `#![no_builtins]` is assumed to not participate in LTO and - // instead goes on to generate object code. + // By default this branch is skipped for `#![no_builtins]` crates so + // they emit native object files (machine code), not LLVM bitcode + // objects for the linker (see rust-lang/rust#146133). + // + // However, when LLVM CFI is enabled (`-Zsanitizer=cfi`), this + // breaks LLVM's expected pipeline: LLVM emits `llvm.type.test` + // intrinsics and related metadata that must be lowered by LLVM's + // `LowerTypeTests` pass before instruction selection during + // link-time LTO. Otherwise, `llvm.type.test` intrinsics and related + // metadata are not lowered by LLVM's `LowerTypeTests` pass before + // reaching the target backend, and LLVM may abort during codegen + // (for example in SelectionDAG type legalization) (see + // rust-lang/rust#142284). + // + // Therefore, with `-Clinker-plugin-lto` and `-Zsanitizer=cfi`, a + // `#![no_builtins]` crate must still use rustc's `EmitObj::Bitcode` + // path (and emit LLVM bitcode in the `.o` for linker-based LTO). EmitObj::Bitcode } else if need_bitcode_in_object(tcx) || sess.target.requires_lto { EmitObj::ObjectCode(BitcodeSection::Full) diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/Cargo.lock b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/Cargo.lock new file mode 100644 index 000000000000..8d2834ad01ab --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/Cargo.lock @@ -0,0 +1,45 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "cfi-types" +version = "0.0.8" + +[[package]] +name = "cross-lang-cfi-types-crate-abort" +version = "0.1.0" +dependencies = [ + "cfi-types", +] + +[[package]] +name = "cross-lang-cfi-types-crate-not-abort" +version = "0.1.0" +dependencies = [ + "cfi-types", +] + +[[package]] +name = "indirect-arity-mismatch-abort" +version = "0.1.0" + +[[package]] +name = "indirect-pointee-type-mismatch-abort" +version = "0.1.0" + +[[package]] +name = "indirect-return-type-mismatch-abort" +version = "0.1.0" + +[[package]] +name = "indirect-type-mismatch-abort" +version = "0.1.0" + +[[package]] +name = "indirect-type-qualifier-mismatch-abort" +version = "0.1.0" + +[[package]] +name = "invalid-branch-target-abort" +version = "0.1.0" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/Cargo.toml new file mode 100644 index 000000000000..2b22762883c0 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/Cargo.toml @@ -0,0 +1,13 @@ +# Workspace mirroring the examples in . +[workspace] +resolver = "2" +members = [ + "invalid-branch-target-abort", + "indirect-arity-mismatch-abort", + "indirect-pointee-type-mismatch-abort", + "indirect-return-type-mismatch-abort", + "indirect-type-qualifier-mismatch-abort", + "indirect-type-mismatch-abort", + "cross-lang-cfi-types-crate-abort", + "cross-lang-cfi-types-crate-not-abort", +] diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/Cargo.toml new file mode 100644 index 000000000000..2b6082a5b653 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "cross-lang-cfi-types-crate-abort" +version = "0.1.0" +edition = "2021" + +[dependencies] +cfi-types = { path = "../vendor/cfi-types" } diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/build.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/build.rs new file mode 100644 index 000000000000..e6a0311d2d1b --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/build.rs @@ -0,0 +1,5 @@ +include!("../shared_build_rs.rs"); + +fn main() { + build_foo_static_lib(&[]); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/src/foo.c b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/src/foo.c new file mode 100644 index 000000000000..9021075763bb --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/src/foo.c @@ -0,0 +1,5 @@ +int +do_twice(int (*fn)(int), int arg) +{ + return fn(arg) + fn(arg); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/src/main.rs new file mode 100644 index 000000000000..dc6490f6d3ff --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/src/main.rs @@ -0,0 +1,36 @@ +// This example demonstrates redirecting control flow using an indirect +// branch/call to a function with different return and parameter types than the +// return type expected and arguments intended/passed at the call/branch site, +// across the FFI boundary using the `cfi_types` crate for cross-language LLVM +// CFI. + +use std::mem; + +use cfi_types::{c_int, c_long}; + +#[link(name = "foo")] +unsafe extern "C" { + fn do_twice(f: unsafe extern "C" fn(c_int) -> c_int, arg: i32) -> i32; +} + +unsafe extern "C" fn add_one(x: c_int) -> c_int { + c_int(x.0 + 1) +} + +unsafe extern "C" fn add_two(x: c_long) -> c_long { + c_long(x.0 + 2) +} + +fn main() { + let answer = unsafe { do_twice(add_one, 5) }; + + println!("The answer is: {}", answer); + + println!("With CFI enabled, you should not see the next answer"); + let f: unsafe extern "C" fn(c_int) -> c_int = unsafe { + mem::transmute::<*const u8, unsafe extern "C" fn(c_int) -> c_int>(add_two as *const u8) + }; + let next_answer = unsafe { do_twice(f, 5) }; + + println!("The next answer is: {}", next_answer); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/Cargo.toml new file mode 100644 index 000000000000..81b068d79742 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "cross-lang-cfi-types-crate-not-abort" +version = "0.1.0" +edition = "2021" + +[dependencies] +cfi-types = { path = "../vendor/cfi-types" } diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/build.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/build.rs new file mode 100644 index 000000000000..e6a0311d2d1b --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/build.rs @@ -0,0 +1,5 @@ +include!("../shared_build_rs.rs"); + +fn main() { + build_foo_static_lib(&[]); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/src/foo.c b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/src/foo.c new file mode 100644 index 000000000000..d02bbb285883 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/src/foo.c @@ -0,0 +1,23 @@ +#include +#include + +// This definition has the type id "_ZTSFvlE". +void +hello_from_c(long arg) +{ + printf("Hello from C!\n"); +} + +// This definition has the type id "_ZTSFvPFvlElE"--this can be ignored for the +// purposes of this example. +void +indirect_call_from_c(void (*fn)(long), long arg) +{ + // This call site tests whether the destination pointer is a member of the + // group derived from the same type id of the fn declaration, which has the + // type id "_ZTSFvlE". + // + // Notice that since the test is at the call site and generated by Clang, + // the type id used in the test is encoded by Clang. + fn(arg); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/src/main.rs new file mode 100644 index 000000000000..a9d1326d6dc4 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/src/main.rs @@ -0,0 +1,67 @@ +use cfi_types::c_long; + +#[link(name = "foo")] +extern "C" { + // This declaration has the type id "_ZTSFvlE" because it uses the CFI types + // for cross-language LLVM CFI support. The cfi_types crate provides a new + // set of C types as user-defined types using the cfi_encoding attribute and + // repr(transparent) to be used for cross-language LLVM CFI support. This + // new set of C types allows the Rust compiler to identify and correctly + // encode C types in extern "C" function types indirectly called across the + // FFI boundary when CFI is enabled. + fn hello_from_c(_: c_long); + + // This declaration has the type id "_ZTSFvPFvlElE" because it uses the CFI + // types for cross-language LLVM CFI support--this can be ignored for the + // purposes of this example. + fn indirect_call_from_c(f: unsafe extern "C" fn(c_long), arg: c_long); +} + +// This definition has the type id "_ZTSFvlE" because it uses the CFI types for +// cross-language LLVM CFI support, similarly to the hello_from_c declaration +// above. +unsafe extern "C" fn hello_from_rust(_: c_long) { + println!("Hello, world!"); +} + +// This definition has the type id "_ZTSFvlE" because it uses the CFI types for +// cross-language LLVM CFI support, similarly to the hello_from_c declaration +// above. +unsafe extern "C" fn hello_from_rust_again(_: c_long) { + println!("Hello from Rust again!"); +} + +// This definition would also have the type id "_ZTSFvPFvlElE" because it uses +// the CFI types for cross-language LLVM CFI support, similarly to the +// hello_from_c declaration above--this can be ignored for the purposes of this +// example. +fn indirect_call(f: unsafe extern "C" fn(c_long), arg: c_long) { + // This indirect call site tests whether the destination pointer is a member + // of the group derived from the same type id of the f declaration, which + // has the type id "_ZTSFvlE" because it uses the CFI types for + // cross-language LLVM CFI support, similarly to the hello_from_c + // declaration above. + unsafe { f(arg) } +} + +// This definition has the type id "_ZTSFvvE"--this can be ignored for the +// purposes of this example. +fn main() { + // This demonstrates an indirect call within Rust-only code using the same + // encoding for hello_from_rust and the test at the indirect call site at + // indirect_call (i.e., "_ZTSFvlE"). + indirect_call(hello_from_rust, c_long(5)); + + // This demonstrates an indirect call across the FFI boundary with the Rust + // compiler and Clang using the same encoding for hello_from_c and the test + // at the indirect call site at indirect_call (i.e., "_ZTSFvlE"). + indirect_call(hello_from_c, c_long(5)); + + // This demonstrates an indirect call to a function passed as a callback + // across the FFI boundary with the Rust compiler and Clang the same + // encoding for the passed-callback declaration and the test at the indirect + // call site at indirect_call_from_c (i.e., "_ZTSFvlE"). + unsafe { + indirect_call_from_c(hello_from_rust_again, c_long(5)); + } +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/Cargo.lock b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/Cargo.lock new file mode 100644 index 000000000000..5c2cbfd47ac5 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "cross-lang-integer-normalization-abort" +version = "0.1.0" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/Cargo.toml new file mode 100644 index 000000000000..b9044fba728a --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "cross-lang-integer-normalization-abort" +version = "0.1.0" +edition = "2021" + +# Not a member of the parent `sanitizer-cfi-build-std-clang` workspace so it can +# be built with different `RUSTFLAGS` (i.e., integer normalization). +[workspace] +members = ["."] +resolver = "2" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/build.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/build.rs new file mode 100644 index 000000000000..de04b350552a --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/build.rs @@ -0,0 +1,5 @@ +include!("../shared_build_rs.rs"); + +fn main() { + build_foo_static_lib(&["-fsanitize-cfi-icall-experimental-normalize-integers"]); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/src/foo.c b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/src/foo.c new file mode 100644 index 000000000000..9021075763bb --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/src/foo.c @@ -0,0 +1,5 @@ +int +do_twice(int (*fn)(int), int arg) +{ + return fn(arg) + fn(arg); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/src/main.rs new file mode 100644 index 000000000000..ef5d1da6ca73 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/src/main.rs @@ -0,0 +1,34 @@ +// This example demonstrates redirecting control flow using an indirect +// branch/call to a function with different return and parameter types than the +// return type expected and arguments intended/passed at the call/branch site, +// across the FFI boundary using integer normalization for cross-language LLVM +// CFI. + +use std::mem; + +#[link(name = "foo")] +extern "C" { + fn do_twice(f: unsafe extern "C" fn(i32) -> i32, arg: i32) -> i32; +} + +unsafe extern "C" fn add_one(x: i32) -> i32 { + x + 1 +} + +unsafe extern "C" fn add_two(x: i64) -> i64 { + x + 2 +} + +fn main() { + let answer = unsafe { do_twice(add_one, 5) }; + + println!("The answer is: {}", answer); + + println!("With CFI enabled, you should not see the next answer"); + let f: unsafe extern "C" fn(i32) -> i32 = unsafe { + mem::transmute::<*const u8, unsafe extern "C" fn(i32) -> i32>(add_two as *const u8) + }; + let next_answer = unsafe { do_twice(f, 5) }; + + println!("The next answer is: {}", next_answer); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/Cargo.lock b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/Cargo.lock new file mode 100644 index 000000000000..957356f828d9 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "cross-lang-integer-normalization-not-abort" +version = "0.1.0" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/Cargo.toml new file mode 100644 index 000000000000..a89a3ae9b350 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "cross-lang-integer-normalization-not-abort" +version = "0.1.0" +edition = "2021" + +# Not a member of the parent `sanitizer-cfi-build-std-clang` workspace so it can +# be built with different `RUSTFLAGS` (i.e., integer normalization). +[workspace] +members = ["."] +resolver = "2" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/build.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/build.rs new file mode 100644 index 000000000000..de04b350552a --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/build.rs @@ -0,0 +1,5 @@ +include!("../shared_build_rs.rs"); + +fn main() { + build_foo_static_lib(&["-fsanitize-cfi-icall-experimental-normalize-integers"]); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/src/foo.c b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/src/foo.c new file mode 100644 index 000000000000..d02bbb285883 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/src/foo.c @@ -0,0 +1,23 @@ +#include +#include + +// This definition has the type id "_ZTSFvlE". +void +hello_from_c(long arg) +{ + printf("Hello from C!\n"); +} + +// This definition has the type id "_ZTSFvPFvlElE"--this can be ignored for the +// purposes of this example. +void +indirect_call_from_c(void (*fn)(long), long arg) +{ + // This call site tests whether the destination pointer is a member of the + // group derived from the same type id of the fn declaration, which has the + // type id "_ZTSFvlE". + // + // Notice that since the test is at the call site and generated by Clang, + // the type id used in the test is encoded by Clang. + fn(arg); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/src/main.rs new file mode 100644 index 000000000000..70a4d9a789e5 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/src/main.rs @@ -0,0 +1,89 @@ +use std::ffi::c_long; + +#[link(name = "foo")] +extern "C" { + // This declaration would have the type id "_ZTSFvlE", but at the time types + // are encoded, all type aliases are already resolved to their respective + // Rust aliased types, so this is encoded either as "_ZTSFvu3i32E" or + // "_ZTSFvu3i64E" depending to what type c_long type alias is resolved to, + // which currently uses the u vendor extended type + // encoding for the Rust integer types--this is the problem demonstrated in + // this example. + fn hello_from_c(_: c_long); + + // This declaration would have the type id "_ZTSFvPFvlElE", but is encoded + // either as "_ZTSFvPFvu3i32ES_E" (compressed) or "_ZTSFvPFvu3i64ES_E" + // (compressed), similarly to the hello_from_c declaration above--this can + // be ignored for the purposes of this example. + fn indirect_call_from_c(f: unsafe extern "C" fn(c_long), arg: c_long); +} + +// This definition would have the type id "_ZTSFvlE", but is encoded either as +// "_ZTSFvu3i32E" or "_ZTSFvu3i64E", similarly to the hello_from_c declaration +// above. +unsafe extern "C" fn hello_from_rust(_: c_long) { + println!("Hello, world!"); +} + +// This definition would have the type id "_ZTSFvlE", but is encoded either as +// "_ZTSFvu3i32E" or "_ZTSFvu3i64E", similarly to the hello_from_c declaration +// above. +unsafe extern "C" fn hello_from_rust_again(_: c_long) { + println!("Hello from Rust again!"); +} + +// This definition would also have the type id "_ZTSFvPFvlElE", but is encoded +// either as "_ZTSFvPFvu3i32ES_E" (compressed) or "_ZTSFvPFvu3i64ES_E" +// (compressed), similarly to the hello_from_c declaration above--this can be +// ignored for the purposes of this example. +fn indirect_call(f: unsafe extern "C" fn(c_long), arg: c_long) { + // This indirect call site tests whether the destination pointer is a member + // of the group derived from the same type id of the f declaration, which + // would have the type id "_ZTSFvlE", but is encoded either as + // "_ZTSFvu3i32E" or "_ZTSFvu3i64E", similarly to the hello_from_c + // declaration above. + // + // Notice that since the test is at the call site and generated by the Rust + // compiler, the type id used in the test is encoded by the Rust compiler. + unsafe { f(arg) } +} + +// This definition has the type id "_ZTSFvvE"--this can be ignored for the +// purposes of this example. +fn main() { + // This demonstrates an indirect call within Rust-only code using the same + // encoding for hello_from_rust and the test at the indirect call site at + // indirect_call (i.e., "_ZTSFvu3i32E" or "_ZTSFvu3i64E"). + indirect_call(hello_from_rust, 5); + + // This demonstrates an indirect call across the FFI boundary with the Rust + // compiler and Clang using different encodings for hello_from_c and the + // test at the indirect call site at indirect_call (i.e., "_ZTSFvu3i32E" or + // "_ZTSFvu3i64E" vs "_ZTSFvlE"). + // + // When using rustc LTO (i.e., -Clto), this works because the type id used + // is from the Rust-declared hello_from_c, which is encoded by the Rust + // compiler (i.e., "_ZTSFvu3i32E" or "_ZTSFvu3i64E"). + // + // When using (proper) LTO (i.e., -Clinker-plugin-lto), this does not work + // because the type id used is from the C-defined hello_from_c, which is + // encoded by Clang (i.e., "_ZTSFvlE"). + indirect_call(hello_from_c, 5); + + // This demonstrates an indirect call to a function passed as a callback + // across the FFI boundary with the Rust compiler and Clang using different + // encodings for the passed-callback declaration and the test at the + // indirect call site at indirect_call_from_c (i.e., "_ZTSFvu3i32E" or + // "_ZTSFvu3i64E" vs "_ZTSFvlE"). + // + // When Rust functions are passed as callbacks across the FFI boundary to be + // called back from C code, the tests are also at the call site but + // generated by Clang instead, so the type ids used in the tests are encoded + // by Clang, which will not match the type ids of declarations encoded by + // the Rust compiler (e.g., hello_from_rust_again). (The same happens the + // other way around for C functions passed as callbacks across the FFI + // boundary to be called back from Rust code.) + unsafe { + indirect_call_from_c(hello_from_rust_again, 5); + } +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-arity-mismatch-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-arity-mismatch-abort/Cargo.toml new file mode 100644 index 000000000000..8c7f2b33265f --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-arity-mismatch-abort/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "indirect-arity-mismatch-abort" +version = "0.1.0" +edition = "2021" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-arity-mismatch-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-arity-mismatch-abort/src/main.rs new file mode 100644 index 000000000000..8cc0dba58b20 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-arity-mismatch-abort/src/main.rs @@ -0,0 +1,30 @@ +// This example demonstrates redirecting control flow using an indirect +// branch/call to a function with a different number of parameters than +// arguments intended/passed at the call/branch site. + +use std::mem; + +fn add_one(x: i32) -> i32 { + x + 1 +} + +fn add_two(x: i32, _y: i32) -> i32 { + x + 2 +} + +fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { + f(arg) + f(arg) +} + +fn main() { + let answer = do_twice(add_one, 5); + + println!("The answer is: {}", answer); + + println!("With CFI enabled, you should not see the next answer"); + let f: fn(i32) -> i32 = + unsafe { mem::transmute::<*const u8, fn(i32) -> i32>(add_two as *const u8) }; + let next_answer = do_twice(f, 5); + + println!("The next answer is: {}", next_answer); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-pointee-type-mismatch-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-pointee-type-mismatch-abort/Cargo.toml new file mode 100644 index 000000000000..0526f844363b --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-pointee-type-mismatch-abort/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "indirect-pointee-type-mismatch-abort" +version = "0.1.0" +edition = "2021" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-pointee-type-mismatch-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-pointee-type-mismatch-abort/src/main.rs new file mode 100644 index 000000000000..ad0d7ddf0192 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-pointee-type-mismatch-abort/src/main.rs @@ -0,0 +1,32 @@ +// This example demonstrates redirecting control flow using an indirect +// branch/call to a function with different return and parameter (i.e., pointee) +// types than the return type expected and arguments intended/passed at the +// call/branch site. + +use std::mem; + +fn add_one(x: *const i32) -> i32 { + unsafe { *x + 1 } +} + +fn add_two(x: *const i64) -> i32 { + unsafe { (*x + 2) as i32 } +} + +fn do_twice(f: fn(*const i32) -> i32, arg: *const i32) -> i32 { + f(arg) + f(arg) +} + +fn main() { + let value: i32 = 5; + let answer = do_twice(add_one, &value); + + println!("The answer is: {}", answer); + + println!("With CFI enabled, you should not see the next answer"); + let f: fn(*const i32) -> i32 = + unsafe { mem::transmute::<*const u8, fn(*const i32) -> i32>(add_two as *const u8) }; + let next_answer = do_twice(f, &value); + + println!("The next answer is: {}", next_answer); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-return-type-mismatch-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-return-type-mismatch-abort/Cargo.toml new file mode 100644 index 000000000000..392975f87227 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-return-type-mismatch-abort/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "indirect-return-type-mismatch-abort" +version = "0.1.0" +edition = "2021" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-return-type-mismatch-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-return-type-mismatch-abort/src/main.rs new file mode 100644 index 000000000000..e21e71ee71a0 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-return-type-mismatch-abort/src/main.rs @@ -0,0 +1,30 @@ +// This example demonstrates redirecting control flow using an indirect +// branch/call to a function with a different return type than the return type +// expected at the call/branch site. + +use std::mem; + +fn add_one(x: i32) -> i32 { + x + 1 +} + +fn add_two(x: i32) -> i64 { + i64::from(x + 2) +} + +fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { + f(arg) + f(arg) +} + +fn main() { + let answer = do_twice(add_one, 5); + + println!("The answer is: {}", answer); + + println!("With CFI enabled, you should not see the next answer"); + let f: fn(i32) -> i32 = + unsafe { mem::transmute::<*const u8, fn(i32) -> i32>(add_two as *const u8) }; + let next_answer = do_twice(f, 5); + + println!("The next answer is: {}", next_answer); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-mismatch-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-mismatch-abort/Cargo.toml new file mode 100644 index 000000000000..e167b2acfbcf --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-mismatch-abort/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "indirect-type-mismatch-abort" +version = "0.1.0" +edition = "2021" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-mismatch-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-mismatch-abort/src/main.rs new file mode 100644 index 000000000000..0f40aa316152 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-mismatch-abort/src/main.rs @@ -0,0 +1,30 @@ +// This example demonstrates redirecting control flow using an indirect +// branch/call to a function with different return and parameter types than the +// return type expected and arguments intended/passed at the call/branch site. + +use std::mem; + +fn add_one(x: i32) -> i32 { + x + 1 +} + +fn add_two(x: i64) -> i64 { + x + 2 +} + +fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { + f(arg) + f(arg) +} + +fn main() { + let answer = do_twice(add_one, 5); + + println!("The answer is: {}", answer); + + println!("With CFI enabled, you should not see the next answer"); + let f: fn(i32) -> i32 = + unsafe { mem::transmute::<*const u8, fn(i32) -> i32>(add_two as *const u8) }; + let next_answer = do_twice(f, 5); + + println!("The next answer is: {}", next_answer); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-qualifier-mismatch-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-qualifier-mismatch-abort/Cargo.toml new file mode 100644 index 000000000000..1f4b3d4b0c9b --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-qualifier-mismatch-abort/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "indirect-type-qualifier-mismatch-abort" +version = "0.1.0" +edition = "2021" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-qualifier-mismatch-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-qualifier-mismatch-abort/src/main.rs new file mode 100644 index 000000000000..e37d883addf1 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-qualifier-mismatch-abort/src/main.rs @@ -0,0 +1,31 @@ +// This example demonstrates redirecting control flow using an indirect +// branch/call to a function with parameter type qualifiers than the argument +// type qualifiers intended/passed at the call/branch site. + +use std::mem; + +fn add_one(x: &i32) -> i32 { + *x + 1 +} + +fn add_two(x: &mut i32) -> i32 { + *x + 2 +} + +fn do_twice(f: fn(&i32) -> i32, arg: &i32) -> i32 { + f(arg) + f(arg) +} + +fn main() { + let value: i32 = 5; + let answer = do_twice(add_one, &value); + + println!("The answer is: {}", answer); + + println!("With CFI enabled, you should not see the next answer"); + let f: fn(&i32) -> i32 = + unsafe { mem::transmute::<*const u8, fn(&i32) -> i32>(add_two as *const u8) }; + let next_answer = do_twice(f, &value); + + println!("The next answer is: {}", next_answer); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/invalid-branch-target-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/invalid-branch-target-abort/Cargo.toml new file mode 100644 index 000000000000..e6af74f961e0 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/invalid-branch-target-abort/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "invalid-branch-target-abort" +version = "0.1.0" +edition = "2021" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/invalid-branch-target-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/invalid-branch-target-abort/src/main.rs new file mode 100644 index 000000000000..0b047bf5fefb --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/invalid-branch-target-abort/src/main.rs @@ -0,0 +1,50 @@ +// This example demonstrates redirecting control flow using an indirect +// branch/call to an invalid destination (i.e., within the body of the +// function). + +use std::mem; + +fn add_one(x: i32) -> i32 { + x + 1 +} + +#[unsafe(naked)] +pub extern "C" fn add_two(_x: i32) -> ! { + // x + 2 preceded by a landing pad/nop block + core::arch::naked_asm!( + r#" + nop + nop + nop + nop + nop + nop + nop + nop + nop + lea eax, [rdi + 2] + ret + "#, + ); +} + +fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { + f(arg) + f(arg) +} + +fn main() { + let answer = do_twice(add_one, 5); + + println!("The answer is: {}", answer); + + println!("With CFI enabled, you should not see the next answer"); + let f: fn(i32) -> i32 = unsafe { + // Offset 0 is a valid branch/call destination (i.e., the function entry + // point), but offsets 1-8 within the landing pad/nop block are invalid + // branch/call destinations (i.e., within the body of the function). + mem::transmute::<*const u8, fn(i32) -> i32>((add_two as *const u8).offset(5)) + }; + let next_answer = do_twice(f, 5); + + println!("The next answer is: {}", next_answer); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/rmake.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/rmake.rs new file mode 100644 index 000000000000..c85d01a71105 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/rmake.rs @@ -0,0 +1,151 @@ +//! Verifies that the examples in build and run with +//!`-Zbuild-std` to prevent regressions such as [rust-lang/rust#142284]. + +//@ needs-sanitizer-cfi +//@ needs-force-clang-based-tests +//@ needs-rust-lld +//@ needs-target-std +//@ ignore-cross-compile +//@ only-x86_64-unknown-linux-gnu + +#![deny(warnings)] + +use std::path::Path; + +use run_make_support::external_deps::rustc::sysroot as rustc_sysroot; +use run_make_support::run::cmd; +use run_make_support::{bin_name, cargo, path, target}; + +fn clang_path() -> String { + if let Ok(d) = std::env::var("LLVM_BIN_DIR") { + let clang = Path::new(d.trim_end_matches('/')).join("clang"); + if clang.exists() { + return clang.display().to_string(); + } + } + if let Ok(clang) = std::env::var("CLANG") { + let clang = Path::new(clang.trim_end_matches('/')); + if clang.exists() { + return clang.display().to_string(); + } + } + "clang".to_string() +} + +fn fuse_ld_path() -> String { + if let Ok(d) = std::env::var("LLVM_BIN_DIR") { + let llvm_bin_dir = Path::new(d.trim_end_matches('/')); + let gcc_ld_lld = llvm_bin_dir.join("gcc-ld").join("ld.lld"); + if gcc_ld_lld.exists() { + return gcc_ld_lld.display().to_string(); + } + let ld_lld = llvm_bin_dir.join("ld.lld"); + if ld_lld.exists() { + return ld_lld.display().to_string(); + } + } + if let Ok(clang) = std::env::var("CLANG") { + let clang = Path::new(clang.trim_end_matches('/')); + if let Some(clang_dir) = clang.parent() { + let gcc_ld_lld = clang_dir.join("gcc-ld").join("ld.lld"); + if gcc_ld_lld.exists() { + return gcc_ld_lld.display().to_string(); + } + let ld_lld = clang_dir.join("ld.lld"); + if ld_lld.exists() { + return ld_lld.display().to_string(); + } + } + } + let target_bin_dir = rustc_sysroot().join("lib").join("rustlib").join(target()).join("bin"); + let gcc_ld_lld = target_bin_dir.join("gcc-ld").join("ld.lld"); + if gcc_ld_lld.exists() { + return gcc_ld_lld.display().to_string(); + } + "ld.lld".to_string() +} + +fn run_and_expect_cfi_abort(target_dir: &Path, target: &str, binary: &str) { + let exe = target_dir.join(target).join("release").join(bin_name(binary)); + let output = cmd(&exe).run_fail(); + output + .assert_stdout_contains("With CFI enabled, you should not see the next answer") + .assert_stdout_not_contains("The next answer is:"); +} + +fn run_and_expect_cfi_not_abort(target_dir: &Path, target: &str, binary: &str) { + let exe = target_dir.join(target).join("release").join(bin_name(binary)); + let output = cmd(&exe).run(); + output.assert_stdout_contains("Hello from C!"); +} + +fn main() { + let clang = clang_path(); + let fuse_ld = fuse_ld_path(); + let tgt = target(); + let target_dir = path("target"); + let lib = std::env::var("LIB").unwrap_or_default(); + + let prior_rustflags = std::env::var("RUSTFLAGS").unwrap_or_default(); + + let rustflags = format!( + "{prior_rustflags} -Clinker-plugin-lto -Clinker={clang} \ + -Clink-arg=-fuse-ld={fuse_ld} -Zsanitizer=cfi \ + -Ctarget-feature=-crt-static" + ) + .trim() + .to_owned(); + + let rustflags_with_integer_normalization = + format!("{rustflags} -Zsanitizer-cfi-normalize-integers").trim().to_owned(); + + let run = |manifest: &Path, rustflags: &str, workspace: bool| { + let mut c = cargo(); + c.arg("build") + .arg("--manifest-path") + .arg(manifest) + .arg("--release") + .arg("-Zbuild-std") + .arg("--target") + .arg(&tgt); + if workspace { + c.arg("--workspace"); + } + c.env("RUSTFLAGS", rustflags) + .env("CC", &clang) + .env("CARGO_TARGET_DIR", &target_dir) + .env("RUSTC_BOOTSTRAP", "1") + .env("LIB", &lib) + .run(); + }; + + run(Path::new("Cargo.toml"), &rustflags, true); + for bin in [ + "invalid-branch-target-abort", + "indirect-arity-mismatch-abort", + "indirect-pointee-type-mismatch-abort", + "indirect-return-type-mismatch-abort", + "indirect-type-qualifier-mismatch-abort", + "indirect-type-mismatch-abort", + "cross-lang-cfi-types-crate-abort", + ] { + run_and_expect_cfi_abort(&target_dir, &tgt, bin); + } + for bin in ["cross-lang-cfi-types-crate-not-abort"] { + run_and_expect_cfi_not_abort(&target_dir, &tgt, bin); + } + + run( + Path::new("cross-lang-integer-normalization-abort/Cargo.toml"), + &rustflags_with_integer_normalization, + false, + ); + run_and_expect_cfi_abort(&target_dir, &tgt, "cross-lang-integer-normalization-abort"); + + run( + Path::new("cross-lang-integer-normalization-not-abort/Cargo.toml"), + &rustflags_with_integer_normalization, + false, + ); + run_and_expect_cfi_not_abort(&target_dir, &tgt, "cross-lang-integer-normalization-not-abort"); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/shared_build_rs.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/shared_build_rs.rs new file mode 100644 index 000000000000..2c2e0fd4ffb8 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/shared_build_rs.rs @@ -0,0 +1,73 @@ +use std::env; +use std::path::{Path, PathBuf}; +use std::process::Command; + +fn clang_path() -> PathBuf { + if let Ok(d) = env::var("LLVM_BIN_DIR") { + let clang = Path::new(d.trim_end_matches('/')).join("clang"); + if clang.exists() { + return clang; + } + } + if let Ok(clang) = env::var("CLANG") { + let clang = Path::new(clang.trim_end_matches('/')); + if clang.exists() { + return clang.to_path_buf(); + } + } + PathBuf::from("clang") +} + +fn llvm_ar_path() -> PathBuf { + if let Ok(d) = env::var("LLVM_BIN_DIR") { + let llvm_ar = Path::new(d.trim_end_matches('/')).join("llvm-ar"); + if llvm_ar.exists() { + return llvm_ar; + } + } + if let Ok(clang) = env::var("CLANG") { + let clang = Path::new(&clang); + if let Some(clang_dir) = clang.parent() { + let llvm_ar = clang_dir.join("llvm-ar"); + if llvm_ar.exists() { + return llvm_ar; + } + } + } + PathBuf::from("llvm-ar") +} + +fn build_foo_static_lib(extra_flags: &[&str]) { + let out_dir = env::var("OUT_DIR").expect("OUT_DIR"); + let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR"); + let c_src = Path::new(&manifest_dir).join("src/foo.c"); + let bc_path = Path::new(&out_dir).join("foo.bc"); + let a_path = Path::new(&out_dir).join("libfoo.a"); + + let clang = clang_path(); + let llvm_ar = llvm_ar_path(); + + let mut clang_args = vec!["-Wall", "-flto=thin", "-fsanitize=cfi"]; + clang_args.extend_from_slice(extra_flags); + clang_args.extend_from_slice(&["-fvisibility=hidden", "-c", "-emit-llvm", "-o"]); + + let st = Command::new(&clang) + .args(&clang_args) + .arg(&bc_path) + .arg(&c_src) + .status() + .unwrap_or_else(|e| panic!("failed to spawn `{}`: {e}", clang.display())); + assert!(st.success(), "`{}` failed with {st}", clang.display()); + + let st = Command::new(&llvm_ar) + .args(["rcs", a_path.to_str().unwrap(), bc_path.to_str().unwrap()]) + .status() + .unwrap_or_else(|e| panic!("failed to spawn `{}`: {e}", llvm_ar.display())); + assert!(st.success(), "`{}` failed with {st}", llvm_ar.display()); + + println!("cargo:rustc-link-search=native={out_dir}"); + println!("cargo:rustc-link-lib=static=foo"); + println!("cargo:rerun-if-changed={}", c_src.display()); + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=../shared_build_rs.rs"); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/vendor/cfi-types/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/vendor/cfi-types/Cargo.toml new file mode 100644 index 000000000000..180bfea79fa0 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/vendor/cfi-types/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "cfi-types" +version = "0.0.8" +edition = "2021" + +description = "CFI types for cross-language LLVM CFI support" +homepage = "https://github.com/rcvalle/rust-crate-cfi-types" +license = "MIT OR Apache-2.0" +repository = "https://github.com/rcvalle/rust-crate-cfi-types" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/vendor/cfi-types/src/lib.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/vendor/cfi-types/src/lib.rs new file mode 100644 index 000000000000..436097c32fd8 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/vendor/cfi-types/src/lib.rs @@ -0,0 +1,96 @@ +//! CFI types for cross-language LLVM CFI support. +//! +//! The cfi_types crate provides a new set of C types as user-defined types +//! using the cfi_encoding attribute and repr(transparent) to be used for +//! cross-language LLVM CFI support. This new set of C types allows the Rust +//! compiler to identify and correctly encode C types in extern "C" function +//! types indirectly called across the FFI boundary when CFI is enabled. +//! +//! The use of these types are optional and are recommended for when enforcement +//! and explicitness of types used across the FFI boundary and no loss of +//! granularity for cross-language LLVM CFI are preferred. +//! +//! Alternatively, the `-Zsanitizer-cfi-normalize-integers` option may be used +//! with the Clang `-fsanitize-cfi-icall-experimental-normalize-integers` option +//! for cross-language LLVM CFI support. + +#![feature(cfg_sanitizer_cfi)] +#![feature(cfi_encoding)] +#![allow(non_camel_case_types)] + +/// CFI type equivalent to Rust's core::ffi::c_char type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "c")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_char(pub core::ffi::c_char); + +/// CFI type equivalent to Rust's core::ffi::c_int type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "i")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_int(pub core::ffi::c_int); + +/// CFI type equivalent to Rust's core::ffi::c_long type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "l")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_long(pub core::ffi::c_long); + +/// CFI type equivalent to Rust's core::ffi::c_longlong type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "x")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_longlong(pub core::ffi::c_longlong); + +/// CFI type equivalent to Rust's core::ffi::c_schar type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "a")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_schar(pub core::ffi::c_schar); + +/// CFI type equivalent to Rust's core::ffi::c_short type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "s")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_short(pub core::ffi::c_short); + +/// CFI type equivalent to Rust's core::ffi::c_uchar type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "h")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_uchar(pub core::ffi::c_uchar); + +/// CFI type equivalent to Rust's core::ffi::c_uint type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "j")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_uint(pub core::ffi::c_uint); + +/// CFI type equivalent to Rust's core::ffi::c_ulong type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "m")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_ulong(pub core::ffi::c_ulong); + +/// CFI type equivalent to Rust's core::ffi::c_ulonglong type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "y")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_ulonglong(pub core::ffi::c_ulonglong); + +/// CFI type equivalent to Rust's core::ffi::c_ushort type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "t")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_ushort(pub core::ffi::c_ushort); From aadf600574a3b72a3ba53723f8a6f498557b297f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 6 May 2026 14:41:52 -0700 Subject: [PATCH 30/41] Document wasi-sdk minimum versions for WASI targets This commit updates the documentation for WASI targets in the rustc book to mention a minimum version of wasi-sdk that's required. Closes 155971 --- .../src/platform-support/wasm32-wasip1.md | 19 +++++++++++---- .../src/platform-support/wasm32-wasip2.md | 23 ++++++++----------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/doc/rustc/src/platform-support/wasm32-wasip1.md b/src/doc/rustc/src/platform-support/wasm32-wasip1.md index eb74edda22de..16499659291a 100644 --- a/src/doc/rustc/src/platform-support/wasm32-wasip1.md +++ b/src/doc/rustc/src/platform-support/wasm32-wasip1.md @@ -70,17 +70,16 @@ be used instead. [`wasi-libc`]: https://github.com/WebAssembly/wasi-libc -## Building the target +## Building the target in rustc -To build this target first acquire a copy of -[`wasi-sdk`](https://github.com/WebAssembly/wasi-sdk/). At this time version 22 -is the minimum needed. +To build this target first acquire a copy of [`wasi-sdk`]. At this time version +33 is the minimum needed. Next configure the `WASI_SDK_PATH` environment variable to point to where this is installed. For example: ```text -export WASI_SDK_PATH=/path/to/wasi-sdk-22.0 +export WASI_SDK_PATH=/path/to/wasi-sdk-33.0 ``` Next be sure to enable LLD when building Rust from source as LLVM's `wasm-ld` @@ -103,6 +102,16 @@ Rust programs can be built for that target: rustc --target wasm32-wasip1 your-code.rs ``` +The `wasm32-wasip1` toolchain comes with a self-contained sysroot meaning that +no external compiler is required when building for this target. Users which +build a `staticlib`, however, or use an external `-Clinker`, are recommended to +use [`wasi-sdk`]. The minimum version required of [`wasi-sdk`] will change over +time as it's updated in Rust and Rust relies on newer features that [`wasi-sdk`] +has. See the documentation above about building the target in rustc for the +minimum known working version. + +[`wasi-sdk`]: https://github.com/WebAssembly/wasi-sdk + ## Cross-compilation This target can be cross-compiled from any hosts. diff --git a/src/doc/rustc/src/platform-support/wasm32-wasip2.md b/src/doc/rustc/src/platform-support/wasm32-wasip2.md index 861083ad4a61..4b7e84d71576 100644 --- a/src/doc/rustc/src/platform-support/wasm32-wasip2.md +++ b/src/doc/rustc/src/platform-support/wasm32-wasip2.md @@ -27,23 +27,20 @@ are required to support components since this target outputs a component as opposed to a core wasm module. As of the time of this writing Wasmtime 17 and above is able to run this target natively with no extra flags. -## Building the target +## Building the target in rustc -To build this target first acquire a copy of -[`wasi-sdk`](https://github.com/WebAssembly/wasi-sdk/). At this time version 22 -is the minimum needed. +See the documentation for the [building the `wasm32-wasip1` target in +rustc](./wasm32-wasip1.md#building-the-target-in-rustc) for more information. The tl;dr; +is that [`wasi-sdk`] is required, and the `wasm32-wasip1` target documents the +minimum version required. -Next configure the `WASI_SDK_PATH` environment variable to point to where this -is installed. For example: +[`wasi-sdk`]: https://github.com/WebAssembly/wasi-sdk -```text -export WASI_SDK_PATH=/path/to/wasi-sdk-22.0 -``` +## Building Rust programs -Next be sure to enable LLD when building Rust from source as LLVM's `wasm-ld` -driver for LLD is required when linking WebAssembly code together. Rust's build -system will automatically pick up any necessary binaries and programs from -`WASI_SDK_PATH`. +For more information see the documentation [`wasm32-wasip1` +target](./wasm32-wasip1.md#building-rust-programs). Replace `wasm32-wasip1` +target strings with `wasm32-wasip2`, however. ## Testing From 8aafed8a1773c5739f6413ce54a243c65c13c723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Tue, 5 May 2026 17:10:34 +0200 Subject: [PATCH 31/41] Add `sync` option to `-Z threads` to force synchronization on one thread --- compiler/rustc_codegen_ssa/src/base.rs | 4 +-- compiler/rustc_interface/src/interface.rs | 6 ++-- compiler/rustc_interface/src/tests.rs | 2 +- compiler/rustc_metadata/src/rmeta/encoder.rs | 2 +- compiler/rustc_middle/src/dep_graph/graph.rs | 2 +- compiler/rustc_query_impl/src/execution.rs | 2 +- compiler/rustc_session/src/config.rs | 6 +--- compiler/rustc_session/src/options.rs | 33 ++++++++++---------- compiler/rustc_session/src/session.rs | 8 +++-- 9 files changed, 33 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 4e2884c8cb63..ad3e636083e0 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -780,14 +780,14 @@ pub fn codegen_crate( // This likely is a temporary measure. Once we don't have to support the // non-parallel compiler anymore, we can compile CGUs end-to-end in // parallel and get rid of the complicated scheduling logic. - let mut pre_compiled_cgus = if tcx.sess.threads() > 1 { + let mut pre_compiled_cgus = if let Some(threads) = tcx.sess.threads() { tcx.sess.time("compile_first_CGU_batch", || { // Try to find one CGU to compile per thread. let cgus: Vec<_> = cgu_reuse .iter() .enumerate() .filter(|&(_, reuse)| reuse == &CguReuse::No) - .take(tcx.sess.threads()) + .take(threads) .collect(); // Compile the found CGUs in parallel. diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index ab8bc1c7f1b3..875ed4ae5d30 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -385,7 +385,9 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se trace!("run_compiler"); // Set parallel mode before thread pool creation, which will create `Lock`s. - rustc_data_structures::sync::set_dyn_thread_safe_mode(config.opts.unstable_opts.threads > 1); + rustc_data_structures::sync::set_dyn_thread_safe_mode( + config.opts.unstable_opts.threads.is_some(), + ); // Check jobserver before run_in_thread_pool_with_globals, which call jobserver::acquire_thread let early_dcx = EarlyDiagCtxt::new(config.opts.error_format); @@ -407,7 +409,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se util::run_in_thread_pool_with_globals( &early_dcx, config.opts.edition, - config.opts.unstable_opts.threads, + config.opts.unstable_opts.threads.unwrap_or(1), &config.extra_symbols, SourceMapInputs { file_loader, path_mapping, hash_kind, checksum_hash_kind }, |current_gcx, jobserver_proxy| { diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index a7e0dd2ac39c..83930bf1249a 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -734,7 +734,7 @@ macro_rules! untracked { untracked!(span_debug, true); untracked!(span_free_formats, true); untracked!(temps_dir, Some(String::from("abc"))); - untracked!(threads, 99); + untracked!(threads, Some(99)); untracked!(time_llvm_passes, true); untracked!(time_passes, true); untracked!(time_passes_format, TimePassesFormat::Json); diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 729a0dda7cf3..a4103f1757f6 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -2465,7 +2465,7 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { return; }; - if tcx.sess.threads() != 1 { + if tcx.sess.threads().is_some() { // Prefetch some queries used by metadata encoding. // This is not necessary for correctness, but is only done for performance reasons. // It can be removed if it turns out to cause trouble or be detrimental to performance. diff --git a/compiler/rustc_middle/src/dep_graph/graph.rs b/compiler/rustc_middle/src/dep_graph/graph.rs index a219809541cc..cc007d35c219 100644 --- a/compiler/rustc_middle/src/dep_graph/graph.rs +++ b/compiler/rustc_middle/src/dep_graph/graph.rs @@ -629,7 +629,7 @@ fn assert_dep_node_not_yet_allocated_in_current_session( let ok = match color { DepNodeColor::Unknown => true, DepNodeColor::Red => false, - DepNodeColor::Green(..) => sess.threads() > 1, // Other threads may mark this green + DepNodeColor::Green(..) => sess.threads().is_some(), // Other threads may mark this green }; if !ok { panic!("{}", msg()) diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index ed9ad8c7a0a6..b614bc14b453 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -294,7 +294,7 @@ fn try_execute_query<'tcx, C: QueryCache, const INCR: bool>( // re-executing the query since `try_start` only checks that the query is not currently // executing, but another thread may have already completed the query and stores it result // in the query cache. - if tcx.sess.threads() > 1 { + if tcx.sess.threads().is_some() { if let Some((value, index)) = query.cache.lookup(&key) { tcx.prof.query_cache_hit(index.into()); return (value, Some(index)); diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index f8da18632a99..40cf5b6e1079 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -2494,11 +2494,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M cg.codegen_units, ); - if unstable_opts.threads == 0 { - early_dcx.early_fatal("value for threads must be a positive non-zero integer"); - } - - if unstable_opts.threads == parse::MAX_THREADS_CAP { + if unstable_opts.threads == Some(parse::MAX_THREADS_CAP) { early_dcx.early_warn(format!("number of threads was capped at {}", parse::MAX_THREADS_CAP)); } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index f7a3387e5238..c484ce58b0ca 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -759,7 +759,7 @@ mod desc { pub(crate) const parse_number: &str = "a number"; pub(crate) const parse_opt_number: &str = parse_number; pub(crate) const parse_frame_pointer: &str = "one of `true`/`yes`/`on`, `false`/`no`/`off`, or (with -Zunstable-options) `non-leaf` or `always`"; - pub(crate) const parse_threads: &str = parse_number; + pub(crate) const parse_threads: &str = "a number or `sync`"; pub(crate) const parse_time_passes_format: &str = "`text` (default) or `json`"; pub(crate) const parse_passes: &str = "a space-separated list of passes, or `all`"; pub(crate) const parse_panic_strategy: &str = "either `unwind`, `abort`, or `immediate-abort`"; @@ -1067,22 +1067,23 @@ pub(crate) fn parse_opt_comma_list(slot: &mut Option>, v: Option<&st } } - pub(crate) fn parse_threads(slot: &mut usize, v: Option<&str>) -> bool { - let ret = match v.and_then(|s| s.parse().ok()) { - Some(0) => { - *slot = std::thread::available_parallelism().map_or(1, NonZero::::get); - true - } - Some(i) => { - *slot = i; - true - } - None => false, + pub(crate) fn parse_threads(slot: &mut Option, v: Option<&str>) -> bool { + let Some(s) = v else { return false }; + if s == "sync" { + // Enable synchronization despite only using one thread. + *slot = Some(1); + return true; + } + let n = match s.parse().ok() { + Some(0) => std::thread::available_parallelism().map_or(1, NonZero::::get), + Some(i) => i, + None => return false, }; // We want to cap the number of threads here to avoid large numbers like 999999 and compiler panics. // This solution was suggested here https://github.com/rust-lang/rust/issues/117638#issuecomment-1800925067 - *slot = slot.clone().min(MAX_THREADS_CAP); - ret + let n = n.min(MAX_THREADS_CAP); + *slot = (n > 1).then_some(n); // Enable synchronization if we're using more than one thread. + true } /// Use this for any numeric option that has a static default. @@ -2670,12 +2671,12 @@ pub(crate) fn parse_assert_incr_state( #[rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field")] thinlto: Option = (None, parse_opt_bool, [TRACKED], "enable ThinLTO when possible"), - /// We default to 1 here since we want to behave like + /// We default to None here since we want to behave like /// a sequential compiler for now. This'll likely be adjusted /// in the future. Note that -Zthreads=0 is the way to get /// the num_cpus behavior. #[rustc_lint_opt_deny_field_access("use `Session::threads` instead of this field")] - threads: usize = (1, parse_threads, [UNTRACKED], + threads: Option = (None, parse_threads, [UNTRACKED], "use a thread pool with N threads"), time_llvm_passes: bool = (false, parse_bool, [UNTRACKED], "measure time of each LLVM pass (default: no)"), diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 3b2fc53381a9..c71f897fba31 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -813,10 +813,12 @@ pub fn must_emit_unwind_tables(&self) -> bool { .unwrap_or(self.panic_strategy().unwinds() || self.target.default_uwtable) } - /// Returns the number of query threads that should be used for this - /// compilation + /// Returns the number of threads used for the thread pool. + /// + /// `None` means thread pool is not used and synchronization is disabled. + /// `Some(n)` means synchronization is enabled with `n` worker threads. #[inline] - pub fn threads(&self) -> usize { + pub fn threads(&self) -> Option { self.opts.unstable_opts.threads } From 769f2f16fe2eabee53bd13ca65eeb37b4019b59c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 7 May 2026 09:27:31 +1000 Subject: [PATCH 32/41] Remove unused `ToStableHashKey` impls. There are quite a few. --- .../src/stable_hasher.rs | 16 ------ compiler/rustc_hir/src/def.rs | 10 ---- compiler/rustc_hir/src/stable_hash_impls.rs | 56 +------------------ compiler/rustc_hir_id/src/lib.rs | 19 ------- compiler/rustc_lint_defs/src/lib.rs | 31 +--------- .../rustc_middle/src/dep_graph/dep_node.rs | 10 +--- compiler/rustc_middle/src/ty/impls_ty.rs | 26 +-------- compiler/rustc_session/src/config.rs | 12 +--- compiler/rustc_span/src/def_id.rs | 18 ------ compiler/rustc_span/src/symbol.rs | 10 +--- compiler/rustc_type_ir/src/fast_reject.rs | 18 ------ 11 files changed, 9 insertions(+), 217 deletions(-) diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index 17effa07e3bc..931317f80110 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -459,22 +459,6 @@ impl StableOrd for String { const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); } -impl ToStableHashKey for String { - type KeyType = String; - #[inline] - fn to_stable_hash_key(&self, _: &mut Hcx) -> Self::KeyType { - self.clone() - } -} - -impl ToStableHashKey for (T1, T2) { - type KeyType = (T1::KeyType, T2::KeyType); - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> Self::KeyType { - (self.0.to_stable_hash_key(hcx), self.1.to_stable_hash_key(hcx)) - } -} - impl StableHash for bool { #[inline] fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index f268192233c0..917b685958da 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -4,7 +4,6 @@ use rustc_ast as ast; use rustc_ast::NodeId; -use rustc_data_structures::stable_hasher::ToStableHashKey; use rustc_data_structures::unord::UnordMap; use rustc_error_messages::{DiagArgValue, IntoDiagArg}; use rustc_macros::{Decodable, Encodable, StableHash}; @@ -712,15 +711,6 @@ fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { } } -impl ToStableHashKey for Namespace { - type KeyType = Namespace; - - #[inline] - fn to_stable_hash_key(&self, _: &mut Hcx) -> Namespace { - *self - } -} - /// Just a helper ‒ separate structure for each namespace. #[derive(Copy, Clone, Default, Debug, StableHash)] pub struct PerNS { diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs index 606a47bc4a22..77a9f3617f98 100644 --- a/compiler/rustc_hir/src/stable_hash_impls.rs +++ b/compiler/rustc_hir/src/stable_hash_impls.rs @@ -1,59 +1,7 @@ -use rustc_data_structures::stable_hasher::{ - StableHash, StableHashCtxt, StableHasher, ToStableHashKey, -}; -use rustc_span::def_id::DefPathHash; +use rustc_data_structures::stable_hasher::{StableHash, StableHashCtxt, StableHasher}; use crate::HashIgnoredAttrId; -use crate::hir::{ - AttributeMap, BodyId, ForeignItemId, ImplItemId, ItemId, OwnerNodes, TraitItemId, -}; -use crate::hir_id::ItemLocalId; - -impl ToStableHashKey for BodyId { - type KeyType = (DefPathHash, ItemLocalId); - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> (DefPathHash, ItemLocalId) { - let BodyId { hir_id } = *self; - hir_id.to_stable_hash_key(hcx) - } -} - -impl ToStableHashKey for ItemId { - type KeyType = DefPathHash; - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> DefPathHash { - self.owner_id.def_id.to_stable_hash_key(hcx) - } -} - -impl ToStableHashKey for TraitItemId { - type KeyType = DefPathHash; - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> DefPathHash { - self.owner_id.def_id.to_stable_hash_key(hcx) - } -} - -impl ToStableHashKey for ImplItemId { - type KeyType = DefPathHash; - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> DefPathHash { - self.owner_id.def_id.to_stable_hash_key(hcx) - } -} - -impl ToStableHashKey for ForeignItemId { - type KeyType = DefPathHash; - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> DefPathHash { - self.owner_id.def_id.to_stable_hash_key(hcx) - } -} +use crate::hir::{AttributeMap, OwnerNodes}; // The following implementations of StableHash for `ItemId`, `TraitItemId`, and // `ImplItemId` deserve special attention. Normally we do not hash `NodeId`s within diff --git a/compiler/rustc_hir_id/src/lib.rs b/compiler/rustc_hir_id/src/lib.rs index 3bbd46f4844e..752ba4e8de39 100644 --- a/compiler/rustc_hir_id/src/lib.rs +++ b/compiler/rustc_hir_id/src/lib.rs @@ -176,22 +176,3 @@ impl StableOrd for ItemLocalId { HirId { owner: OwnerId { def_id: CRATE_DEF_ID }, local_id: ItemLocalId::ZERO }; pub const CRATE_OWNER_ID: OwnerId = OwnerId { def_id: CRATE_DEF_ID }; - -impl ToStableHashKey for HirId { - type KeyType = (DefPathHash, ItemLocalId); - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> (DefPathHash, ItemLocalId) { - let def_path_hash = self.owner.def_id.to_stable_hash_key(hcx); - (def_path_hash, self.local_id) - } -} - -impl ToStableHashKey for ItemLocalId { - type KeyType = ItemLocalId; - - #[inline] - fn to_stable_hash_key(&self, _: &mut Hcx) -> ItemLocalId { - *self - } -} diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 517f2954c546..025f36a34f00 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -3,12 +3,11 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::stable_hasher::{ - StableCompare, StableHash, StableHashCtxt, StableHasher, ToStableHashKey, + StableCompare, StableHash, StableHashCtxt, StableHasher, }; use rustc_error_messages::{DiagArgValue, IntoDiagArg}; -use rustc_hir_id::{HirId, ItemLocalId}; +use rustc_hir_id::HirId; use rustc_macros::{Decodable, Encodable, StableHash}; -use rustc_span::def_id::DefPathHash; pub use rustc_span::edition::Edition; use rustc_span::{AttrId, Ident, Symbol, sym}; use serde::{Deserialize, Serialize}; @@ -154,23 +153,6 @@ fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHas } } -impl ToStableHashKey for LintExpectationId { - type KeyType = (DefPathHash, ItemLocalId, u16, u16); - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> Self::KeyType { - match self { - LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => { - let (def_path_hash, lint_idx) = hir_id.to_stable_hash_key(hcx); - (def_path_hash, lint_idx, *attr_index, *lint_index) - } - _ => { - unreachable!("StableHash should only be called for a filled `LintExpectationId`") - } - } - } -} - /// Setting for how to handle a lint. /// /// See: @@ -623,15 +605,6 @@ fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHas } } -impl ToStableHashKey for LintId { - type KeyType = &'static str; - - #[inline] - fn to_stable_hash_key(&self, _: &mut Hcx) -> &'static str { - self.lint_name_raw() - } -} - impl StableCompare for LintId { const CAN_USE_UNSTABLE_SORT: bool = true; diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index 38ceac7ae5bd..60a4b66d3918 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -51,7 +51,7 @@ use std::hash::Hash; use rustc_data_structures::fingerprint::{Fingerprint, PackedFingerprint}; -use rustc_data_structures::stable_hasher::{StableHasher, StableOrd, ToStableHashKey}; +use rustc_data_structures::stable_hasher::{StableHasher, StableOrd}; use rustc_hir::def_id::DefId; use rustc_hir::definitions::DefPathHash; use rustc_macros::{Decodable, Encodable, StableHash}; @@ -231,13 +231,7 @@ pub fn from_cgu_name(cgu_name: &str) -> WorkProductId { WorkProductId { hash: hasher.finish() } } } -impl ToStableHashKey for WorkProductId { - type KeyType = Fingerprint; - #[inline] - fn to_stable_hash_key(&self, _: &mut Hcx) -> Self::KeyType { - self.hash - } -} + impl StableOrd for WorkProductId { // Fingerprint can use unstable (just a tuple of `u64`s), so WorkProductId can as well const CAN_USE_UNSTABLE_SORT: bool = true; diff --git a/compiler/rustc_middle/src/ty/impls_ty.rs b/compiler/rustc_middle/src/ty/impls_ty.rs index 729a5049d633..1f22e0af9c00 100644 --- a/compiler/rustc_middle/src/ty/impls_ty.rs +++ b/compiler/rustc_middle/src/ty/impls_ty.rs @@ -7,11 +7,10 @@ use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{ - HashingControls, StableHash, StableHashCtxt, StableHasher, ToStableHashKey, + HashingControls, StableHash, StableHashCtxt, StableHasher, }; use tracing::trace; -use crate::middle::region; use crate::{mir, ty}; impl<'tcx, H, T> StableHash for &'tcx ty::list::RawList @@ -45,20 +44,6 @@ fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHas } } -impl<'tcx, H, T> ToStableHashKey for &'tcx ty::list::RawList -where - T: StableHash, -{ - type KeyType = Fingerprint; - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> Fingerprint { - let mut hasher = StableHasher::new(); - self.stable_hash(hcx, &mut hasher); - hasher.finish() - } -} - impl<'tcx> StableHash for ty::GenericArg<'tcx> { fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { self.kind().stable_hash(hcx, hasher); @@ -81,12 +66,3 @@ fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHas self.into_parts().stable_hash(hcx, hasher); } } - -impl ToStableHashKey for region::Scope { - type KeyType = region::Scope; - - #[inline] - fn to_stable_hash_key(&self, _: &mut Hcx) -> region::Scope { - *self - } -} diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index f8da18632a99..913cfc893e88 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -14,7 +14,7 @@ use externs::{ExternOpt, split_extern_opt}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_data_structures::stable_hasher::{StableHasher, StableOrd, ToStableHashKey}; +use rustc_data_structures::stable_hasher::{StableHasher, StableOrd}; use rustc_errors::emitter::HumanReadableErrorType; use rustc_errors::{ColorConfig, DiagCtxtFlags}; use rustc_feature::UnstableFeatures; @@ -628,7 +628,6 @@ pub enum OutputType { )* } - impl StableOrd for OutputType { const CAN_USE_UNSTABLE_SORT: bool = true; @@ -636,15 +635,6 @@ impl StableOrd for OutputType { const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); } - impl ToStableHashKey for OutputType { - type KeyType = Self; - - fn to_stable_hash_key(&self, _: &mut Hcx) -> Self::KeyType { - *self - } - } - - impl OutputType { pub fn iter_all() -> impl Iterator { static ALL_VARIANTS: &[OutputType] = &[ diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs index d07c7fcfd468..6ce3d6b309f0 100644 --- a/compiler/rustc_span/src/def_id.rs +++ b/compiler/rustc_span/src/def_id.rs @@ -465,24 +465,6 @@ fn to_stable_hash_key(&self, hcx: &mut Hcx) -> DefPathHash } } -impl ToStableHashKey for CrateNum { - type KeyType = DefPathHash; - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> DefPathHash { - self.as_def_id().to_stable_hash_key(hcx) - } -} - -impl ToStableHashKey for DefPathHash { - type KeyType = DefPathHash; - - #[inline] - fn to_stable_hash_key(&self, _: &mut Hcx) -> DefPathHash { - *self - } -} - macro_rules! typed_def_id { ($Name:ident, $LocalName:ident) => { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable, StableHash)] diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 695103a249b4..7c46dd4c37ab 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -8,7 +8,7 @@ use rustc_arena::DroplessArena; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_data_structures::stable_hasher::{ - StableCompare, StableHash, StableHashCtxt, StableHasher, ToStableHashKey, + StableCompare, StableHash, StableHashCtxt, StableHasher, }; use rustc_data_structures::sync::Lock; use rustc_macros::{Decodable, Encodable, StableHash, symbols}; @@ -2639,14 +2639,6 @@ fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHas } } -impl ToStableHashKey for Symbol { - type KeyType = String; - #[inline] - fn to_stable_hash_key(&self, _: &mut Hcx) -> String { - self.as_str().to_string() - } -} - impl StableCompare for Symbol { const CAN_USE_UNSTABLE_SORT: bool = true; diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs index 4e4be901dbff..26d98b5d8ad7 100644 --- a/compiler/rustc_type_ir/src/fast_reject.rs +++ b/compiler/rustc_type_ir/src/fast_reject.rs @@ -5,12 +5,6 @@ use rustc_ast_ir::Mutability; #[cfg(feature = "nightly")] -use rustc_data_structures::fingerprint::Fingerprint; -#[cfg(feature = "nightly")] -use rustc_data_structures::stable_hasher::{ - StableHash, StableHashCtxt, StableHasher, ToStableHashKey, -}; -#[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, StableHash}; use crate::inherent::*; @@ -48,18 +42,6 @@ pub enum SimplifiedType { Error, } -#[cfg(feature = "nightly")] -impl ToStableHashKey for SimplifiedType { - type KeyType = Fingerprint; - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> Fingerprint { - let mut hasher = StableHasher::new(); - self.stable_hash(hcx, &mut hasher); - hasher.finish() - } -} - /// Generic parameters are pretty much just bound variables, e.g. /// the type of `fn foo<'a, T>(x: &'a T) -> u32 { ... }` can be thought of as /// `for<'a, T> fn(&'a T) -> u32`. From f7649e7c4b9dd7522a50a3fbb26f76229e31d721 Mon Sep 17 00:00:00 2001 From: Asuna Date: Wed, 6 May 2026 05:45:52 +0000 Subject: [PATCH 33/41] Rename and move `downcast` to `project_downcast_named` for `InterpCx` --- .../src/const_eval/machine.rs | 4 +- .../src/const_eval/type_info.rs | 60 ++++++++----------- .../src/const_eval/type_info/adt.rs | 12 ++-- .../src/interpret/projection.rs | 17 ++++++ 4 files changed, 49 insertions(+), 44 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index c7ee14b4b5dd..fefbf6adfd13 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -609,7 +609,7 @@ fn call_intrinsic( let ty = ecx.read_type_id(&args[0])?; let layout = ecx.layout_of(ty)?; let variant_index = if layout.is_sized() { - let (variant, variant_place) = ecx.downcast(dest, sym::Some)?; + let (variant, variant_place) = ecx.project_downcast_named(dest, sym::Some)?; let size_field_place = ecx.project_field(&variant_place, FieldIdx::ZERO)?; ecx.write_scalar( ScalarInt::try_from_target_usize(layout.size.bytes(), ecx.tcx.tcx).unwrap(), @@ -617,7 +617,7 @@ fn call_intrinsic( )?; variant } else { - ecx.downcast(dest, sym::None)?.0 + ecx.project_downcast_named(dest, sym::None)?.0 }; ecx.write_discriminant(variant_index, dest)?; } diff --git a/compiler/rustc_const_eval/src/const_eval/type_info.rs b/compiler/rustc_const_eval/src/const_eval/type_info.rs index 7a90ac828796..3d8d9592459c 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; -use rustc_abi::{ExternAbi, FieldIdx, VariantIdx}; +use rustc_abi::{ExternAbi, FieldIdx}; use rustc_ast::Mutability; use rustc_hir::LangItem; use rustc_middle::span_bug; @@ -12,27 +12,11 @@ use crate::const_eval::CompileTimeMachine; use crate::interpret::{ - CtfeProvenance, Immediate, InterpCx, InterpResult, MPlaceTy, MemoryKind, Projectable, Scalar, - Writeable, interp_ok, + CtfeProvenance, Immediate, InterpCx, InterpResult, MPlaceTy, MemoryKind, Scalar, Writeable, + interp_ok, }; impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { - /// Equivalent to `project_downcast`, but identifies the variant by name instead of index. - pub fn downcast<'a>( - &self, - place: &(impl Writeable<'tcx, CtfeProvenance> + 'a), - name: Symbol, - ) -> InterpResult<'tcx, (VariantIdx, impl Writeable<'tcx, CtfeProvenance> + 'a)> { - let variants = place.layout().ty.ty_adt_def().unwrap().variants(); - let variant_idx = variants - .iter_enumerated() - .find(|(_idx, var)| var.name == name) - .unwrap_or_else(|| panic!("got {name} but expected one of {variants:#?}")) - .0; - - interp_ok((variant_idx, self.project_downcast(place, variant_idx)?)) - } - // A general method to write an array to a static slice place. fn allocate_fill_and_write_slice_ptr( &mut self, @@ -83,7 +67,7 @@ pub(crate) fn write_type_info( let variant_index = match ty.kind() { ty::Tuple(fields) => { let (variant, variant_place) = - self.downcast(&field_dest, sym::Tuple)?; + self.project_downcast_named(&field_dest, sym::Tuple)?; // project to the single tuple variant field of `type_info::Tuple` struct type let tuple_place = self.project_field(&variant_place, FieldIdx::ZERO)?; assert_eq!( @@ -102,7 +86,7 @@ pub(crate) fn write_type_info( } ty::Array(ty, len) => { let (variant, variant_place) = - self.downcast(&field_dest, sym::Array)?; + self.project_downcast_named(&field_dest, sym::Array)?; let array_place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_array_type_info(array_place, *ty, *len)?; @@ -111,7 +95,7 @@ pub(crate) fn write_type_info( } ty::Slice(ty) => { let (variant, variant_place) = - self.downcast(&field_dest, sym::Slice)?; + self.project_downcast_named(&field_dest, sym::Slice)?; let slice_place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_slice_type_info(slice_place, *ty)?; @@ -123,16 +107,17 @@ pub(crate) fn write_type_info( } ty::Bool => { let (variant, _variant_place) = - self.downcast(&field_dest, sym::Bool)?; + self.project_downcast_named(&field_dest, sym::Bool)?; variant } ty::Char => { let (variant, _variant_place) = - self.downcast(&field_dest, sym::Char)?; + self.project_downcast_named(&field_dest, sym::Char)?; variant } ty::Int(int_ty) => { - let (variant, variant_place) = self.downcast(&field_dest, sym::Int)?; + let (variant, variant_place) = + self.project_downcast_named(&field_dest, sym::Int)?; let place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_int_type_info( place, @@ -142,7 +127,8 @@ pub(crate) fn write_type_info( variant } ty::Uint(uint_ty) => { - let (variant, variant_place) = self.downcast(&field_dest, sym::Int)?; + let (variant, variant_place) = + self.project_downcast_named(&field_dest, sym::Int)?; let place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_int_type_info( place, @@ -153,18 +139,19 @@ pub(crate) fn write_type_info( } ty::Float(float_ty) => { let (variant, variant_place) = - self.downcast(&field_dest, sym::Float)?; + self.project_downcast_named(&field_dest, sym::Float)?; let place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_float_type_info(place, float_ty.bit_width())?; variant } ty::Str => { - let (variant, _variant_place) = self.downcast(&field_dest, sym::Str)?; + let (variant, _variant_place) = + self.project_downcast_named(&field_dest, sym::Str)?; variant } ty::Ref(_, ty, mutability) => { let (variant, variant_place) = - self.downcast(&field_dest, sym::Reference)?; + self.project_downcast_named(&field_dest, sym::Reference)?; let reference_place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_reference_type_info(reference_place, *ty, *mutability)?; @@ -173,7 +160,7 @@ pub(crate) fn write_type_info( } ty::RawPtr(ty, mutability) => { let (variant, variant_place) = - self.downcast(&field_dest, sym::Pointer)?; + self.project_downcast_named(&field_dest, sym::Pointer)?; let pointer_place = self.project_field(&variant_place, FieldIdx::ZERO)?; @@ -183,14 +170,14 @@ pub(crate) fn write_type_info( } ty::Dynamic(predicates, region) => { let (variant, variant_place) = - self.downcast(&field_dest, sym::DynTrait)?; + self.project_downcast_named(&field_dest, sym::DynTrait)?; let dyn_place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_dyn_trait_type_info(dyn_place, *predicates, *region)?; variant } ty::FnPtr(sig, fn_header) => { let (variant, variant_place) = - self.downcast(&field_dest, sym::FnPtr)?; + self.project_downcast_named(&field_dest, sym::FnPtr)?; let fn_ptr_place = self.project_field(&variant_place, FieldIdx::ZERO)?; @@ -214,7 +201,7 @@ pub(crate) fn write_type_info( | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(..) - | ty::Error(_) => self.downcast(&field_dest, sym::Other)?.0, + | ty::Error(_) => self.project_downcast_named(&field_dest, sym::Other)?.0, }; self.write_discriminant(variant_index, &field_dest)? } @@ -416,16 +403,17 @@ pub(crate) fn write_fn_ptr_type_info( sym::abi => match fn_sig_kind.abi() { ExternAbi::C { .. } => { let (rust_variant, _rust_place) = - self.downcast(&field_place, sym::ExternC)?; + self.project_downcast_named(&field_place, sym::ExternC)?; self.write_discriminant(rust_variant, &field_place)?; } ExternAbi::Rust => { let (rust_variant, _rust_place) = - self.downcast(&field_place, sym::ExternRust)?; + self.project_downcast_named(&field_place, sym::ExternRust)?; self.write_discriminant(rust_variant, &field_place)?; } other_abi => { - let (variant, variant_place) = self.downcast(&field_place, sym::Named)?; + let (variant, variant_place) = + self.project_downcast_named(&field_place, sym::Named)?; let str_place = self.allocate_str_dedup(other_abi.as_str())?; let str_ref = self.mplace_to_imm_ptr(&str_place, None)?; let payload = self.project_field(&variant_place, FieldIdx::ZERO)?; diff --git a/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs b/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs index 2143313bbbad..9016064de8fd 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs @@ -22,7 +22,7 @@ pub(crate) fn write_adt_type_info( let (adt_ty, adt_def) = adt; let variant_idx = match adt_def.adt_kind() { AdtKind::Struct => { - let (variant, variant_place) = self.downcast(place, sym::Struct)?; + let (variant, variant_place) = self.project_downcast_named(place, sym::Struct)?; let place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_struct_type_info( place, @@ -32,7 +32,7 @@ pub(crate) fn write_adt_type_info( variant } AdtKind::Union => { - let (variant, variant_place) = self.downcast(place, sym::Union)?; + let (variant, variant_place) = self.project_downcast_named(place, sym::Union)?; let place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_union_type_info( place, @@ -42,7 +42,7 @@ pub(crate) fn write_adt_type_info( variant } AdtKind::Enum => { - let (variant, variant_place) = self.downcast(place, sym::Enum)?; + let (variant, variant_place) = self.project_downcast_named(place, sym::Enum)?; let place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_enum_type_info(place, adt, generics)?; variant @@ -219,13 +219,13 @@ fn write_generic_lifetime( _region: Region<'tcx>, place: MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { - let (variant_idx, _) = self.downcast(&place, sym::Lifetime)?; + let (variant_idx, _) = self.project_downcast_named(&place, sym::Lifetime)?; self.write_discriminant(variant_idx, &place)?; interp_ok(()) } fn write_generic_type(&mut self, ty: Ty<'tcx>, place: MPlaceTy<'tcx>) -> InterpResult<'tcx> { - let (variant_idx, variant_place) = self.downcast(&place, sym::Type)?; + let (variant_idx, variant_place) = self.project_downcast_named(&place, sym::Type)?; let generic_type_place = self.project_field(&variant_place, FieldIdx::ZERO)?; for (field_idx, field_def) in generic_type_place @@ -251,7 +251,7 @@ fn write_generic_type(&mut self, ty: Ty<'tcx>, place: MPlaceTy<'tcx>) -> InterpR fn write_generic_const(&mut self, c: Const<'tcx>, place: MPlaceTy<'tcx>) -> InterpResult<'tcx> { let ConstKind::Value(c) = c.kind() else { bug!("expected a computed const, got {c:?}") }; - let (variant_idx, variant_place) = self.downcast(&place, sym::Const)?; + let (variant_idx, variant_place) = self.project_downcast_named(&place, sym::Const)?; let const_place = self.project_field(&variant_place, FieldIdx::ZERO)?; for (field_idx, field_def) in const_place diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index db72c02e308c..27f91b2b89b2 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -14,6 +14,7 @@ use rustc_middle::ty::Ty; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::{bug, mir, span_bug, ty}; +use rustc_span::Symbol; use tracing::{debug, instrument}; use super::{ @@ -227,6 +228,22 @@ pub fn project_downcast>( base.offset(Size::ZERO, layout, self) } + /// Equivalent to `project_downcast`, but identifies the variant by name instead of index. + pub fn project_downcast_named>( + &self, + base: &P, + name: Symbol, + ) -> InterpResult<'tcx, (VariantIdx, P)> { + let variants = base.layout().ty.ty_adt_def().unwrap().variants(); + let variant_idx = variants + .iter_enumerated() + .find(|(_idx, var)| var.name == name) + .unwrap_or_else(|| panic!("got {name} but expected one of {variants:#?}")) + .0; + + interp_ok((variant_idx, self.project_downcast(base, variant_idx)?)) + } + /// Compute the offset and field layout for accessing the given index. pub fn project_index>( &self, From e8873630a0f68fe2f46a38db2478b37c8aa22f36 Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 5 May 2026 18:22:35 +0200 Subject: [PATCH 34/41] core: drop unmapped ZSTs in array `map` --- ...sroot_tests-128bit-atomic-operations.patch | 2 +- library/core/src/array/drain.rs | 69 +++++++++++++------ library/coretests/tests/array.rs | 34 +++++++-- library/coretests/tests/lib.rs | 1 + 4 files changed, 77 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch index 7ba4475e3145..7194d8144ca6 100644 --- a/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch +++ b/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch @@ -16,6 +16,7 @@ index 1e336bf..35e6f54 100644 +++ b/coretests/tests/lib.rs @@ -2,4 +2,3 @@ // tidy-alphabetical-start + #![cfg_attr(not(panic = "abort"), feature(reentrant_lock))] -#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![feature(array_ptr_get)] #![feature(array_try_from_fn)] @@ -36,4 +37,3 @@ index b735957..ea728b6 100644 #[cfg(target_has_atomic = "ptr")] -- 2.26.2.7.g19db9cfb68 - diff --git a/library/core/src/array/drain.rs b/library/core/src/array/drain.rs index 17792dca583d..b2ff54bdfa21 100644 --- a/library/core/src/array/drain.rs +++ b/library/core/src/array/drain.rs @@ -1,8 +1,8 @@ use crate::marker::{Destruct, PhantomData}; -use crate::mem::{ManuallyDrop, SizedTypeProperties, conjure_zst}; -use crate::ptr::{NonNull, drop_in_place, from_raw_parts_mut, null_mut}; +use crate::mem::{ManuallyDrop, SizedTypeProperties, conjure_zst, transmute}; +use crate::ptr::{NonNull, drop_in_place, from_raw_parts_mut, without_provenance_mut}; -impl<'l, 'f, T, U, const N: usize, F: FnMut(T) -> U> Drain<'l, 'f, T, N, F> { +impl<'l, 'f, T, U, F: FnMut(T) -> U> Drain<'l, 'f, T, F> { /// This function returns a function that lets you index the given array in const. /// As implemented it can optimize better than iterators, and can be constified. /// It acts like a sort of guard (owns the array) and iterator combined, which can be implemented @@ -14,9 +14,11 @@ impl<'l, 'f, T, U, const N: usize, F: FnMut(T) -> U> Drain<'l, 'f, T, N, F> { /// This will also not actually store the array. /// /// SAFETY: must only be called `N` times. Thou shalt not drop the array either. - // FIXME(const-hack): this is a hack for `let guard = Guard(array); |i| f(guard[i])`. #[rustc_const_unstable(feature = "array_try_map", issue = "79711")] - pub(super) const unsafe fn new(array: &'l mut ManuallyDrop<[T; N]>, f: &'f mut F) -> Self { + pub(super) const unsafe fn new( + array: &'l mut ManuallyDrop<[T; N]>, + f: &'f mut F, + ) -> Self { // dont drop the array, transfers "ownership" to Self let ptr: NonNull = NonNull::from_mut(array).cast(); // SAFETY: @@ -24,16 +26,17 @@ impl<'l, 'f, T, U, const N: usize, F: FnMut(T) -> U> Drain<'l, 'f, T, N, F> { // at the end of `slice`. `end` will never be dereferenced, only checked // for direct pointer equality with `ptr` to check if the drainer is done. unsafe { - let end = if T::IS_ZST { null_mut() } else { ptr.as_ptr().add(N) }; - Self { ptr, end, f, l: PhantomData } + let end_or_len = + if T::IS_ZST { without_provenance_mut(N) } else { ptr.as_ptr().add(N) }; + Self { ptr, end_or_len, f, l: PhantomData } } } } /// See [`Drain::new`]; this is our fake iterator. #[unstable(feature = "array_try_map", issue = "79711")] -pub(super) struct Drain<'l, 'f, T, const N: usize, F> { - // FIXME(const-hack): This is essentially a slice::IterMut<'static>, replace when possible. +pub(super) struct Drain<'l, 'f, T, F> { + // FIXME(const-hack): This is a slice::IterMut<'l>, replace when possible. /// The pointer to the next element to return, or the past-the-end location /// if the drainer is empty. /// @@ -41,16 +44,16 @@ pub(super) struct Drain<'l, 'f, T, const N: usize, F> { /// As we "own" this array, we dont need to store any lifetime. ptr: NonNull, /// For non-ZSTs, the non-null pointer to the past-the-end element. - /// For ZSTs, this is null. - end: *mut T, + /// For ZSTs, this is the number of unprocessed items. + end_or_len: *mut T, f: &'f mut F, - l: PhantomData<&'l mut [T; N]>, + l: PhantomData<&'l mut [T]>, } #[rustc_const_unstable(feature = "array_try_map", issue = "79711")] #[unstable(feature = "array_try_map", issue = "79711")] -impl const FnOnce<(usize,)> for &mut Drain<'_, '_, T, N, F> +impl const FnOnce<(usize,)> for &mut Drain<'_, '_, T, F> where F: [const] FnMut(T) -> U, { @@ -63,7 +66,7 @@ extern "rust-call" fn call_once(mut self, args: (usize,)) -> Self::Output { } #[rustc_const_unstable(feature = "array_try_map", issue = "79711")] #[unstable(feature = "array_try_map", issue = "79711")] -impl const FnMut<(usize,)> for &mut Drain<'_, '_, T, N, F> +impl const FnMut<(usize,)> for &mut Drain<'_, '_, T, F> where F: [const] FnMut(T) -> U, { @@ -73,6 +76,16 @@ extern "rust-call" fn call_mut( (_ /* ignore argument */,): (usize,), ) -> Self::Output { if T::IS_ZST { + #[expect(ptr_to_integer_transmute_in_consts)] + // SAFETY: + // This is equivalent to `self.end_or_len.addr`, but that's not + // available in `const`. `self.end_or_len` doesn't have provenance, + // so transmuting is fine. + let len = unsafe { transmute::<*mut T, usize>(self.end_or_len) }; + // SAFETY: + // The caller guarantees that this is never called more than N times + // (see `Drain::new`), hence this cannot underflow. + self.end_or_len = without_provenance_mut(unsafe { len.unchecked_sub(1) }); // its UB to call this more than N times, so returning more ZSTs is valid. // SAFETY: its a ZST? we conjur. (self.f)(unsafe { conjure_zst::() }) @@ -88,20 +101,32 @@ extern "rust-call" fn call_mut( } #[rustc_const_unstable(feature = "array_try_map", issue = "79711")] #[unstable(feature = "array_try_map", issue = "79711")] -impl const Drop for Drain<'_, '_, T, N, F> { +impl const Drop for Drain<'_, '_, T, F> { fn drop(&mut self) { - if !T::IS_ZST { + let slice = if T::IS_ZST { + from_raw_parts_mut::<[T]>( + self.ptr.as_ptr(), + #[expect(ptr_to_integer_transmute_in_consts)] + // SAFETY: + // This is equivalent to `self.end_or_len.addr`, but that's not + // available in `const`. `self.end_or_len` doesn't have provenance, + // so transmuting is fine. + unsafe { + transmute::<*mut T, usize>(self.end_or_len) + }, + ) + } else { // SAFETY: we cant read more than N elements - let slice = unsafe { + unsafe { from_raw_parts_mut::<[T]>( self.ptr.as_ptr(), // SAFETY: `start <= end` - self.end.offset_from_unsigned(self.ptr.as_ptr()), + self.end_or_len.offset_from_unsigned(self.ptr.as_ptr()), ) - }; + } + }; - // SAFETY: By the type invariant, we're allowed to drop all these. (we own it, after all) - unsafe { drop_in_place(slice) } - } + // SAFETY: By the type invariant, we're allowed to drop all these. (we own it, after all) + unsafe { drop_in_place(slice) } } } diff --git a/library/coretests/tests/array.rs b/library/coretests/tests/array.rs index 43fed944e928..a3b0e59278f7 100644 --- a/library/coretests/tests/array.rs +++ b/library/coretests/tests/array.rs @@ -1,3 +1,4 @@ +use core::cell::Cell; use core::num::NonZero; use core::sync::atomic::{AtomicUsize, Ordering}; use core::{array, assert_eq}; @@ -168,8 +169,6 @@ fn iterator_debug() { #[test] fn iterator_drops() { - use core::cell::Cell; - // This test makes sure the correct number of elements are dropped. The `R` // type is just a reference to a `Cell` that is incremented when an `R` is // dropped. @@ -337,8 +336,6 @@ fn drop(&mut self) { #[test] fn cell_allows_array_cycle() { - use core::cell::Cell; - #[derive(Debug)] struct B<'a> { a: [Cell>>; 2], @@ -513,7 +510,6 @@ fn array_rsplit_array_mut_out_of_bounds() { #[test] fn array_intoiter_advance_by() { - use std::cell::Cell; struct DropCounter<'a>(usize, &'a Cell); impl Drop for DropCounter<'_> { fn drop(&mut self) { @@ -566,7 +562,6 @@ fn drop(&mut self) { #[test] fn array_intoiter_advance_back_by() { - use std::cell::Cell; struct DropCounter<'a>(usize, &'a Cell); impl Drop for DropCounter<'_> { fn drop(&mut self) { @@ -718,6 +713,33 @@ fn drop(&mut self) { } } +#[cfg(not(panic = "abort"))] +#[test] +fn array_map_drops_unmapped_zst_elements_on_panic() { + use std::sync::ReentrantLock; + + static DROPPED: ReentrantLock> = ReentrantLock::new(Cell::new(0)); + + struct ZstDrop; + impl Drop for ZstDrop { + fn drop(&mut self) { + DROPPED.lock().update(|x| x + 1); + } + } + + let dropped = DROPPED.lock(); + dropped.set(0); + let array = [const { ZstDrop }; 5]; + let success = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + let _ = array.map(|x| { + drop(x); + assert_eq!(dropped.get(), 1); + }); + })); + assert!(success.is_err()); + assert_eq!(dropped.get(), 5); +} + // This covers the `PartialEq::<[T]>::eq` impl for `[T; N]` when it returns false. #[test] fn array_eq() { diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index c4292c2a421b..12b81fea9d27 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -1,4 +1,5 @@ // tidy-alphabetical-start +#![cfg_attr(not(panic = "abort"), feature(reentrant_lock))] #![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![feature(array_ptr_get)] #![feature(array_try_from_fn)] From e2ea0ba07a12bc328880a4ecc0a6c2153a46b332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Wed, 6 May 2026 16:29:19 +0200 Subject: [PATCH 35/41] add new solver revisions to already fixed normalization tests There were fixed by https://github.com/rust-lang/rust/pull/140672 --- ... => normalization-overflow.current.stderr} | 2 +- .../normalization-overflow.next.stderr | 9 ++++++ .../normalization-overflow.rs | 11 +++++-- ...-alias-mutual-recursion.feature_new.stderr | 21 +++++++++++++ ...-alias-mutual-recursion.feature_old.stderr | 27 +++++++++++++++++ ...pe-alias-mutual-recursion.gated_new.stderr | 30 +++++++++++++++++++ ...pe-alias-mutual-recursion.gated_old.stderr | 30 +++++++++++++++++++ .../infinite-type-alias-mutual-recursion.rs | 21 +++++++++---- 8 files changed, 142 insertions(+), 9 deletions(-) rename tests/ui/associated-inherent-types/{normalization-overflow.stderr => normalization-overflow.current.stderr} (78%) create mode 100644 tests/ui/associated-inherent-types/normalization-overflow.next.stderr create mode 100644 tests/ui/infinite/infinite-type-alias-mutual-recursion.feature_new.stderr create mode 100644 tests/ui/infinite/infinite-type-alias-mutual-recursion.feature_old.stderr create mode 100644 tests/ui/infinite/infinite-type-alias-mutual-recursion.gated_new.stderr create mode 100644 tests/ui/infinite/infinite-type-alias-mutual-recursion.gated_old.stderr diff --git a/tests/ui/associated-inherent-types/normalization-overflow.stderr b/tests/ui/associated-inherent-types/normalization-overflow.current.stderr similarity index 78% rename from tests/ui/associated-inherent-types/normalization-overflow.stderr rename to tests/ui/associated-inherent-types/normalization-overflow.current.stderr index 05aad31c5f41..e3e662788cf0 100644 --- a/tests/ui/associated-inherent-types/normalization-overflow.stderr +++ b/tests/ui/associated-inherent-types/normalization-overflow.current.stderr @@ -1,5 +1,5 @@ error: overflow evaluating associated type `T::This` - --> $DIR/normalization-overflow.rs:9:5 + --> $DIR/normalization-overflow.rs:14:5 | LL | type This = Self::This; | ^^^^^^^^^ diff --git a/tests/ui/associated-inherent-types/normalization-overflow.next.stderr b/tests/ui/associated-inherent-types/normalization-overflow.next.stderr new file mode 100644 index 000000000000..5f0939b9f776 --- /dev/null +++ b/tests/ui/associated-inherent-types/normalization-overflow.next.stderr @@ -0,0 +1,9 @@ +error[E0271]: type mismatch resolving `T::This normalizes-to _` + --> $DIR/normalization-overflow.rs:14:5 + | +LL | type This = Self::This; + | ^^^^^^^^^ types differ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/associated-inherent-types/normalization-overflow.rs b/tests/ui/associated-inherent-types/normalization-overflow.rs index 4228238aa7b7..c5505578f4f0 100644 --- a/tests/ui/associated-inherent-types/normalization-overflow.rs +++ b/tests/ui/associated-inherent-types/normalization-overflow.rs @@ -1,12 +1,19 @@ -#![feature(inherent_associated_types)] +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver + +#![feature(inherent_associated_types, rustc_attrs)] #![allow(incomplete_features)] +#![rustc_no_implicit_bounds] // FIXME(fmease): I'd prefer to report a cycle error here instead of an overflow one. struct T; impl T { - type This = Self::This; //~ ERROR overflow evaluating associated type `T::This` + type This = Self::This; + //[current]~^ ERROR: overflow evaluating associated type `T::This` + //[next]~^^ ERROR: type mismatch resolving `T::This normalizes-to _` } fn main() {} diff --git a/tests/ui/infinite/infinite-type-alias-mutual-recursion.feature_new.stderr b/tests/ui/infinite/infinite-type-alias-mutual-recursion.feature_new.stderr new file mode 100644 index 000000000000..1d6ace580951 --- /dev/null +++ b/tests/ui/infinite/infinite-type-alias-mutual-recursion.feature_new.stderr @@ -0,0 +1,21 @@ +error[E0271]: type mismatch resolving `X3 normalizes-to _` + --> $DIR/infinite-type-alias-mutual-recursion.rs:12:1 + | +LL | type X1 = X2; + | ^^^^^^^ types differ + +error[E0271]: type mismatch resolving `X1 normalizes-to _` + --> $DIR/infinite-type-alias-mutual-recursion.rs:16:1 + | +LL | type X2 = X3; + | ^^^^^^^ types differ + +error[E0271]: type mismatch resolving `X2 normalizes-to _` + --> $DIR/infinite-type-alias-mutual-recursion.rs:19:1 + | +LL | type X3 = X1; + | ^^^^^^^ types differ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/infinite/infinite-type-alias-mutual-recursion.feature_old.stderr b/tests/ui/infinite/infinite-type-alias-mutual-recursion.feature_old.stderr new file mode 100644 index 000000000000..c7adc3af02fa --- /dev/null +++ b/tests/ui/infinite/infinite-type-alias-mutual-recursion.feature_old.stderr @@ -0,0 +1,27 @@ +error[E0275]: overflow normalizing the type alias `X2` + --> $DIR/infinite-type-alias-mutual-recursion.rs:12:1 + | +LL | type X1 = X2; + | ^^^^^^^ + | + = note: in case this is a recursive type alias, consider using a struct, enum, or union instead + +error[E0275]: overflow normalizing the type alias `X3` + --> $DIR/infinite-type-alias-mutual-recursion.rs:16:1 + | +LL | type X2 = X3; + | ^^^^^^^ + | + = note: in case this is a recursive type alias, consider using a struct, enum, or union instead + +error[E0275]: overflow normalizing the type alias `X1` + --> $DIR/infinite-type-alias-mutual-recursion.rs:19:1 + | +LL | type X3 = X1; + | ^^^^^^^ + | + = note: in case this is a recursive type alias, consider using a struct, enum, or union instead + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/infinite/infinite-type-alias-mutual-recursion.gated_new.stderr b/tests/ui/infinite/infinite-type-alias-mutual-recursion.gated_new.stderr new file mode 100644 index 000000000000..888e1d18dea7 --- /dev/null +++ b/tests/ui/infinite/infinite-type-alias-mutual-recursion.gated_new.stderr @@ -0,0 +1,30 @@ +error[E0391]: cycle detected when expanding type alias `X1` + --> $DIR/infinite-type-alias-mutual-recursion.rs:12:11 + | +LL | type X1 = X2; + | ^^ + | +note: ...which requires expanding type alias `X2`... + --> $DIR/infinite-type-alias-mutual-recursion.rs:16:11 + | +LL | type X2 = X3; + | ^^ +note: ...which requires expanding type alias `X3`... + --> $DIR/infinite-type-alias-mutual-recursion.rs:19:11 + | +LL | type X3 = X1; + | ^^ + = note: ...which again requires expanding type alias `X1`, completing the cycle + = note: type aliases cannot be recursive + = help: consider using a struct, enum, or union instead to break the cycle + = help: see for more information +note: cycle used when checking that `X1` is well-formed + --> $DIR/infinite-type-alias-mutual-recursion.rs:12:1 + | +LL | type X1 = X2; + | ^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/infinite/infinite-type-alias-mutual-recursion.gated_old.stderr b/tests/ui/infinite/infinite-type-alias-mutual-recursion.gated_old.stderr new file mode 100644 index 000000000000..888e1d18dea7 --- /dev/null +++ b/tests/ui/infinite/infinite-type-alias-mutual-recursion.gated_old.stderr @@ -0,0 +1,30 @@ +error[E0391]: cycle detected when expanding type alias `X1` + --> $DIR/infinite-type-alias-mutual-recursion.rs:12:11 + | +LL | type X1 = X2; + | ^^ + | +note: ...which requires expanding type alias `X2`... + --> $DIR/infinite-type-alias-mutual-recursion.rs:16:11 + | +LL | type X2 = X3; + | ^^ +note: ...which requires expanding type alias `X3`... + --> $DIR/infinite-type-alias-mutual-recursion.rs:19:11 + | +LL | type X3 = X1; + | ^^ + = note: ...which again requires expanding type alias `X1`, completing the cycle + = note: type aliases cannot be recursive + = help: consider using a struct, enum, or union instead to break the cycle + = help: see for more information +note: cycle used when checking that `X1` is well-formed + --> $DIR/infinite-type-alias-mutual-recursion.rs:12:1 + | +LL | type X1 = X2; + | ^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/infinite/infinite-type-alias-mutual-recursion.rs b/tests/ui/infinite/infinite-type-alias-mutual-recursion.rs index 6a43c4ac7872..24e1318ca3d7 100644 --- a/tests/ui/infinite/infinite-type-alias-mutual-recursion.rs +++ b/tests/ui/infinite/infinite-type-alias-mutual-recursion.rs @@ -1,14 +1,23 @@ -//@ revisions: feature gated +//@ revisions: feature_old gated_old feature_new gated_new //@ ignore-parallel-frontend query cycle -#![cfg_attr(feature, feature(lazy_type_alias))] +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [feature_new] compile-flags: -Znext-solver +//@ [gated_new] compile-flags: -Znext-solver + +#![feature(rustc_attrs)] +#![rustc_no_implicit_bounds] +#![cfg_attr(any(feature_old, feature_new), feature(lazy_type_alias))] #![allow(incomplete_features)] type X1 = X2; -//[gated]~^ ERROR cycle detected when expanding type alias `X1` -//[feature]~^^ ERROR: overflow normalizing the type alias `X2` +//[gated_old,gated_new]~^ ERROR cycle detected when expanding type alias `X1` +//[feature_old]~^^ ERROR: overflow normalizing the type alias `X2` +//[feature_new]~^^^ ERROR: type mismatch resolving `X3 normalizes-to _` type X2 = X3; -//[feature]~^ ERROR: overflow normalizing the type alias `X3` +//[feature_old]~^ ERROR: overflow normalizing the type alias `X3` +//[feature_new]~^^ ERROR: type mismatch resolving `X1 normalizes-to _` type X3 = X1; -//[feature]~^ ERROR: overflow normalizing the type alias `X1` +//[feature_old]~^ ERROR: overflow normalizing the type alias `X1` +//[feature_new]~^^ ERROR: type mismatch resolving `X2 normalizes-to _` fn main() {} From 4922210ea6ff5fa7143330729ff322b829bc3fa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Wed, 6 May 2026 19:04:58 +0200 Subject: [PATCH 36/41] add normalization test with field with diverging alias Another example from https://github.com/rust-lang/trait-system-refactor-initiative/issues/139 and which was likely fixed by https://github.com/rust-lang/rust/pull/140672 as well. --- ...e-diverging-alias-in-struct.current.stderr | 9 +++++++ ...lize-diverging-alias-in-struct.next.stderr | 18 +++++++++++++ .../normalize-diverging-alias-in-struct.rs | 27 +++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 tests/ui/traits/normalize/normalize-diverging-alias-in-struct.current.stderr create mode 100644 tests/ui/traits/normalize/normalize-diverging-alias-in-struct.next.stderr create mode 100644 tests/ui/traits/normalize/normalize-diverging-alias-in-struct.rs diff --git a/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.current.stderr b/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.current.stderr new file mode 100644 index 000000000000..7787d6b68f25 --- /dev/null +++ b/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.current.stderr @@ -0,0 +1,9 @@ +error[E0275]: overflow evaluating the requirement `::Diverges == _` + --> $DIR/normalize-diverging-alias-in-struct.rs:21:12 + | +LL | field: Box<::Diverges>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.next.stderr b/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.next.stderr new file mode 100644 index 000000000000..d7046d2b058b --- /dev/null +++ b/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.next.stderr @@ -0,0 +1,18 @@ +error[E0271]: type mismatch resolving `::Diverges normalizes-to _` + --> $DIR/normalize-diverging-alias-in-struct.rs:21:12 + | +LL | field: Box<::Diverges>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ + +error[E0271]: type mismatch resolving `::Diverges normalizes-to _` + --> $DIR/normalize-diverging-alias-in-struct.rs:21:12 + | +LL | field: Box<::Diverges>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ + | +note: required by a bound in `Box` + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.rs b/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.rs new file mode 100644 index 000000000000..45b2cb56c25f --- /dev/null +++ b/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.rs @@ -0,0 +1,27 @@ +// Ensure that structs with fields whose types contain diverging aliases are properly caught with +// both solvers. +// MCVE from https://github.com/rust-lang/trait-system-refactor-initiative/issues/139#issuecomment-2703127026. + +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver + +#![feature(rustc_attrs)] +#![rustc_no_implicit_bounds] + +trait Trait { + type Diverges; +} + +impl Trait for T { + type Diverges = D::Diverges; +} + +struct Foo { + field: Box<::Diverges>, + //[current]~^ ERROR: overflow evaluating the requirement `::Diverges == _` + //[next]~^^ ERROR: type mismatch resolving `::Diverges normalizes-to _` + //[next]~| ERROR: type mismatch resolving `::Diverges normalizes-to _` +} + +fn main() {} From 6cc6a63392b1a0cbce630a816f4a2f93799e501a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Wed, 6 May 2026 19:18:23 +0200 Subject: [PATCH 37/41] add normalization test with defaulted type param with diverging alias Another example from https://github.com/rust-lang/trait-system-refactor-initiative/issues/139 and which was fixed in 1.89 `nightly-2025-06-18`. --- ...rging-alias-in-defaulted-type-parameter.rs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 tests/ui/traits/normalize/normalize-diverging-alias-in-defaulted-type-parameter.rs diff --git a/tests/ui/traits/normalize/normalize-diverging-alias-in-defaulted-type-parameter.rs b/tests/ui/traits/normalize/normalize-diverging-alias-in-defaulted-type-parameter.rs new file mode 100644 index 000000000000..c359f8363e53 --- /dev/null +++ b/tests/ui/traits/normalize/normalize-diverging-alias-in-defaulted-type-parameter.rs @@ -0,0 +1,25 @@ +// Ensure that defaulted type parameters whose default contains diverging aliases are properly +// caught with both solvers. This is currently not the case, and this is tracked in issue +// https://github.com/rust-lang/rust/issues/156271. +// MCVE from https://github.com/rust-lang/trait-system-refactor-initiative/issues/139#issuecomment-2704576249. + +//@ revisions: current next +//@ check-pass +//@ known-bug: #156271 +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver + +#![feature(rustc_attrs)] +#![rustc_no_implicit_bounds] + +trait Trait { + type Diverges; +} + +impl Trait for T { + type Diverges = D::Diverges; +} + +struct Bar::Diverges>(*mut T); + +fn main() {} From 130b1d881c7850b5c28ec0dc6a5315279b7c1bdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Thu, 7 May 2026 10:48:12 +0200 Subject: [PATCH 38/41] move some normalization tests to the dedicated folder --- .../traits/{ => normalize}/assoc-type-hrtb-normalization-30472.rs | 0 tests/ui/traits/{ => normalize}/copy-impl-cannot-normalize.rs | 0 tests/ui/traits/{ => normalize}/copy-impl-cannot-normalize.stderr | 0 tests/ui/traits/{ => normalize}/deep-norm-pending.rs | 0 tests/ui/traits/{ => normalize}/deep-norm-pending.stderr | 0 .../{ => normalize}/normalize-associated-type-in-where-clause.rs | 0 tests/ui/traits/{ => normalize}/normalize-conflicting-impls.rs | 0 .../ui/traits/{ => normalize}/normalize-conflicting-impls.stderr | 0 tests/ui/traits/{ => normalize}/normalize-supertrait.rs | 0 tests/ui/traits/{ => normalize}/pointee-normalize-equate.rs | 0 .../{ => normalize}/self-referential-param-env-normalization.rs | 0 .../self-referential-param-env-normalization.stderr | 0 .../unconstrained-projection-normalization-2.current.stderr | 0 .../unconstrained-projection-normalization-2.next.stderr | 0 .../{ => normalize}/unconstrained-projection-normalization-2.rs | 0 .../unconstrained-projection-normalization.current.stderr | 0 .../unconstrained-projection-normalization.next.stderr | 0 .../{ => normalize}/unconstrained-projection-normalization.rs | 0 18 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/traits/{ => normalize}/assoc-type-hrtb-normalization-30472.rs (100%) rename tests/ui/traits/{ => normalize}/copy-impl-cannot-normalize.rs (100%) rename tests/ui/traits/{ => normalize}/copy-impl-cannot-normalize.stderr (100%) rename tests/ui/traits/{ => normalize}/deep-norm-pending.rs (100%) rename tests/ui/traits/{ => normalize}/deep-norm-pending.stderr (100%) rename tests/ui/traits/{ => normalize}/normalize-associated-type-in-where-clause.rs (100%) rename tests/ui/traits/{ => normalize}/normalize-conflicting-impls.rs (100%) rename tests/ui/traits/{ => normalize}/normalize-conflicting-impls.stderr (100%) rename tests/ui/traits/{ => normalize}/normalize-supertrait.rs (100%) rename tests/ui/traits/{ => normalize}/pointee-normalize-equate.rs (100%) rename tests/ui/traits/{ => normalize}/self-referential-param-env-normalization.rs (100%) rename tests/ui/traits/{ => normalize}/self-referential-param-env-normalization.stderr (100%) rename tests/ui/traits/{ => normalize}/unconstrained-projection-normalization-2.current.stderr (100%) rename tests/ui/traits/{ => normalize}/unconstrained-projection-normalization-2.next.stderr (100%) rename tests/ui/traits/{ => normalize}/unconstrained-projection-normalization-2.rs (100%) rename tests/ui/traits/{ => normalize}/unconstrained-projection-normalization.current.stderr (100%) rename tests/ui/traits/{ => normalize}/unconstrained-projection-normalization.next.stderr (100%) rename tests/ui/traits/{ => normalize}/unconstrained-projection-normalization.rs (100%) diff --git a/tests/ui/traits/assoc-type-hrtb-normalization-30472.rs b/tests/ui/traits/normalize/assoc-type-hrtb-normalization-30472.rs similarity index 100% rename from tests/ui/traits/assoc-type-hrtb-normalization-30472.rs rename to tests/ui/traits/normalize/assoc-type-hrtb-normalization-30472.rs diff --git a/tests/ui/traits/copy-impl-cannot-normalize.rs b/tests/ui/traits/normalize/copy-impl-cannot-normalize.rs similarity index 100% rename from tests/ui/traits/copy-impl-cannot-normalize.rs rename to tests/ui/traits/normalize/copy-impl-cannot-normalize.rs diff --git a/tests/ui/traits/copy-impl-cannot-normalize.stderr b/tests/ui/traits/normalize/copy-impl-cannot-normalize.stderr similarity index 100% rename from tests/ui/traits/copy-impl-cannot-normalize.stderr rename to tests/ui/traits/normalize/copy-impl-cannot-normalize.stderr diff --git a/tests/ui/traits/deep-norm-pending.rs b/tests/ui/traits/normalize/deep-norm-pending.rs similarity index 100% rename from tests/ui/traits/deep-norm-pending.rs rename to tests/ui/traits/normalize/deep-norm-pending.rs diff --git a/tests/ui/traits/deep-norm-pending.stderr b/tests/ui/traits/normalize/deep-norm-pending.stderr similarity index 100% rename from tests/ui/traits/deep-norm-pending.stderr rename to tests/ui/traits/normalize/deep-norm-pending.stderr diff --git a/tests/ui/traits/normalize-associated-type-in-where-clause.rs b/tests/ui/traits/normalize/normalize-associated-type-in-where-clause.rs similarity index 100% rename from tests/ui/traits/normalize-associated-type-in-where-clause.rs rename to tests/ui/traits/normalize/normalize-associated-type-in-where-clause.rs diff --git a/tests/ui/traits/normalize-conflicting-impls.rs b/tests/ui/traits/normalize/normalize-conflicting-impls.rs similarity index 100% rename from tests/ui/traits/normalize-conflicting-impls.rs rename to tests/ui/traits/normalize/normalize-conflicting-impls.rs diff --git a/tests/ui/traits/normalize-conflicting-impls.stderr b/tests/ui/traits/normalize/normalize-conflicting-impls.stderr similarity index 100% rename from tests/ui/traits/normalize-conflicting-impls.stderr rename to tests/ui/traits/normalize/normalize-conflicting-impls.stderr diff --git a/tests/ui/traits/normalize-supertrait.rs b/tests/ui/traits/normalize/normalize-supertrait.rs similarity index 100% rename from tests/ui/traits/normalize-supertrait.rs rename to tests/ui/traits/normalize/normalize-supertrait.rs diff --git a/tests/ui/traits/pointee-normalize-equate.rs b/tests/ui/traits/normalize/pointee-normalize-equate.rs similarity index 100% rename from tests/ui/traits/pointee-normalize-equate.rs rename to tests/ui/traits/normalize/pointee-normalize-equate.rs diff --git a/tests/ui/traits/self-referential-param-env-normalization.rs b/tests/ui/traits/normalize/self-referential-param-env-normalization.rs similarity index 100% rename from tests/ui/traits/self-referential-param-env-normalization.rs rename to tests/ui/traits/normalize/self-referential-param-env-normalization.rs diff --git a/tests/ui/traits/self-referential-param-env-normalization.stderr b/tests/ui/traits/normalize/self-referential-param-env-normalization.stderr similarity index 100% rename from tests/ui/traits/self-referential-param-env-normalization.stderr rename to tests/ui/traits/normalize/self-referential-param-env-normalization.stderr diff --git a/tests/ui/traits/unconstrained-projection-normalization-2.current.stderr b/tests/ui/traits/normalize/unconstrained-projection-normalization-2.current.stderr similarity index 100% rename from tests/ui/traits/unconstrained-projection-normalization-2.current.stderr rename to tests/ui/traits/normalize/unconstrained-projection-normalization-2.current.stderr diff --git a/tests/ui/traits/unconstrained-projection-normalization-2.next.stderr b/tests/ui/traits/normalize/unconstrained-projection-normalization-2.next.stderr similarity index 100% rename from tests/ui/traits/unconstrained-projection-normalization-2.next.stderr rename to tests/ui/traits/normalize/unconstrained-projection-normalization-2.next.stderr diff --git a/tests/ui/traits/unconstrained-projection-normalization-2.rs b/tests/ui/traits/normalize/unconstrained-projection-normalization-2.rs similarity index 100% rename from tests/ui/traits/unconstrained-projection-normalization-2.rs rename to tests/ui/traits/normalize/unconstrained-projection-normalization-2.rs diff --git a/tests/ui/traits/unconstrained-projection-normalization.current.stderr b/tests/ui/traits/normalize/unconstrained-projection-normalization.current.stderr similarity index 100% rename from tests/ui/traits/unconstrained-projection-normalization.current.stderr rename to tests/ui/traits/normalize/unconstrained-projection-normalization.current.stderr diff --git a/tests/ui/traits/unconstrained-projection-normalization.next.stderr b/tests/ui/traits/normalize/unconstrained-projection-normalization.next.stderr similarity index 100% rename from tests/ui/traits/unconstrained-projection-normalization.next.stderr rename to tests/ui/traits/normalize/unconstrained-projection-normalization.next.stderr diff --git a/tests/ui/traits/unconstrained-projection-normalization.rs b/tests/ui/traits/normalize/unconstrained-projection-normalization.rs similarity index 100% rename from tests/ui/traits/unconstrained-projection-normalization.rs rename to tests/ui/traits/normalize/unconstrained-projection-normalization.rs From ddddb4d10975302345d43cdc9a9d5cc9e4cf234e Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Thu, 7 May 2026 23:33:00 +0900 Subject: [PATCH 39/41] add regression test for closure return ICE --- .../closures/return-in-closure-with-fn-bound.rs | 8 ++++++++ .../return-in-closure-with-fn-bound.stderr | 15 +++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 tests/ui/closures/return-in-closure-with-fn-bound.rs create mode 100644 tests/ui/closures/return-in-closure-with-fn-bound.stderr diff --git a/tests/ui/closures/return-in-closure-with-fn-bound.rs b/tests/ui/closures/return-in-closure-with-fn-bound.rs new file mode 100644 index 000000000000..5e05cec98ca1 --- /dev/null +++ b/tests/ui/closures/return-in-closure-with-fn-bound.rs @@ -0,0 +1,8 @@ +// Regression test for https://github.com/rust-lang/rust/issues/155893. + +fn func(_f: impl Fn()) { + func(|| return 2) + //~^ ERROR mismatched types +} + +fn main() {} diff --git a/tests/ui/closures/return-in-closure-with-fn-bound.stderr b/tests/ui/closures/return-in-closure-with-fn-bound.stderr new file mode 100644 index 000000000000..c51e6faca42b --- /dev/null +++ b/tests/ui/closures/return-in-closure-with-fn-bound.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> $DIR/return-in-closure-with-fn-bound.rs:4:20 + | +LL | func(|| return 2) + | ^ expected `()`, found integer + | +note: return type inferred to be `()` here + --> $DIR/return-in-closure-with-fn-bound.rs:4:20 + | +LL | func(|| return 2) + | ^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. From 2d88ee87ddaa584a19e438528f60f825f735076a Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 26 Feb 2026 23:08:22 +0200 Subject: [PATCH 40/41] Reborrow traits --- compiler/rustc_borrowck/src/borrow_set.rs | 47 ++- compiler/rustc_borrowck/src/dataflow.rs | 2 +- compiler/rustc_borrowck/src/lib.rs | 31 ++ .../src/polonius/legacy/loan_invalidations.rs | 15 + compiler/rustc_borrowck/src/type_check/mod.rs | 120 ++++++ compiler/rustc_codegen_cranelift/src/base.rs | 5 + compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 8 + .../src/check_consts/check.rs | 4 + .../src/check_consts/qualifs.rs | 2 + .../src/check_consts/resolver.rs | 13 + .../rustc_const_eval/src/interpret/step.rs | 5 + compiler/rustc_hir/src/lang_items.rs | 2 +- .../src/coherence/builtin.rs | 354 +++++++++++++++++- compiler/rustc_hir_analysis/src/errors.rs | 18 + compiler/rustc_hir_typeck/src/coercion.rs | 99 ++++- .../rustc_hir_typeck/src/expr_use_visitor.rs | 12 +- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 3 + compiler/rustc_lint/src/autorefs.rs | 1 + compiler/rustc_middle/src/mir/pretty.rs | 8 + compiler/rustc_middle/src/mir/statement.rs | 8 + compiler/rustc_middle/src/mir/syntax.rs | 19 + compiler/rustc_middle/src/mir/visit.rs | 14 + compiler/rustc_middle/src/thir.rs | 12 + compiler/rustc_middle/src/thir/visit.rs | 1 + compiler/rustc_middle/src/ty/adjustment.rs | 8 + .../src/builder/expr/as_place.rs | 6 + .../src/builder/expr/as_rvalue.rs | 4 + .../src/builder/expr/category.rs | 3 +- .../rustc_mir_build/src/builder/expr/into.rs | 10 + .../rustc_mir_build/src/check_unsafety.rs | 3 +- compiler/rustc_mir_build/src/thir/cx/expr.rs | 7 + .../src/thir/pattern/check_match.rs | 6 + compiler/rustc_mir_build/src/thir/print.rs | 7 + .../src/impls/borrowed_locals.rs | 3 +- .../src/move_paths/builder.rs | 5 +- .../src/add_subtyping_projections.rs | 3 + .../src/dataflow_const_prop.rs | 2 +- compiler/rustc_mir_transform/src/gvn.rs | 15 + .../src/known_panics_lint.rs | 6 +- compiler/rustc_mir_transform/src/lint.rs | 1 + .../rustc_mir_transform/src/promote_consts.rs | 6 + compiler/rustc_mir_transform/src/validate.rs | 2 +- compiler/rustc_public/src/mir/body.rs | 8 + compiler/rustc_public/src/mir/pretty.rs | 7 + compiler/rustc_public/src/mir/visit.rs | 5 + .../src/unstable/convert/stable/mir.rs | 5 + compiler/rustc_span/src/symbol.rs | 2 + compiler/rustc_ty_utils/src/consts.rs | 6 + library/core/src/marker.rs | 16 + library/core/src/ops/mod.rs | 3 - library/core/src/ops/reborrow.rs | 16 - .../clippy_utils/src/qualify_min_const_fn.rs | 2 +- .../feature-gate-reborrow-coerce-shared.rs | 2 +- ...feature-gate-reborrow-coerce-shared.stderr | 4 +- .../ui/feature-gates/feature-gate-reborrow.rs | 2 +- .../feature-gate-reborrow.stderr | 4 +- tests/ui/reborrow/custom_marker.rs | 17 + .../ui/reborrow/custom_marker_assign_deref.rs | 26 ++ .../reborrow/custom_marker_coerce_shared.rs | 22 ++ .../custom_marker_coerce_shared_copy.rs | 22 ++ .../custom_marker_coerce_shared_move.rs | 21 ++ .../custom_marker_coerce_shared_move.stderr | 16 + tests/ui/reborrow/custom_marker_deref.rs | 17 + tests/ui/reborrow/custom_marker_mut_a_b.rs | 17 + .../ui/reborrow/custom_marker_mut_a_b.stderr | 14 + tests/ui/reborrow/custom_marker_mut_self.rs | 15 + .../ui/reborrow/custom_marker_mut_self.stderr | 15 + tests/ui/reborrow/custom_marker_mut_self_a.rs | 18 + .../reborrow/custom_marker_mut_self_a.stderr | 28 ++ tests/ui/reborrow/custom_marker_mut_self_b.rs | 17 + .../reborrow/custom_marker_mut_self_b.stderr | 16 + .../reborrow/custom_marker_two_lifetimes.rs | 8 + .../custom_marker_two_lifetimes.stderr | 8 + tests/ui/reborrow/custom_mut.rs | 11 +- tests/ui/reborrow/custom_mut.stderr | 29 -- tests/ui/reborrow/custom_mut_coerce_shared.rs | 17 +- .../reborrow/custom_mut_coerce_shared.stderr | 19 - 77 files changed, 1249 insertions(+), 106 deletions(-) delete mode 100644 library/core/src/ops/reborrow.rs create mode 100644 tests/ui/reborrow/custom_marker.rs create mode 100644 tests/ui/reborrow/custom_marker_assign_deref.rs create mode 100644 tests/ui/reborrow/custom_marker_coerce_shared.rs create mode 100644 tests/ui/reborrow/custom_marker_coerce_shared_copy.rs create mode 100644 tests/ui/reborrow/custom_marker_coerce_shared_move.rs create mode 100644 tests/ui/reborrow/custom_marker_coerce_shared_move.stderr create mode 100644 tests/ui/reborrow/custom_marker_deref.rs create mode 100644 tests/ui/reborrow/custom_marker_mut_a_b.rs create mode 100644 tests/ui/reborrow/custom_marker_mut_a_b.stderr create mode 100644 tests/ui/reborrow/custom_marker_mut_self.rs create mode 100644 tests/ui/reborrow/custom_marker_mut_self.stderr create mode 100644 tests/ui/reborrow/custom_marker_mut_self_a.rs create mode 100644 tests/ui/reborrow/custom_marker_mut_self_a.stderr create mode 100644 tests/ui/reborrow/custom_marker_mut_self_b.rs create mode 100644 tests/ui/reborrow/custom_marker_mut_self_b.stderr create mode 100644 tests/ui/reborrow/custom_marker_two_lifetimes.rs create mode 100644 tests/ui/reborrow/custom_marker_two_lifetimes.stderr delete mode 100644 tests/ui/reborrow/custom_mut.stderr delete mode 100644 tests/ui/reborrow/custom_mut_coerce_shared.stderr diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index 4644c210137f..6d6870364231 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -2,11 +2,12 @@ use std::ops::Index; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_hir::Mutability; use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::{MutatingUseContext, NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{self, Body, Local, Location, traversal}; -use rustc_middle::span_bug; use rustc_middle::ty::{RegionVid, TyCtxt}; +use rustc_middle::{bug, span_bug, ty}; use rustc_mir_dataflow::move_paths::MoveData; use tracing::debug; @@ -300,6 +301,50 @@ fn visit_assign( idx }; + self.local_map.entry(borrowed_place.local).or_default().insert(idx); + } else if let &mir::Rvalue::Reborrow(target, mutability, borrowed_place) = rvalue { + let borrowed_place_ty = borrowed_place.ty(self.body, self.tcx).ty; + let &ty::Adt(reborrowed_adt, _reborrowed_args) = borrowed_place_ty.kind() else { + unreachable!() + }; + let &ty::Adt(target_adt, assigned_args) = target.kind() else { unreachable!() }; + let Some(ty::GenericArgKind::Lifetime(region)) = assigned_args.get(0).map(|r| r.kind()) + else { + bug!( + "hir-typeck passed but {} does not have a lifetime argument", + if mutability == Mutability::Mut { "Reborrow" } else { "CoerceShared" } + ); + }; + let region = region.as_var(); + let kind = if mutability == Mutability::Mut { + // Reborrow + if target_adt.did() != reborrowed_adt.did() { + bug!( + "hir-typeck passed but Reborrow involves mismatching types at {location:?}" + ) + } + + mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default } + } else { + // CoerceShared + if target_adt.did() == reborrowed_adt.did() { + bug!( + "hir-typeck passed but CoerceShared involves matching types at {location:?}" + ) + } + mir::BorrowKind::Shared + }; + let borrow = BorrowData { + kind, + region, + reserve_location: location, + activation_location: TwoPhaseActivation::NotTwoPhase, + borrowed_place, + assigned_place: *assigned_place, + }; + let (idx, _) = self.location_map.insert_full(location, borrow); + let idx = BorrowIndex::from(idx); + self.local_map.entry(borrowed_place.local).or_default().insert(idx); } diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 8ddfa4b61edd..ef167644f0a9 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -549,7 +549,7 @@ fn apply_primary_statement_effect( ) { match &stmt.kind { mir::StatementKind::Assign(box (lhs, rhs)) => { - if let mir::Rvalue::Ref(_, _, place) = rhs { + if let mir::Rvalue::Ref(_, _, place) | mir::Rvalue::Reborrow(_, _, place) = rhs { if place.ignore_borrow( self.tcx, self.body, diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index acdeea91a189..25bcce8fdbf4 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1265,6 +1265,7 @@ fn check_access_for_conflict( let mut error_reported = false; let borrows_in_scope = self.borrows_in_scope(location, state); + debug!(?borrows_in_scope, ?location); each_borrow_involving_path( self, @@ -1507,6 +1508,36 @@ fn consume_rvalue( ); } + &Rvalue::Reborrow(_target, mutability, place) => { + let access_kind = ( + Deep, + if mutability == Mutability::Mut { + Write(WriteKind::MutableBorrow(BorrowKind::Mut { + kind: MutBorrowKind::Default, + })) + } else { + Read(ReadKind::Borrow(BorrowKind::Shared)) + }, + ); + + self.access_place( + location, + (place, span), + access_kind, + LocalMutationIsAllowed::Yes, + state, + ); + + let action = InitializationRequiringAction::Borrow; + + self.check_if_path_or_subpath_is_moved( + location, + action, + (place.as_ref(), span), + state, + ); + } + &Rvalue::RawPtr(kind, place) => { let access_kind = match kind { RawPtrKind::Mut => ( diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index 136719a323ce..da3fc0763a43 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -274,6 +274,21 @@ fn consume_rvalue(&mut self, location: Location, rvalue: &Rvalue<'tcx>) { self.access_place(location, place, access_kind, LocalMutationIsAllowed::No); } + &Rvalue::Reborrow(_target, mutability, place) => { + let access_kind = ( + Deep, + if mutability == Mutability::Mut { + Reservation(WriteKind::MutableBorrow(BorrowKind::Mut { + kind: MutBorrowKind::TwoPhaseBorrow, + })) + } else { + Read(ReadKind::Borrow(BorrowKind::Shared)) + }, + ); + + self.access_place(location, place, access_kind, LocalMutationIsAllowed::No); + } + &Rvalue::RawPtr(kind, place) => { let access_kind = match kind { RawPtrKind::Mut => ( diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 8998ced10bf9..48389d935499 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1580,6 +1580,15 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { self.add_reborrow_constraint(location, *region, borrowed_place); } + Rvalue::Reborrow(target, mutability, borrowed_place) => { + self.add_generic_reborrow_constraint( + *mutability, + location, + borrowed_place, + *target, + ); + } + Rvalue::BinaryOp( BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge, box (left, right), @@ -2218,6 +2227,7 @@ fn rvalue_user_ty(&self, rvalue: &Rvalue<'tcx>) -> Option, + dest_ty: Ty<'tcx>, + ) { + let Self { borrow_set, location_table, polonius_facts, constraints, infcx, body, .. } = + self; + + debug!( + "add_generic_reborrow_constraint({:?}, {:?}, {:?}, {:?})", + mutability, location, borrowed_place, dest_ty + ); + + let tcx = infcx.tcx; + let def = body.source.def_id().expect_local(); + let upvars = tcx.closure_captures(def); + let field = + path_utils::is_upvar_field_projection(tcx, upvars, borrowed_place.as_ref(), body); + let category = if let Some(field) = field { + ConstraintCategory::ClosureUpvar(field) + } else { + ConstraintCategory::Boring + }; + + let borrowed_ty = borrowed_place.ty(self.body, tcx).ty; + + let ty::Adt(dest_adt, dest_args) = dest_ty.kind() else { bug!() }; + let [dest_arg, ..] = ***dest_args else { bug!() }; + let ty::GenericArgKind::Lifetime(dest_region) = dest_arg.kind() else { bug!() }; + constraints.liveness_constraints.add_location(dest_region.as_var(), location); + + // In Polonius mode, we also push a `loan_issued_at` fact + // linking the loan to the region. + if let Some(polonius_facts) = polonius_facts { + let _prof_timer = infcx.tcx.prof.generic_activity("polonius_fact_generation"); + if let Some(borrow_index) = borrow_set.get_index_of(&location) { + let region_vid = dest_region.as_var(); + polonius_facts.loan_issued_at.push(( + region_vid.into(), + borrow_index, + location_table.mid_index(location), + )); + } + } + + if mutability.is_not() { + // FIXME(reborrow): for CoerceShared we need to relate the types manually, field by + // field. We cannot just attempt to relate `T` and `::Target` by + // calling relate_types as they are (generally) two unrelated user-defined ADTs, such as + // `CustomMut<'a>` and `CustomRef<'a>`, or `CustomMut<'a, T>` and `CustomRef<'a, T>`. + // Field-by-field relate_types is expected to work based on the wf-checks that the + // CoerceShared trait performs. + let ty::Adt(borrowed_adt, borrowed_args) = borrowed_ty.kind() else { unreachable!() }; + let borrowed_fields = borrowed_adt.all_fields().collect::>(); + for dest_field in dest_adt.all_fields() { + let Some(borrowed_field) = + borrowed_fields.iter().find(|f| f.name == dest_field.name) + else { + continue; + }; + let dest_ty = dest_field.ty(tcx, dest_args); + let borrowed_ty = borrowed_field.ty(tcx, borrowed_args); + if let ( + ty::Ref(borrow_region, _, Mutability::Mut), + ty::Ref(ref_region, _, Mutability::Not), + ) = (borrowed_ty.kind(), dest_ty.kind()) + { + self.relate_types( + borrowed_ty.peel_refs(), + ty::Variance::Covariant, + dest_ty.peel_refs(), + location.to_locations(), + category, + ) + .unwrap(); + self.constraints.outlives_constraints.push(OutlivesConstraint { + sup: ref_region.as_var(), + sub: borrow_region.as_var(), + locations: location.to_locations(), + span: location.to_locations().span(self.body), + category, + variance_info: ty::VarianceDiagInfo::default(), + from_closure: false, + }); + } else { + self.relate_types( + borrowed_ty, + ty::Variance::Covariant, + dest_ty, + location.to_locations(), + category, + ) + .unwrap(); + } + } + } else { + // Exclusive reborrow + self.relate_types( + borrowed_ty, + ty::Variance::Covariant, + dest_ty, + location.to_locations(), + category, + ) + .unwrap(); + } + } + fn prove_aggregate_predicates( &mut self, aggregate_kind: &AggregateKind<'tcx>, diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 3d15ad819310..1d90c8e0dadc 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -629,6 +629,11 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: let ref_ = place.place_ref(fx, lval.layout()); lval.write_cvalue(fx, ref_); } + Rvalue::Reborrow(_, _, place) => { + let cplace = codegen_place(fx, place); + let val = cplace.to_cvalue(fx); + lval.write_cvalue(fx, val) + } Rvalue::ThreadLocalRef(def_id) => { let val = crate::constant::codegen_tls_ref(fx, def_id, lval.layout()); lval.write_cvalue(fx, val); diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 9a491c9c3eb1..45c6621ba013 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -518,6 +518,14 @@ pub(crate) fn codegen_rvalue_operand( self.codegen_place_to_pointer(bx, place, mk_ref) } + // Note: Exclusive reborrowing is always equal to a memcpy, as the types do not change. + // Generic shared reborrowing is not (necessarily) a simple memcpy, but currently the + // coherence check places such restrictions on the CoerceShared trait as to guarantee + // that it is. + mir::Rvalue::Reborrow(_, _, place) => { + self.codegen_operand(bx, &mir::Operand::Copy(place)) + } + mir::Rvalue::RawPtr(kind, place) => { let mk_ptr = move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| { Ty::new_ptr(tcx, ty, kind.to_mutbl_lossy()) diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index b66d69b92407..d7b18518aba9 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -610,6 +610,10 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { } } + Rvalue::Reborrow(..) => { + // FIXME(reborrow): figure out if this is relevant at all. + } + Rvalue::RawPtr(RawPtrKind::FakeForPtrMetadata, place) => { // These are only inserted for slice length, so the place must already be indirect. // This implies we do not have to worry about whether the borrow escapes. diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index 6f7fccdaf266..e1cab9af046b 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -252,6 +252,8 @@ pub fn in_rvalue<'tcx, Q, F>( in_place::(cx, in_local, place.as_ref()) } + Rvalue::Reborrow(_, _, place) => in_place::(cx, in_local, place.as_ref()), + Rvalue::WrapUnsafeBinder(op, _) => in_operand::(cx, in_local, op), Rvalue::Aggregate(kind, operands) => { diff --git a/compiler/rustc_const_eval/src/check_consts/resolver.rs b/compiler/rustc_const_eval/src/check_consts/resolver.rs index 044b8b091b8d..a230f797b56f 100644 --- a/compiler/rustc_const_eval/src/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/check_consts/resolver.rs @@ -191,6 +191,19 @@ fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { } } + mir::Rvalue::Reborrow(target, mutability, borrowed_place) => { + // A Reborrow allows mutation if it is Reborrow or if the CoerceShared target isn't + // Freeze. + if !borrowed_place.is_indirect() + && (mutability.is_mut() || !target.is_freeze(self.ccx.tcx, self.ccx.typing_env)) + { + if Q::in_any_value_of_ty(self.ccx, *target) { + self.state.qualif.insert(borrowed_place.local); + self.state.borrow.insert(borrowed_place.local); + } + } + } + mir::Rvalue::Cast(..) | mir::Rvalue::Use(..) | mir::Rvalue::CopyForDeref(..) diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 6aa2971c900c..8376d0499990 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -230,6 +230,11 @@ pub fn eval_rvalue_into_place( })?; } + Reborrow(_, _, place) => { + let op = self.eval_place_to_op(place, Some(dest.layout))?; + self.copy_op(&op, &dest)?; + } + RawPtr(kind, place) => { // Figure out whether this is an addr_of of an already raw place. let place_base_raw = if place.is_indirect_first_projection() { diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 133efb77317b..36cf398dedb1 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -437,7 +437,7 @@ fn stable_hash(&self, _: &mut Hcx, hasher: &mut StableHashe // Reborrowing related lang-items Reborrow, sym::reborrow, reborrow, Target::Trait, GenericRequirement::Exact(0); - CoerceShared, sym::coerce_shared, coerce_shared, Target::Trait, GenericRequirement::Exact(0); + CoerceShared, sym::coerce_shared, coerce_shared, Target::Trait, GenericRequirement::Exact(1); // Field representing types. FieldRepresentingType, sym::field_representing_type, field_representing_type, Target::Struct, GenericRequirement::Exact(3); diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index ba22ee6d1aa8..0fb565b67d04 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -9,12 +9,13 @@ use rustc_hir::ItemKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; -use rustc_infer::infer::{self, RegionResolutionError, SubregionOrigin, TyCtxtInferExt}; -use rustc_infer::traits::Obligation; +use rustc_infer::infer::{self, InferCtxt, RegionResolutionError, SubregionOrigin, TyCtxtInferExt}; +use rustc_infer::traits::{Obligation, PredicateObligations}; use rustc_middle::ty::adjustment::CoerceUnsizedInfo; use rustc_middle::ty::print::PrintTraitRefExt as _; +use rustc_middle::ty::relate::solver_relating::RelateExt; use rustc_middle::ty::{ - self, Ty, TyCtxt, TypeVisitableExt, TypingMode, suggest_constraining_type_params, + self, Ty, TyCtxt, TypeVisitableExt, TypingMode, Unnormalized, suggest_constraining_type_params, }; use rustc_span::{DUMMY_SP, Span, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; @@ -22,7 +23,7 @@ ConstParamTyImplementationError, CopyImplementationError, InfringingFieldsReason, type_allowed_to_implement_const_param_ty, type_allowed_to_implement_copy, }; -use rustc_trait_selection::traits::{self, ObligationCause, ObligationCtxt}; +use rustc_trait_selection::traits::{self, FulfillmentError, ObligationCause, ObligationCtxt}; use tracing::debug; use crate::errors; @@ -43,6 +44,8 @@ pub(super) fn check_trait<'tcx>( visit_implementation_of_const_param_ty(checker) })?; checker.check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized)?; + checker.check(lang_items.reborrow(), visit_implementation_of_reborrow)?; + checker.check(lang_items.coerce_shared(), visit_implementation_of_coerce_shared)?; checker .check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn)?; checker.check( @@ -259,6 +262,28 @@ fn visit_implementation_of_coerce_unsized(checker: &Checker<'_>) -> Result<(), E tcx.ensure_result().coerce_unsized_info(impl_did) } +fn visit_implementation_of_reborrow(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { + let tcx = checker.tcx; + let impl_did = checker.impl_def_id; + debug!("visit_implementation_of_reborrow: impl_did={:?}", impl_did); + + // Just compute this for the side-effects, in particular reporting + // errors; other parts of the code may demand it for the info of + // course. + reborrow_info(tcx, impl_did) +} + +fn visit_implementation_of_coerce_shared(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { + let tcx = checker.tcx; + let impl_did = checker.impl_def_id; + debug!("visit_implementation_of_coerce_shared: impl_did={:?}", impl_did); + + // Just compute this for the side-effects, in particular reporting + // errors; other parts of the code may demand it for the info of + // course. + coerce_shared_info(tcx, impl_did) +} + fn is_from_coerce_pointee_derive(tcx: TyCtxt<'_>, span: Span) -> bool { span.ctxt() .outer_expn_data() @@ -444,6 +469,327 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() } } +fn structurally_normalize_ty<'tcx>( + tcx: TyCtxt<'tcx>, + infcx: &InferCtxt<'tcx>, + impl_did: LocalDefId, + span: Span, + ty: Unnormalized<'tcx, Ty<'tcx>>, +) -> Option<(Ty<'tcx>, PredicateObligations<'tcx>)> { + let ocx = ObligationCtxt::new(infcx); + let Ok(normalized_ty) = ocx.structurally_normalize_ty( + &traits::ObligationCause::misc(span, impl_did), + tcx.param_env(impl_did), + ty, + ) else { + // We shouldn't have errors here in the old solver, except for + // evaluate/fulfill mismatches, but that's not a reason for an ICE. + return None; + }; + let errors = ocx.try_evaluate_obligations(); + if !errors.is_empty() { + if infcx.next_trait_solver() { + unreachable!(); + } + // We shouldn't have errors here in the old solver, except for + // evaluate/fulfill mismatches, but that's not a reason for an ICE. + debug!(?errors, "encountered errors while fulfilling"); + return None; + } + + Some((normalized_ty, ocx.into_pending_obligations())) +} + +pub(crate) fn reborrow_info<'tcx>( + tcx: TyCtxt<'tcx>, + impl_did: LocalDefId, +) -> Result<(), ErrorGuaranteed> { + debug!("compute_reborrow_info(impl_did={:?})", impl_did); + let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let span = tcx.def_span(impl_did); + let trait_name = "Reborrow"; + + let reborrow_trait = tcx.require_lang_item(LangItem::Reborrow, span); + + let source = tcx.type_of(impl_did).instantiate_identity().skip_norm_wip(); + let trait_ref = tcx.impl_trait_ref(impl_did).instantiate_identity().skip_norm_wip(); + + if trait_impl_lifetime_params_count(tcx, impl_did) != 1 { + return Err(tcx + .dcx() + .emit_err(errors::CoerceSharedNotSingleLifetimeParam { span, trait_name })); + } + + assert_eq!(trait_ref.def_id, reborrow_trait); + let param_env = tcx.param_env(impl_did); + assert!(!source.has_escaping_bound_vars()); + + let (def, args) = match source.kind() { + &ty::Adt(def, args) if def.is_struct() => (def, args), + _ => { + // Note: reusing error here as it takes trait_name as argument. + return Err(tcx.dcx().emit_err(errors::CoerceUnsizedNonStruct { span, trait_name })); + } + }; + + let lifetimes_count = generic_lifetime_params_count(args); + let data_fields = collect_struct_data_fields(tcx, def, args); + + if lifetimes_count != 1 { + let item = tcx.hir_expect_item(impl_did); + let _span = if let ItemKind::Impl(hir::Impl { of_trait: Some(of_trait), .. }) = &item.kind { + of_trait.trait_ref.path.span + } else { + tcx.def_span(impl_did) + }; + + return Err(tcx.dcx().emit_err(errors::CoerceSharedMulti { span, trait_name })); + } + + if data_fields.is_empty() { + return Ok(()); + } + + // We've found some data fields. They must all be either be Copy or Reborrow. + for (field, span) in data_fields { + if assert_field_type_is_reborrow( + tcx, + &infcx, + reborrow_trait, + impl_did, + param_env, + field, + span, + ) + .is_ok() + { + // Field implements Reborrow. + return Ok(()); + } + + // Field does not implement Reborrow: it must be Copy. + assert_field_type_is_copy(tcx, &infcx, impl_did, param_env, field, span)?; + } + + Ok(()) +} + +fn assert_field_type_is_reborrow<'tcx>( + tcx: TyCtxt<'tcx>, + infcx: &InferCtxt<'tcx>, + reborrow_trait: DefId, + impl_did: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + span: Span, +) -> Result<(), Vec>> { + if ty.ref_mutability() == Some(ty::Mutability::Mut) { + // Mutable references are Reborrow but not really. + return Ok(()); + } + let ocx = ObligationCtxt::new_with_diagnostics(infcx); + let cause = traits::ObligationCause::misc(span, impl_did); + let obligation = + Obligation::new(tcx, cause, param_env, ty::TraitRef::new(tcx, reborrow_trait, [ty])); + ocx.register_obligation(obligation); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); + + if !errors.is_empty() { Err(errors) } else { Ok(()) } +} + +pub(crate) fn coerce_shared_info<'tcx>( + tcx: TyCtxt<'tcx>, + impl_did: LocalDefId, +) -> Result<(), ErrorGuaranteed> { + debug!("compute_coerce_shared_info(impl_did={:?})", impl_did); + let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let span = tcx.def_span(impl_did); + let trait_name = "CoerceShared"; + + let coerce_shared_trait = tcx.require_lang_item(LangItem::CoerceShared, span); + + let source = tcx.type_of(impl_did).instantiate_identity().skip_norm_wip(); + let trait_ref = tcx.impl_trait_ref(impl_did).instantiate_identity().skip_norm_wip(); + + if trait_impl_lifetime_params_count(tcx, impl_did) != 1 { + return Err(tcx + .dcx() + .emit_err(errors::CoerceSharedNotSingleLifetimeParam { span, trait_name })); + } + + assert_eq!(trait_ref.def_id, coerce_shared_trait); + let Some((target, _obligations)) = structurally_normalize_ty( + tcx, + &infcx, + impl_did, + span, + Unnormalized::new_wip(trait_ref.args.type_at(1)), + ) else { + todo!("something went wrong with structurally_normalize_ty"); + }; + + let param_env = tcx.param_env(impl_did); + assert!(!source.has_escaping_bound_vars()); + + let data = match (source.kind(), target.kind()) { + (&ty::Adt(def_a, args_a), &ty::Adt(def_b, args_b)) + if def_a.is_struct() && def_b.is_struct() => + { + // Check that both A and B have exactly one lifetime argument, and that they have the + // same number of data fields that is not more than 1. The eventual intention is to + // support multiple lifetime arguments (with the reborrowed lifetimes inferred from + // usage one way or another) and multiple data fields with B allowed to leave out fields + // from A. The current state is just the simplest choice. + let a_lifetimes_count = generic_lifetime_params_count(args_a); + let a_data_fields = collect_struct_data_fields(tcx, def_a, args_a); + let b_lifetimes_count = generic_lifetime_params_count(args_b); + let b_data_fields = collect_struct_data_fields(tcx, def_b, args_b); + + if a_lifetimes_count != 1 + || b_lifetimes_count != 1 + || a_data_fields.len() > 1 + || b_data_fields.len() > 1 + || a_data_fields.len() != b_data_fields.len() + { + let item = tcx.hir_expect_item(impl_did); + let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(of_trait), .. }) = + &item.kind + { + of_trait.trait_ref.path.span + } else { + tcx.def_span(impl_did) + }; + + return Err(tcx.dcx().emit_err(errors::CoerceSharedMulti { span, trait_name })); + } + + if a_data_fields.len() == 1 { + // We found one data field for both: we'll attempt to perform CoerceShared between + // them below. + let (a, span_a) = a_data_fields[0]; + let (b, span_b) = b_data_fields[0]; + + Some((a, b, coerce_shared_trait, span_a, span_b)) + } else { + // We found no data fields in either: this is a reborrowable marker type being + // coerced into a shared marker. That is fine too. + None + } + } + + _ => { + // Note: reusing CoerceUnsizedNonStruct error as it takes trait_name as argument. + return Err(tcx.dcx().emit_err(errors::CoerceUnsizedNonStruct { span, trait_name })); + } + }; + + // We've proven that we have two types with one lifetime each and 0 or 1 data fields each. + if let Some((source, target, trait_def_id, source_field_span, _target_field_span)) = data { + // struct Source(SourceData); + // struct Target(TargetData); + // + // 1 data field each; they must be the same type and Copy, or relate to one another using + // CoerceShared. + if source.ref_mutability() == Some(ty::Mutability::Mut) + && target.ref_mutability() == Some(ty::Mutability::Not) + && infcx + .eq_structurally_relating_aliases( + param_env, + source.peel_refs(), + target.peel_refs(), + source_field_span, + ) + .is_ok() + { + // &mut T implements CoerceShared to &T, except not really. + return Ok(()); + } + if infcx + .eq_structurally_relating_aliases(param_env, source, target, source_field_span) + .is_err() + { + // The two data fields don't agree on a common type; this means + // that they must be `A: CoerceShared`. Register an obligation + // for that. + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); + let cause = traits::ObligationCause::misc(span, impl_did); + let obligation = Obligation::new( + tcx, + cause, + param_env, + ty::TraitRef::new(tcx, trait_def_id, [source, target]), + ); + ocx.register_obligation(obligation); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); + + if !errors.is_empty() { + return Err(infcx.err_ctxt().report_fulfillment_errors(errors)); + } + // Finally, resolve all regions. + ocx.resolve_regions_and_report_errors(impl_did, param_env, [])?; + } else { + // Types match: check that it is Copy. + assert_field_type_is_copy(tcx, &infcx, impl_did, param_env, source, source_field_span)?; + } + } + + Ok(()) +} + +fn trait_impl_lifetime_params_count(tcx: TyCtxt<'_>, did: LocalDefId) -> usize { + tcx.generics_of(did) + .own_params + .iter() + .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime)) + .count() +} + +fn generic_lifetime_params_count(args: &[ty::GenericArg<'_>]) -> usize { + args.iter().filter(|arg| arg.as_region().is_some()).count() +} + +fn collect_struct_data_fields<'tcx>( + tcx: TyCtxt<'tcx>, + def: ty::AdtDef<'tcx>, + args: ty::GenericArgsRef<'tcx>, +) -> Vec<(Ty<'tcx>, Span)> { + def.non_enum_variant() + .fields + .iter() + .filter_map(|f| { + // Ignore PhantomData fields + let ty = f.ty(tcx, args); + if ty.is_phantom_data() { + return None; + } + Some((ty, tcx.def_span(f.did))) + }) + .collect() +} + +fn assert_field_type_is_copy<'tcx>( + tcx: TyCtxt<'tcx>, + infcx: &InferCtxt<'tcx>, + impl_did: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + span: Span, +) -> Result<(), ErrorGuaranteed> { + let copy_trait = tcx.require_lang_item(LangItem::Copy, span); + let ocx = ObligationCtxt::new_with_diagnostics(infcx); + let cause = traits::ObligationCause::misc(span, impl_did); + let obligation = + Obligation::new(tcx, cause, param_env, ty::TraitRef::new(tcx, copy_trait, [ty])); + ocx.register_obligation(obligation); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); + + if !errors.is_empty() { + Err(infcx.err_ctxt().report_fulfillment_errors(errors)) + } else { + Ok(()) + } +} + pub(crate) fn coerce_unsized_info<'tcx>( tcx: TyCtxt<'tcx>, impl_did: LocalDefId, diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 1bb54f27dcbd..e0528a3e3b49 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1358,6 +1358,24 @@ pub(crate) struct CoerceMulti { pub fields: MultiSpan, } +#[derive(Diagnostic)] +#[diag( + "implementing `{$trait_name}` requires that a single lifetime parameter is passed between source and target" +)] +pub(crate) struct CoerceSharedNotSingleLifetimeParam { + #[primary_span] + pub span: Span, + pub trait_name: &'static str, +} + +#[derive(Diagnostic)] +#[diag("implementing `{$trait_name}` does not allow multiple lifetimes or fields to be coerced")] +pub(crate) struct CoerceSharedMulti { + #[primary_span] + pub span: Span, + pub trait_name: &'static str, +} + #[derive(Diagnostic)] #[diag("the trait `{$trait_name}` may only be implemented for a coercion between structures", code = E0377)] pub(crate) struct CoerceUnsizedNonStruct { diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index abd5f38f0ed0..9d9a42c13234 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -283,7 +283,8 @@ fn coerce(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { } // Examine the target type and consider type-specific coercions, such - // as auto-borrowing, coercing pointer mutability, or pin-ergonomics. + // as auto-borrowing, coercing pointer mutability, pin-ergonomics, or + // generic reborrow. match *b.kind() { ty::RawPtr(_, b_mutbl) => { return self.coerce_to_raw_ptr(a, b, b_mutbl); @@ -297,6 +298,26 @@ fn coerce(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { _ if let Some(to_pin_ref) = self.maybe_to_pin_ref(a, b) => { return self.coerce_to_pin_ref(to_pin_ref); } + ty::Adt(_, _) + if self.tcx.features().reborrow() + && self + .fcx + .infcx + .type_implements_trait( + self.tcx + .lang_items() + .reborrow() + .expect("Unexpectedly using core/std without reborrow"), + [b], + self.fcx.param_env, + ) + .must_apply_modulo_regions() => + { + let reborrow_coerce = self.commit_if_ok(|_| self.coerce_reborrow(a, b)); + if reborrow_coerce.is_ok() { + return reborrow_coerce; + } + } _ => {} } @@ -320,6 +341,14 @@ fn coerce(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { // It cannot convert closures that require unsafe. self.coerce_closure_to_fn(a, b) } + ty::Adt(_, _) if self.tcx.features().reborrow() => { + let reborrow_coerce = self.commit_if_ok(|_| self.coerce_shared_reborrow(a, b)); + if reborrow_coerce.is_ok() { + reborrow_coerce + } else { + self.unify(a, b, ForceLeakCheck::No) + } + } _ => { // Otherwise, just use unification rules. self.unify(a, b, ForceLeakCheck::No) @@ -934,6 +963,74 @@ fn coerce_to_pin_ref( Ok(coerce) } + /// Applies generic exclusive reborrowing on type implementing `Reborrow`. + #[instrument(skip(self), level = "trace")] + fn coerce_reborrow(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { + debug_assert!(self.shallow_resolve(a) == a); + debug_assert!(self.shallow_resolve(b) == b); + + // We need to make sure the two types are compatible for reborrow. + let (ty::Adt(a_def, _), ty::Adt(b_def, _)) = (a.kind(), b.kind()) else { + return Err(TypeError::Mismatch); + }; + if a_def.did() == b_def.did() { + // Reborrow is applicable here + self.unify_and( + a, + b, + [], + Adjust::GenericReborrow(ty::Mutability::Mut), + ForceLeakCheck::No, + ) + } else { + // FIXME: CoerceShared check goes here, error for now + Err(TypeError::Mismatch) + } + } + + /// Applies generic exclusive reborrowing on type implementing `Reborrow`. + #[instrument(skip(self), level = "trace")] + fn coerce_shared_reborrow(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { + debug_assert!(self.shallow_resolve(a) == a); + debug_assert!(self.shallow_resolve(b) == b); + + // We need to make sure the two types are compatible for reborrow. + let (ty::Adt(a_def, _), ty::Adt(b_def, _)) = (a.kind(), b.kind()) else { + return Err(TypeError::Mismatch); + }; + if a_def.did() == b_def.did() { + // CoerceShared cannot be T -> T. + return Err(TypeError::Mismatch); + } + let Some(coerce_shared_trait_did) = self.tcx.lang_items().coerce_shared() else { + return Err(TypeError::Mismatch); + }; + let coerce_shared_trait_ref = ty::TraitRef::new(self.tcx, coerce_shared_trait_did, [a, b]); + let obligation = traits::Obligation::new( + self.tcx, + ObligationCause::dummy(), + self.param_env, + ty::Binder::dummy(coerce_shared_trait_ref), + ); + let ocx = ObligationCtxt::new(&self.infcx); + ocx.register_obligation(obligation); + let errs = ocx.evaluate_obligations_error_on_ambiguity(); + if errs.is_empty() { + Ok(InferOk { + value: ( + vec![Adjustment { + kind: Adjust::GenericReborrow(ty::Mutability::Not), + target: b, + }], + b, + ), + obligations: ocx.into_pending_obligations(), + }) + } else { + Err(TypeError::Mismatch) + } + } + fn coerce_from_fn_pointer( &self, a: Ty<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index a6129d97a328..010f96f85a22 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -751,6 +751,15 @@ fn walk_adjustment(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> { adjustment::Adjust::Borrow(ref autoref) => { self.walk_autoref(expr, &place_with_id, autoref); } + + adjustment::Adjust::GenericReborrow(_reborrow) => { + // To build an expression as a place expression, it needs to be a field + // projection or deref at the outmost layer. So it is field projection or deref + // on an adjusted value. But this means that adjustment is applied on a + // subexpression that is not the final operand/rvalue for function call or + // assignment. This is a contradiction. + unreachable!("Reborrow trait usage during adjustment walk"); + } } place_with_id = self.cat_expr_adjusted(expr, place_with_id, adjustment)?; } @@ -1282,7 +1291,8 @@ fn cat_expr_adjusted_with( adjustment::Adjust::NeverToAny | adjustment::Adjust::Pointer(_) - | adjustment::Adjust::Borrow(_) => { + | adjustment::Adjust::Borrow(_) + | adjustment::Adjust::GenericReborrow(..) => { // Result is an rvalue. Ok(self.cat_rvalue(expr.hir_id, target)) } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index eae0cb5cb4e2..6dd6741b48ea 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -343,6 +343,9 @@ pub(crate) fn apply_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec { // FIXME(const_trait_impl): We should probably enforce these. } + Adjust::GenericReborrow(_) => { + // FIXME(reborrow): figure out if we have effects to enforce here. + } Adjust::Borrow(_) => { // No effects to enforce here. } diff --git a/compiler/rustc_lint/src/autorefs.rs b/compiler/rustc_lint/src/autorefs.rs index 9a374488ab6f..24759f3ee4a0 100644 --- a/compiler/rustc_lint/src/autorefs.rs +++ b/compiler/rustc_lint/src/autorefs.rs @@ -173,6 +173,7 @@ fn has_implicit_borrow(Adjustment { kind, .. }: &Adjustment<'_>) -> Option<(Muta Adjust::NeverToAny | Adjust::Pointer(..) | Adjust::Deref(DerefAdjustKind::Builtin | DerefAdjustKind::Pin) + | Adjust::GenericReborrow(..) | Adjust::Borrow(AutoBorrow::RawPtr(..) | AutoBorrow::Pin(..)) => None, } } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 380ba959c25f..e1bc29d5c368 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1128,6 +1128,14 @@ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { write!(fmt, "&{region}{kind_str}{place:?}") } + Reborrow(target, mutability, ref place) => { + write!( + fmt, + "{target:?}({} {place:?})", + if mutability.is_mut() { "reborrow" } else { "coerce shared" } + ) + } + CopyForDeref(ref place) => write!(fmt, "deref_copy {place:#?}"), RawPtr(mutability, ref place) => { diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index c5945848baad..c1f1b398ac14 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -766,6 +766,7 @@ pub fn is_safe_to_remove(&self) -> bool { | Rvalue::CopyForDeref(_) | Rvalue::Repeat(_, _) | Rvalue::Ref(_, _, _) + | Rvalue::Reborrow(_, _, _) | Rvalue::ThreadLocalRef(_) | Rvalue::RawPtr(_, _) | Rvalue::Cast( @@ -790,6 +791,12 @@ pub fn is_safe_to_remove(&self) -> bool { } } + /// Returns true if rvalue is a generic Reborrow coercion (usage of Reborrow or CoerceShared + /// trait). + pub fn is_generic_reborrow(&self) -> bool { + matches!(self, Self::Reborrow(..)) + } + pub fn ty(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> Ty<'tcx> where D: ?Sized + HasLocalDecls<'tcx>, @@ -804,6 +811,7 @@ pub fn ty(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> Ty<'tcx> let place_ty = place.ty(local_decls, tcx).ty; Ty::new_ref(tcx, reg, place_ty, bk.to_mutbl_lossy()) } + Rvalue::Reborrow(target, _, _) => target, Rvalue::RawPtr(kind, ref place) => { let place_ty = place.ty(local_decls, tcx).ty; Ty::new_ptr(tcx, place_ty, kind.to_mutbl_lossy()) diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 0eeefd4060be..8b015e6cecaa 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1461,6 +1461,25 @@ pub enum Rvalue<'tcx> { /// Wraps a value in an unsafe binder. WrapUnsafeBinder(Operand<'tcx>, Ty<'tcx>), + + /// Creates a bitwise copy of the indicated place with the same type (if Mut) or its + /// CoerceShared target type (if Not). The type is known to be an ADT with exactly one lifetime + /// parameter, and it is known to implement the Reborrow trait (for Mut), and the CoerceShared + /// trait (only if Not). The CoerceShared target type is known to also have exactly one lifetime + /// parameter, implement Copy and (currently) have the same memory layout as the source type. + /// + /// The borrow checker uses the single lifetime in the source and target types to create a + /// Covariant outlives-bound between the source and target with the Mutability of the Reborrow. + /// This makes accessing the source value for writes (and reads if Mut) for the lifetime of the + /// target value a borrow check error, imitating `&mut T` and `&T`'s reborrowing on user ADTs. + /// + /// Future work may add support for multiple lifetimes and changing memory layout as part of + /// CoerceShared. These may be end up implemented as multiple MIR operations. + /// + /// This is produced by the [`ExprKind::Reborrow`]. + /// + /// [`ExprKind::Reborrow`]: crate::thir::ExprKind::Reborrow + Reborrow(Ty<'tcx>, Mutability, Place<'tcx>), } #[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, StableHash)] diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index c660c77cd84a..d73eac59dfb5 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -718,6 +718,18 @@ fn super_rvalue( self.visit_place(path, ctx, location); } + Rvalue::Reborrow(target, mutability, place) => { + self.visit_ty($(& $mutability)? *target, TyContext::Location(location)); + self.visit_place( + place, + match mutability { + Mutability::Not => PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow), + Mutability::Mut => PlaceContext::MutatingUse(MutatingUseContext::Borrow), + }, + location + ); + } + Rvalue::CopyForDeref(place) => { self.visit_place( place, @@ -802,6 +814,8 @@ fn super_rvalue( self.visit_operand(op, location); self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); } + + } } diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 887eb3d88890..0cc57e5021f8 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -546,6 +546,18 @@ pub enum ExprKind<'tcx> { Yield { value: ExprId, }, + /// Use of an ADT that implements the Reborrow (for Mut) or CoerceShared traits (for Not). This + /// expression is produced by the [`Adjust::GenericReborrow`] in places where normally the ADT + /// would be moved or assigned over. Instead, this produces an [`Rvalue::Reborrow`] which + /// produces a bitwise copy of the source ADT and disables the source for the copy's lifetime. + /// + /// [`Adjust::GenericReborrow`]: crate::ty::adjustment::Adjust::GenericReborrow + /// [`Rvalue::Reborrow`]: mir::Rvalue::Reborrow + Reborrow { + source: ExprId, + mutability: Mutability, + target: Ty<'tcx>, + }, } /// Represents the association of a field identifier and an expression. diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index aa1b6b1663bf..3361e43b15dd 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -187,6 +187,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( } ThreadLocalRef(_) => {} Yield { value } => visitor.visit_expr(&visitor.thir()[value]), + Reborrow { .. } => {} } } diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index 7d4f56d89c7a..7174427e517d 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -103,6 +103,14 @@ pub enum Adjust { Borrow(AutoBorrow), Pointer(PointerCoercion), + + /// Take a user-type T implementing the Reborrow trait (for Mut) or the CoerceShared trait (for + /// Not) and reborrow as `T` or `CoreceShared`. + /// + /// This produces an [`ExprKind::Reborrow`]. + /// + /// [`ExprKind::Reborrow`]: crate::thir::ExprKind::Reborrow + GenericReborrow(hir::Mutability), } #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, StableHash, TypeFoldable, TypeVisitable)] diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs index b95b565322f1..ff7e518f91a8 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -590,6 +590,12 @@ fn expr_as_place( let temp = unpack!(block = this.as_temp(block, temp_lifetime, expr_id, mutability)); block.and(PlaceBuilder::from(temp)) } + ExprKind::Reborrow { .. } => { + // FIXME(reborrow): it should currently be impossible to end up evaluating a + // Reborrow expression as a place. That might not in the future, but what this then + // evaluates to requires further thought. + unreachable!(); + } } } diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs index 3cf4e43160e5..903ff07a8893 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs @@ -434,6 +434,10 @@ pub(crate) fn as_rvalue( ); block.and(Rvalue::Use(operand, WithRetag::Yes)) } + ExprKind::Reborrow { source, mutability, target } => { + let temp = unpack!(block = this.as_temp(block, scope, source, mutability)); + block.and(Rvalue::Reborrow(target, mutability, temp.into())) + } } } diff --git a/compiler/rustc_mir_build/src/builder/expr/category.rs b/compiler/rustc_mir_build/src/builder/expr/category.rs index 5404d9800c3f..eb6a0754358d 100644 --- a/compiler/rustc_mir_build/src/builder/expr/category.rs +++ b/compiler/rustc_mir_build/src/builder/expr/category.rs @@ -43,7 +43,8 @@ pub(crate) fn of(ek: &ExprKind<'_>) -> Option { | ExprKind::PlaceTypeAscription { .. } | ExprKind::ValueTypeAscription { .. } | ExprKind::PlaceUnwrapUnsafeBinder { .. } - | ExprKind::ValueUnwrapUnsafeBinder { .. } => Some(Category::Place), + | ExprKind::ValueUnwrapUnsafeBinder { .. } + | ExprKind::Reborrow { .. } => Some(Category::Place), ExprKind::LogicalOp { .. } | ExprKind::Match { .. } diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 08379760d8c1..7413ab9bd238 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -908,6 +908,16 @@ fn is_supported_loop_match_type(ty: Ty<'_>) -> bool { this.cfg.push_assign(block, source_info, destination, rvalue); block.unit() } + ExprKind::Reborrow { source, mutability, target } => { + let place = unpack!(block = this.as_place(block, source)); + this.cfg.push_assign( + block, + source_info, + destination, + Rvalue::Reborrow(target, mutability, place.into()), + ); + block.unit() + } }; if !expr_is_block_or_scope { diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index aceddcc54de9..055932b5c30d 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -397,7 +397,8 @@ fn visit_expr(&mut self, expr: &'a Expr<'tcx>) { | ExprKind::If { .. } | ExprKind::InlineAsm { .. } | ExprKind::LogicalOp { .. } - | ExprKind::Use { .. } => { + | ExprKind::Use { .. } + | ExprKind::Reborrow { .. } => { // We don't need to save the old value and restore it // because all the place expressions can't have more // than one child. diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 69260792a95d..b32d7dce4f4d 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -221,6 +221,13 @@ fn apply_adjustment( debug!(?kind); kind } + Adjust::GenericReborrow(mutability) => { + let expr = self.thir.exprs.push(expr); + let kind = + ExprKind::Reborrow { source: expr, mutability, target: adjustment.target }; + + kind + } }; Expr { temp_scope_id, ty: adjustment.target, span, kind } diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 4dc3e02ace71..5b786f5a710f 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -364,6 +364,12 @@ fn is_known_valid_scrutinee(&self, scrutinee: &Expr<'tcx>) -> bool { | VarRef { .. } | ZstLiteral { .. } | Yield { .. } => true, + ExprKind::Reborrow { .. } => { + // FIXME(reborrow): matching on a Reborrow expression should be impossible + // currently. Whether this remains to be true, and if the reborrow result then is a + // known valid scrutinee requires further thought. + unreachable!("Reborrow expression in match") + } } } diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index ea34e5f4d97d..5330e3397db8 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -600,6 +600,13 @@ fn print_expr_kind(&mut self, expr_kind: &ExprKind<'tcx>, depth_lvl: usize) { self.print_expr(*value, depth_lvl + 2); print_indented!(self, "}", depth_lvl); } + ExprKind::Reborrow { source: _, mutability: _, target: _ } => { + print_indented!(self, "Reborrow {", depth_lvl); + print_indented!(self, "source:", depth_lvl + 1); + print_indented!(self, "mutability:", depth_lvl + 1); + print_indented!(self, "ty:", depth_lvl + 1); + print_indented!(self, "}", depth_lvl); + } } } diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index d5548266aa01..9ec68f5260c0 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -78,7 +78,8 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { // We ignore fake borrows as these get removed after analysis and shouldn't effect // the layout of generators. Rvalue::RawPtr(_, borrowed_place) - | Rvalue::Ref(_, BorrowKind::Mut { .. } | BorrowKind::Shared, borrowed_place) => { + | Rvalue::Ref(_, BorrowKind::Mut { .. } | BorrowKind::Shared, borrowed_place) + | Rvalue::Reborrow(_, _, borrowed_place) => { if !borrowed_place.is_indirect() { self.trans.gen_(borrowed_place.local); } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index ab53baae4327..905f36a3edb0 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -438,7 +438,10 @@ fn gather_rvalue(&mut self, rvalue: &Rvalue<'tcx>) { } } Rvalue::CopyForDeref(..) => unreachable!(), - Rvalue::Ref(..) | Rvalue::RawPtr(..) | Rvalue::Discriminant(..) => {} + Rvalue::Ref(..) + | Rvalue::Reborrow(..) + | Rvalue::RawPtr(..) + | Rvalue::Discriminant(..) => {} } } diff --git a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs index a6a60fddf909..fc31d502087f 100644 --- a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs +++ b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs @@ -23,6 +23,9 @@ fn visit_assign( rvalue: &mut Rvalue<'tcx>, location: Location, ) { + if rvalue.is_generic_reborrow() { + return; + } // We don't need to do anything for deref temps as they are // not part of the source code, but used for desugaring purposes. if self.local_decls[place.local].is_deref_temp() { diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index c28147ff7eb1..1c465977ec6c 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -469,7 +469,7 @@ fn handle_rvalue( Rvalue::Discriminant(place) => state.get_discr(place.as_ref(), &self.map), Rvalue::Use(operand, _) => return self.handle_operand(operand, state), Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in runtime MIR"), - Rvalue::Ref(..) | Rvalue::RawPtr(..) => { + Rvalue::Ref(..) | Rvalue::Reborrow(..) | Rvalue::RawPtr(..) => { // We don't track such places. return ValueOrPlace::TOP; } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index a7376fa7e65a..77f068ab7a66 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1073,6 +1073,21 @@ fn simplify_rvalue( self.simplify_place_projection(place, location); return self.new_pointer(*place, AddressKind::Ref(borrow_kind)); } + Rvalue::Reborrow(_, mutbl, place) => { + if mutbl == Mutability::Mut { + // Note: this is adapted from simplify_aggregate. + let mut operand = Operand::Copy(place); + let val = self.simplify_operand(&mut operand, location); + // FIXME(reborrow): Is it correct to make these retagging assignments? + *rvalue = Rvalue::Use(Operand::Copy(place), WithRetag::Yes); + return val; + } else { + // FIXME(reborrow): CoerceShared should perform effectively a copy followed by a + // transmute, or possibly something more complicated in the future. For now we + // leave this unoptimised. + return None; + } + } Rvalue::RawPtr(mutbl, ref mut place) => { self.simplify_place_projection(place, location); return self.new_pointer(*place, AddressKind::Address(mutbl)); diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 45c8bae295d9..6c2ca9166b10 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -421,8 +421,8 @@ fn check_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) -> Option< } // Do not try creating references (#67862) - Rvalue::RawPtr(_, place) | Rvalue::Ref(_, _, place) => { - trace!("skipping RawPtr | Ref for {:?}", place); + Rvalue::RawPtr(_, place) | Rvalue::Ref(_, _, place) | Rvalue::Reborrow(_, _, place) => { + trace!("skipping RawPtr | Ref | Reborrow for {:?}", place); // This may be creating mutable references or immutable references to cells. // If that happens, the pointed to value could be mutated via that reference. @@ -553,7 +553,7 @@ fn eval_rvalue(&mut self, rvalue: &Rvalue<'tcx>, dest: &Place<'tcx>) -> Option<( self.eval_operand(operand)?.into() } - CopyForDeref(place) => self.eval_place(place)?.into(), + CopyForDeref(place) | Reborrow(_, _, place) => self.eval_place(place)?.into(), BinaryOp(bin_op, box (ref left, ref right)) => { let left = self.eval_operand(left)?; diff --git a/compiler/rustc_mir_transform/src/lint.rs b/compiler/rustc_mir_transform/src/lint.rs index e450e6754da1..e0b6b2a1104e 100644 --- a/compiler/rustc_mir_transform/src/lint.rs +++ b/compiler/rustc_mir_transform/src/lint.rs @@ -96,6 +96,7 @@ fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { | Rvalue::UnaryOp(..) | Rvalue::BinaryOp(..) | Rvalue::Ref(..) + | Rvalue::Reborrow(..) | Rvalue::RawPtr(..) | Rvalue::Discriminant(..) => false, }; diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 041ff45c11d7..340975c8fa70 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -580,6 +580,12 @@ fn validate_rvalue(&mut self, rvalue: &Rvalue<'tcx>) -> Result<(), Unpromotable> self.validate_ref(*kind, place)?; } + Rvalue::Reborrow(_, _, place) => { + // FIXME(reborrow): should probably have a place_simplified like above. + let op = &Operand::Copy(*place); + self.validate_operand(op)? + } + Rvalue::Aggregate(_, operands) => { for o in operands { self.validate_operand(o)?; diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index a6cbd93a6a73..746662e6a302 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1152,7 +1152,7 @@ macro_rules! check_kinds { ); } } - Rvalue::Ref(..) => {} + Rvalue::Ref(..) | Rvalue::Reborrow(..) => {} Rvalue::BinaryOp(op, vals) => { use BinOp::*; let a = vals.0.ty(&self.body.local_decls, self.tcx); diff --git a/compiler/rustc_public/src/mir/body.rs b/compiler/rustc_public/src/mir/body.rs index 6aeed20b1f48..f9b5f9af951e 100644 --- a/compiler/rustc_public/src/mir/body.rs +++ b/compiler/rustc_public/src/mir/body.rs @@ -586,6 +586,13 @@ pub enum Rvalue { /// Yields the operand unchanged, except for possibly a retag. Use(Operand, WithRetag), + + /// Creates a bitwise copy of the source type, producing either a value of the same type (when + /// Mutability::Mut) or a different type with a guaranteed equal memory layout defined by the + /// CoerceShared trait. See [`Rvalue::Reborrow`] for a more detailed explanation. + /// + /// [`Rvalue::Reborrow`]: rustc_middle::mir::Rvalue::Reborrow + Reborrow(Ty, Mutability, Place), } impl Rvalue { @@ -600,6 +607,7 @@ pub fn ty(&self, locals: &[LocalDecl]) -> Result { let place_ty = place.ty(locals)?; Ok(Ty::new_ref(reg.clone(), place_ty, bk.to_mutable_lossy())) } + Rvalue::Reborrow(target, _, _) => Ok(*target), Rvalue::AddressOf(mutability, place) => { let place_ty = place.ty(locals)?; Ok(Ty::new_ptr(place_ty, mutability.to_mutable_lossy())) diff --git a/compiler/rustc_public/src/mir/pretty.rs b/compiler/rustc_public/src/mir/pretty.rs index dac39c636e31..dec4044fd260 100644 --- a/compiler/rustc_public/src/mir/pretty.rs +++ b/compiler/rustc_public/src/mir/pretty.rs @@ -379,6 +379,13 @@ fn pretty_rvalue(writer: &mut W, rval: &Rvalue) -> io::Result<()> { }; write!(writer, "{kind}{place:?}") } + Rvalue::Reborrow(target, mutability, place) => { + let kind = match mutability { + Mutability::Not => "Reborrow", + Mutability::Mut => "CoerceShared", + }; + write!(writer, "${kind}({place:?}) as {target}") + } Rvalue::Repeat(op, cnst) => { write!(writer, "[{}; {}]", pretty_operand(op), pretty_ty_const(cnst)) } diff --git a/compiler/rustc_public/src/mir/visit.rs b/compiler/rustc_public/src/mir/visit.rs index cf6540cc6c77..5a3afc993735 100644 --- a/compiler/rustc_public/src/mir/visit.rs +++ b/compiler/rustc_public/src/mir/visit.rs @@ -272,6 +272,11 @@ fn super_rvalue(&mut self, rvalue: &$($mutability)? Rvalue, location: Location) let pcx = PlaceContext { is_mut: matches!(kind, BorrowKind::Mut { .. }) }; self.visit_place(place, pcx, location); } + Rvalue::Reborrow(target, mutability, place) => { + self.visit_ty(target, location); + let pcx = PlaceContext { is_mut: matches!(mutability, Mutability::Mut) }; + self.visit_place(place, pcx, location); + } Rvalue::Repeat(op, constant) => { self.visit_operand(op, location); self.visit_ty_const(constant, location); diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index 43c81e3dc02a..7e76d5a91ac6 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -204,6 +204,11 @@ fn stable<'cx>( kind.stable(tables, cx), place.stable(tables, cx), ), + Reborrow(target, kind, place) => crate::mir::Rvalue::Reborrow( + target.stable(tables, cx), + kind.stable(tables, cx), + place.stable(tables, cx), + ), ThreadLocalRef(def_id) => { crate::mir::Rvalue::ThreadLocalRef(tables.crate_item(*def_id)) } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index ed8637580775..90f6bf669a2b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -286,6 +286,7 @@ Rc, RcWeak, Ready, + Reborrow, RefCell, Reference, Relaxed, @@ -624,6 +625,7 @@ cmse_nonsecure_entry, coerce_pointee_validated, coerce_shared, + coerce_shared_target, coerce_unsized, coff, cold, diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 49e0bdde3787..8ed32df65645 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -208,6 +208,9 @@ fn recurse_build<'tcx>( | ExprKind::ThreadLocalRef(_) => { error(GenericConstantTooComplexSub::OperationNotSupported(node.span))? } + ExprKind::Reborrow { .. } => { + todo!(); + } }) } @@ -305,6 +308,9 @@ fn expr_is_poly(&mut self, expr: &thir::Expr<'tcx>) -> bool { | thir::ExprKind::InlineAsm(_) | thir::ExprKind::ThreadLocalRef(_) | thir::ExprKind::Yield { .. } => false, + thir::ExprKind::Reborrow { .. } => { + todo!(); + } } } fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool { diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index f56a4d7308e9..9e6b02f00983 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -1347,3 +1347,19 @@ pub trait FnPtr: Copy + Clone { pub trait CoercePointeeValidated { /* compiler built-in */ } + +/// Allows value to be reborrowed as exclusive, creating a copy of the value +/// that disables the source for reads and writes for the lifetime of the copy. +#[lang = "reborrow"] +#[unstable(feature = "reborrow", issue = "145612")] +pub trait Reborrow { + /* compiler built-in */ +} + +/// Allows reborrowable value to be reborrowed as shared, creating a copy +/// that disables the source for writes for the lifetime of the copy. +#[lang = "coerce_shared"] +#[unstable(feature = "reborrow", issue = "145612")] +pub trait CoerceShared: Reborrow { + /* compiler built-in */ +} diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs index ab1ad407ee28..87dd873fdb57 100644 --- a/library/core/src/ops/mod.rs +++ b/library/core/src/ops/mod.rs @@ -149,7 +149,6 @@ mod index; mod index_range; mod range; -mod reborrow; mod try_trait; mod unsize; @@ -190,8 +189,6 @@ pub use self::range::{OneSidedRange, OneSidedRangeBound}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::range::{Range, RangeFrom, RangeFull, RangeTo}; -#[unstable(feature = "reborrow", issue = "145612")] -pub use self::reborrow::{CoerceShared, Reborrow}; #[unstable(feature = "try_trait_v2_residual", issue = "91285")] pub use self::try_trait::Residual; #[unstable(feature = "try_trait_v2_yeet", issue = "96374")] diff --git a/library/core/src/ops/reborrow.rs b/library/core/src/ops/reborrow.rs deleted file mode 100644 index f83f4233a4de..000000000000 --- a/library/core/src/ops/reborrow.rs +++ /dev/null @@ -1,16 +0,0 @@ -/// Allows value to be reborrowed as exclusive, creating a copy of the value -/// that disables the source for reads and writes for the lifetime of the copy. -#[lang = "reborrow"] -#[unstable(feature = "reborrow", issue = "145612")] -pub trait Reborrow { - // Empty. -} - -/// Allows reborrowable value to be reborrowed as shared, creating a copy -/// that disables the source for writes for the lifetime of the copy. -#[lang = "coerce_shared"] -#[unstable(feature = "reborrow", issue = "145612")] -pub trait CoerceShared: Reborrow { - /// The type of this value when reborrowed as shared. - type Target: Copy; -} diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index f4ef9f78b35c..0f5e021788c6 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -134,7 +134,7 @@ fn check_rvalue<'tcx>( ) -> McfResult { match rvalue { Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())), - Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) => { + Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::Reborrow(_, _, place) | Rvalue::RawPtr(_, place) => { check_place(cx, *place, span, body, msrv) }, Rvalue::CopyForDeref(place) => check_place(cx, *place, span, body, msrv), diff --git a/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.rs b/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.rs index c8ca45370891..48a14959d8d6 100644 --- a/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.rs +++ b/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.rs @@ -1,3 +1,3 @@ -use std::ops::CoerceShared; //~ ERROR use of unstable library feature `reborrow` +use std::marker::CoerceShared; //~ ERROR use of unstable library feature `reborrow` fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.stderr b/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.stderr index dbbbcdf2fd57..c4c5e06778af 100644 --- a/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.stderr +++ b/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.stderr @@ -1,8 +1,8 @@ error[E0658]: use of unstable library feature `reborrow` --> $DIR/feature-gate-reborrow-coerce-shared.rs:1:5 | -LL | use std::ops::CoerceShared; - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | use std::marker::CoerceShared; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #145612 for more information = help: add `#![feature(reborrow)]` to the crate attributes to enable diff --git a/tests/ui/feature-gates/feature-gate-reborrow.rs b/tests/ui/feature-gates/feature-gate-reborrow.rs index 96eecfb28a10..f016f6c6bfa5 100644 --- a/tests/ui/feature-gates/feature-gate-reborrow.rs +++ b/tests/ui/feature-gates/feature-gate-reborrow.rs @@ -1,3 +1,3 @@ -use std::ops::Reborrow; //~ ERROR use of unstable library feature `reborrow` +use std::marker::Reborrow; //~ ERROR use of unstable library feature `reborrow` fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-reborrow.stderr b/tests/ui/feature-gates/feature-gate-reborrow.stderr index 1224909f564b..5e3033f3bf1f 100644 --- a/tests/ui/feature-gates/feature-gate-reborrow.stderr +++ b/tests/ui/feature-gates/feature-gate-reborrow.stderr @@ -1,8 +1,8 @@ error[E0658]: use of unstable library feature `reborrow` --> $DIR/feature-gate-reborrow.rs:1:5 | -LL | use std::ops::Reborrow; - | ^^^^^^^^^^^^^^^^^^ +LL | use std::marker::Reborrow; + | ^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #145612 for more information = help: add `#![feature(reborrow)]` to the crate attributes to enable diff --git a/tests/ui/reborrow/custom_marker.rs b/tests/ui/reborrow/custom_marker.rs new file mode 100644 index 000000000000..80689d81d0cc --- /dev/null +++ b/tests/ui/reborrow/custom_marker.rs @@ -0,0 +1,17 @@ +//@ run-pass + +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} + +fn method<'a>(_a: CustomMarker<'a>) -> &'a () { + &() +} + +fn main() { + let a = CustomMarker(PhantomData); + let _ = method(a); + let _ = method(a); +} diff --git a/tests/ui/reborrow/custom_marker_assign_deref.rs b/tests/ui/reborrow/custom_marker_assign_deref.rs new file mode 100644 index 000000000000..79ea2a35acda --- /dev/null +++ b/tests/ui/reborrow/custom_marker_assign_deref.rs @@ -0,0 +1,26 @@ +//@ run-pass + +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} + +impl<'a> std::ops::Deref for CustomMarker<'a> { + type Target = CustomMarker<'a>; + fn deref(&self) -> &Self::Target { + self + } +} + +impl<'a> std::ops::DerefMut for CustomMarker<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + self + } +} + +fn main() { + let mut a = CustomMarker(PhantomData); + + *a = CustomMarker(PhantomData); +} diff --git a/tests/ui/reborrow/custom_marker_coerce_shared.rs b/tests/ui/reborrow/custom_marker_coerce_shared.rs new file mode 100644 index 000000000000..17c7bac98d17 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_coerce_shared.rs @@ -0,0 +1,22 @@ +//@ run-pass + +#![feature(reborrow)] +use std::marker::{CoerceShared, PhantomData, Reborrow}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} +#[derive(Debug, Clone, Copy)] +struct CustomMarkerRef<'a>(PhantomData<&'a ()>); +impl<'a> CoerceShared> for CustomMarker<'a> {} + + +fn method<'a>(_a: CustomMarkerRef<'a>) -> &'a () { + &() +} + +fn main() { + let a = CustomMarker(PhantomData); + let b = method(a); + let c = method(a); + let _ = (b, c); +} diff --git a/tests/ui/reborrow/custom_marker_coerce_shared_copy.rs b/tests/ui/reborrow/custom_marker_coerce_shared_copy.rs new file mode 100644 index 000000000000..56bc1f896da0 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_coerce_shared_copy.rs @@ -0,0 +1,22 @@ +//@ run-pass + +#![feature(reborrow)] +use std::marker::{CoerceShared, PhantomData, Reborrow}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} +#[derive(Debug, Clone, Copy)] +struct CustomMarkerRef<'a>(PhantomData<&'a ()>); +impl<'a> CoerceShared> for CustomMarker<'a> {} + + +fn method<'a>(_a: CustomMarkerRef<'a>) -> &'a () { + &() +} + +fn main() { + let a = CustomMarker(PhantomData); + let b = method(a); + let c = method(a); + let _ = (&a, b, c); +} diff --git a/tests/ui/reborrow/custom_marker_coerce_shared_move.rs b/tests/ui/reborrow/custom_marker_coerce_shared_move.rs new file mode 100644 index 000000000000..532d13da258c --- /dev/null +++ b/tests/ui/reborrow/custom_marker_coerce_shared_move.rs @@ -0,0 +1,21 @@ +#![feature(reborrow)] +use std::marker::{CoerceShared, PhantomData, Reborrow}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} +#[derive(Clone, Copy)] +struct CustomMarkerRef<'a>(PhantomData<&'a ()>); +impl<'a> CoerceShared> for CustomMarker<'a> {} + + +fn method<'a>(_a: CustomMarkerRef<'a>) -> &'a () { + &() +} + +fn main() { + let a = CustomMarker(PhantomData); + let b = method(a); + let c = method(a); + let _ = (a, b, c); + //~^ ERROR: cannot move out of `a` because it is borrowed +} diff --git a/tests/ui/reborrow/custom_marker_coerce_shared_move.stderr b/tests/ui/reborrow/custom_marker_coerce_shared_move.stderr new file mode 100644 index 000000000000..90382af3ce30 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_coerce_shared_move.stderr @@ -0,0 +1,16 @@ +error[E0505]: cannot move out of `a` because it is borrowed + --> $DIR/custom_marker_coerce_shared_move.rs:19:14 + | +LL | let a = CustomMarker(PhantomData); + | - binding `a` declared here +LL | let b = method(a); + | - borrow of `a` occurs here +LL | let c = method(a); +LL | let _ = (a, b, c); + | ^ - borrow later used here + | | + | move out of `a` occurs here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0505`. diff --git a/tests/ui/reborrow/custom_marker_deref.rs b/tests/ui/reborrow/custom_marker_deref.rs new file mode 100644 index 000000000000..74b9bac22ed0 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_deref.rs @@ -0,0 +1,17 @@ +//@ run-pass + +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} + +fn method<'a>(_a: CustomMarker<'a>) -> &'a () { + &() +} + +fn main() { + let mut a = CustomMarker(PhantomData); + let b = &mut a; + let _ = method(*b); +} diff --git a/tests/ui/reborrow/custom_marker_mut_a_b.rs b/tests/ui/reborrow/custom_marker_mut_a_b.rs new file mode 100644 index 000000000000..3baf320b583b --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_a_b.rs @@ -0,0 +1,17 @@ +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} + +fn method<'a>(_a: CustomMarker<'a>) -> &'a () { + &() +} + +fn main() { + let a = CustomMarker(PhantomData); + let b = method(a); + let c = method(a); + //~^ ERROR: cannot borrow `a` as mutable more than once at a time + let _ = (b, c); +} diff --git a/tests/ui/reborrow/custom_marker_mut_a_b.stderr b/tests/ui/reborrow/custom_marker_mut_a_b.stderr new file mode 100644 index 000000000000..36e3bc291803 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_a_b.stderr @@ -0,0 +1,14 @@ +error[E0499]: cannot borrow `a` as mutable more than once at a time + --> $DIR/custom_marker_mut_a_b.rs:14:20 + | +LL | let b = method(a); + | - first mutable borrow occurs here +LL | let c = method(a); + | ^ second mutable borrow occurs here +LL | +LL | let _ = (b, c); + | - first borrow later used here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0499`. diff --git a/tests/ui/reborrow/custom_marker_mut_self.rs b/tests/ui/reborrow/custom_marker_mut_self.rs new file mode 100644 index 000000000000..a688f503517d --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_self.rs @@ -0,0 +1,15 @@ +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} + +fn method<'a>(_a: CustomMarker<'a>) -> &'a () { + &() +} + +fn main() { + let a = CustomMarker(PhantomData); + let b = method(a); + let _ = (a, b); //~ERROR cannot move out of `a` because it is borrowed +} diff --git a/tests/ui/reborrow/custom_marker_mut_self.stderr b/tests/ui/reborrow/custom_marker_mut_self.stderr new file mode 100644 index 000000000000..77262eed339d --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_self.stderr @@ -0,0 +1,15 @@ +error[E0505]: cannot move out of `a` because it is borrowed + --> $DIR/custom_marker_mut_self.rs:14:14 + | +LL | let a = CustomMarker(PhantomData); + | - binding `a` declared here +LL | let b = method(a); + | - borrow of `a` occurs here +LL | let _ = (a, b); + | ^ - borrow later used here + | | + | move out of `a` occurs here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0505`. diff --git a/tests/ui/reborrow/custom_marker_mut_self_a.rs b/tests/ui/reborrow/custom_marker_mut_self_a.rs new file mode 100644 index 000000000000..f4cc8defb05e --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_self_a.rs @@ -0,0 +1,18 @@ +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} + +fn method<'a>(_a: CustomMarker<'a>) -> &'a () { + &() +} + +fn main() { + let a = CustomMarker(PhantomData); + let b = method(a); + let _ = method(a); + //~^ ERROR: cannot borrow `a` as mutable more than once at a time + let _ = (a, b); + //~^ ERROR: cannot move out of `a` because it is borrowed +} diff --git a/tests/ui/reborrow/custom_marker_mut_self_a.stderr b/tests/ui/reborrow/custom_marker_mut_self_a.stderr new file mode 100644 index 000000000000..4241b6ec15a2 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_self_a.stderr @@ -0,0 +1,28 @@ +error[E0499]: cannot borrow `a` as mutable more than once at a time + --> $DIR/custom_marker_mut_self_a.rs:14:20 + | +LL | let b = method(a); + | - first mutable borrow occurs here +LL | let _ = method(a); + | ^ second mutable borrow occurs here +LL | +LL | let _ = (a, b); + | - first borrow later used here + +error[E0505]: cannot move out of `a` because it is borrowed + --> $DIR/custom_marker_mut_self_a.rs:16:14 + | +LL | let a = CustomMarker(PhantomData); + | - binding `a` declared here +LL | let b = method(a); + | - borrow of `a` occurs here +... +LL | let _ = (a, b); + | ^ - borrow later used here + | | + | move out of `a` occurs here + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0499, E0505. +For more information about an error, try `rustc --explain E0499`. diff --git a/tests/ui/reborrow/custom_marker_mut_self_b.rs b/tests/ui/reborrow/custom_marker_mut_self_b.rs new file mode 100644 index 000000000000..16356954908b --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_self_b.rs @@ -0,0 +1,17 @@ +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} + +fn method<'a>(_a: CustomMarker<'a>) -> &'a () { + &() +} + +fn main() { + let a = CustomMarker(PhantomData); + let _ = method(a); + let b = method(a); + let _ = (a, b); + //~^ ERROR: cannot move out of `a` because it is borrowed +} diff --git a/tests/ui/reborrow/custom_marker_mut_self_b.stderr b/tests/ui/reborrow/custom_marker_mut_self_b.stderr new file mode 100644 index 000000000000..adca4331f1bb --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_self_b.stderr @@ -0,0 +1,16 @@ +error[E0505]: cannot move out of `a` because it is borrowed + --> $DIR/custom_marker_mut_self_b.rs:15:14 + | +LL | let a = CustomMarker(PhantomData); + | - binding `a` declared here +LL | let _ = method(a); +LL | let b = method(a); + | - borrow of `a` occurs here +LL | let _ = (a, b); + | ^ - borrow later used here + | | + | move out of `a` occurs here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0505`. diff --git a/tests/ui/reborrow/custom_marker_two_lifetimes.rs b/tests/ui/reborrow/custom_marker_two_lifetimes.rs new file mode 100644 index 000000000000..d03282145054 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_two_lifetimes.rs @@ -0,0 +1,8 @@ +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +struct CustomMarker<'a, 'b>(PhantomData<&'a mut ()>, PhantomData<&'b ()>); +impl<'a, 'b> Reborrow for CustomMarker<'a, 'b> {} +//~^ ERROR: implementing `Reborrow` requires that a single lifetime parameter is passed between source and target + +fn main() {} diff --git a/tests/ui/reborrow/custom_marker_two_lifetimes.stderr b/tests/ui/reborrow/custom_marker_two_lifetimes.stderr new file mode 100644 index 000000000000..ce5c4d09aeb7 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_two_lifetimes.stderr @@ -0,0 +1,8 @@ +error: implementing `Reborrow` requires that a single lifetime parameter is passed between source and target + --> $DIR/custom_marker_two_lifetimes.rs:5:1 + | +LL | impl<'a, 'b> Reborrow for CustomMarker<'a, 'b> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/reborrow/custom_mut.rs b/tests/ui/reborrow/custom_mut.rs index 1e7c46932382..39b5ed490610 100644 --- a/tests/ui/reborrow/custom_mut.rs +++ b/tests/ui/reborrow/custom_mut.rs @@ -1,13 +1,16 @@ -#![feature(reborrow)] -use std::ops::Reborrow; +//@ run-pass +#![feature(reborrow)] +use std::marker::Reborrow; + +#[allow(unused)] struct CustomMut<'a, T>(&'a mut T); impl<'a, T> Reborrow for CustomMut<'a, T> {} -fn method(a: CustomMut<'_, ()>) {} +fn method(_: CustomMut<'_, ()>) {} fn main() { let a = CustomMut(&mut ()); let _ = method(a); - let _ = method(a); //~ERROR use of moved value: `a` + let _ = method(a); } diff --git a/tests/ui/reborrow/custom_mut.stderr b/tests/ui/reborrow/custom_mut.stderr deleted file mode 100644 index 3b3f47b62d6f..000000000000 --- a/tests/ui/reborrow/custom_mut.stderr +++ /dev/null @@ -1,29 +0,0 @@ -error[E0382]: use of moved value: `a` - --> $DIR/custom_mut.rs:12:20 - | -LL | let a = CustomMut(&mut ()); - | - move occurs because `a` has type `CustomMut<'_, ()>`, which does not implement the `Copy` trait -LL | let _ = method(a); - | - value moved here -LL | let _ = method(a); - | ^ value used here after move - | -note: consider changing this parameter type in function `method` to borrow instead if owning the value isn't necessary - --> $DIR/custom_mut.rs:7:14 - | -LL | fn method(a: CustomMut<'_, ()>) {} - | ------ ^^^^^^^^^^^^^^^^^ this parameter takes ownership of the value - | | - | in this function -note: if `CustomMut<'_, ()>` implemented `Clone`, you could clone the value - --> $DIR/custom_mut.rs:4:1 - | -LL | struct CustomMut<'a, T>(&'a mut T); - | ^^^^^^^^^^^^^^^^^^^^^^^ consider implementing `Clone` for this type -... -LL | let _ = method(a); - | - you could clone this value - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/reborrow/custom_mut_coerce_shared.rs b/tests/ui/reborrow/custom_mut_coerce_shared.rs index e2d25835c093..dcc02db5802b 100644 --- a/tests/ui/reborrow/custom_mut_coerce_shared.rs +++ b/tests/ui/reborrow/custom_mut_coerce_shared.rs @@ -1,11 +1,12 @@ -#![feature(reborrow)] -use std::ops::{CoerceShared, Reborrow}; +//@ run-pass +#![feature(reborrow)] +use std::marker::{CoerceShared, Reborrow}; + +#[allow(unused)] struct CustomMut<'a, T>(&'a mut T); impl<'a, T> Reborrow for CustomMut<'a, T> {} -impl<'a, T> CoerceShared for CustomMut<'a, T> { - type Target = CustomRef<'a, T>; -} +impl<'a, T> CoerceShared> for CustomMut<'a, T> {} struct CustomRef<'a, T>(&'a T); @@ -16,13 +17,9 @@ fn clone(&self) -> Self { } impl<'a, T> Copy for CustomRef<'a, T> {} -fn method(a: CustomRef<'_, ()>) {} //~NOTE function defined here +fn method(_a: CustomRef<'_, ()>) {} fn main() { let a = CustomMut(&mut ()); method(a); - //~^ ERROR mismatched types - //~| NOTE expected `CustomRef<'_, ()>`, found `CustomMut<'_, ()>` - //~| NOTE arguments to this function are incorrect - //~| NOTE expected struct `CustomRef<'_, ()>` } diff --git a/tests/ui/reborrow/custom_mut_coerce_shared.stderr b/tests/ui/reborrow/custom_mut_coerce_shared.stderr deleted file mode 100644 index 508651badc0a..000000000000 --- a/tests/ui/reborrow/custom_mut_coerce_shared.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/custom_mut_coerce_shared.rs:23:12 - | -LL | method(a); - | ------ ^ expected `CustomRef<'_, ()>`, found `CustomMut<'_, ()>` - | | - | arguments to this function are incorrect - | - = note: expected struct `CustomRef<'_, ()>` - found struct `CustomMut<'_, ()>` -note: function defined here - --> $DIR/custom_mut_coerce_shared.rs:19:4 - | -LL | fn method(a: CustomRef<'_, ()>) {} - | ^^^^^^ -------------------- - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0308`. From 506693e7d632ab9fe267536608a3770ed71a09b8 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 1 May 2026 11:05:26 -0700 Subject: [PATCH 41/41] Support `-Cpanic=unwind` on WASI targets This commit is some minor updates/restructuring in a few locations with the end result being supporting `-Cpanic=unwind` on WASI targets. This continues to be off-by-default insofar as WASI targets default to `-Cpanic=abort`, meaning that actually using anything in this commit requires `-Zbuild-std`. Specifically the changes made here are: * The self-contained sysroot for WASI targets now contains a copy of `libunwind.a` from wasi-sdk, first shipped with wasi-sdk-33 (also updated here). * The `unwind` crate here in this repository uses the `libunwind` module instead of the custom bare-metal wasm implementation of exceptions. This means that Rust uses the `_Unwind_*` symbols which allows it to interoperate with C/C++/etc. * Wasm targets are all updated to pass the LLVM argument `-wasm-use-legacy-eh=false` to differ from LLVM's/clang's default of using the legacy exception handling proposal for WebAssembly. This has no effect by default because `panic=abort` is used on most targets. Emscripten is exempted from this as the Emscripten target is explicitly intended to follow LLVM's/clang's defaults. * There's a single test in the test suite that links to the `panic_unwind` crate which ended up requiring `-Wexceptions` from Wasmtime, so the test parts were updated and Wasmtime was updated in CI, too. The net result of all of this is that this should not actually affect any WebAssembly target's default behavior. Optionally, though, WASI programs can be built with exception handling via: RUSTFLAGS='-Cpanic=unwind' cargo +nightly run -Z build-std --target wasm32-wasip2 Effectively `-Zbuild-std` and `-Cpanic=unwind` is all that's necessary to enable this support on wasm targets. Finally, this ends up closing 154593 as well. The WASI targets are now defined to use `-lunwind` to implement unwinding. This means that the in-tree definition of `__cpp_exception` is no longer of concern and the definition is always sourced externally. If Rust is linked with other C/C++ code using WASI then these idioms are compatible with wasi-sdk, for example, to use that as a linker. The main caveat is that when using an external linker the `-fwasm-exceptions` argument needs to be passed to `clang` for it to be able to find the `libunwind.a` library to link against. Closes 154593 --- compiler/rustc_target/src/spec/base/wasm.rs | 8 ++++++++ .../spec/targets/wasm32_unknown_emscripten.rs | 5 +++++ library/unwind/src/lib.rs | 1 + library/unwind/src/libunwind.rs | 9 ++++++++- src/bootstrap/src/core/build_steps/compile.rs | 10 ++++++++++ src/bootstrap/src/lib.rs | 2 +- .../docker/host-x86_64/dist-various-2/Dockerfile | 4 ++-- src/ci/docker/host-x86_64/pr-check-2/Dockerfile | 4 ++-- .../docker/host-x86_64/test-various/Dockerfile | 8 ++++---- tests/assembly-llvm/wasm_exceptions.rs | 16 ++++++++-------- 10 files changed, 49 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_target/src/spec/base/wasm.rs b/compiler/rustc_target/src/spec/base/wasm.rs index 1f0f564a7790..587eeac14beb 100644 --- a/compiler/rustc_target/src/spec/base/wasm.rs +++ b/compiler/rustc_target/src/spec/base/wasm.rs @@ -110,6 +110,14 @@ macro_rules! args { // representation, so this is disabled. generate_arange_section: false, + // Differ from LLVM's default to use the legacy exception-handling + // proposal instructions and use the standard exception-handling + // instructions. Note that this is only applicable when unwinding is + // actually turned on, which it's not by default on this target. For + // `-Zbuild-std` builds, however, this affects when rebuilding libstd + // with unwinding. + llvm_args: cvs!["-wasm-use-legacy-eh=false"], + ..Default::default() } } diff --git a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs index 4b6f5b655760..e640e3116459 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs @@ -31,6 +31,11 @@ pub(crate) fn target() -> Target { panic_strategy: PanicStrategy::Unwind, no_default_libraries: false, families: cvs!["unix", "wasm"], + // Explicitly override the `base::wasm`'s `llvm_args` back to empty. The + // base is to force using the most standard exception-handling + // instructions, when enabled, but this target is intended to follow + // Emscripten, which is whatever LLVM defaults to. + llvm_args: cvs![], ..base::wasm::options() }; Target { diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index cce6ca748ccc..22568d5f6f1f 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -33,6 +33,7 @@ target_os = "psp", target_os = "solid_asp3", all(target_vendor = "fortanix", target_env = "sgx"), + all(target_os = "wasi", panic = "unwind"), ) => { mod libunwind; pub use libunwind::*; diff --git a/library/unwind/src/libunwind.rs b/library/unwind/src/libunwind.rs index 091efa9c5129..ead6e0827501 100644 --- a/library/unwind/src/libunwind.rs +++ b/library/unwind/src/libunwind.rs @@ -75,7 +75,7 @@ pub enum _Unwind_Reason_Code { #[cfg(all(target_arch = "wasm32", target_os = "emscripten"))] pub const unwinder_private_data_size: usize = 20; -#[cfg(all(target_arch = "wasm32", target_os = "linux"))] +#[cfg(all(target_arch = "wasm32", any(target_os = "linux", target_os = "wasi")))] pub const unwinder_private_data_size: usize = 2; #[cfg(target_arch = "hexagon")] @@ -111,6 +111,13 @@ pub enum _Unwind_Context {} ), link(name = "unwind", kind = "static", modifiers = "-bundle") )] +// Explicitly link the `unwind` library on WASI targets. +// +// This is provided in the self-contained sysroot for WASI targets by default. +// Note that Rust defaults to `-Cpanic=abort` on WASI targets meaning that this +// doesn't end up getting used by default, but this does mean that with +// `-Zbuild-std` this'll automatically link it in. +#[cfg_attr(target_os = "wasi", link(name = "unwind"))] unsafe extern "C-unwind" { pub fn _Unwind_Resume(exception: *mut _Unwind_Exception) -> !; } diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 46d05b9d5d2f..68a4f928464f 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -457,6 +457,16 @@ fn copy_self_contained_objects( DependencyType::TargetSelfContained, ); } + if srcdir.join("eh").exists() { + copy_and_stamp( + builder, + &libdir_self_contained, + &srcdir.join("eh"), + "libunwind.a", + &mut target_deps, + DependencyType::TargetSelfContained, + ); + } } else if target.is_windows_gnu() || target.is_windows_gnullvm() { for obj in ["crt2.o", "dllcrt2.o"].iter() { let src = compiler_file(builder, &builder.cc(target), target, CLang::C, obj); diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index fd3d129c231d..6518224576c9 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1500,7 +1500,7 @@ fn default_wasi_runner(&self, target: TargetSelection) -> Option { if let Some(path) = finder.maybe_have("wasmtime") && let Ok(mut path) = path.into_os_string().into_string() { - path.push_str(" run -C cache=n --dir ."); + path.push_str(" run -Wexceptions -C cache=n --dir ."); // Make sure that tests have access to RUSTC_BOOTSTRAP. This (for example) is // required for libtest to work on beta/stable channels. // diff --git a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile index 71ff3ea3a76a..041493380129 100644 --- a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile @@ -87,9 +87,9 @@ RUN /tmp/build-fuchsia-toolchain.sh COPY host-x86_64/dist-various-2/build-x86_64-fortanix-unknown-sgx-toolchain.sh /tmp/ RUN /tmp/build-x86_64-fortanix-unknown-sgx-toolchain.sh -RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-32/wasi-sdk-32.0-x86_64-linux.tar.gz | \ +RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-33/wasi-sdk-33.0-x86_64-linux.tar.gz | \ tar -xz -ENV WASI_SDK_PATH=/tmp/wasi-sdk-32.0-x86_64-linux +ENV WASI_SDK_PATH=/tmp/wasi-sdk-33.0-x86_64-linux COPY scripts/freebsd-toolchain.sh /tmp/ RUN /tmp/freebsd-toolchain.sh i686 diff --git a/src/ci/docker/host-x86_64/pr-check-2/Dockerfile b/src/ci/docker/host-x86_64/pr-check-2/Dockerfile index 23976199b8a9..acbd99db6c4f 100644 --- a/src/ci/docker/host-x86_64/pr-check-2/Dockerfile +++ b/src/ci/docker/host-x86_64/pr-check-2/Dockerfile @@ -21,9 +21,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ mingw-w64 \ && rm -rf /var/lib/apt/lists/* -RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-32/wasi-sdk-32.0-x86_64-linux.tar.gz | \ +RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-33/wasi-sdk-33.0-x86_64-linux.tar.gz | \ tar -xz -ENV WASI_SDK_PATH=/wasi-sdk-32.0-x86_64-linux +ENV WASI_SDK_PATH=/wasi-sdk-33.0-x86_64-linux ENV RUST_CONFIGURE_ARGS="--set rust.validate-mir-opts=3" diff --git a/src/ci/docker/host-x86_64/test-various/Dockerfile b/src/ci/docker/host-x86_64/test-various/Dockerfile index 104a40403d3e..26e77bc5da20 100644 --- a/src/ci/docker/host-x86_64/test-various/Dockerfile +++ b/src/ci/docker/host-x86_64/test-various/Dockerfile @@ -43,9 +43,9 @@ WORKDIR / COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh -RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-32/wasi-sdk-32.0-x86_64-linux.tar.gz | \ +RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-33/wasi-sdk-33.0-x86_64-linux.tar.gz | \ tar -xz -ENV WASI_SDK_PATH=/wasi-sdk-32.0-x86_64-linux +ENV WASI_SDK_PATH=/wasi-sdk-33.0-x86_64-linux ENV RUST_CONFIGURE_ARGS="--musl-root-x86_64=/usr/local/x86_64-linux-musl \ --set rust.lld" @@ -57,9 +57,9 @@ ENV RUST_CONFIGURE_ARGS="--musl-root-x86_64=/usr/local/x86_64-linux-musl \ ENV NO_DEBUG_ASSERTIONS=1 ENV NO_OVERFLOW_CHECKS=1 -RUN curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v38.0.4/wasmtime-v38.0.4-x86_64-linux.tar.xz | \ +RUN curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v44.0.1/wasmtime-v44.0.1-x86_64-linux.tar.xz | \ tar -xJ -ENV PATH="$PATH:/wasmtime-v38.0.4-x86_64-linux" +ENV PATH="$PATH:/wasmtime-v44.0.1-x86_64-linux" ENV WASM_WASIP_TARGET=wasm32-wasip1 ENV WASM_WASIP_SCRIPT="python3 /checkout/x.py --stage 2 test --host= --target $WASM_WASIP_TARGET \ diff --git a/tests/assembly-llvm/wasm_exceptions.rs b/tests/assembly-llvm/wasm_exceptions.rs index 704e8026f3f4..69127462a019 100644 --- a/tests/assembly-llvm/wasm_exceptions.rs +++ b/tests/assembly-llvm/wasm_exceptions.rs @@ -33,11 +33,11 @@ pub fn test_cleanup() { } // CHECK-NOT: call - // CHECK: try + // CHECK: try_table (catch_all_ref 0) // CHECK: call may_panic - // CHECK: catch_all - // CHECK: rethrow - // CHECK: end_try + // CHECK: end_try_table + // CHECK: call log_number + // CHECK: throw_ref } // CHECK-LABEL: test_rtry: @@ -57,11 +57,11 @@ pub fn test_rtry() { } // CHECK-NOT: call - // CHECK: try + // CHECK: try_table (catch __cpp_exception 0) // CHECK: call may_panic - // CHECK: catch + // CHECK: end_try_table // CHECK: call log_number // CHECK: call log_number - // CHECK-NOT: rethrow - // CHECK: end_try + // CHECK-NOT: throw_ref + // CHECK: end_function }