From bc1a5662647d537d0fe3c95d87c98492d290436c Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Tue, 17 Feb 2026 05:19:14 +0000 Subject: [PATCH 01/46] Prepare for merging from rust-lang/rust This updates the rust-version file to 3c9faa0d037b9eecda4a440cc482ff7f960fb8a5. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 28c3e88535f61..ba1dac73653e7 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -7bee525095c0872e87c038c412c781b9bbb3f5dc +3c9faa0d037b9eecda4a440cc482ff7f960fb8a5 From 7f1734352e67625f58db15fc1970f6093bf7003b Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Wed, 18 Feb 2026 05:19:36 +0000 Subject: [PATCH 02/46] Prepare for merging from rust-lang/rust This updates the rust-version file to 8387095803f21a256a9a772ac1f9b41ed4d5aa0a. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index ba1dac73653e7..ccb61b282a614 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -3c9faa0d037b9eecda4a440cc482ff7f960fb8a5 +8387095803f21a256a9a772ac1f9b41ed4d5aa0a From c57ecb082f5e6f8034df84c5f8d0778164b5b7e3 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Thu, 19 Feb 2026 05:19:11 +0000 Subject: [PATCH 03/46] Prepare for merging from rust-lang/rust This updates the rust-version file to e0cb264b814526acb82def4b5810e394a2ed294f. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index ccb61b282a614..befd6c8a91962 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -8387095803f21a256a9a772ac1f9b41ed4d5aa0a +e0cb264b814526acb82def4b5810e394a2ed294f From 8b03e83d2dec60884b07a9b51f419517cec43db7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 19 Feb 2026 08:37:22 +0100 Subject: [PATCH 04/46] fix genmc build --- src/tools/miri/src/concurrency/genmc/shims.rs | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/tools/miri/src/concurrency/genmc/shims.rs b/src/tools/miri/src/concurrency/genmc/shims.rs index 4685dfd1b8dd7..cac03a21afc52 100644 --- a/src/tools/miri/src/concurrency/genmc/shims.rs +++ b/src/tools/miri/src/concurrency/genmc/shims.rs @@ -24,24 +24,22 @@ impl GenmcCtx { } } +/// Small helper to get the arguments of an intercepted function call. +fn get_fn_args<'tcx, const N: usize>( + instance: ty::Instance<'tcx>, + args: &[FnArg<'tcx>], +) -> InterpResult<'tcx, [OpTy<'tcx>; N]> { + let args = MiriInterpCx::copy_fn_args(args); // FIXME: Should `InPlace` arguments be reset to uninit? + if let Ok(ops) = args.try_into() { + return interp_ok(ops); + } + panic!("{} is a diagnostic item expected to have {} arguments", instance, N); +} + // Handling of code intercepted by Miri in GenMC mode, such as assume statement or `std::sync::Mutex`. impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {} trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { - /// Small helper to get the arguments of an intercepted function call. - fn get_fn_args( - &self, - instance: ty::Instance<'tcx>, - args: &[FnArg<'tcx>], - ) -> InterpResult<'tcx, [OpTy<'tcx>; N]> { - let this = self.eval_context_ref(); - let args = this.copy_fn_args(args); // FIXME: Should `InPlace` arguments be reset to uninit? - if let Ok(ops) = args.try_into() { - return interp_ok(ops); - } - panic!("{} is a diagnostic item expected to have {} arguments", instance, N); - } - /**** Blocking functionality ****/ /// Handle a thread getting blocked by a user assume (not an automatically generated assume). @@ -202,15 +200,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // NOTE: When adding new intercepted functions here, they must also be added to `fn get_function_kind` in `concurrency/genmc/scheduling.rs`. use rustc_span::sym; if this.tcx.is_diagnostic_item(sym::sys_mutex_lock, instance.def_id()) { - let [mutex] = this.get_fn_args(instance, args)?; + let [mutex] = get_fn_args(instance, args)?; let mutex = this.deref_pointer(&mutex)?; this.intercept_mutex_lock(mutex)?; } else if this.tcx.is_diagnostic_item(sym::sys_mutex_try_lock, instance.def_id()) { - let [mutex] = this.get_fn_args(instance, args)?; + let [mutex] = get_fn_args(instance, args)?; let mutex = this.deref_pointer(&mutex)?; this.intercept_mutex_try_lock(mutex, dest)?; } else if this.tcx.is_diagnostic_item(sym::sys_mutex_unlock, instance.def_id()) { - let [mutex] = this.get_fn_args(instance, args)?; + let [mutex] = get_fn_args(instance, args)?; let mutex = this.deref_pointer(&mutex)?; this.intercept_mutex_unlock(mutex)?; } else { From 054d4af414fe4d6a58502b30174b501636272059 Mon Sep 17 00:00:00 2001 From: Yang Lin Date: Fri, 13 Feb 2026 17:24:38 +0800 Subject: [PATCH 05/46] Print a warning when trying to open a file in `/proc` --- src/tools/miri/src/diagnostics.rs | 3 ++ src/tools/miri/src/shims/unix/fs.rs | 17 +++++++++- .../miri/tests/pass/open_a_file_in_proc.rs | 11 +++++++ .../tests/pass/open_a_file_in_proc.stderr | 32 +++++++++++++++++++ 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 src/tools/miri/tests/pass/open_a_file_in_proc.rs create mode 100644 src/tools/miri/tests/pass/open_a_file_in_proc.stderr diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 64c7096fc5c2e..612023c4f486b 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -149,6 +149,7 @@ pub enum NonHaltingDiagnostic { failure_ordering: AtomicReadOrd, effective_failure_ordering: AtomicReadOrd, }, + FileInProcOpened, } /// Level of Miri specific diagnostics @@ -654,6 +655,7 @@ impl<'tcx> MiriMachine<'tcx> { | ProgressReport { .. } | WeakMemoryOutdatedLoad { .. } => ("tracking was triggered here".to_string(), DiagLevel::Note), + FileInProcOpened => ("open a file in `/proc`".to_string(), DiagLevel::Warning), }; let title = match &e { @@ -701,6 +703,7 @@ impl<'tcx> MiriMachine<'tcx> { }; format!("GenMC currently does not model the failure ordering for `compare_exchange`. {was_upgraded_msg}. Miri with GenMC might miss bugs related to this memory access.") } + FileInProcOpened => format!("files in `/proc` can bypass the Abstract Machine and might not work properly in Miri") }; let notes = match &e { diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index a01755ef95ae7..d82a4d2313156 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -7,7 +7,7 @@ use std::fs::{ rename, }; use std::io::{self, ErrorKind, Read, Seek, SeekFrom, Write}; -use std::path::{Path, PathBuf}; +use std::path::{self, Path, PathBuf}; use std::time::SystemTime; use rustc_abi::Size; @@ -355,6 +355,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let path_raw = this.read_pointer(path_raw)?; let path = this.read_path_from_c_str(path_raw)?; + + match path::absolute(&path) { + Ok(path) => { + // "/proc" does not work properly from inside Miri, warn about opening files there. + if matches!( + this.tcx.sess.target.os, + Os::Linux | Os::Android | Os::Illumos | Os::Solaris + ) && (path == Path::new("/proc") || path.starts_with("/proc/")) + { + this.machine.emit_diagnostic(NonHaltingDiagnostic::FileInProcOpened); + } + } + Err(_) => { /* when the path is empty, it is also unrelative to "/proc" issue */ } + } + let flag = this.read_scalar(flag)?.to_i32()?; let mut options = OpenOptions::new(); diff --git a/src/tools/miri/tests/pass/open_a_file_in_proc.rs b/src/tools/miri/tests/pass/open_a_file_in_proc.rs new file mode 100644 index 0000000000000..8c9887656d384 --- /dev/null +++ b/src/tools/miri/tests/pass/open_a_file_in_proc.rs @@ -0,0 +1,11 @@ +//@compile-flags: -Zmiri-disable-isolation +//@only-target: linux android illumos +//@ignore-host: windows + +fn main() { + let _ = match std::fs::File::open("/proc/doesnotexist ") { + Ok(_f) => {} + Err(_msg) => {} + }; + (); +} diff --git a/src/tools/miri/tests/pass/open_a_file_in_proc.stderr b/src/tools/miri/tests/pass/open_a_file_in_proc.stderr new file mode 100644 index 0000000000000..0ba31ed5adac0 --- /dev/null +++ b/src/tools/miri/tests/pass/open_a_file_in_proc.stderr @@ -0,0 +1,32 @@ +warning: files in `/proc` can bypass the Abstract Machine and might not work properly in Miri + --> RUSTLIB/std/src/sys/fs/PLATFORM.rs:LL:CC + | +LL | let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ open a file in `/proc` + | + = note: stack backtrace: + 0: std::sys::fs::PLATFORM::File::open_c::{closure#0} + at RUSTLIB/std/src/sys/fs/PLATFORM.rs:LL:CC + 1: std::sys::pal::PLATFORM::cvt_r + at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC + 2: std::sys::fs::PLATFORM::File::open_c + at RUSTLIB/std/src/sys/fs/PLATFORM.rs:LL:CC + 3: std::sys::fs::PLATFORM::File::open::{closure#0} + at RUSTLIB/std/src/sys/fs/PLATFORM.rs:LL:CC + 4: std::sys::helpers::small_c_string::run_with_cstr_stack + at RUSTLIB/std/src/sys/helpers/small_c_string.rs:LL:CC + 5: std::sys::helpers::small_c_string::run_with_cstr + at RUSTLIB/std/src/sys/helpers/small_c_string.rs:LL:CC + 6: std::sys::helpers::small_c_string::run_path_with_cstr + at RUSTLIB/std/src/sys/helpers/small_c_string.rs:LL:CC + 7: std::sys::fs::PLATFORM::File::open + at RUSTLIB/std/src/sys/fs/PLATFORM.rs:LL:CC + 8: std::fs::OpenOptions::_open + at RUSTLIB/std/src/fs.rs:LL:CC + 9: std::fs::OpenOptions::open + at RUSTLIB/std/src/fs.rs:LL:CC + 10: std::fs::File::open + at RUSTLIB/std/src/fs.rs:LL:CC + 11: main + at tests/pass/open_a_file_in_proc.rs:LL:CC + From 220d5142380a8c633ad829c35c045d45d27d52ec Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 21 Feb 2026 12:23:30 +0100 Subject: [PATCH 06/46] clean up path check --- src/tools/miri/src/shims/unix/fs.rs | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index d82a4d2313156..9873af85b989b 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -354,24 +354,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let path_raw = this.read_pointer(path_raw)?; - let path = this.read_path_from_c_str(path_raw)?; + let flag = this.read_scalar(flag)?.to_i32()?; - match path::absolute(&path) { - Ok(path) => { - // "/proc" does not work properly from inside Miri, warn about opening files there. - if matches!( - this.tcx.sess.target.os, - Os::Linux | Os::Android | Os::Illumos | Os::Solaris - ) && (path == Path::new("/proc") || path.starts_with("/proc/")) - { - this.machine.emit_diagnostic(NonHaltingDiagnostic::FileInProcOpened); - } - } - Err(_) => { /* when the path is empty, it is also unrelative to "/proc" issue */ } + let path = this.read_path_from_c_str(path_raw)?; + // Files in `/proc` won't work properly. + if matches!(this.tcx.sess.target.os, Os::Linux | Os::Android | Os::Illumos | Os::Solaris) + && path::absolute(&path).is_ok_and(|path| path.starts_with("/proc")) + { + this.machine.emit_diagnostic(NonHaltingDiagnostic::FileInProcOpened); } - let flag = this.read_scalar(flag)?.to_i32()?; - let mut options = OpenOptions::new(); let o_rdonly = this.eval_libc_i32("O_RDONLY"); From e3062fa780fefc860c1d5005b9665751c4d14283 Mon Sep 17 00:00:00 2001 From: Stypox Date: Mon, 16 Feb 2026 18:24:19 +0100 Subject: [PATCH 07/46] Avoid keeping ThreadData borrowed while formatting tracing arguments Fixes RefCell being borrowed mutably twice, see https://github.com/rust-lang/miri/issues/4563#issuecomment-3415871750 . There are some places in rustc where the implementation of an argument passed to a tracing span makes a nested tracing call, e.g. [here](https://github.com/rust-lang/rust/blob/f6092f224d2b1774b31033f12d0bee626943b02f/compiler/rustc_const_eval/src/interpret/step.rs#L82-L89)->[here](https://github.com/rust-lang/rust/blob/f6092f224d2b1774b31033f12d0bee626943b02f/compiler/rustc_public/src/ty.rs#L265)->[here](https://github.com/rust-lang/rust/blob/f6092f224d2b1774b31033f12d0bee626943b02f/compiler/rustc_span/src/lib.rs#L2302). This is what caused the crash. Co-authored-by: Ralf Jung --- src/tools/miri/src/bin/log/tracing_chrome.rs | 31 ++++++++++++------- .../src/bin/log/tracing_chrome_instant.rs | 1 + 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/tools/miri/src/bin/log/tracing_chrome.rs b/src/tools/miri/src/bin/log/tracing_chrome.rs index 85b4de62a5ec4..bfcb73ed1d769 100644 --- a/src/tools/miri/src/bin/log/tracing_chrome.rs +++ b/src/tools/miri/src/bin/log/tracing_chrome.rs @@ -53,6 +53,7 @@ use std::{ use crate::log::tracing_chrome_instant::TracingChromeInstant; /// Contains thread-local data for threads that send tracing spans or events. +#[derive(Clone)] struct ThreadData { /// A unique ID for this thread, will populate "tid" field in the output trace file. tid: usize, @@ -562,15 +563,20 @@ where #[inline(always)] fn with_elapsed_micros_subtracting_tracing(&self, f: impl Fn(f64, usize, &Sender)) { THREAD_DATA.with(|value| { - let mut thread_data = value.borrow_mut(); - let (ThreadData { tid, out, start }, new_thread) = match thread_data.as_mut() { - Some(thread_data) => (thread_data, false), - None => { - let tid = self.max_tid.fetch_add(1, Ordering::SeqCst); - let out = self.out.lock().unwrap().clone(); - let start = TracingChromeInstant::setup_for_thread_and_start(tid); - *thread_data = Some(ThreadData { tid, out, start }); - (thread_data.as_mut().unwrap(), true) + // Make sure not to keep `value` borrowed when calling `f` below, since the user tracing + // code that `f` might invoke (e.g. fmt::Debug argument formatting) may contain nested + // tracing calls that would cause `value` to be doubly-borrowed mutably. + let (ThreadData { tid, out, mut start }, new_thread) = { + let mut thread_data = value.borrow_mut(); + match thread_data.as_mut() { + Some(thread_data) => (thread_data.clone(), false), + None => { + let tid = self.max_tid.fetch_add(1, Ordering::SeqCst); + let out = self.out.lock().unwrap().clone(); + let start = TracingChromeInstant::setup_for_thread_and_start(tid); + *thread_data = Some(ThreadData { tid, out: out.clone(), start: start.clone() }); + (ThreadData { tid, out, start }, true) + } } }; @@ -580,10 +586,13 @@ where Some(name) => name.to_owned(), None => tid.to_string(), }; - let _ignored = out.send(Message::NewThread(*tid, name)); + let _ignored = out.send(Message::NewThread(tid, name)); } - f(ts, *tid, out); + f(ts, tid, &out); }); + + // we have to re-borrow here, see comment above + value.borrow_mut().as_mut().unwrap().start = start; }); } } diff --git a/src/tools/miri/src/bin/log/tracing_chrome_instant.rs b/src/tools/miri/src/bin/log/tracing_chrome_instant.rs index 04705b8846d9c..57000b39384e3 100644 --- a/src/tools/miri/src/bin/log/tracing_chrome_instant.rs +++ b/src/tools/miri/src/bin/log/tracing_chrome_instant.rs @@ -20,6 +20,7 @@ /// /// This measures time using [std::time::Instant], except for x86/x86_64 Linux machines, where /// [std::time::Instant] is too slow (~1.5us) and thus `rdtsc` is used instead (~5ns). +#[derive(Clone)] pub enum TracingChromeInstant { WallTime { /// The time at which this instant was created, shifted forward to account From 041e3a57a2a111604b3b4ba762fea164cb59d638 Mon Sep 17 00:00:00 2001 From: Stypox Date: Mon, 16 Feb 2026 18:13:11 +0100 Subject: [PATCH 08/46] Remove RDTSC timer and always rely on Instant instead Fixes https://github.com/rust-lang/miri/issues/4563 --- src/tools/miri/src/bin/log/mod.rs | 1 - src/tools/miri/src/bin/log/tracing_chrome.rs | 44 +++-- .../src/bin/log/tracing_chrome_instant.rs | 184 ------------------ 3 files changed, 26 insertions(+), 203 deletions(-) delete mode 100644 src/tools/miri/src/bin/log/tracing_chrome_instant.rs diff --git a/src/tools/miri/src/bin/log/mod.rs b/src/tools/miri/src/bin/log/mod.rs index 22f74dd46b540..f3b2fdb5348e0 100644 --- a/src/tools/miri/src/bin/log/mod.rs +++ b/src/tools/miri/src/bin/log/mod.rs @@ -1,3 +1,2 @@ pub mod setup; mod tracing_chrome; -mod tracing_chrome_instant; diff --git a/src/tools/miri/src/bin/log/tracing_chrome.rs b/src/tools/miri/src/bin/log/tracing_chrome.rs index bfcb73ed1d769..2caea73a6e3da 100644 --- a/src/tools/miri/src/bin/log/tracing_chrome.rs +++ b/src/tools/miri/src/bin/log/tracing_chrome.rs @@ -50,8 +50,6 @@ use std::{ thread::JoinHandle, }; -use crate::log::tracing_chrome_instant::TracingChromeInstant; - /// Contains thread-local data for threads that send tracing spans or events. #[derive(Clone)] struct ThreadData { @@ -62,7 +60,7 @@ struct ThreadData { out: Sender, /// The instant in time this thread was started. All events happening on this thread will be /// saved to the trace file with a timestamp (the "ts" field) measured relative to this instant. - start: TracingChromeInstant, + start: std::time::Instant, } thread_local! { @@ -566,33 +564,43 @@ where // Make sure not to keep `value` borrowed when calling `f` below, since the user tracing // code that `f` might invoke (e.g. fmt::Debug argument formatting) may contain nested // tracing calls that would cause `value` to be doubly-borrowed mutably. - let (ThreadData { tid, out, mut start }, new_thread) = { + let (ThreadData { tid, out, start }, new_thread) = { let mut thread_data = value.borrow_mut(); match thread_data.as_mut() { Some(thread_data) => (thread_data.clone(), false), None => { let tid = self.max_tid.fetch_add(1, Ordering::SeqCst); let out = self.out.lock().unwrap().clone(); - let start = TracingChromeInstant::setup_for_thread_and_start(tid); - *thread_data = Some(ThreadData { tid, out: out.clone(), start: start.clone() }); + let start = std::time::Instant::now(); + *thread_data = Some(ThreadData { tid, out: out.clone(), start }); (ThreadData { tid, out, start }, true) } } }; - start.with_elapsed_micros_subtracting_tracing(|ts| { - if new_thread { - let name = match std::thread::current().name() { - Some(name) => name.to_owned(), - None => tid.to_string(), - }; - let _ignored = out.send(Message::NewThread(tid, name)); - } - f(ts, tid, &out); - }); + // Obtain the current time (before executing `f`). + let instant_before_f = std::time::Instant::now(); + + // Using the current time (`instant_before_f`), calculate the elapsed time (in + // microseconds) since `start` was instantiated, accounting for any time that was + // previously spent executing `f`. The "accounting" part is not computed in this + // line, but is rather done by shifting forward the `start` down below. + let ts = (instant_before_f - start).as_nanos() as f64 / 1000.0; + + // Run the function (supposedly a function internal to the tracing infrastructure). + if new_thread { + let name = match std::thread::current().name() { + Some(name) => name.to_owned(), + None => tid.to_string(), + }; + let _ignored = out.send(Message::NewThread(tid, name)); + } + f(ts, tid, &out); - // we have to re-borrow here, see comment above - value.borrow_mut().as_mut().unwrap().start = start; + // Measure how much time was spent executing `f` and shift `start` forward + // by that amount. This "removes" that time from the trace. + // See comment at the top of this function for why we have to re-borrow here. + value.borrow_mut().as_mut().unwrap().start += std::time::Instant::now() - instant_before_f; }); } } diff --git a/src/tools/miri/src/bin/log/tracing_chrome_instant.rs b/src/tools/miri/src/bin/log/tracing_chrome_instant.rs deleted file mode 100644 index 57000b39384e3..0000000000000 --- a/src/tools/miri/src/bin/log/tracing_chrome_instant.rs +++ /dev/null @@ -1,184 +0,0 @@ -//! Code in this class was in part inspired by -//! . -//! A useful resource is also -//! , -//! although this file does not implement TSC synchronization but instead pins threads to CPUs, -//! since the former is not reliable (i.e. it might lead to non-monotonic time measurements). -//! Another useful resource for future improvements might be measureme's time measurement utils: -//! . -//! Documentation about how the Linux kernel chooses a clock source can be found here: -//! . -#![cfg(feature = "tracing")] - -/// This alternative `TracingChromeInstant` implementation was made entirely to suit the needs of -/// [crate::log::tracing_chrome], and shouldn't be used for anything else. It features two functions: -/// - [TracingChromeInstant::setup_for_thread_and_start], which sets up the current thread to do -/// proper time tracking and returns a point in time to use as "t=0", and -/// - [TracingChromeInstant::with_elapsed_micros_subtracting_tracing], which allows -/// obtaining how much time elapsed since [TracingChromeInstant::setup_for_thread_and_start] was -/// called while accounting for (and subtracting) the time spent inside tracing-related functions. -/// -/// This measures time using [std::time::Instant], except for x86/x86_64 Linux machines, where -/// [std::time::Instant] is too slow (~1.5us) and thus `rdtsc` is used instead (~5ns). -#[derive(Clone)] -pub enum TracingChromeInstant { - WallTime { - /// The time at which this instant was created, shifted forward to account - /// for time spent in tracing functions as explained in - /// [TracingChromeInstant::with_elapsed_micros_subtracting_tracing]'s comments. - start_instant: std::time::Instant, - }, - #[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))] - Tsc { - /// The value in the TSC counter when this instant was created, shifted forward to account - /// for time spent in tracing functions as explained in - /// [TracingChromeInstant::with_elapsed_micros_subtracting_tracing]'s comments. - start_tsc: u64, - /// The period of the TSC counter in microseconds. - tsc_to_microseconds: f64, - }, -} - -impl TracingChromeInstant { - /// Can be thought of as the same as [std::time::Instant::now()], but also does some setup to - /// make TSC stable in case TSC is available. This is supposed to be called (at most) once per - /// thread since the thread setup takes a few milliseconds. - /// - /// WARNING: If TSC is available, `incremental_thread_id` is used to pick to which CPU to pin - /// the current thread. Thread IDs should be assigned contiguously starting from 0. Be aware - /// that the current thread will be restricted to one CPU for the rest of the execution! - pub fn setup_for_thread_and_start(incremental_thread_id: usize) -> TracingChromeInstant { - #[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))] - if *tsc::IS_TSC_AVAILABLE.get_or_init(tsc::is_tsc_available) { - // We need to lock this thread to a specific CPU, because CPUs' TSC timers might be out - // of sync. - tsc::set_cpu_affinity(incremental_thread_id); - - // Can only use tsc_to_microseconds() and rdtsc() after having set the CPU affinity! - // We compute tsc_to_microseconds anew for every new thread just in case some CPU core - // has a different TSC frequency. - let tsc_to_microseconds = tsc::tsc_to_microseconds(); - let start_tsc = tsc::rdtsc(); - return TracingChromeInstant::Tsc { start_tsc, tsc_to_microseconds }; - } - - let _ = incremental_thread_id; // otherwise we get a warning when the TSC branch is disabled - TracingChromeInstant::WallTime { start_instant: std::time::Instant::now() } - } - - /// Calls `f` with the time elapsed in microseconds since this [TracingChromeInstant] was built - /// by [TracingChromeInstant::setup_for_thread_and_start], while subtracting all time previously - /// spent executing other `f`s passed to this function. This behavior allows subtracting time - /// spent in functions that log tracing data (which `f` is supposed to be) from the tracing time - /// measurements. - /// - /// Note: microseconds are used as the time unit since that's what Chrome trace files should - /// contain, see the definition of the "ts" field in - /// . - #[inline(always)] - pub fn with_elapsed_micros_subtracting_tracing(&mut self, f: impl Fn(f64)) { - match self { - TracingChromeInstant::WallTime { start_instant } => { - // Obtain the current time (before executing `f`). - let instant_before_f = std::time::Instant::now(); - - // Using the current time (`instant_before_f`) and the `start_instant` stored in - // `self`, calculate the elapsed time (in microseconds) since this instant was - // instantiated, accounting for any time that was previously spent executing `f`. - // The "accounting" part is not computed in this line, but is rather done by - // shifting forward the `start_instant` down below. - let ts = (instant_before_f - *start_instant).as_nanos() as f64 / 1000.0; - - // Run the function (supposedly a function internal to the tracing infrastructure). - f(ts); - - // Measure how much time was spent executing `f` and shift `start_instant` forward - // by that amount. This "removes" that time from the trace. - *start_instant += std::time::Instant::now() - instant_before_f; - } - - #[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))] - TracingChromeInstant::Tsc { start_tsc, tsc_to_microseconds } => { - // the comments above also apply here, since it's the same logic - let tsc_before_f = tsc::rdtsc(); - let ts = ((tsc_before_f - *start_tsc) as f64) * (*tsc_to_microseconds); - f(ts); - *start_tsc += tsc::rdtsc() - tsc_before_f; - } - } - } -} - -#[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))] -mod tsc { - - pub static IS_TSC_AVAILABLE: std::sync::OnceLock = std::sync::OnceLock::new(); - - /// Reads the timestamp-counter register. Will give monotonic answers only when called from the - /// same thread, because the TSC of different CPUs might be out of sync. - #[inline(always)] - pub(super) fn rdtsc() -> u64 { - #[cfg(target_arch = "x86")] - use core::arch::x86::_rdtsc; - #[cfg(target_arch = "x86_64")] - use core::arch::x86_64::_rdtsc; - - unsafe { _rdtsc() } - } - - /// Estimates the frequency of the TSC counter by waiting 10ms in a busy loop and - /// looking at how much the TSC increased in the meantime. - pub(super) fn tsc_to_microseconds() -> f64 { - const BUSY_WAIT: std::time::Duration = std::time::Duration::from_millis(10); - let tsc_start = rdtsc(); - let instant_start = std::time::Instant::now(); - while instant_start.elapsed() < BUSY_WAIT { - // `thread::sleep()` is not very precise at waking up the program at the right time, - // so use a busy loop instead. - core::hint::spin_loop(); - } - let tsc_end = rdtsc(); - (BUSY_WAIT.as_nanos() as f64) / 1000.0 / ((tsc_end - tsc_start) as f64) - } - - /// Checks whether the TSC counter is available and runs at a constant rate independently - /// of CPU frequency even across different power states of the CPU (i.e. checks for the - /// `invariant_tsc` CPUID flag). - pub(super) fn is_tsc_available() -> bool { - #[cfg(target_arch = "x86")] - use core::arch::x86::__cpuid; - #[cfg(target_arch = "x86_64")] - use core::arch::x86_64::__cpuid; - - // implemented like https://docs.rs/raw-cpuid/latest/src/raw_cpuid/extended.rs.html#965-967 - const LEAF: u32 = 0x80000007; // this is the leaf for "advanced power management info" - let cpuid = __cpuid(LEAF); - (cpuid.edx & (1 << 8)) != 0 // EDX bit 8 indicates invariant TSC - } - - /// Forces the current thread to run on a single CPU, which ensures the TSC counter is monotonic - /// (since TSCs of different CPUs might be out-of-sync). `incremental_thread_id` is used to pick - /// to which CPU to pin the current thread, and should be an incremental number that starts from - /// 0. - pub(super) fn set_cpu_affinity(incremental_thread_id: usize) { - let cpu_id = match std::thread::available_parallelism() { - Ok(available_parallelism) => incremental_thread_id % available_parallelism, - _ => panic!("Could not determine CPU count to properly set CPU affinity"), - }; - - let mut set = unsafe { std::mem::zeroed::() }; - unsafe { libc::CPU_SET(cpu_id, &mut set) }; - - // Set the current thread's core affinity. - if unsafe { - libc::sched_setaffinity( - 0, // Defaults to current thread - size_of::(), - &set as *const _, - ) - } != 0 - { - panic!("Could not set CPU affinity") - } - } -} From b0b2e8c6cde5a11d7bd7aa2e564806606df29ed2 Mon Sep 17 00:00:00 2001 From: Stypox Date: Mon, 16 Feb 2026 18:41:12 +0100 Subject: [PATCH 09/46] Update documentation for tracing --- src/tools/miri/CONTRIBUTING.md | 2 ++ src/tools/miri/doc/tracing.md | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/CONTRIBUTING.md b/src/tools/miri/CONTRIBUTING.md index f9cb60c66d916..852ea26ab89e0 100644 --- a/src/tools/miri/CONTRIBUTING.md +++ b/src/tools/miri/CONTRIBUTING.md @@ -189,6 +189,8 @@ you can visualize in [Perfetto](https://ui.perfetto.dev/). For example: MIRI_TRACING=1 ./miri run --features=tracing tests/pass/hello.rs ``` +See [doc/tracing.md](./doc/tracing.md) for more information. + ### UI testing We use ui-testing in Miri, meaning we generate `.stderr` and `.stdout` files for the output diff --git a/src/tools/miri/doc/tracing.md b/src/tools/miri/doc/tracing.md index d7114af947dc1..1208ca8d6ec7c 100644 --- a/src/tools/miri/doc/tracing.md +++ b/src/tools/miri/doc/tracing.md @@ -2,6 +2,9 @@ Miri can be traced to understand how much time is spent in its various components (e.g. borrow tracker, data race checker, etc.). When tracing is enabled, running Miri will create a `.json` trace file that can be opened and analyzed in [Perfetto](https://ui.perfetto.dev/). For any questions regarding this documentation you may contact [Stypox](https://rust-lang.zulipchat.com/#narrow/dm/627563-Stypox) on Zulip. +> [!WARNING] +> Tracing in Miri at the moment is broken due to two bugs in tracing libraries: https://github.com/tokio-rs/tracing/pull/3392 and https://github.com/davidbarsky/tracing-tree/issues/93. Also see https://github.com/rust-lang/miri/issues/4752. + ## Obtaining a trace file ### From the Miri codebase @@ -277,9 +280,12 @@ So the solution was to copy-paste [the only file](https://github.com/thoren-d/tr ### Time measurements -tracing-chrome originally used `std::time::Instant` to measure time, however on some x86/x86_64 Linux systems it might be unbearably slow since the underlying system call (`clock_gettime`) would take ≈1.3µs. Read more [here](https://btorpey.github.io/blog/2014/02/18/clock-sources-in-linux/) about how the Linux kernel chooses the clock source. +tracing-chrome uses `std::time::Instant` to measure time. On most modern systems this is ok, since the underlying system call (`clock_gettime`) uses very fast hardware counters (e.g. `tsc`) and has a latency of ≈16ns. + +On some x86/x86_64 Linux systems, however, `tsc` is not "reliable" and the system thus relies on other timers, e.g. `hpet` which takes ≈1.3µs. Read [here](https://btorpey.github.io/blog/2014/02/18/clock-sources-in-linux/) how the Linux kernel chooses the clock source, and how to check if your system is using `tsc`. If it doesn't use `tsc`, then expect most of the trace time being spent in time measurements, which degrades traces' usefulness... See [here](https://github.com/rust-lang/miri/issues/4563) for some discussion. -Therefore, on x86/x86_64 Linux systems with a CPU that has an invariant TSC counter, we read from that instead to measure time, which takes only ≈13ns. There are unfortunately a lot of caveats to this approach though, as explained [in the code](https://github.com/rust-lang/miri/blob/master/src/bin/log/tracing_chrome_instant.rs) and [in the PR](https://github.com/rust-lang/miri/pull/4524). The most impactful one is that: every thread spawned in Miri that wants to trace something (which requires measuring time) needs to pin itself to a single CPU core (using `sched_setaffinity`). +> [!WARNING] +> A (somewhat risky) workaround is to add `tsc=reliable clocksource=tsc hpet=disable` to the kernel boot parameters, which forces it to use `tsc` even if it is unreliable. But this may render the system unstable, so try it at your own risk! ## Other useful stuff From 7be626903c786021ed1bf50b5601758714e6f5c4 Mon Sep 17 00:00:00 2001 From: Will Schlitzer Date: Sat, 21 Feb 2026 22:32:24 -0500 Subject: [PATCH 10/46] Fix typo in tracing.md for `tracing_separate_thread` Remove duplicate "the" in documentation. --- src/tools/miri/doc/tracing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/doc/tracing.md b/src/tools/miri/doc/tracing.md index 1208ca8d6ec7c..1fbdd945d977e 100644 --- a/src/tools/miri/doc/tracing.md +++ b/src/tools/miri/doc/tracing.md @@ -243,7 +243,7 @@ let _trace = enter_trace_span!(M, "borrow_tracker", borrow_tracker = "on_stack_p ### `tracing_separate_thread` parameter -Miri saves traces using the the `tracing_chrome` `tracing::Layer` so that they can be visualized in Perfetto. To instruct `tracing_chrome` to put some spans on a separate trace thread/line than other spans when viewed in Perfetto, you can pass `tracing_separate_thread = tracing::field::Empty` to the tracing macros. This is useful to separate out spans which just indicate the current step or program frame being processed by the interpreter. As explained in [The timeline](#the-timeline), those spans end up under the "Global Legacy Events" track. You should use a value of `tracing::field::Empty` so that other tracing layers (e.g. the logger) will ignore the `tracing_separate_thread` field. For example: +Miri saves traces using the `tracing_chrome` `tracing::Layer` so that they can be visualized in Perfetto. To instruct `tracing_chrome` to put some spans on a separate trace thread/line than other spans when viewed in Perfetto, you can pass `tracing_separate_thread = tracing::field::Empty` to the tracing macros. This is useful to separate out spans which just indicate the current step or program frame being processed by the interpreter. As explained in [The timeline](#the-timeline), those spans end up under the "Global Legacy Events" track. You should use a value of `tracing::field::Empty` so that other tracing layers (e.g. the logger) will ignore the `tracing_separate_thread` field. For example: ```rust let _trace = enter_trace_span!(M, step::eval_statement, tracing_separate_thread = tracing::field::Empty); ``` From 4a2f129022035bd99d54d978b31b2e4fb35e1c8a Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sun, 22 Feb 2026 05:16:15 +0000 Subject: [PATCH 11/46] Prepare for merging from rust-lang/rust This updates the rust-version file to 5fb2ff8611e5a4af4dc85977cfdecfbf3ffa6ade. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index befd6c8a91962..379f038b6d1ca 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -e0cb264b814526acb82def4b5810e394a2ed294f +5fb2ff8611e5a4af4dc85977cfdecfbf3ffa6ade From 3607191d85628251e5c02f65326a7f4fd17ae488 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 22 Feb 2026 13:36:12 +0100 Subject: [PATCH 12/46] register Miri-specific symbols with the interner --- src/tools/miri/src/bin/miri.rs | 7 ++++ .../miri/src/concurrency/genmc/scheduling.rs | 9 ++--- src/tools/miri/src/concurrency/genmc/shims.rs | 1 - src/tools/miri/src/intrinsics/mod.rs | 2 +- src/tools/miri/src/lib.rs | 2 ++ src/tools/miri/src/sym.rs | 36 +++++++++++++++++++ 6 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 src/tools/miri/src/sym.rs diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 14528759472c8..3766debb159d0 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -193,7 +193,11 @@ fn make_miri_codegen_backend(opts: &Options, target: &Target) -> Box( @@ -354,6 +358,9 @@ impl rustc_driver::Callbacks for MiriDepCompilerCalls { ) } }); + + // Register our custom extra symbols. + config.extra_symbols = miri::sym::EXTRA_SYMBOLS.into(); } fn after_analysis<'tcx>( diff --git a/src/tools/miri/src/concurrency/genmc/scheduling.rs b/src/tools/miri/src/concurrency/genmc/scheduling.rs index c760126d787d1..54e87c05818de 100644 --- a/src/tools/miri/src/concurrency/genmc/scheduling.rs +++ b/src/tools/miri/src/concurrency/genmc/scheduling.rs @@ -5,7 +5,8 @@ use rustc_middle::ty::{self, Ty}; use super::GenmcCtx; use crate::{ - InterpCx, InterpResult, MiriMachine, TerminationInfo, ThreadId, interp_ok, throw_machine_stop, + InterpCx, InterpResult, MiriMachine, TerminationInfo, ThreadId, interp_ok, sym, + throw_machine_stop, }; enum NextInstrKind { @@ -76,11 +77,11 @@ fn get_function_kind<'tcx>( // NOTE: Functions intercepted by Miri in `concurrency/genmc/intercep.rs` must also be added here. // Such intercepted functions, like `sys::Mutex::lock`, should be treated as atomics to ensure we call the scheduler when we encounter one of them. // These functions must also be classified whether they may have load semantics. - if ecx.tcx.is_diagnostic_item(rustc_span::sym::sys_mutex_lock, callee_def_id) - || ecx.tcx.is_diagnostic_item(rustc_span::sym::sys_mutex_try_lock, callee_def_id) + if ecx.tcx.is_diagnostic_item(sym::sys_mutex_lock, callee_def_id) + || ecx.tcx.is_diagnostic_item(sym::sys_mutex_try_lock, callee_def_id) { return interp_ok(MaybeAtomic(ActionKind::Load)); - } else if ecx.tcx.is_diagnostic_item(rustc_span::sym::sys_mutex_unlock, callee_def_id) { + } else if ecx.tcx.is_diagnostic_item(sym::sys_mutex_unlock, callee_def_id) { return interp_ok(MaybeAtomic(ActionKind::NonLoad)); } // The next step is a call to a regular Rust function. diff --git a/src/tools/miri/src/concurrency/genmc/shims.rs b/src/tools/miri/src/concurrency/genmc/shims.rs index cac03a21afc52..ff9d43e70a46d 100644 --- a/src/tools/miri/src/concurrency/genmc/shims.rs +++ b/src/tools/miri/src/concurrency/genmc/shims.rs @@ -198,7 +198,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); // NOTE: When adding new intercepted functions here, they must also be added to `fn get_function_kind` in `concurrency/genmc/scheduling.rs`. - use rustc_span::sym; if this.tcx.is_diagnostic_item(sym::sys_mutex_lock, instance.def_id()) { let [mutex] = get_fn_args(instance, args)?; let mutex = this.deref_pointer(&mutex)?; diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index f09fc6c187896..ae7ec1fdcce51 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -10,7 +10,7 @@ pub use self::atomic::AtomicRmwOp; use rand::Rng; use rustc_abi::Size; use rustc_middle::{mir, ty}; -use rustc_span::{Symbol, sym}; +use rustc_span::Symbol; use self::atomic::EvalContextExt as _; use self::math::EvalContextExt as _; diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 232a24306d65e..1bfc0cceafb83 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -17,6 +17,7 @@ #![feature(derive_coerce_pointee)] #![feature(arbitrary_self_types)] #![feature(iter_advance_by)] +#![feature(macro_metavar_expr)] // Configure clippy and other lints #![allow( clippy::collapsible_else_if, @@ -87,6 +88,7 @@ mod math; mod operator; mod provenance_gc; mod shims; +pub mod sym; // Establish a "crate-wide prelude": we often import `crate::*`. // Make all those symbols available in the same place as our own. diff --git a/src/tools/miri/src/sym.rs b/src/tools/miri/src/sym.rs new file mode 100644 index 0000000000000..7c870494f216c --- /dev/null +++ b/src/tools/miri/src/sym.rs @@ -0,0 +1,36 @@ +#![allow(non_upper_case_globals)] + +#[doc(no_inline)] +pub use rustc_span::sym::*; + +macro_rules! val { + ($name:ident) => { + stringify!($name) + }; + ($name:ident $value:literal) => { + $value + }; +} + +macro_rules! generate { + ($($name:ident $(: $value:literal)? ,)*) => { + /// To be supplied to `rustc_interface::Config` + pub const EXTRA_SYMBOLS: &[&str] = &[ + $( + val!($name $($value)?), + )* + ]; + + $( + pub const $name: rustc_span::Symbol = rustc_span::Symbol::new(rustc_span::symbol::PREDEFINED_SYMBOLS_COUNT + ${index()}); + )* + }; +} + +// List of extra symbols to be included in Miri. +// An alternative content can be specified using a colon after the symbol name. +generate! { + sys_mutex_lock, + sys_mutex_try_lock, + sys_mutex_unlock, +} From e7e512da465d520ac084f5f4fa235210debf5296 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Mon, 23 Feb 2026 05:22:16 +0000 Subject: [PATCH 13/46] Prepare for merging from rust-lang/rust This updates the rust-version file to c78a29473a68f07012904af11c92ecffa68fcc75. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 379f038b6d1ca..b6e1b2bc55df4 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -5fb2ff8611e5a4af4dc85977cfdecfbf3ffa6ade +c78a29473a68f07012904af11c92ecffa68fcc75 From 7ed7b2b820c16789955a5d9936ad23bc62a1ab4b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 24 Feb 2026 09:11:58 +0100 Subject: [PATCH 14/46] Prepare for merging from rust-lang/rust This updates the rust-version file to b3869b94cd1ed4bfa2eb28f301535d5e9599c713. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index b6e1b2bc55df4..bef3f42f77755 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -c78a29473a68f07012904af11c92ecffa68fcc75 +b3869b94cd1ed4bfa2eb28f301535d5e9599c713 From 2e17c9b81a0446cdfaae88e038bbfbba7d105b5a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 24 Feb 2026 22:29:26 +0100 Subject: [PATCH 15/46] remove FIXME about size of Pointer type --- src/tools/miri/src/machine.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 7883673cdd6a2..000f4ac4cecaa 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -300,7 +300,8 @@ pub enum ProvenanceExtra { #[cfg(target_pointer_width = "64")] static_assert_size!(StrictPointer, 24); -// FIXME: this would with in 24bytes but layout optimizations are not smart enough +// Pointer does not fit as the layout algorithm isn't smart enough (but also, we tried using +// pattern types to get a larger niche that makes this fit and it didn't improve performance). // #[cfg(target_pointer_width = "64")] //static_assert_size!(Pointer, 24); #[cfg(target_pointer_width = "64")] From d398074f2293b166b07e63fd7f090f912c22ac64 Mon Sep 17 00:00:00 2001 From: WhySoBad <49595640+WhySoBad@users.noreply.github.com> Date: Sun, 22 Feb 2026 01:21:00 +0100 Subject: [PATCH 16/46] Start socket shim Basic shim which allows opening and closing a socket --- .../miri/src/shims/unix/foreign_items.rs | 12 ++ src/tools/miri/src/shims/unix/mod.rs | 2 + src/tools/miri/src/shims/unix/socket.rs | 157 ++++++++++++++++++ .../libc/libc-socket-with-isolation.rs | 19 +++ .../libc/libc-socket-with-isolation.stderr | 2 + .../miri/tests/pass-dep/libc/libc-socket.rs | 19 +++ 6 files changed, 211 insertions(+) create mode 100644 src/tools/miri/src/shims/unix/socket.rs create mode 100644 src/tools/miri/tests/pass-dep/libc/libc-socket-with-isolation.rs create mode 100644 src/tools/miri/tests/pass-dep/libc/libc-socket-with-isolation.stderr create mode 100644 src/tools/miri/tests/pass-dep/libc/libc-socket.rs diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 48e2ebd0f13ea..130643ec680d2 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -539,6 +539,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } + // Network sockets + "socket" => { + let [domain, type_, protocol] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, i32, i32) -> i32), + link_name, + abi, + args, + )?; + let result = this.socket(domain, type_, protocol)?; + this.write_scalar(result, dest)?; + } + // Time "gettimeofday" => { let [tv, tz] = this.check_shim_sig( diff --git a/src/tools/miri/src/shims/unix/mod.rs b/src/tools/miri/src/shims/unix/mod.rs index 660e4ded5cda7..5ea49926fb9fd 100644 --- a/src/tools/miri/src/shims/unix/mod.rs +++ b/src/tools/miri/src/shims/unix/mod.rs @@ -4,6 +4,7 @@ mod env; mod fd; mod fs; mod mem; +mod socket; mod sync; mod thread; mod unnamed_socket; @@ -21,6 +22,7 @@ pub use self::fd::{EvalContextExt as _, UnixFileDescription}; pub use self::fs::{DirTable, EvalContextExt as _}; pub use self::linux_like::epoll::EpollInterestTable; pub use self::mem::EvalContextExt as _; +pub use self::socket::EvalContextExt as _; pub use self::sync::EvalContextExt as _; pub use self::thread::{EvalContextExt as _, ThreadNameResult}; pub use self::unnamed_socket::EvalContextExt as _; diff --git a/src/tools/miri/src/shims/unix/socket.rs b/src/tools/miri/src/shims/unix/socket.rs new file mode 100644 index 0000000000000..66fe5d8a44a40 --- /dev/null +++ b/src/tools/miri/src/shims/unix/socket.rs @@ -0,0 +1,157 @@ +use std::cell::Cell; +use std::net::{TcpListener, TcpStream}; + +use rustc_const_eval::interpret::{InterpResult, interp_ok}; +use rustc_middle::throw_unsup_format; +use rustc_target::spec::Os; + +use crate::shims::files::{FdId, FileDescription}; +use crate::{OpTy, Scalar, *}; + +#[derive(Debug, PartialEq)] +enum SocketFamily { + // IPv4 internet protocols + IPv4, + // IPv6 internet protocols + IPv6, +} + +#[derive(Debug)] +enum SocketType { + /// Reliable full-duplex communication, based on connections. + Stream, +} + +#[allow(unused)] +#[derive(Debug)] +enum SocketKind { + TcpListener(TcpListener), + TcpStream(TcpStream), +} + +#[allow(unused)] +#[derive(Debug)] +struct Socket { + /// Family of the socket, used to ensure socket only binds/connects to address of + /// same family. + family: SocketFamily, + /// Type of the socket, either datagram or stream. + /// Only stream is supported at the moment! + socket_type: SocketType, + /// Whether this fd is non-blocking or not. + is_non_block: Cell, +} + +impl FileDescription for Socket { + fn name(&self) -> &'static str { + "socket" + } + + fn destroy<'tcx>( + self, + _self_id: FdId, + _communicate_allowed: bool, + _ecx: &mut MiriInterpCx<'tcx>, + ) -> InterpResult<'tcx, std::io::Result<()>> + where + Self: Sized, + { + interp_ok(Ok(())) + } + + fn get_flags<'tcx>(&self, ecx: &mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, Scalar> { + let mut flags = ecx.eval_libc_i32("O_RDWR"); + + if self.is_non_block.get() { + flags |= ecx.eval_libc_i32("O_NONBLOCK"); + } + + interp_ok(Scalar::from_i32(flags)) + } + + fn set_flags<'tcx>( + &self, + mut _flag: i32, + _ecx: &mut MiriInterpCx<'tcx>, + ) -> InterpResult<'tcx, Scalar> { + throw_unsup_format!("fcntl: socket flags aren't supported") + } +} + +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + /// For more information on the arguments see the socket manpage: + /// + fn socket( + &mut self, + domain: &OpTy<'tcx>, + type_: &OpTy<'tcx>, + protocol: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + + let domain = this.read_scalar(domain)?.to_i32()?; + let mut flags = this.read_scalar(type_)?.to_i32()?; + let protocol = this.read_scalar(protocol)?.to_i32()?; + + // Reject if isolation is enabled + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`socket`", reject_with)?; + this.set_last_error(LibcError("EACCES"))?; + return interp_ok(Scalar::from_i32(-1)); + } + + let mut is_sock_nonblock = false; + + // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so + // if there is anything left at the end, that's an unsupported flag. + if matches!(this.tcx.sess.target.os, Os::Linux | Os::Android | Os::FreeBsd) { + // SOCK_NONBLOCK and SOCK_CLOEXEC only exist on Linux, Android and FreeBSD. + let sock_nonblock = this.eval_libc_i32("SOCK_NONBLOCK"); + let sock_cloexec = this.eval_libc_i32("SOCK_CLOEXEC"); + if flags & sock_nonblock == sock_nonblock { + is_sock_nonblock = true; + flags &= !sock_nonblock; + } + if flags & sock_cloexec == sock_cloexec { + // We don't support `exec` so we can ignore this. + flags &= !sock_cloexec; + } + } + + let family = if domain == this.eval_libc_i32("AF_INET") { + SocketFamily::IPv4 + } else if domain == this.eval_libc_i32("AF_INET6") { + SocketFamily::IPv6 + } else { + throw_unsup_format!( + "socket: domain {:#x} is unsupported, only AF_INET and \ + AF_INET6 are allowed.", + domain + ); + }; + + if flags != this.eval_libc_i32("SOCK_STREAM") { + throw_unsup_format!( + "socket: type {:#x} is unsupported, only SOCK_STREAM, \ + SOCK_CLOEXEC and SOCK_NONBLOCK are allowed", + flags + ); + } + if protocol != 0 { + throw_unsup_format!( + "socket: socket protocol {protocol} is unsupported, \ + only 0 is allowed" + ); + } + + let fds = &mut this.machine.fds; + let fd = fds.new_ref(Socket { + family, + is_non_block: Cell::new(is_sock_nonblock), + socket_type: SocketType::Stream, + }); + + interp_ok(Scalar::from_i32(fds.insert(fd))) + } +} diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket-with-isolation.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket-with-isolation.rs new file mode 100644 index 0000000000000..ce4df4de72b6d --- /dev/null +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket-with-isolation.rs @@ -0,0 +1,19 @@ +//@ignore-target: windows # No libc socket on Windows +//@ignore-target: solaris # Socket is a macro for __xnet7_socket which has no shim +//@ignore-target: illumos # Socket is a macro for __xnet7_socket which has no shim +//@compile-flags: -Zmiri-isolation-error=warn-nobacktrace + +use std::io::ErrorKind; + +#[path = "../../utils/libc.rs"] +mod libc_utils; +use libc_utils::*; + +fn main() { + unsafe { + let err = errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap_err(); + assert_eq!(err.kind(), ErrorKind::PermissionDenied); + // check that it is the right kind of `PermissionDenied` + assert_eq!(err.raw_os_error(), Some(libc::EACCES)); + } +} diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket-with-isolation.stderr b/src/tools/miri/tests/pass-dep/libc/libc-socket-with-isolation.stderr new file mode 100644 index 0000000000000..36fc0a5aac328 --- /dev/null +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket-with-isolation.stderr @@ -0,0 +1,2 @@ +warning: `socket` was made to return an error due to isolation + diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket.rs new file mode 100644 index 0000000000000..ac9f13367642d --- /dev/null +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket.rs @@ -0,0 +1,19 @@ +//@ignore-target: windows # No libc socket on Windows +//@ignore-target: solaris # Does socket is a macro for __xnet7_socket which has no shim +//@ignore-target: illumos # Does socket is a macro for __xnet7_socket which has no shim +//@compile-flags: -Zmiri-disable-isolation + +#[path = "../../utils/libc.rs"] +mod libc_utils; +use libc_utils::*; + +fn main() { + test_socket_close(); +} + +fn test_socket_close() { + unsafe { + let sockfd = errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap(); + errno_check(libc::close(sockfd)); + } +} From 6d271ba50557a3ea5065fb0ef72d02274cf65c74 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 26 Feb 2026 18:00:37 +0100 Subject: [PATCH 17/46] Prepare for merging from rust-lang/rust This updates the rust-version file to bb779a91568ac1ee0b8a9dcb6b69219ef30b18a3. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index bef3f42f77755..4e570e3693161 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -b3869b94cd1ed4bfa2eb28f301535d5e9599c713 +bb779a91568ac1ee0b8a9dcb6b69219ef30b18a3 From 470706d13fe7da769185868f78d32ce9264afcbf Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 26 Feb 2026 19:10:26 +0100 Subject: [PATCH 18/46] silence clippy lint that makes code harder to read --- src/tools/miri/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 176d4976cec65..5787efdf8cd1a 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -40,6 +40,7 @@ clippy::needless_lifetimes, clippy::too_long_first_doc_paragraph, clippy::len_zero, + clippy::collapsible_match, // We are not implementing queries here so it's fine rustc::potential_query_instability, )] From b8d5538f51c278ca19f9c3e83ad44275d32492ef Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 26 Feb 2026 19:20:46 +0100 Subject: [PATCH 19/46] add test for deallocating partially-interior-mutable ref --- .../both_borrows/mixed_cell_deallocate.rs | 18 ++++++++++++++ .../mixed_cell_deallocate.stack.stderr | 23 ++++++++++++++++++ .../mixed_cell_deallocate.tree.stderr | 24 +++++++++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 src/tools/miri/tests/fail/both_borrows/mixed_cell_deallocate.rs create mode 100644 src/tools/miri/tests/fail/both_borrows/mixed_cell_deallocate.stack.stderr create mode 100644 src/tools/miri/tests/fail/both_borrows/mixed_cell_deallocate.tree.stderr diff --git a/src/tools/miri/tests/fail/both_borrows/mixed_cell_deallocate.rs b/src/tools/miri/tests/fail/both_borrows/mixed_cell_deallocate.rs new file mode 100644 index 0000000000000..73776827aec06 --- /dev/null +++ b/src/tools/miri/tests/fail/both_borrows/mixed_cell_deallocate.rs @@ -0,0 +1,18 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + +use std::alloc; +use std::cell::Cell; + +type T = (Cell, i32); + +// Deallocating `x` is UB because not all bytes are in an `UnsafeCell`. +fn foo(x: &T) { + let layout = alloc::Layout::new::(); + unsafe { alloc::dealloc(x as *const _ as *mut T as *mut u8, layout) }; //~ERROR: dealloc +} + +fn main() { + let b: Box = Box::new((Cell::new(0), 0)); + foo(unsafe { std::mem::transmute(Box::into_raw(b)) }); +} diff --git a/src/tools/miri/tests/fail/both_borrows/mixed_cell_deallocate.stack.stderr b/src/tools/miri/tests/fail/both_borrows/mixed_cell_deallocate.stack.stderr new file mode 100644 index 0000000000000..ae99f17fd9c22 --- /dev/null +++ b/src/tools/miri/tests/fail/both_borrows/mixed_cell_deallocate.stack.stderr @@ -0,0 +1,23 @@ +error: Undefined Behavior: attempting deallocation using at ALLOC, but that tag only grants SharedReadOnly permission for this location + --> tests/fail/both_borrows/mixed_cell_deallocate.rs:LL:CC + | +LL | unsafe { alloc::dealloc(x as *const _ as *mut T as *mut u8, layout) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information +help: was created by a SharedReadOnly retag at offsets [0x4..0xc] + --> tests/fail/both_borrows/mixed_cell_deallocate.rs:LL:CC + | +LL | unsafe { alloc::dealloc(x as *const _ as *mut T as *mut u8, layout) }; + | ^ + = note: stack backtrace: + 0: foo + at tests/fail/both_borrows/mixed_cell_deallocate.rs:LL:CC + 1: main + at tests/fail/both_borrows/mixed_cell_deallocate.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/both_borrows/mixed_cell_deallocate.tree.stderr b/src/tools/miri/tests/fail/both_borrows/mixed_cell_deallocate.tree.stderr new file mode 100644 index 0000000000000..5dc2ffc27c1c7 --- /dev/null +++ b/src/tools/miri/tests/fail/both_borrows/mixed_cell_deallocate.tree.stderr @@ -0,0 +1,24 @@ +error: Undefined Behavior: deallocation through at ALLOC[0x4] is forbidden + --> tests/fail/both_borrows/mixed_cell_deallocate.rs:LL:CC + | +LL | unsafe { alloc::dealloc(x as *const _ as *mut T as *mut u8, layout) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information + = help: the accessed tag has state Frozen which forbids this deallocation (acting as a child write access) +help: the accessed tag was created here, in the initial state Cell + --> tests/fail/both_borrows/mixed_cell_deallocate.rs:LL:CC + | +LL | fn foo(x: &T) { + | ^ + = note: stack backtrace: + 0: foo + at tests/fail/both_borrows/mixed_cell_deallocate.rs:LL:CC + 1: main + at tests/fail/both_borrows/mixed_cell_deallocate.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + From 7b8534e0111661602ade251f3c5362235d9d4863 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 28 Feb 2026 16:52:38 +0100 Subject: [PATCH 20/46] Prepare for merging from rust-lang/rust This updates the rust-version file to ba1567989ee7774a1fb53aa680a8e4e8daa0f519. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 4e570e3693161..3e265317484a6 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -bb779a91568ac1ee0b8a9dcb6b69219ef30b18a3 +ba1567989ee7774a1fb53aa680a8e4e8daa0f519 From a49c0837a63fb13bc2a330dcf30c6b6abff39c1c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 1 Mar 2026 12:38:43 +0100 Subject: [PATCH 21/46] native-lib: better organize fn ptr tests --- ...r.notrace.stderr => call_fn_ptr.notrace.stderr} | 12 ++++++------ .../fail/{call_function_ptr.rs => call_fn_ptr.rs} | 0 ...n_ptr.trace.stderr => call_fn_ptr.trace.stderr} | 12 ++++++------ src/tools/miri/tests/native-lib/fn_ptr.c | 11 +++++++++++ .../miri/tests/native-lib/pass/ptr_read_access.rs | 14 -------------- src/tools/miri/tests/native-lib/ptr_read_access.c | 13 ------------- src/tools/miri/tests/ui.rs | 3 ++- 7 files changed, 25 insertions(+), 40 deletions(-) rename src/tools/miri/tests/native-lib/fail/{call_function_ptr.notrace.stderr => call_fn_ptr.notrace.stderr} (77%) rename src/tools/miri/tests/native-lib/fail/{call_function_ptr.rs => call_fn_ptr.rs} (100%) rename src/tools/miri/tests/native-lib/fail/{call_function_ptr.trace.stderr => call_fn_ptr.trace.stderr} (79%) create mode 100644 src/tools/miri/tests/native-lib/fn_ptr.c diff --git a/src/tools/miri/tests/native-lib/fail/call_function_ptr.notrace.stderr b/src/tools/miri/tests/native-lib/fail/call_fn_ptr.notrace.stderr similarity index 77% rename from src/tools/miri/tests/native-lib/fail/call_function_ptr.notrace.stderr rename to src/tools/miri/tests/native-lib/fail/call_fn_ptr.notrace.stderr index faabba9ca7257..5e7764652fa74 100644 --- a/src/tools/miri/tests/native-lib/fail/call_function_ptr.notrace.stderr +++ b/src/tools/miri/tests/native-lib/fail/call_fn_ptr.notrace.stderr @@ -1,5 +1,5 @@ warning: sharing memory with a native function called via FFI - --> tests/native-lib/fail/call_function_ptr.rs:LL:CC + --> tests/native-lib/fail/call_fn_ptr.rs:LL:CC | LL | call_fn_ptr(Some(nop)); | ^^^^^^^^^^^^^^^^^^^^^^ sharing memory with a native function @@ -10,12 +10,12 @@ LL | call_fn_ptr(Some(nop)); = help: what this means is that Miri will easily miss Undefined Behavior related to incorrect usage of this shared memory, so you should not take a clean Miri run as a signal that your FFI code is UB-free = note: stack backtrace: 0: pass_fn_ptr - at tests/native-lib/fail/call_function_ptr.rs:LL:CC + at tests/native-lib/fail/call_fn_ptr.rs:LL:CC 1: main - at tests/native-lib/fail/call_function_ptr.rs:LL:CC + at tests/native-lib/fail/call_fn_ptr.rs:LL:CC error: unsupported operation: calling a function pointer through the FFI boundary - --> tests/native-lib/fail/call_function_ptr.rs:LL:CC + --> tests/native-lib/fail/call_fn_ptr.rs:LL:CC | LL | call_fn_ptr(Some(nop)); | ^^^^^^^^^^^^^^^^^^^^^^ unsupported operation occurred here @@ -23,9 +23,9 @@ LL | call_fn_ptr(Some(nop)); = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support = note: stack backtrace: 0: pass_fn_ptr - at tests/native-lib/fail/call_function_ptr.rs:LL:CC + at tests/native-lib/fail/call_fn_ptr.rs:LL:CC 1: main - at tests/native-lib/fail/call_function_ptr.rs:LL:CC + at tests/native-lib/fail/call_fn_ptr.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/native-lib/fail/call_function_ptr.rs b/src/tools/miri/tests/native-lib/fail/call_fn_ptr.rs similarity index 100% rename from src/tools/miri/tests/native-lib/fail/call_function_ptr.rs rename to src/tools/miri/tests/native-lib/fail/call_fn_ptr.rs diff --git a/src/tools/miri/tests/native-lib/fail/call_function_ptr.trace.stderr b/src/tools/miri/tests/native-lib/fail/call_fn_ptr.trace.stderr similarity index 79% rename from src/tools/miri/tests/native-lib/fail/call_function_ptr.trace.stderr rename to src/tools/miri/tests/native-lib/fail/call_fn_ptr.trace.stderr index e56a5ece782b5..7418b08ebe110 100644 --- a/src/tools/miri/tests/native-lib/fail/call_function_ptr.trace.stderr +++ b/src/tools/miri/tests/native-lib/fail/call_fn_ptr.trace.stderr @@ -1,5 +1,5 @@ warning: sharing memory with a native function called via FFI - --> tests/native-lib/fail/call_function_ptr.rs:LL:CC + --> tests/native-lib/fail/call_fn_ptr.rs:LL:CC | LL | call_fn_ptr(Some(nop)); | ^^^^^^^^^^^^^^^^^^^^^^ sharing memory with a native function @@ -11,12 +11,12 @@ LL | call_fn_ptr(Some(nop)); = help: tracing memory accesses in native code is not yet fully implemented, so there can be further imprecisions beyond what is documented here = note: stack backtrace: 0: pass_fn_ptr - at tests/native-lib/fail/call_function_ptr.rs:LL:CC + at tests/native-lib/fail/call_fn_ptr.rs:LL:CC 1: main - at tests/native-lib/fail/call_function_ptr.rs:LL:CC + at tests/native-lib/fail/call_fn_ptr.rs:LL:CC error: unsupported operation: calling a function pointer through the FFI boundary - --> tests/native-lib/fail/call_function_ptr.rs:LL:CC + --> tests/native-lib/fail/call_fn_ptr.rs:LL:CC | LL | call_fn_ptr(Some(nop)); | ^^^^^^^^^^^^^^^^^^^^^^ unsupported operation occurred here @@ -24,9 +24,9 @@ LL | call_fn_ptr(Some(nop)); = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support = note: stack backtrace: 0: pass_fn_ptr - at tests/native-lib/fail/call_function_ptr.rs:LL:CC + at tests/native-lib/fail/call_fn_ptr.rs:LL:CC 1: main - at tests/native-lib/fail/call_function_ptr.rs:LL:CC + at tests/native-lib/fail/call_fn_ptr.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/native-lib/fn_ptr.c b/src/tools/miri/tests/native-lib/fn_ptr.c new file mode 100644 index 0000000000000..99eb8d458c2d3 --- /dev/null +++ b/src/tools/miri/tests/native-lib/fn_ptr.c @@ -0,0 +1,11 @@ +#include +#include + +// See comments in build_native_lib() +#define EXPORT __attribute__((visibility("default"))) + +EXPORT void call_fn_ptr(void f(void)) { + if (f != NULL) { + f(); + } +} diff --git a/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs b/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs index 36eff04a03c05..ad4c84d3e83b8 100644 --- a/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs +++ b/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs @@ -10,7 +10,6 @@ fn main() { test_access_simple(); test_access_nested(); test_access_static(); - pass_fn_ptr(); } /// Test function that dereferences an int pointer and prints its contents from C. @@ -82,16 +81,3 @@ fn test_access_static() { assert_eq!(unsafe { access_static(&STATIC) }, 9001); } - -fn pass_fn_ptr() { - extern "C" { - fn pass_fn_ptr(s: Option); - } - - extern "C" fn nop() {} - - unsafe { - pass_fn_ptr(None); // this one is fine - pass_fn_ptr(Some(nop)); // this one is not - } -} diff --git a/src/tools/miri/tests/native-lib/ptr_read_access.c b/src/tools/miri/tests/native-lib/ptr_read_access.c index 44ba13aa54a62..58017d809af89 100644 --- a/src/tools/miri/tests/native-lib/ptr_read_access.c +++ b/src/tools/miri/tests/native-lib/ptr_read_access.c @@ -62,16 +62,3 @@ EXPORT int32_t access_static(const Static *s_ptr) { EXPORT uintptr_t do_one_deref(const int32_t ***ptr) { return (uintptr_t)*ptr; } - -/* Test: pass_fn_ptr */ - -EXPORT void pass_fn_ptr(void f(void)) { - (void)f; // suppress unused warning -} - -/* Test: function_ptrs */ -EXPORT void call_fn_ptr(void f(void)) { - if (f != NULL) { - f(); - } -} diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index 047cdeb357c20..2a6151737d6c0 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -42,7 +42,7 @@ fn build_native_lib(target: &str) -> PathBuf { std::fs::create_dir_all(&so_target_dir) .expect("Failed to create directory for shared object file"); // We use a platform-neutral file extension to avoid having to hard-code alternatives. - let native_lib_path = so_target_dir.join("native-lib.module"); + let native_lib_path = so_target_dir.join("native-lib-tests.so"); let cc_output = Command::new(cc) .args([ "-shared", @@ -58,6 +58,7 @@ fn build_native_lib(target: &str) -> PathBuf { "tests/native-lib/aggregate_arguments.c", "tests/native-lib/ptr_read_access.c", "tests/native-lib/ptr_write_access.c", + "tests/native-lib/fn_ptr.c", // Ensure we notice serious problems in the C code. "-Wall", "-Wextra", From 71f4b75236c6137da231d510e212203253d65e2e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 1 Mar 2026 12:46:32 +0100 Subject: [PATCH 22/46] native-lib: also test fn ptrs that are instances of generics --- src/tools/miri/src/alloc_addresses/mod.rs | 6 +++- .../call_fn_ptr_with_generic.notrace.stderr | 31 ++++++++++++++++++ .../fail/call_fn_ptr_with_generic.rs | 22 +++++++++++++ .../call_fn_ptr_with_generic.trace.stderr | 32 +++++++++++++++++++ src/tools/miri/tests/native-lib/fn_ptr.c | 6 ++++ 5 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 src/tools/miri/tests/native-lib/fail/call_fn_ptr_with_generic.notrace.stderr create mode 100644 src/tools/miri/tests/native-lib/fail/call_fn_ptr_with_generic.rs create mode 100644 src/tools/miri/tests/native-lib/fail/call_fn_ptr_with_generic.trace.stderr diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index 32897eb89a83c..91559e76e68c6 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -168,7 +168,11 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { if let Some(GlobalAlloc::Function { instance, .. }) = this.tcx.try_get_global_alloc(alloc_id) { - let fn_sig = this.tcx.fn_sig(instance.def_id()).skip_binder().skip_binder(); + let fn_sig = this.tcx.instantiate_bound_regions_with_erased( + this.tcx + .fn_sig(instance.def_id()) + .instantiate(*this.tcx, instance.args), + ); let fn_ptr = crate::shims::native_lib::build_libffi_closure(this, fn_sig)?; #[expect( diff --git a/src/tools/miri/tests/native-lib/fail/call_fn_ptr_with_generic.notrace.stderr b/src/tools/miri/tests/native-lib/fail/call_fn_ptr_with_generic.notrace.stderr new file mode 100644 index 0000000000000..e0536a4afe583 --- /dev/null +++ b/src/tools/miri/tests/native-lib/fail/call_fn_ptr_with_generic.notrace.stderr @@ -0,0 +1,31 @@ +warning: sharing memory with a native function called via FFI + --> tests/native-lib/fail/call_fn_ptr_with_generic.rs:LL:CC + | +LL | call_fn_ptr(id::); + | ^^^^^^^^^^^^^^^^^^^^^^ sharing memory with a native function + | + = help: when memory is shared with a native function call, Miri stops tracking initialization and provenance for that memory + = help: in particular, Miri assumes that the native call initializes all memory it has access to + = help: Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory + = help: what this means is that Miri will easily miss Undefined Behavior related to incorrect usage of this shared memory, so you should not take a clean Miri run as a signal that your FFI code is UB-free + = note: stack backtrace: + 0: pass_fn_ptr + at tests/native-lib/fail/call_fn_ptr_with_generic.rs:LL:CC + 1: main + at tests/native-lib/fail/call_fn_ptr_with_generic.rs:LL:CC + +error: unsupported operation: calling a function pointer through the FFI boundary + --> tests/native-lib/fail/call_fn_ptr_with_generic.rs:LL:CC + | +LL | call_fn_ptr(id::); + | ^^^^^^^^^^^^^^^^^^^^^^ unsupported operation occurred here + | + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = note: stack backtrace: + 0: pass_fn_ptr + at tests/native-lib/fail/call_fn_ptr_with_generic.rs:LL:CC + 1: main + at tests/native-lib/fail/call_fn_ptr_with_generic.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + diff --git a/src/tools/miri/tests/native-lib/fail/call_fn_ptr_with_generic.rs b/src/tools/miri/tests/native-lib/fail/call_fn_ptr_with_generic.rs new file mode 100644 index 0000000000000..8be29c0c121a2 --- /dev/null +++ b/src/tools/miri/tests/native-lib/fail/call_fn_ptr_with_generic.rs @@ -0,0 +1,22 @@ +//@revisions: trace notrace +//@[trace] only-target: x86_64-unknown-linux-gnu i686-unknown-linux-gnu +//@[trace] compile-flags: -Zmiri-native-lib-enable-tracing +//@compile-flags: -Zmiri-permissive-provenance + +fn main() { + pass_fn_ptr() +} + +fn pass_fn_ptr() { + extern "C" { + fn call_fn_ptr(s: extern "C" fn(i32) -> i32); + } + + extern "C" fn id(x: T) -> T { + x + } + + unsafe { + call_fn_ptr(id::); //~ ERROR: unsupported operation: calling a function pointer through the FFI boundary + } +} diff --git a/src/tools/miri/tests/native-lib/fail/call_fn_ptr_with_generic.trace.stderr b/src/tools/miri/tests/native-lib/fail/call_fn_ptr_with_generic.trace.stderr new file mode 100644 index 0000000000000..24a33e6458685 --- /dev/null +++ b/src/tools/miri/tests/native-lib/fail/call_fn_ptr_with_generic.trace.stderr @@ -0,0 +1,32 @@ +warning: sharing memory with a native function called via FFI + --> tests/native-lib/fail/call_fn_ptr_with_generic.rs:LL:CC + | +LL | call_fn_ptr(id::); + | ^^^^^^^^^^^^^^^^^^^^^^ sharing memory with a native function + | + = help: when memory is shared with a native function call, Miri can only track initialisation and provenance on a best-effort basis + = help: in particular, Miri assumes that the native call initializes all memory it has written to + = help: Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory + = help: what this means is that Miri will easily miss Undefined Behavior related to incorrect usage of this shared memory, so you should not take a clean Miri run as a signal that your FFI code is UB-free + = help: tracing memory accesses in native code is not yet fully implemented, so there can be further imprecisions beyond what is documented here + = note: stack backtrace: + 0: pass_fn_ptr + at tests/native-lib/fail/call_fn_ptr_with_generic.rs:LL:CC + 1: main + at tests/native-lib/fail/call_fn_ptr_with_generic.rs:LL:CC + +error: unsupported operation: calling a function pointer through the FFI boundary + --> tests/native-lib/fail/call_fn_ptr_with_generic.rs:LL:CC + | +LL | call_fn_ptr(id::); + | ^^^^^^^^^^^^^^^^^^^^^^ unsupported operation occurred here + | + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = note: stack backtrace: + 0: pass_fn_ptr + at tests/native-lib/fail/call_fn_ptr_with_generic.rs:LL:CC + 1: main + at tests/native-lib/fail/call_fn_ptr_with_generic.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + diff --git a/src/tools/miri/tests/native-lib/fn_ptr.c b/src/tools/miri/tests/native-lib/fn_ptr.c index 99eb8d458c2d3..752e27b3e8a0a 100644 --- a/src/tools/miri/tests/native-lib/fn_ptr.c +++ b/src/tools/miri/tests/native-lib/fn_ptr.c @@ -9,3 +9,9 @@ EXPORT void call_fn_ptr(void f(void)) { f(); } } + +EXPORT void call_fn_ptr_with_arg(int32_t f(int32_t)) { + if (f != NULL) { + f(42); + } +} From a36fc5ca41e5595fbb9c9fd4b6755bc8e336f459 Mon Sep 17 00:00:00 2001 From: MousseARaser06 Date: Sun, 1 Feb 2026 13:42:00 +0100 Subject: [PATCH 23/46] FCNTL F_SETFL Ignore creation flags --- src/tools/miri/src/shims/unix/fd.rs | 27 ++++++++++++++++++- .../miri/src/shims/unix/unnamed_socket.rs | 6 ----- .../miri/tests/pass-dep/libc/libc-pipe.rs | 18 +++++++++++++ 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index 3c2e14c181682..aea6642f47c42 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -194,7 +194,32 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; let [flag] = check_min_vararg_count("fcntl(fd, F_SETFL, ...)", varargs)?; - let flag = this.read_scalar(flag)?.to_i32()?; + let mut flag = this.read_scalar(flag)?.to_i32()?; + + let allowed_flags = match this.tcx.sess.target.os { + Os::MacOs => + this.eval_libc_i32("O_NONBLOCK") + | this.eval_libc_i32("O_APPEND") + | this.eval_libc_i32("O_ASYNC"), + Os::FreeBsd => + this.eval_libc_i32("O_NONBLOCK") + | this.eval_libc_i32("O_APPEND") + | this.eval_libc_i32("O_DIRECT") + | this.eval_libc_i32("O_ASYNC"), + Os::Solaris | Os::Illumos => + this.eval_libc_i32("O_NONBLOCK") + | this.eval_libc_i32("O_APPEND") + | this.eval_libc_i32("O_DIRECT"), + // Linux + Android match case + _ => + this.eval_libc_i32("O_NONBLOCK") + | this.eval_libc_i32("O_APPEND") + | this.eval_libc_i32("O_DIRECT") + | this.eval_libc_i32("O_NOATIME") + | this.eval_libc_i32("O_ASYNC"), + }; + + flag &= allowed_flags; fd.set_flags(flag, this) } diff --git a/src/tools/miri/src/shims/unix/unnamed_socket.rs b/src/tools/miri/src/shims/unix/unnamed_socket.rs index cc371b43a6815..b33c294b3a166 100644 --- a/src/tools/miri/src/shims/unix/unnamed_socket.rs +++ b/src/tools/miri/src/shims/unix/unnamed_socket.rs @@ -173,9 +173,6 @@ impl FileDescription for AnonSocket { // FIXME: File creation flags should be ignored. let o_nonblock = ecx.eval_libc_i32("O_NONBLOCK"); - let o_rdonly = ecx.eval_libc_i32("O_RDONLY"); - let o_wronly = ecx.eval_libc_i32("O_WRONLY"); - let o_rdwr = ecx.eval_libc_i32("O_RDWR"); // O_NONBLOCK flag can be set / unset by user. if flag & o_nonblock == o_nonblock { @@ -185,9 +182,6 @@ impl FileDescription for AnonSocket { self.is_nonblock.set(false); } - // Ignore all file access mode flags. - flag &= !(o_rdonly | o_wronly | o_rdwr); - // Throw error if there is any unsupported flag. if flag != 0 { throw_unsup_format!( diff --git a/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs b/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs index db68daed53961..8f8d4f85c0109 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs @@ -148,6 +148,24 @@ fn test_pipe_setfl_getfl() { errno_result(unsafe { libc::fcntl(fds[0], libc::F_GETFL) }).unwrap(), libc::O_RDONLY ); + + // Test if ignored flags are indeed ignored. + errno_check(unsafe { + libc::fcntl( + fds[0], + libc::F_SETFL, + libc::O_RDWR + | libc::O_CREAT + | libc::O_EXCL + | libc::O_NOCTTY + | libc::O_TRUNC + | libc::O_NONBLOCK, + ) + }); + assert_eq!( + errno_result(unsafe { libc::fcntl(fds[0], libc::F_GETFL) }).unwrap(), + libc::O_NONBLOCK | libc::O_RDONLY + ); } /// Test the behaviour of F_SETFL/F_GETFL when a fd is blocking. From 7dc80de53de6bf90f94601e76a88323baf2caf5d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 1 Mar 2026 14:24:53 +0100 Subject: [PATCH 24/46] use ignorelist instead of allowlist for file flags --- src/tools/miri/src/shims/unix/fd.rs | 43 +++++++------------ .../miri/src/shims/unix/unnamed_socket.rs | 2 - 2 files changed, 15 insertions(+), 30 deletions(-) diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index aea6642f47c42..460015d4c3ccc 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -194,34 +194,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; let [flag] = check_min_vararg_count("fcntl(fd, F_SETFL, ...)", varargs)?; - let mut flag = this.read_scalar(flag)?.to_i32()?; - - let allowed_flags = match this.tcx.sess.target.os { - Os::MacOs => - this.eval_libc_i32("O_NONBLOCK") - | this.eval_libc_i32("O_APPEND") - | this.eval_libc_i32("O_ASYNC"), - Os::FreeBsd => - this.eval_libc_i32("O_NONBLOCK") - | this.eval_libc_i32("O_APPEND") - | this.eval_libc_i32("O_DIRECT") - | this.eval_libc_i32("O_ASYNC"), - Os::Solaris | Os::Illumos => - this.eval_libc_i32("O_NONBLOCK") - | this.eval_libc_i32("O_APPEND") - | this.eval_libc_i32("O_DIRECT"), - // Linux + Android match case - _ => - this.eval_libc_i32("O_NONBLOCK") - | this.eval_libc_i32("O_APPEND") - | this.eval_libc_i32("O_DIRECT") - | this.eval_libc_i32("O_NOATIME") - | this.eval_libc_i32("O_ASYNC"), - }; - - flag &= allowed_flags; - - fd.set_flags(flag, this) + let flag = this.read_scalar(flag)?.to_i32()?; + + // Ignore flags that never get stored by SETFL. + // "File access mode (O_RDONLY, O_WRONLY, O_RDWR) and file + // creation flags (i.e., O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC) + // in arg are ignored." + let ignored_flags = this.eval_libc_i32("O_RDONLY") + | this.eval_libc_i32("O_WRONLY") + | this.eval_libc_i32("O_RDWR") + | this.eval_libc_i32("O_CREAT") + | this.eval_libc_i32("O_EXCL") + | this.eval_libc_i32("O_NOCTTY") + | this.eval_libc_i32("O_TRUNC"); + + fd.set_flags(flag & !ignored_flags, this) } cmd if this.tcx.sess.target.os == Os::MacOs && cmd == this.eval_libc_i32("F_FULLFSYNC") => diff --git a/src/tools/miri/src/shims/unix/unnamed_socket.rs b/src/tools/miri/src/shims/unix/unnamed_socket.rs index b33c294b3a166..ea34f72feee5b 100644 --- a/src/tools/miri/src/shims/unix/unnamed_socket.rs +++ b/src/tools/miri/src/shims/unix/unnamed_socket.rs @@ -170,8 +170,6 @@ impl FileDescription for AnonSocket { mut flag: i32, ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, Scalar> { - // FIXME: File creation flags should be ignored. - let o_nonblock = ecx.eval_libc_i32("O_NONBLOCK"); // O_NONBLOCK flag can be set / unset by user. From 7f3bbe371f402ddaa7c1dab98d519a3a086e9ad3 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Thu, 5 Feb 2026 23:16:43 +0800 Subject: [PATCH 25/46] Report unused features --- compiler/rustc_driver_impl/src/lib.rs | 6 +- compiler/rustc_feature/src/builtin_attrs.rs | 2 +- compiler/rustc_feature/src/lib.rs | 2 +- compiler/rustc_feature/src/unstable.rs | 16 ++++- compiler/rustc_interface/src/callbacks.rs | 23 +++++- compiler/rustc_lint_defs/src/builtin.rs | 5 -- compiler/rustc_middle/src/dep_graph/graph.rs | 72 ++++++++++++------- compiler/rustc_middle/src/ty/context.rs | 32 ++++++++- .../rustc_query_impl/src/dep_kind_vtables.rs | 2 +- compiler/rustc_session/src/session.rs | 6 ++ 10 files changed, 129 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 38a11b427c1f6..f0629f7591f7b 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -338,7 +338,11 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) } } - Some(Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend)) + let linker = Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend); + + tcx.report_unused_features(); + + Some(linker) }); // Linking is done outside the `compiler.enter()` so that the diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index db8f459ef0451..db6e9298e235e 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1330,7 +1330,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ safety: AttributeSafety::Normal, template: template!(NameValueStr: "name"), duplicates: ErrorFollowing, - gate: Gated{ + gate: Gated { feature: sym::rustc_attrs, message: "use of an internal attribute", check: Features::rustc_attrs, diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index 619726f0d5d8f..9d046bdef1cf3 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -137,5 +137,5 @@ pub use builtin_attrs::{ pub use removed::REMOVED_LANG_FEATURES; pub use unstable::{ DEPENDENT_FEATURES, EnabledLangFeature, EnabledLibFeature, Features, INCOMPATIBLE_FEATURES, - UNSTABLE_LANG_FEATURES, + TRACK_FEATURE, UNSTABLE_LANG_FEATURES, }; diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index f36102fdc73f0..60b72fa9c1300 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -3,11 +3,18 @@ use std::path::PathBuf; use std::time::{SystemTime, UNIX_EPOCH}; +use rustc_data_structures::AtomicRef; use rustc_data_structures::fx::FxHashSet; use rustc_span::{Span, Symbol, sym}; use super::{Feature, to_nonzero}; +fn default_track_feature(_: Symbol) {} + +/// Recording used features in the dependency graph so incremental can +/// replay used features when needed. +pub static TRACK_FEATURE: AtomicRef = AtomicRef::new(&(default_track_feature as _)); + #[derive(PartialEq)] enum FeatureStatus { Default, @@ -103,7 +110,12 @@ impl Features { /// Is the given feature enabled (via `#[feature(...)]`)? pub fn enabled(&self, feature: Symbol) -> bool { - self.enabled_features.contains(&feature) + if self.enabled_features.contains(&feature) { + TRACK_FEATURE(feature); + true + } else { + false + } } } @@ -124,7 +136,7 @@ macro_rules! declare_features { impl Features { $( pub fn $feature(&self) -> bool { - self.enabled_features.contains(&sym::$feature) + self.enabled(sym::$feature) } )* diff --git a/compiler/rustc_interface/src/callbacks.rs b/compiler/rustc_interface/src/callbacks.rs index 4a1da0f50cc23..9ddbc496e6d43 100644 --- a/compiler/rustc_interface/src/callbacks.rs +++ b/compiler/rustc_interface/src/callbacks.rs @@ -12,8 +12,9 @@ use std::fmt; use rustc_errors::DiagInner; -use rustc_middle::dep_graph::TaskDepsRef; +use rustc_middle::dep_graph::{DepNodeIndex, QuerySideEffect, TaskDepsRef}; use rustc_middle::ty::tls; +use rustc_span::Symbol; fn track_span_parent(def_id: rustc_span::def_id::LocalDefId) { tls::with_context_opt(|icx| { @@ -51,6 +52,25 @@ fn track_diagnostic(diagnostic: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) }) } +fn track_feature(feature: Symbol) { + tls::with_context_opt(|icx| { + let Some(icx) = icx else { + return; + }; + let tcx = icx.tcx; + + if let Some(dep_node_index) = tcx.sess.used_features.lock().get(&feature).copied() { + tcx.dep_graph.read_index(DepNodeIndex::from_u32(dep_node_index)); + } else { + let dep_node_index = tcx + .dep_graph + .encode_side_effect(tcx, QuerySideEffect::CheckFeature { symbol: feature }); + tcx.sess.used_features.lock().insert(feature, dep_node_index.as_u32()); + tcx.dep_graph.read_index(dep_node_index); + } + }) +} + /// This is a callback from `rustc_hir` as it cannot access the implicit state /// in `rustc_middle` otherwise. fn def_id_debug(def_id: rustc_hir::def_id::DefId, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -70,4 +90,5 @@ pub fn setup_callbacks() { rustc_span::SPAN_TRACK.swap(&(track_span_parent as fn(_))); rustc_hir::def_id::DEF_ID_DEBUG.swap(&(def_id_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); rustc_errors::TRACK_DIAGNOSTIC.swap(&(track_diagnostic as _)); + rustc_feature::TRACK_FEATURE.swap(&(track_feature as _)); } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 8d498b32cd8af..c1df8aac6f319 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -1088,11 +1088,6 @@ declare_lint! { /// crate-level [`feature` attributes]. /// /// [`feature` attributes]: https://doc.rust-lang.org/nightly/unstable-book/ - /// - /// Note: This lint is currently not functional, see [issue #44232] for - /// more details. - /// - /// [issue #44232]: https://github.com/rust-lang/rust/issues/44232 pub UNUSED_FEATURES, Warn, "unused features found in crate-level `#[feature]` directives" diff --git a/compiler/rustc_middle/src/dep_graph/graph.rs b/compiler/rustc_middle/src/dep_graph/graph.rs index a71373c62f38e..882e859247782 100644 --- a/compiler/rustc_middle/src/dep_graph/graph.rs +++ b/compiler/rustc_middle/src/dep_graph/graph.rs @@ -17,6 +17,7 @@ use rustc_index::IndexVec; use rustc_macros::{Decodable, Encodable}; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; use rustc_session::Session; +use rustc_span::Symbol; use tracing::{debug, instrument}; #[cfg(debug_assertions)] use {super::debug::EdgeFilter, std::env}; @@ -45,6 +46,11 @@ pub enum QuerySideEffect { /// the query as green, as that query will have the side /// effect dep node as a dependency. Diagnostic(DiagInner), + /// Records the feature used during query execution. + /// This feature will be inserted into `sess.used_features` + /// if we mark the query as green, as that query will have + /// the side effect dep node as a dependency. + CheckFeature { symbol: Symbol }, } #[derive(Clone)] pub struct DepGraph { @@ -514,29 +520,40 @@ impl DepGraph { } } - /// This encodes a diagnostic by creating a node with an unique index and associating - /// `diagnostic` with it, for use in the next session. + /// This encodes a side effect by creating a node with an unique index and associating + /// it with the node, for use in the next session. #[inline] pub fn record_diagnostic<'tcx>(&self, tcx: TyCtxt<'tcx>, diagnostic: &DiagInner) { if let Some(ref data) = self.data { read_deps(|task_deps| match task_deps { TaskDepsRef::EvalAlways | TaskDepsRef::Ignore => return, TaskDepsRef::Forbid | TaskDepsRef::Allow(..) => { - self.read_index(data.encode_diagnostic(tcx, diagnostic)); + let dep_node_index = data + .encode_side_effect(tcx, QuerySideEffect::Diagnostic(diagnostic.clone())); + self.read_index(dep_node_index); } }) } } - /// This forces a diagnostic node green by running its side effect. `prev_index` would - /// refer to a node created used `encode_diagnostic` in the previous session. + /// This forces a side effect node green by running its side effect. `prev_index` would + /// refer to a node created used `encode_side_effect` in the previous session. #[inline] - pub fn force_diagnostic_node<'tcx>( + pub fn force_side_effect<'tcx>(&self, tcx: TyCtxt<'tcx>, prev_index: SerializedDepNodeIndex) { + if let Some(ref data) = self.data { + data.force_side_effect(tcx, prev_index); + } + } + + #[inline] + pub fn encode_side_effect<'tcx>( &self, tcx: TyCtxt<'tcx>, - prev_index: SerializedDepNodeIndex, - ) { + side_effect: QuerySideEffect, + ) -> DepNodeIndex { if let Some(ref data) = self.data { - data.force_diagnostic_node(tcx, prev_index); + data.encode_side_effect(tcx, side_effect) + } else { + self.next_virtual_depnode_index() } } @@ -673,10 +690,14 @@ impl DepGraphData { self.debug_loaded_from_disk.lock().insert(dep_node); } - /// This encodes a diagnostic by creating a node with an unique index and associating - /// `diagnostic` with it, for use in the next session. + /// This encodes a side effect by creating a node with an unique index and associating + /// it with the node, for use in the next session. #[inline] - fn encode_diagnostic<'tcx>(&self, tcx: TyCtxt<'tcx>, diagnostic: &DiagInner) -> DepNodeIndex { + fn encode_side_effect<'tcx>( + &self, + tcx: TyCtxt<'tcx>, + side_effect: QuerySideEffect, + ) -> DepNodeIndex { // Use `send_new` so we get an unique index, even though the dep node is not. let dep_node_index = self.current.encoder.send_new( DepNode { @@ -684,28 +705,21 @@ impl DepGraphData { key_fingerprint: PackedFingerprint::from(Fingerprint::ZERO), }, Fingerprint::ZERO, - // We want the side effect node to always be red so it will be forced and emit the - // diagnostic. + // We want the side effect node to always be red so it will be forced and run the + // side effect. std::iter::once(DepNodeIndex::FOREVER_RED_NODE).collect(), ); - let side_effect = QuerySideEffect::Diagnostic(diagnostic.clone()); tcx.store_side_effect(dep_node_index, side_effect); dep_node_index } - /// This forces a diagnostic node green by running its side effect. `prev_index` would - /// refer to a node created used `encode_diagnostic` in the previous session. + /// This forces a side effect node green by running its side effect. `prev_index` would + /// refer to a node created used `encode_side_effect` in the previous session. #[inline] - fn force_diagnostic_node<'tcx>(&self, tcx: TyCtxt<'tcx>, prev_index: SerializedDepNodeIndex) { + fn force_side_effect<'tcx>(&self, tcx: TyCtxt<'tcx>, prev_index: SerializedDepNodeIndex) { with_deps(TaskDepsRef::Ignore, || { let side_effect = tcx.load_side_effect(prev_index).unwrap(); - match &side_effect { - QuerySideEffect::Diagnostic(diagnostic) => { - tcx.dcx().emit_diagnostic(diagnostic.clone()); - } - } - // Use `send_and_color` as `promote_node_and_deps_to_current` expects all // green dependencies. `send_and_color` will also prevent multiple nodes // being encoded for concurrent calls. @@ -720,6 +734,16 @@ impl DepGraphData { std::iter::once(DepNodeIndex::FOREVER_RED_NODE).collect(), true, ); + + match &side_effect { + QuerySideEffect::Diagnostic(diagnostic) => { + tcx.dcx().emit_diagnostic(diagnostic.clone()); + } + QuerySideEffect::CheckFeature { symbol } => { + tcx.sess.used_features.lock().insert(*symbol, dep_node_index.as_u32()); + } + } + // This will just overwrite the same value for concurrent calls. tcx.store_side_effect(dep_node_index, side_effect); }) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 27f2a534adb59..68819445a06c4 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -36,7 +36,7 @@ use rustc_hir::definitions::{DefPathData, Definitions, DisambiguatorState}; use rustc_hir::intravisit::VisitorExt; use rustc_hir::lang_items::LangItem; use rustc_hir::limit::Limit; -use rustc_hir::{self as hir, HirId, Node, TraitCandidate, find_attr}; +use rustc_hir::{self as hir, CRATE_HIR_ID, HirId, Node, TraitCandidate, find_attr}; use rustc_index::IndexVec; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; use rustc_session::Session; @@ -1688,6 +1688,36 @@ impl<'tcx> TyCtxt<'tcx> { self.sess.dcx().emit_fatal(crate::error::FailedWritingFile { path: &path, error }); } } + + pub fn report_unused_features(self) { + // Collect first to avoid holding the lock while linting. + let used_features = self.sess.used_features.lock(); + let unused_features = self + .features() + .enabled_features_iter_stable_order() + .filter(|(f, _)| { + !used_features.contains_key(f) + // FIXME: `restricted_std` is used to tell a standard library built + // for a platform that it doesn't know how to support. But it + // could only gate a private mod (see `__restricted_std_workaround`) + // with `cfg(not(restricted_std))`, so it cannot be recorded as used + // in downstream crates. It should never be linted, but should we + // hack this in the linter to ignore it? + && f.as_str() != "restricted_std" + }) + .collect::>(); + + for (feature, span) in unused_features { + self.node_span_lint( + rustc_session::lint::builtin::UNUSED_FEATURES, + CRATE_HIR_ID, + span, + |lint| { + lint.primary_message(format!("feature `{}` is declared but not used", feature)); + }, + ); + } + } } macro_rules! nop_lift { diff --git a/compiler/rustc_query_impl/src/dep_kind_vtables.rs b/compiler/rustc_query_impl/src/dep_kind_vtables.rs index fa82a0413b1ad..847bec8c640ff 100644 --- a/compiler/rustc_query_impl/src/dep_kind_vtables.rs +++ b/compiler/rustc_query_impl/src/dep_kind_vtables.rs @@ -42,7 +42,7 @@ mod non_query { is_eval_always: false, key_fingerprint_style: KeyFingerprintStyle::Unit, force_from_dep_node_fn: Some(|tcx, _, prev_index| { - tcx.dep_graph.force_diagnostic_node(tcx, prev_index); + tcx.dep_graph.force_side_effect(tcx, prev_index); true }), promote_from_disk_fn: None, diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index dc18b05c75763..30840a4872733 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -166,6 +166,11 @@ pub struct Session { /// Used by `-Zmir-opt-bisect-limit` to assign an index to each /// optimization-pass execution candidate during this compilation. pub mir_opt_bisect_eval_count: AtomicUsize, + + /// Enabled features that are used in the current compilation. + /// + /// The value is the `DepNodeIndex` of the node encodes the used feature. + pub used_features: Lock>, } #[derive(Clone, Copy)] @@ -1096,6 +1101,7 @@ pub fn build_session( replaced_intrinsics: FxHashSet::default(), // filled by `run_compiler` thin_lto_supported: true, // filled by `run_compiler` mir_opt_bisect_eval_count: AtomicUsize::new(0), + used_features: Lock::default(), }; validate_commandline_args_with_session_available(&sess); From 28b6bcb42d216ec5b77c9cb59c7bbd04c2d1d29d Mon Sep 17 00:00:00 2001 From: mu001999 Date: Thu, 5 Feb 2026 23:22:09 +0800 Subject: [PATCH 26/46] Add tests for unused-features --- tests/incremental/lint-unused-features.rs | 41 +++++++++++++++++++ .../unused-language-features.rs | 25 +++++++++++ .../unused-language-features.stderr | 38 +++++++++++++++++ .../unused-library-features.rs | 13 ++++++ .../unused-library-features.stderr | 34 +++++++++++++++ .../unused-features/used-language-features.rs | 22 ++++++++++ .../unused-features/used-library-features.rs | 17 ++++++++ 7 files changed, 190 insertions(+) create mode 100644 tests/incremental/lint-unused-features.rs create mode 100644 tests/ui/lint/unused-features/unused-language-features.rs create mode 100644 tests/ui/lint/unused-features/unused-language-features.stderr create mode 100644 tests/ui/lint/unused-features/unused-library-features.rs create mode 100644 tests/ui/lint/unused-features/unused-library-features.stderr create mode 100644 tests/ui/lint/unused-features/used-language-features.rs create mode 100644 tests/ui/lint/unused-features/used-library-features.rs diff --git a/tests/incremental/lint-unused-features.rs b/tests/incremental/lint-unused-features.rs new file mode 100644 index 0000000000000..a7f66504f6a0c --- /dev/null +++ b/tests/incremental/lint-unused-features.rs @@ -0,0 +1,41 @@ +//@ revisions: rpass cfail +//@ ignore-backends: gcc + +#![deny(unused_features)] + +// Used language features +#![feature(box_patterns)] +#![feature(decl_macro)] +#![cfg_attr(all(), feature(rustc_attrs))] + +// Used library features +#![feature(error_iter)] +//[cfail]~^ ERROR feature `error_iter` is declared but not used +#![cfg_attr(all(), feature(allocator_api))] +//[cfail]~^ ERROR feature `allocator_api` is declared but not used + +pub fn use_box_patterns(b: Box) -> i32 { + let box x = b; + x +} + +macro m() {} +pub fn use_decl_macro() { + m!(); +} + +#[rustc_dummy] +pub fn use_rustc_attrs() {} + +#[cfg(rpass)] +pub fn use_error_iter(e: &(dyn std::error::Error + 'static)) { + for _ in e.sources() {} +} + +#[cfg(rpass)] +pub fn use_allocator_api() { + use std::alloc::Global; + let _ = Vec::::new_in(Global); +} + +fn main() {} diff --git a/tests/ui/lint/unused-features/unused-language-features.rs b/tests/ui/lint/unused-features/unused-language-features.rs new file mode 100644 index 0000000000000..9334c1df0408a --- /dev/null +++ b/tests/ui/lint/unused-features/unused-language-features.rs @@ -0,0 +1,25 @@ +#![crate_type = "lib"] +#![deny(unused_features)] + +// Unused language features +#![feature(coroutines)] +//~^ ERROR feature `coroutines` is declared but not used +#![feature(coroutine_clone)] +//~^ ERROR feature `coroutine_clone` is declared but not used +#![feature(stmt_expr_attributes)] +//~^ ERROR feature `stmt_expr_attributes` is declared but not used +#![feature(asm_unwind)] +//~^ ERROR feature `asm_unwind` is declared but not used + +// Enabled via cfg_attr, unused +#![cfg_attr(all(), feature(negative_impls))] +//~^ ERROR feature `negative_impls` is declared but not used + +// Not enabled via cfg_attr, so should not warn even if unused +#![cfg_attr(any(), feature(never_type))] + +macro_rules! use_asm_unwind { + () => { + unsafe { std::arch::asm!("", options(may_unwind)) }; + } +} diff --git a/tests/ui/lint/unused-features/unused-language-features.stderr b/tests/ui/lint/unused-features/unused-language-features.stderr new file mode 100644 index 0000000000000..3cede1a6fe726 --- /dev/null +++ b/tests/ui/lint/unused-features/unused-language-features.stderr @@ -0,0 +1,38 @@ +error: feature `coroutines` is declared but not used + --> $DIR/unused-language-features.rs:5:12 + | +LL | #![feature(coroutines)] + | ^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/unused-language-features.rs:2:9 + | +LL | #![deny(unused_features)] + | ^^^^^^^^^^^^^^^ + +error: feature `coroutine_clone` is declared but not used + --> $DIR/unused-language-features.rs:7:12 + | +LL | #![feature(coroutine_clone)] + | ^^^^^^^^^^^^^^^ + +error: feature `stmt_expr_attributes` is declared but not used + --> $DIR/unused-language-features.rs:9:12 + | +LL | #![feature(stmt_expr_attributes)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: feature `asm_unwind` is declared but not used + --> $DIR/unused-language-features.rs:11:12 + | +LL | #![feature(asm_unwind)] + | ^^^^^^^^^^ + +error: feature `negative_impls` is declared but not used + --> $DIR/unused-language-features.rs:15:28 + | +LL | #![cfg_attr(all(), feature(negative_impls))] + | ^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/lint/unused-features/unused-library-features.rs b/tests/ui/lint/unused-features/unused-library-features.rs new file mode 100644 index 0000000000000..75afd32e56cb3 --- /dev/null +++ b/tests/ui/lint/unused-features/unused-library-features.rs @@ -0,0 +1,13 @@ +#![crate_type = "lib"] +#![deny(unused_features)] + +// Unused library features +#![feature(step_trait)] +//~^ ERROR feature `step_trait` is declared but not used +#![feature(is_sorted)] +//~^ ERROR feature `is_sorted` is declared but not used +//~^^ WARN the feature `is_sorted` has been stable since 1.82.0 and no longer requires an attribute to enable + +// Enabled via cfg_attr, unused +#![cfg_attr(all(), feature(slice_ptr_get))] +//~^ ERROR feature `slice_ptr_get` is declared but not used diff --git a/tests/ui/lint/unused-features/unused-library-features.stderr b/tests/ui/lint/unused-features/unused-library-features.stderr new file mode 100644 index 0000000000000..e259058d6c33b --- /dev/null +++ b/tests/ui/lint/unused-features/unused-library-features.stderr @@ -0,0 +1,34 @@ +warning: the feature `is_sorted` has been stable since 1.82.0 and no longer requires an attribute to enable + --> $DIR/unused-library-features.rs:7:12 + | +LL | #![feature(is_sorted)] + | ^^^^^^^^^ + | + = note: `#[warn(stable_features)]` on by default + +error: feature `step_trait` is declared but not used + --> $DIR/unused-library-features.rs:5:12 + | +LL | #![feature(step_trait)] + | ^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/unused-library-features.rs:2:9 + | +LL | #![deny(unused_features)] + | ^^^^^^^^^^^^^^^ + +error: feature `is_sorted` is declared but not used + --> $DIR/unused-library-features.rs:7:12 + | +LL | #![feature(is_sorted)] + | ^^^^^^^^^ + +error: feature `slice_ptr_get` is declared but not used + --> $DIR/unused-library-features.rs:12:28 + | +LL | #![cfg_attr(all(), feature(slice_ptr_get))] + | ^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors; 1 warning emitted + diff --git a/tests/ui/lint/unused-features/used-language-features.rs b/tests/ui/lint/unused-features/used-language-features.rs new file mode 100644 index 0000000000000..7da4866a00d15 --- /dev/null +++ b/tests/ui/lint/unused-features/used-language-features.rs @@ -0,0 +1,22 @@ +//@ check-pass + +#![crate_type = "lib"] +#![deny(unused_features)] + +// Used language features +#![feature(box_patterns)] +#![feature(decl_macro)] +#![cfg_attr(all(), feature(rustc_attrs))] + +pub fn use_box_patterns(b: Box) -> i32 { + let box x = b; + x +} + +macro m() {} +pub fn use_decl_macro() { + m!(); +} + +#[rustc_dummy] +pub fn use_rustc_attrs() {} diff --git a/tests/ui/lint/unused-features/used-library-features.rs b/tests/ui/lint/unused-features/used-library-features.rs new file mode 100644 index 0000000000000..1747c7741880e --- /dev/null +++ b/tests/ui/lint/unused-features/used-library-features.rs @@ -0,0 +1,17 @@ +//@ check-pass + +#![crate_type = "lib"] +#![deny(unused_features)] + +// Used library features +#![feature(error_iter)] +#![cfg_attr(all(), feature(allocator_api))] + +pub fn use_error_iter(e: &(dyn std::error::Error + 'static)) { + for _ in e.sources() {} +} + +pub fn use_allocator_api() { + use std::alloc::Global; + let _ = Vec::::new_in(Global); +} From d0a182f48560975399309251bc322025bb9eaf35 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Sun, 1 Mar 2026 18:31:21 +0800 Subject: [PATCH 27/46] Remove unused features in compiler --- compiler/rustc_errors/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index eab919f08ab50..ada99296f8d55 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -7,9 +7,7 @@ #![allow(rustc::direct_use_of_rustc_type_ir)] #![cfg_attr(bootstrap, feature(assert_matches))] #![feature(associated_type_defaults)] -#![feature(box_patterns)] #![feature(default_field_values)] -#![feature(error_reporter)] #![feature(macro_metavar_expr_concat)] #![feature(negative_impls)] #![feature(never_type)] From dc3db2373c2b5f5853dea973e4ce3c30686534f4 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 13 Feb 2026 13:18:25 +0000 Subject: [PATCH 28/46] Slightly simplify write_bitcode_to_file handling This also causes bitcode to be saved for the allocator shim with -Csave-temps. --- compiler/rustc_codegen_llvm/src/back/write.rs | 15 +++++---------- compiler/rustc_codegen_ssa/src/back/write.rs | 2 -- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index d7ab1356fafe7..efd4e55d5a856 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -335,13 +335,13 @@ pub(crate) fn save_temp_bitcode( &module.name, cgcx.invocation_temp.as_deref(), ); - write_bitcode_to_file(module, &path) + write_bitcode_to_file(&module.module_llvm, &path) } -fn write_bitcode_to_file(module: &ModuleCodegen, path: &Path) { +fn write_bitcode_to_file(module: &ModuleLlvm, path: &Path) { unsafe { let path = path_to_c_string(&path); - let llmod = module.module_llvm.llmod(); + let llmod = module.llmod(); llvm::LLVMWriteBitcodeToFile(llmod, path.as_ptr()); } } @@ -905,13 +905,8 @@ pub(crate) fn optimize( let _handlers = DiagnosticHandlers::new(cgcx, shared_emitter, llcx, module, CodegenDiagnosticsStage::Opt); - if config.emit_no_opt_bc { - let out = cgcx.output_filenames.temp_path_ext_for_cgu( - "no-opt.bc", - &module.name, - cgcx.invocation_temp.as_deref(), - ); - write_bitcode_to_file(module, &out) + if module.kind == ModuleKind::Regular { + save_temp_bitcode(cgcx, module, "no-opt"); } // FIXME(ZuseZ4): support SanitizeHWAddress and prevent illegal/unsupported opts diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 0d210eacf9a83..f1a6d9bd10504 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -94,7 +94,6 @@ pub struct ModuleConfig { // Flags indicating which outputs to produce. pub emit_pre_lto_bc: bool, - pub emit_no_opt_bc: bool, pub emit_bc: bool, pub emit_ir: bool, pub emit_asm: bool, @@ -195,7 +194,6 @@ impl ModuleConfig { save_temps || need_pre_lto_bitcode_for_incr_comp(sess), false ), - emit_no_opt_bc: if_regular!(save_temps, false), emit_bc: if_regular!( save_temps || sess.opts.output_types.contains_key(&OutputType::Bitcode), save_temps From 9e917ee7a6c4694b243829a692d3b02b125a6da1 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 15 Feb 2026 16:12:35 +0000 Subject: [PATCH 29/46] Replace spawn_named_thread method with thread_profiler --- compiler/rustc_codegen_llvm/src/lib.rs | 32 ++++--------------- compiler/rustc_codegen_ssa/src/back/write.rs | 29 +++++++++++------ .../rustc_codegen_ssa/src/traits/backend.rs | 13 ++------ 3 files changed, 29 insertions(+), 45 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index c03b0ac9157a8..2c5f283e1e9ac 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -79,24 +79,18 @@ pub(crate) use macros::TryFromU32; #[derive(Clone)] pub struct LlvmCodegenBackend(()); -struct TimeTraceProfiler { - enabled: bool, -} +struct TimeTraceProfiler {} impl TimeTraceProfiler { - fn new(enabled: bool) -> Self { - if enabled { - unsafe { llvm::LLVMRustTimeTraceProfilerInitialize() } - } - TimeTraceProfiler { enabled } + fn new() -> Self { + unsafe { llvm::LLVMRustTimeTraceProfilerInitialize() } + TimeTraceProfiler {} } } impl Drop for TimeTraceProfiler { fn drop(&mut self) { - if self.enabled { - unsafe { llvm::LLVMRustTimeTraceProfilerFinishThread() } - } + unsafe { llvm::LLVMRustTimeTraceProfilerFinishThread() } } } @@ -131,20 +125,8 @@ impl ExtraBackendMethods for LlvmCodegenBackend { back::write::target_machine_factory(sess, optlvl, target_features) } - fn spawn_named_thread( - time_trace: bool, - name: String, - f: F, - ) -> std::io::Result> - where - F: FnOnce() -> T, - F: Send + 'static, - T: Send + 'static, - { - std::thread::Builder::new().name(name).spawn(move || { - let _profiler = TimeTraceProfiler::new(time_trace); - f() - }) + fn thread_profiler() -> Box { + Box::new(TimeTraceProfiler::new()) } } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index f1a6d9bd10504..b1b5da7da8b42 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -1470,7 +1470,9 @@ fn start_executing_work( // Each LLVM module is automatically sent back to the coordinator for LTO if // necessary. There's already optimizations in place to avoid sending work // back to the coordinator if LTO isn't requested. - return B::spawn_named_thread(cgcx.time_trace, "coordinator".to_string(), move || { + let f = move || { + let _profiler = if cgcx.time_trace { B::thread_profiler() } else { Box::new(()) }; + // This is where we collect codegen units that have gone all the way // through codegen and LLVM. let mut compiled_modules = vec![]; @@ -1811,8 +1813,11 @@ fn start_executing_work( B::codegen(&cgcx, &prof, &shared_emitter, allocator_module, &allocator_config) }), })) - }) - .expect("failed to spawn coordinator thread"); + }; + return std::thread::Builder::new() + .name("coordinator".to_owned()) + .spawn(f) + .expect("failed to spawn coordinator thread"); // A heuristic that determines if we have enough LLVM WorkItems in the // queue so that the main thread can do LLVM work instead of codegen @@ -1891,7 +1896,10 @@ fn spawn_work<'a, B: ExtraBackendMethods>( let cgcx = cgcx.clone(); let prof = prof.clone(); - B::spawn_named_thread(cgcx.time_trace, work.short_description(), move || { + let name = work.short_description(); + let f = move || { + let _profiler = if cgcx.time_trace { B::thread_profiler() } else { Box::new(()) }; + let result = std::panic::catch_unwind(AssertUnwindSafe(|| match work { WorkItem::Optimize(m) => execute_optimize_work_item(&cgcx, &prof, shared_emitter, m), WorkItem::CopyPostLtoArtifacts(m) => WorkItemResult::Finished( @@ -1912,8 +1920,8 @@ fn spawn_work<'a, B: ExtraBackendMethods>( Err(_) => Message::WorkItem:: { result: Err(None) }, }; drop(coordinator_send.send(msg)); - }) - .expect("failed to spawn work thread"); + }; + std::thread::Builder::new().name(name).spawn(f).expect("failed to spawn work thread"); } fn spawn_thin_lto_work( @@ -1927,7 +1935,10 @@ fn spawn_thin_lto_work( let cgcx = cgcx.clone(); let prof = prof.clone(); - B::spawn_named_thread(cgcx.time_trace, work.short_description(), move || { + let name = work.short_description(); + let f = move || { + let _profiler = if cgcx.time_trace { B::thread_profiler() } else { Box::new(()) }; + let result = std::panic::catch_unwind(AssertUnwindSafe(|| match work { ThinLtoWorkItem::CopyPostLtoArtifacts(m) => { execute_copy_from_cache_work_item(&cgcx, &prof, shared_emitter, m) @@ -1950,8 +1961,8 @@ fn spawn_thin_lto_work( Err(_) => ThinLtoMessage::WorkItem { result: Err(None) }, }; drop(coordinator_send.send(msg)); - }) - .expect("failed to spawn work thread"); + }; + std::thread::Builder::new().name(name).spawn(f).expect("failed to spawn work thread"); } enum SharedEmitterMessage { diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 8df1ecc0fff57..776f7cde6c98b 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -165,17 +165,8 @@ pub trait ExtraBackendMethods: target_features: &[String], ) -> TargetMachineFactoryFn; - fn spawn_named_thread( - _time_trace: bool, - name: String, - f: F, - ) -> std::io::Result> - where - F: FnOnce() -> T, - F: Send + 'static, - T: Send + 'static, - { - std::thread::Builder::new().name(name).spawn(f) + fn thread_profiler() -> Box { + Box::new(()) } /// Returns `true` if this backend can be safely called from multiple threads. From 22d4bb25925221d011b8573e6cbd88f5c3b036e4 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 15 Feb 2026 16:31:51 +0000 Subject: [PATCH 30/46] Move print_pass_timings and print_statistics calls to rustc_interface --- compiler/rustc_codegen_gcc/src/lib.rs | 8 -------- compiler/rustc_codegen_llvm/src/lib.rs | 18 ++++++++++-------- compiler/rustc_codegen_ssa/src/back/write.rs | 10 ---------- .../rustc_codegen_ssa/src/traits/backend.rs | 4 ++++ compiler/rustc_codegen_ssa/src/traits/write.rs | 2 -- compiler/rustc_interface/src/queries.rs | 9 +++++++++ 6 files changed, 23 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 529a5085c30f4..3409c0e023405 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -455,14 +455,6 @@ impl WriteBackendMethods for GccCodegenBackend { unreachable!() } - fn print_pass_timings(&self) { - unimplemented!(); - } - - fn print_statistics(&self) { - unimplemented!() - } - fn optimize( _cgcx: &CodegenContext, _prof: &SelfProfilerRef, diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 2c5f283e1e9ac..46b26832d2079 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -135,14 +135,6 @@ impl WriteBackendMethods for LlvmCodegenBackend { type ModuleBuffer = back::lto::ModuleBuffer; type TargetMachine = OwnedTargetMachine; type ThinData = back::lto::ThinData; - fn print_pass_timings(&self) { - let timings = llvm::build_string(|s| unsafe { llvm::LLVMRustPrintPassTimings(s) }).unwrap(); - print!("{timings}"); - } - fn print_statistics(&self) { - let stats = llvm::build_string(|s| unsafe { llvm::LLVMRustPrintStatistics(s) }).unwrap(); - print!("{stats}"); - } fn run_and_optimize_fat_lto( cgcx: &CodegenContext, prof: &SelfProfilerRef, @@ -371,6 +363,16 @@ impl CodegenBackend for LlvmCodegenBackend { (compiled_modules, work_products) } + fn print_pass_timings(&self) { + let timings = llvm::build_string(|s| unsafe { llvm::LLVMRustPrintPassTimings(s) }).unwrap(); + print!("{timings}"); + } + + fn print_statistics(&self) { + let stats = llvm::build_string(|s| unsafe { llvm::LLVMRustPrintStatistics(s) }).unwrap(); + print!("{stats}"); + } + fn link( &self, sess: &Session, diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index b1b5da7da8b42..55951fde62f68 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -2243,16 +2243,6 @@ impl OngoingCodegen { copy_all_cgu_workproducts_to_incr_comp_cache_dir(sess, &compiled_modules); produce_final_output_artifacts(sess, &compiled_modules, &self.output_filenames); - // FIXME: time_llvm_passes support - does this use a global context or - // something? - if sess.codegen_units().as_usize() == 1 && sess.opts.unstable_opts.time_llvm_passes { - self.backend.print_pass_timings() - } - - if sess.print_llvm_stats() { - self.backend.print_statistics() - } - (compiled_modules, work_products) } diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 776f7cde6c98b..5b0275b0d1df3 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -119,6 +119,10 @@ pub trait CodegenBackend { outputs: &OutputFilenames, ) -> (CompiledModules, FxIndexMap); + fn print_pass_timings(&self) {} + + fn print_statistics(&self) {} + /// This is called on the returned [`CompiledModules`] from [`join_codegen`](Self::join_codegen). fn link( &self, diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index d33dfc1d014b5..89bb1e6e88d55 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -39,8 +39,6 @@ pub trait WriteBackendMethods: Clone + 'static { modules: Vec<(String, Self::ModuleBuffer)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> (Vec>, Vec); - fn print_pass_timings(&self); - fn print_statistics(&self); fn optimize( cgcx: &CodegenContext, prof: &SelfProfilerRef, diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index f4fcd4471d3fd..170393f3b8179 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -58,6 +58,15 @@ impl Linker { } } }); + + if sess.codegen_units().as_usize() == 1 && sess.opts.unstable_opts.time_llvm_passes { + codegen_backend.print_pass_timings() + } + + if sess.print_llvm_stats() { + codegen_backend.print_statistics() + } + sess.timings.end_section(sess.dcx(), TimingSection::Codegen); if sess.opts.incremental.is_some() From eff0d4c6f4c5e8f9f88d43180cd83ba5e913c2da Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 15 Feb 2026 16:52:46 +0000 Subject: [PATCH 31/46] Fuse codegen into LTO optimize methods --- compiler/rustc_codegen_gcc/src/back/lto.rs | 12 ++++++------ compiler/rustc_codegen_gcc/src/back/write.rs | 11 +++-------- compiler/rustc_codegen_gcc/src/lib.rs | 14 ++++++++------ compiler/rustc_codegen_llvm/src/back/lto.rs | 11 ++++++----- compiler/rustc_codegen_llvm/src/lib.rs | 12 ++++++------ compiler/rustc_codegen_ssa/src/back/write.rs | 8 +++----- compiler/rustc_codegen_ssa/src/traits/write.rs | 8 ++++---- 7 files changed, 36 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs index a08e3dc0df87d..347a15a392af9 100644 --- a/compiler/rustc_codegen_gcc/src/back/lto.rs +++ b/compiler/rustc_codegen_gcc/src/back/lto.rs @@ -26,7 +26,7 @@ use object::read::archive::ArchiveFile; use rustc_codegen_ssa::back::lto::SerializedModule; use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput, SharedEmitter}; use rustc_codegen_ssa::traits::*; -use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file}; +use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind, looks_like_rust_object_file}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_errors::{DiagCtxt, DiagCtxtHandle}; @@ -34,7 +34,7 @@ use rustc_log::tracing::info; use rustc_session::config::Lto; use tempfile::{TempDir, tempdir}; -use crate::back::write::save_temp_bitcode; +use crate::back::write::{codegen, save_temp_bitcode}; use crate::errors::LtoBitcodeFromRlib; use crate::{GccCodegenBackend, GccContext, LtoMode, to_gcc_opt_level}; @@ -112,7 +112,7 @@ pub(crate) fn run_fat( shared_emitter: &SharedEmitter, each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, -) -> ModuleCodegen { +) -> CompiledModule { let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); let dcx = dcx.handle(); let lto_data = prepare_lto(cgcx, each_linked_rlib_for_lto, dcx); @@ -132,12 +132,12 @@ pub(crate) fn run_fat( fn fat_lto( cgcx: &CodegenContext, prof: &SelfProfilerRef, - _dcx: DiagCtxtHandle<'_>, + dcx: DiagCtxtHandle<'_>, modules: Vec>, mut serialized_modules: Vec<(SerializedModule, CString)>, tmp_path: TempDir, //symbols_below_threshold: &[String], -) -> ModuleCodegen { +) -> CompiledModule { let _timer = prof.generic_activity("GCC_fat_lto_build_monolithic_module"); info!("going for a fat lto"); @@ -260,7 +260,7 @@ fn fat_lto( // of now. module.module_llvm.temp_dir = Some(tmp_path); - module + codegen(cgcx, prof, dcx, module, &cgcx.module_config) } pub struct ModuleBuffer(PathBuf); diff --git a/compiler/rustc_codegen_gcc/src/back/write.rs b/compiler/rustc_codegen_gcc/src/back/write.rs index ddf13558027bd..24ea2b66ba7d3 100644 --- a/compiler/rustc_codegen_gcc/src/back/write.rs +++ b/compiler/rustc_codegen_gcc/src/back/write.rs @@ -2,12 +2,10 @@ use std::{env, fs}; use gccjit::{Context, OutputKind}; use rustc_codegen_ssa::back::link::ensure_removed; -use rustc_codegen_ssa::back::write::{ - BitcodeSection, CodegenContext, EmitObj, ModuleConfig, SharedEmitter, -}; +use rustc_codegen_ssa::back::write::{BitcodeSection, CodegenContext, EmitObj, ModuleConfig}; use rustc_codegen_ssa::{CompiledModule, ModuleCodegen}; use rustc_data_structures::profiling::SelfProfilerRef; -use rustc_errors::DiagCtxt; +use rustc_errors::DiagCtxtHandle; use rustc_fs_util::link_or_copy; use rustc_log::tracing::debug; use rustc_session::config::OutputType; @@ -20,13 +18,10 @@ use crate::{GccContext, LtoMode}; pub(crate) fn codegen( cgcx: &CodegenContext, prof: &SelfProfilerRef, - shared_emitter: &SharedEmitter, + dcx: DiagCtxtHandle<'_>, module: ModuleCodegen, config: &ModuleConfig, ) -> CompiledModule { - let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); - let dcx = dcx.handle(); - let _timer = prof.generic_activity_with_arg("GCC_module_codegen", &*module.name); { let context = &module.module_llvm.context; diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 3409c0e023405..ed809eb33a53f 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -92,7 +92,7 @@ use rustc_codegen_ssa::{CompiledModule, CompiledModules, CrateInfo, ModuleCodege use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sync::IntoDynSyncSend; -use rustc_errors::DiagCtxtHandle; +use rustc_errors::{DiagCtxt, DiagCtxtHandle}; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; @@ -429,7 +429,7 @@ impl WriteBackendMethods for GccCodegenBackend { type ModuleBuffer = ModuleBuffer; type ThinData = (); - fn run_and_optimize_fat_lto( + fn optimize_and_codegen_fat_lto( cgcx: &CodegenContext, prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, @@ -438,7 +438,7 @@ impl WriteBackendMethods for GccCodegenBackend { _exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, - ) -> ModuleCodegen { + ) -> CompiledModule { back::lto::run_fat(cgcx, prof, shared_emitter, each_linked_rlib_for_lto, modules) } @@ -465,13 +465,13 @@ impl WriteBackendMethods for GccCodegenBackend { module.module_llvm.context.set_optimization_level(to_gcc_opt_level(config.opt_level)); } - fn optimize_thin( + fn optimize_and_codegen_thin( _cgcx: &CodegenContext, _prof: &SelfProfilerRef, _shared_emitter: &SharedEmitter, _tm_factory: TargetMachineFactoryFn, _thin: ThinModule, - ) -> ModuleCodegen { + ) -> CompiledModule { unreachable!() } @@ -482,7 +482,9 @@ impl WriteBackendMethods for GccCodegenBackend { module: ModuleCodegen, config: &ModuleConfig, ) -> CompiledModule { - back::write::codegen(cgcx, prof, shared_emitter, module, config) + let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); + let dcx = dcx.handle(); + back::write::codegen(cgcx, prof, dcx, module, config) } fn serialize_module(_module: Self::Module, _is_thin: bool) -> Self::ModuleBuffer { diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index c235437aee751..f6cd229cb106d 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -12,7 +12,7 @@ use rustc_codegen_ssa::back::write::{ CodegenContext, FatLtoInput, SharedEmitter, TargetMachineFactoryFn, }; use rustc_codegen_ssa::traits::*; -use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file}; +use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind, looks_like_rust_object_file}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::profiling::SelfProfilerRef; @@ -24,7 +24,8 @@ use rustc_session::config::{self, Lto}; use tracing::{debug, info}; use crate::back::write::{ - self, CodegenDiagnosticsStage, DiagnosticHandlers, bitcode_section_name, save_temp_bitcode, + self, CodegenDiagnosticsStage, DiagnosticHandlers, bitcode_section_name, codegen, + save_temp_bitcode, }; use crate::errors::{LlvmError, LtoBitcodeFromRlib}; use crate::llvm::{self, build_string}; @@ -709,13 +710,13 @@ impl ModuleBufferMethods for ModuleBuffer { } } -pub(crate) fn optimize_thin_module( +pub(crate) fn optimize_and_codegen_thin_module( cgcx: &CodegenContext, prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, tm_factory: TargetMachineFactoryFn, thin_module: ThinModule, -) -> ModuleCodegen { +) -> CompiledModule { let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); let dcx = dcx.handle(); @@ -794,7 +795,7 @@ pub(crate) fn optimize_thin_module( save_temp_bitcode(cgcx, &module, "thin-lto-after-pm"); } } - module + codegen(cgcx, prof, shared_emitter, module, &cgcx.module_config) } /// Maps LLVM module identifiers to their corresponding LLVM LTO cache keys diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 46b26832d2079..cda1e470fce4c 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -135,7 +135,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { type ModuleBuffer = back::lto::ModuleBuffer; type TargetMachine = OwnedTargetMachine; type ThinData = back::lto::ThinData; - fn run_and_optimize_fat_lto( + fn optimize_and_codegen_fat_lto( cgcx: &CodegenContext, prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, @@ -143,7 +143,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, - ) -> ModuleCodegen { + ) -> CompiledModule { let mut module = back::lto::run_fat( cgcx, prof, @@ -158,7 +158,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { let dcx = dcx.handle(); back::lto::run_pass_manager(cgcx, prof, dcx, &mut module, false); - module + back::write::codegen(cgcx, prof, shared_emitter, module, &cgcx.module_config) } fn run_thin_lto( cgcx: &CodegenContext, @@ -188,14 +188,14 @@ impl WriteBackendMethods for LlvmCodegenBackend { ) { back::write::optimize(cgcx, prof, shared_emitter, module, config) } - fn optimize_thin( + fn optimize_and_codegen_thin( cgcx: &CodegenContext, prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, tm_factory: TargetMachineFactoryFn, thin: ThinModule, - ) -> ModuleCodegen { - back::lto::optimize_thin_module(cgcx, prof, shared_emitter, tm_factory, thin) + ) -> CompiledModule { + back::lto::optimize_and_codegen_thin_module(cgcx, prof, shared_emitter, tm_factory, thin) } fn codegen( cgcx: &CodegenContext, diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 55951fde62f68..31a77952ea382 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -988,7 +988,7 @@ fn do_fat_lto( needs_fat_lto.push(FatLtoInput::Serialized { name: wp.cgu_name, buffer: module }) } - let module = B::run_and_optimize_fat_lto( + B::optimize_and_codegen_fat_lto( cgcx, prof, &shared_emitter, @@ -996,8 +996,7 @@ fn do_fat_lto( exported_symbols_for_lto, each_linked_rlib_for_lto, needs_fat_lto, - ); - B::codegen(cgcx, prof, &shared_emitter, module, &cgcx.module_config) + ) } fn do_thin_lto( @@ -1162,8 +1161,7 @@ fn execute_thin_lto_work_item( ) -> CompiledModule { let _timer = prof.generic_activity_with_arg("codegen_module_perform_lto", module.name()); - let module = B::optimize_thin(cgcx, prof, &shared_emitter, tm_factory, module); - B::codegen(cgcx, prof, &shared_emitter, module, &cgcx.module_config) + B::optimize_and_codegen_thin(cgcx, prof, &shared_emitter, tm_factory, module) } /// Messages sent to the coordinator. diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 89bb1e6e88d55..d218b0f31722f 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -18,7 +18,7 @@ pub trait WriteBackendMethods: Clone + 'static { /// Performs fat LTO by merging all modules into a single one, running autodiff /// if necessary and running any further optimizations - fn run_and_optimize_fat_lto( + fn optimize_and_codegen_fat_lto( cgcx: &CodegenContext, prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, @@ -26,7 +26,7 @@ pub trait WriteBackendMethods: Clone + 'static { exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, - ) -> ModuleCodegen; + ) -> CompiledModule; /// Performs thin LTO by performing necessary global analysis and returning two /// lists, one of the modules that need optimization and another for modules that /// can simply be copied over from the incr. comp. cache. @@ -46,13 +46,13 @@ pub trait WriteBackendMethods: Clone + 'static { module: &mut ModuleCodegen, config: &ModuleConfig, ); - fn optimize_thin( + fn optimize_and_codegen_thin( cgcx: &CodegenContext, prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, tm_factory: TargetMachineFactoryFn, thin: ThinModule, - ) -> ModuleCodegen; + ) -> CompiledModule; fn codegen( cgcx: &CodegenContext, prof: &SelfProfilerRef, From 6ea5244ebf7589720a32474de9410e9bc73981eb Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 15 Feb 2026 16:38:39 +0000 Subject: [PATCH 32/46] Move some methods to WriteBackendMethods --- compiler/rustc_codegen_gcc/src/lib.rs | 20 ++++++------- compiler/rustc_codegen_llvm/src/lib.rs | 21 +++++++------ compiler/rustc_codegen_ssa/src/back/write.rs | 30 +++++++++---------- .../rustc_codegen_ssa/src/traits/backend.rs | 14 +-------- .../rustc_codegen_ssa/src/traits/write.rs | 11 +++++++ 5 files changed, 47 insertions(+), 49 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index ed809eb33a53f..592eb68ce2750 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -371,16 +371,6 @@ impl ExtraBackendMethods for GccCodegenBackend { self.lto_supported.load(Ordering::SeqCst), ) } - - fn target_machine_factory( - &self, - _sess: &Session, - _opt_level: OptLevel, - _features: &[String], - ) -> TargetMachineFactoryFn { - // TODO(antoyo): set opt level. - Arc::new(|_, _| ()) - } } #[derive(Clone, Copy, PartialEq)] @@ -429,6 +419,16 @@ impl WriteBackendMethods for GccCodegenBackend { type ModuleBuffer = ModuleBuffer; type ThinData = (); + fn target_machine_factory( + &self, + _sess: &Session, + _opt_level: OptLevel, + _features: &[String], + ) -> TargetMachineFactoryFn { + // TODO(antoyo): set opt level. + Arc::new(|_, _| ()) + } + fn optimize_and_codegen_fat_lto( cgcx: &CodegenContext, prof: &SelfProfilerRef, diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index cda1e470fce4c..62bba6c9bfc21 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -116,6 +116,16 @@ impl ExtraBackendMethods for LlvmCodegenBackend { ) -> (ModuleCodegen, u64) { base::compile_codegen_unit(tcx, cgu_name) } +} + +impl WriteBackendMethods for LlvmCodegenBackend { + type Module = ModuleLlvm; + type ModuleBuffer = back::lto::ModuleBuffer; + type TargetMachine = OwnedTargetMachine; + type ThinData = back::lto::ThinData; + fn thread_profiler() -> Box { + Box::new(TimeTraceProfiler::new()) + } fn target_machine_factory( &self, sess: &Session, @@ -124,17 +134,6 @@ impl ExtraBackendMethods for LlvmCodegenBackend { ) -> TargetMachineFactoryFn { back::write::target_machine_factory(sess, optlvl, target_features) } - - fn thread_profiler() -> Box { - Box::new(TimeTraceProfiler::new()) - } -} - -impl WriteBackendMethods for LlvmCodegenBackend { - type Module = ModuleLlvm; - type ModuleBuffer = back::lto::ModuleBuffer; - type TargetMachine = OwnedTargetMachine; - type ThinData = back::lto::ThinData; fn optimize_and_codegen_fat_lto( cgcx: &CodegenContext, prof: &SelfProfilerRef, diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 31a77952ea382..3734e273e0b5d 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -354,7 +354,7 @@ pub struct CodegenContext { pub parallel: bool, } -fn generate_thin_lto_work( +fn generate_thin_lto_work( cgcx: &CodegenContext, prof: &SelfProfilerRef, dcx: DiagCtxtHandle<'_>, @@ -822,7 +822,7 @@ pub(crate) fn compute_per_cgu_lto_type( } } -fn execute_optimize_work_item( +fn execute_optimize_work_item( cgcx: &CodegenContext, prof: &SelfProfilerRef, shared_emitter: SharedEmitter, @@ -967,7 +967,7 @@ fn execute_copy_from_cache_work_item( } } -fn do_fat_lto( +fn do_fat_lto( cgcx: &CodegenContext, prof: &SelfProfilerRef, shared_emitter: SharedEmitter, @@ -999,7 +999,7 @@ fn do_fat_lto( ) } -fn do_thin_lto( +fn do_thin_lto( cgcx: &CodegenContext, prof: &SelfProfilerRef, shared_emitter: SharedEmitter, @@ -1152,7 +1152,7 @@ fn do_thin_lto( compiled_modules } -fn execute_thin_lto_work_item( +fn execute_thin_lto_work_item( cgcx: &CodegenContext, prof: &SelfProfilerRef, shared_emitter: SharedEmitter, @@ -1879,7 +1879,7 @@ fn start_executing_work( #[must_use] pub(crate) struct WorkerFatalError; -fn spawn_work<'a, B: ExtraBackendMethods>( +fn spawn_work<'a, B: WriteBackendMethods>( cgcx: &CodegenContext, prof: &'a SelfProfilerRef, shared_emitter: SharedEmitter, @@ -1922,7 +1922,7 @@ fn spawn_work<'a, B: ExtraBackendMethods>( std::thread::Builder::new().name(name).spawn(f).expect("failed to spawn work thread"); } -fn spawn_thin_lto_work( +fn spawn_thin_lto_work( cgcx: &CodegenContext, prof: &SelfProfilerRef, shared_emitter: SharedEmitter, @@ -2109,20 +2109,20 @@ impl SharedEmitterMain { } } -pub struct Coordinator { +pub struct Coordinator { sender: Sender>, future: Option, ()>>>, // Only used for the Message type. phantom: PhantomData, } -impl Coordinator { +impl Coordinator { fn join(mut self) -> std::thread::Result, ()>> { self.future.take().unwrap().join() } } -impl Drop for Coordinator { +impl Drop for Coordinator { fn drop(&mut self) { if let Some(future) = self.future.take() { // If we haven't joined yet, signal to the coordinator that it should spawn no more @@ -2133,7 +2133,7 @@ impl Drop for Coordinator { } } -pub struct OngoingCodegen { +pub struct OngoingCodegen { pub backend: B, pub output_filenames: Arc, // Field order below is intended to terminate the coordinator thread before two fields below @@ -2144,7 +2144,7 @@ pub struct OngoingCodegen { pub shared_emitter_main: SharedEmitterMain, } -impl OngoingCodegen { +impl OngoingCodegen { pub fn join(self, sess: &Session) -> (CompiledModules, FxIndexMap) { self.shared_emitter_main.check(sess, true); @@ -2267,7 +2267,7 @@ impl OngoingCodegen { } } -pub(crate) fn submit_codegened_module_to_llvm( +pub(crate) fn submit_codegened_module_to_llvm( coordinator: &Coordinator, module: ModuleCodegen, cost: u64, @@ -2276,7 +2276,7 @@ pub(crate) fn submit_codegened_module_to_llvm( drop(coordinator.sender.send(Message::CodegenDone:: { llvm_work_item, cost })); } -pub(crate) fn submit_post_lto_module_to_llvm( +pub(crate) fn submit_post_lto_module_to_llvm( coordinator: &Coordinator, module: CachedModuleCodegen, ) { @@ -2284,7 +2284,7 @@ pub(crate) fn submit_post_lto_module_to_llvm( drop(coordinator.sender.send(Message::CodegenDone:: { llvm_work_item, cost: 0 })); } -pub(crate) fn submit_pre_lto_module_to_llvm( +pub(crate) fn submit_pre_lto_module_to_llvm( tcx: TyCtxt<'_>, coordinator: &Coordinator, module: CachedModuleCodegen, diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 5b0275b0d1df3..7b95562ddda37 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -10,14 +10,13 @@ use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; use rustc_session::Session; -use rustc_session::config::{self, CrateType, OutputFilenames, PrintRequest}; +use rustc_session::config::{CrateType, OutputFilenames, PrintRequest}; use rustc_span::Symbol; use super::CodegenObject; use super::write::WriteBackendMethods; use crate::back::archive::ArArchiveBuilderBuilder; use crate::back::link::link_binary; -use crate::back::write::TargetMachineFactoryFn; use crate::{CompiledModules, CrateInfo, ModuleCodegen, TargetConfig}; pub trait BackendTypes { @@ -162,17 +161,6 @@ pub trait ExtraBackendMethods: cgu_name: Symbol, ) -> (ModuleCodegen, u64); - fn target_machine_factory( - &self, - sess: &Session, - opt_level: config::OptLevel, - target_features: &[String], - ) -> TargetMachineFactoryFn; - - fn thread_profiler() -> Box { - Box::new(()) - } - /// Returns `true` if this backend can be safely called from multiple threads. /// /// Defaults to `true`. diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index d218b0f31722f..5d2313092fa84 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -1,8 +1,10 @@ +use std::any::Any; use std::path::PathBuf; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_errors::DiagCtxtHandle; use rustc_middle::dep_graph::WorkProduct; +use rustc_session::{Session, config}; use crate::back::lto::{SerializedModule, ThinModule}; use crate::back::write::{ @@ -16,6 +18,15 @@ pub trait WriteBackendMethods: Clone + 'static { type ModuleBuffer: ModuleBufferMethods; type ThinData: Send + Sync; + fn thread_profiler() -> Box { + Box::new(()) + } + fn target_machine_factory( + &self, + sess: &Session, + opt_level: config::OptLevel, + target_features: &[String], + ) -> TargetMachineFactoryFn; /// Performs fat LTO by merging all modules into a single one, running autodiff /// if necessary and running any further optimizations fn optimize_and_codegen_fat_lto( From e78a9f73d6331059b496cd0376bf9695847562c8 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 3 Mar 2026 20:47:26 +0100 Subject: [PATCH 33/46] enable `PassMode::Indirect { on_stack: true }` tail call arguments --- compiler/rustc_codegen_ssa/src/mir/block.rs | 74 +++++------- tests/ui/explicit-tail-calls/support/basic.rs | 98 ++++++++++++++++ .../ui/explicit-tail-calls/support/bystack.rs | 109 ++++++++++++++++++ tests/ui/explicit-tail-calls/support/byval.rs | 109 ++++++++++++++++++ 4 files changed, 344 insertions(+), 46 deletions(-) create mode 100644 tests/ui/explicit-tail-calls/support/basic.rs create mode 100644 tests/ui/explicit-tail-calls/support/bystack.rs create mode 100644 tests/ui/explicit-tail-calls/support/byval.rs diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 0d42ccc1a73c9..cf643931717be 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1256,55 +1256,37 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } CallKind::Tail => { - match fn_abi.args[i].mode { - PassMode::Indirect { on_stack: false, .. } => { - let Some(tmp) = tail_call_temporaries[i].take() else { - span_bug!( - fn_span, - "missing temporary for indirect tail call argument #{i}" - ) - }; - - let local = self.mir.args_iter().nth(i).unwrap(); - - match &self.locals[local] { - LocalRef::Place(arg) => { - bx.typed_place_copy(arg.val, tmp.val, fn_abi.args[i].layout); - op.val = Ref(arg.val); - } - LocalRef::Operand(arg) => { - let Ref(place_value) = arg.val else { - bug!("only `Ref` should use `PassMode::Indirect`"); - }; - bx.typed_place_copy( - place_value, - tmp.val, - fn_abi.args[i].layout, - ); - op.val = arg.val; - } - LocalRef::UnsizedPlace(_) => { - span_bug!(fn_span, "unsized types are not supported") - } - LocalRef::PendingOperand => { - span_bug!(fn_span, "argument local should not be pending") - } - }; - - bx.lifetime_end(tmp.val.llval, tmp.layout.size); - } - PassMode::Indirect { on_stack: true, .. } => { - // FIXME: some LLVM backends (notably x86) do not correctly pass byval - // arguments to tail calls (as of LLVM 21). See also: - // - // - https://github.com/rust-lang/rust/pull/144232#discussion_r2218543841 - // - https://github.com/rust-lang/rust/issues/144855 + if let PassMode::Indirect { on_stack: false, .. } = fn_abi.args[i].mode { + let Some(tmp) = tail_call_temporaries[i].take() else { span_bug!( fn_span, - "arguments using PassMode::Indirect {{ on_stack: true, .. }} are currently not supported for tail calls" + "missing temporary for indirect tail call argument #{i}" ) - } - _ => (), + }; + + let local = self.mir.args_iter().nth(i).unwrap(); + + match &self.locals[local] { + LocalRef::Place(arg) => { + bx.typed_place_copy(arg.val, tmp.val, fn_abi.args[i].layout); + op.val = Ref(arg.val); + } + LocalRef::Operand(arg) => { + let Ref(place_value) = arg.val else { + bug!("only `Ref` should use `PassMode::Indirect`"); + }; + bx.typed_place_copy(place_value, tmp.val, fn_abi.args[i].layout); + op.val = arg.val; + } + LocalRef::UnsizedPlace(_) => { + span_bug!(fn_span, "unsized types are not supported") + } + LocalRef::PendingOperand => { + span_bug!(fn_span, "argument local should not be pending") + } + }; + + bx.lifetime_end(tmp.val.llval, tmp.layout.size); } } } diff --git a/tests/ui/explicit-tail-calls/support/basic.rs b/tests/ui/explicit-tail-calls/support/basic.rs new file mode 100644 index 0000000000000..b4c5ebf139250 --- /dev/null +++ b/tests/ui/explicit-tail-calls/support/basic.rs @@ -0,0 +1,98 @@ +//@ build-pass +//@ ignore-backends: gcc +//@ add-minicore +//@ min-llvm-version: 22 +// +//@ revisions: host +//@ revisions: i686 +//@[i686] compile-flags: --target i686-unknown-linux-gnu +//@[i686] needs-llvm-components: x86 +//@ revisions: x86-64 +//@[x86-64] compile-flags: --target x86_64-unknown-linux-gnu +//@[x86-64] needs-llvm-components: x86 +//@ revisions: x86-64-win +//@[x86-64-win] compile-flags: --target x86_64-pc-windows-msvc +//@[x86-64-win] needs-llvm-components: x86 +//@ revisions: arm +//@[arm] compile-flags: --target arm-unknown-linux-gnueabi +//@[arm] needs-llvm-components: arm +//@ revisions: thumb +//@[thumb] compile-flags: --target thumbv8m.main-none-eabi +//@[thumb] needs-llvm-components: arm +//@ revisions: aarch64 +//@[aarch64] compile-flags: --target aarch64-unknown-linux-gnu +//@[aarch64] needs-llvm-components: aarch64 +//@ revisions: s390x +//@[s390x] compile-flags: --target s390x-unknown-linux-gnu +//@[s390x] needs-llvm-components: systemz +//@ revisions: sparc +//@[sparc] compile-flags: --target sparc-unknown-linux-gnu +//@[sparc] needs-llvm-components: sparc +//@ revisions: sparc64 +//@[sparc64] compile-flags: --target sparc64-unknown-linux-gnu +//@[sparc64] needs-llvm-components: sparc +//@ revisions: powerpc64 +//@[powerpc64] compile-flags: --target powerpc64-unknown-linux-gnu +//@[powerpc64] needs-llvm-components: powerpc +//@ revisions: riscv +//@[riscv] compile-flags: --target riscv64gc-unknown-linux-gnu +//@[riscv] needs-llvm-components: riscv +//@ revisions: loongarch32 +//@[loongarch32] compile-flags: --target loongarch32-unknown-none +//@[loongarch32] needs-llvm-components: loongarch +//@ revisions: loongarch64 +//@[loongarch64] compile-flags: --target loongarch64-unknown-linux-gnu +//@[loongarch64] needs-llvm-components: loongarch +//@ revisions: bpf +//@[bpf] compile-flags: --target bpfeb-unknown-none +//@[bpf] needs-llvm-components: bpf +//@ revisions: m68k +//@[m68k] compile-flags: --target m68k-unknown-linux-gnu +//@[m68k] needs-llvm-components: m68k +//@ revisions: nvptx64 +//@[nvptx64] compile-flags: --target nvptx64-nvidia-cuda +//@[nvptx64] needs-llvm-components: nvptx +// +// Wasm needs a special target feature. +// +//@ revisions: wasm +//@[wasm] compile-flags: --target wasm32-unknown-unknown -Ctarget-feature=+tail-call +//@[wasm] needs-llvm-components: webassembly +//@ revisions: wasip1 +//@[wasip1] compile-flags: --target wasm32-wasip1 -Ctarget-feature=+tail-call +//@[wasip1] needs-llvm-components: webassembly +// +// Failing cases (just zero support) +// +// //@ revisions: powerpc +// //@[powerpc] compile-flags: --target powerpc-unknown-linux-gnu +// //@[powerpc] needs-llvm-components: powerpc +// //@ revisions: aix +// //@[aix] compile-flags: --target powerpc64-ibm-aix +// //@[aix] needs-llvm-components: powerpc +// //@ revisions: csky +// //@[csky] compile-flags: --target csky-unknown-linux-gnuabiv2 +// //@[csky] needs-llvm-components: csky +// //@ revisions: mips +// //@[mips] compile-flags: --target mips-unknown-linux-gnu +// //@[mips] needs-llvm-components: mips +// //@ revisions: mips64 +// //@[mips64] compile-flags: --target mips64-unknown-linux-gnuabi64 +// //@[mips64] needs-llvm-components: mips +#![feature(no_core, explicit_tail_calls)] +#![expect(incomplete_features)] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::*; + +#[inline(never)] +fn simple1(x: u64) -> u64 { + x +} + +#[unsafe(no_mangle)] +fn simple2(x: u64) -> u64 { + become simple1(x); +} diff --git a/tests/ui/explicit-tail-calls/support/bystack.rs b/tests/ui/explicit-tail-calls/support/bystack.rs new file mode 100644 index 0000000000000..f0a399e9c07e8 --- /dev/null +++ b/tests/ui/explicit-tail-calls/support/bystack.rs @@ -0,0 +1,109 @@ +//@ build-pass +//@ ignore-backends: gcc +//@ add-minicore +//@ min-llvm-version: 22 +// +//@ revisions: host +//@ revisions: i686 +//@[i686] compile-flags: --target i686-unknown-linux-gnu +//@[i686] needs-llvm-components: x86 +//@ revisions: x86-64 +//@[x86-64] compile-flags: --target x86_64-unknown-linux-gnu +//@[x86-64] needs-llvm-components: x86 +//@ revisions: x86-64-win +//@[x86-64-win] compile-flags: --target x86_64-pc-windows-msvc +//@[x86-64-win] needs-llvm-components: x86 +//@ revisions: arm +//@[arm] compile-flags: --target arm-unknown-linux-gnueabi +//@[arm] needs-llvm-components: arm +//@ revisions: thumb +//@[thumb] compile-flags: --target thumbv8m.main-none-eabi +//@[thumb] needs-llvm-components: arm +//@ revisions: aarch64 +//@[aarch64] compile-flags: --target aarch64-unknown-linux-gnu +//@[aarch64] needs-llvm-components: aarch64 +//@ revisions: s390x +//@[s390x] compile-flags: --target s390x-unknown-linux-gnu +//@[s390x] needs-llvm-components: systemz +//@ revisions: sparc +//@[sparc] compile-flags: --target sparc-unknown-linux-gnu +//@[sparc] needs-llvm-components: sparc +//@ revisions: sparc64 +//@[sparc64] compile-flags: --target sparc64-unknown-linux-gnu +//@[sparc64] needs-llvm-components: sparc +//@ revisions: powerpc64 +//@[powerpc64] compile-flags: --target powerpc64-unknown-linux-gnu +//@[powerpc64] needs-llvm-components: powerpc +//@ revisions: loongarch32 +//@[loongarch32] compile-flags: --target loongarch32-unknown-none +//@[loongarch32] needs-llvm-components: loongarch +//@ revisions: loongarch64 +//@[loongarch64] compile-flags: --target loongarch64-unknown-linux-gnu +//@[loongarch64] needs-llvm-components: loongarch +//@ revisions: bpf +//@[bpf] compile-flags: --target bpfeb-unknown-none +//@[bpf] needs-llvm-components: bpf +//@ revisions: m68k +//@[m68k] compile-flags: --target m68k-unknown-linux-gnu +//@[m68k] needs-llvm-components: m68k +//@ revisions: nvptx64 +//@[nvptx64] compile-flags: --target nvptx64-nvidia-cuda +//@[nvptx64] needs-llvm-components: nvptx +// +// Riscv does not support byval in LLVM 22 (but wil in LLVM 23) +// +// //@ revisions: riscv +// //@[riscv] compile-flags: --target riscv64gc-unknown-linux-gnu +// //@[riscv] needs-llvm-components: riscv +// +// Wasm needs a special target feature. +// +//@ revisions: wasm +//@[wasm] compile-flags: --target wasm32-unknown-unknown -Ctarget-feature=+tail-call +//@[wasm] needs-llvm-components: webassembly +//@ revisions: wasip1 +//@[wasip1] compile-flags: --target wasm32-wasip1 -Ctarget-feature=+tail-call +//@[wasip1] needs-llvm-components: webassembly +// +// Failing cases (just zero support) +// +// //@ revisions: powerpc +// //@[powerpc] compile-flags: --target powerpc-unknown-linux-gnu +// //@[powerpc] needs-llvm-components: powerpc +// //@ revisions: aix +// //@[aix] compile-flags: --target powerpc64-ibm-aix +// //@[aix] needs-llvm-components: powerpc +// //@ revisions: csky +// //@[csky] compile-flags: --target csky-unknown-linux-gnuabiv2 +// //@[csky] needs-llvm-components: csky +// //@ revisions: mips +// //@[mips] compile-flags: --target mips-unknown-linux-gnu +// //@[mips] needs-llvm-components: mips +// //@ revisions: mips64 +// //@[mips64] compile-flags: --target mips64-unknown-linux-gnuabi64 +// //@[mips64] needs-llvm-components: mips +#![feature(no_core, explicit_tail_calls)] +#![expect(incomplete_features)] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::*; + +#[repr(C)] +struct PassedByVal { + a: u64, + b: u64, + c: u64, + d: u64, +} + +#[inline(never)] +extern "C" fn callee(x: PassedByVal) -> PassedByVal { + x +} + +#[unsafe(no_mangle)] +extern "C" fn byval(x: PassedByVal) -> PassedByVal { + become callee(x); +} diff --git a/tests/ui/explicit-tail-calls/support/byval.rs b/tests/ui/explicit-tail-calls/support/byval.rs new file mode 100644 index 0000000000000..7e29ae5f0644d --- /dev/null +++ b/tests/ui/explicit-tail-calls/support/byval.rs @@ -0,0 +1,109 @@ +//@ build-pass +//@ ignore-backends: gcc +//@ add-minicore +//@ min-llvm-version: 22 +// +//@ revisions: host +//@ revisions: i686 +//@[i686] compile-flags: --target i686-unknown-linux-gnu +//@[i686] needs-llvm-components: x86 +//@ revisions: x86-64 +//@[x86-64] compile-flags: --target x86_64-unknown-linux-gnu +//@[x86-64] needs-llvm-components: x86 +//@ revisions: x86-64-win +//@[x86-64-win] compile-flags: --target x86_64-pc-windows-msvc +//@[x86-64-win] needs-llvm-components: x86 +//@ revisions: arm +//@[arm] compile-flags: --target arm-unknown-linux-gnueabi +//@[arm] needs-llvm-components: arm +//@ revisions: thumb +//@[thumb] compile-flags: --target thumbv8m.main-none-eabi +//@[thumb] needs-llvm-components: arm +//@ revisions: aarch64 +//@[aarch64] compile-flags: --target aarch64-unknown-linux-gnu +//@[aarch64] needs-llvm-components: aarch64 +//@ revisions: s390x +//@[s390x] compile-flags: --target s390x-unknown-linux-gnu +//@[s390x] needs-llvm-components: systemz +//@ revisions: sparc +//@[sparc] compile-flags: --target sparc-unknown-linux-gnu +//@[sparc] needs-llvm-components: sparc +//@ revisions: sparc64 +//@[sparc64] compile-flags: --target sparc64-unknown-linux-gnu +//@[sparc64] needs-llvm-components: sparc +//@ revisions: powerpc64 +//@[powerpc64] compile-flags: --target powerpc64-unknown-linux-gnu +//@[powerpc64] needs-llvm-components: powerpc +//@ revisions: loongarch32 +//@[loongarch32] compile-flags: --target loongarch32-unknown-none +//@[loongarch32] needs-llvm-components: loongarch +//@ revisions: loongarch64 +//@[loongarch64] compile-flags: --target loongarch64-unknown-linux-gnu +//@[loongarch64] needs-llvm-components: loongarch +//@ revisions: bpf +//@[bpf] compile-flags: --target bpfeb-unknown-none +//@[bpf] needs-llvm-components: bpf +//@ revisions: m68k +//@[m68k] compile-flags: --target m68k-unknown-linux-gnu +//@[m68k] needs-llvm-components: m68k +//@ revisions: nvptx64 +//@[nvptx64] compile-flags: --target nvptx64-nvidia-cuda +//@[nvptx64] needs-llvm-components: nvptx +// +// Riscv does not support byval in LLVM 22 (but wil in LLVM 23) +// +// //@ revisions: riscv +// //@[riscv] compile-flags: --target riscv64gc-unknown-linux-gnu +// //@[riscv] needs-llvm-components: riscv +// +// Wasm needs a special target feature. +// +//@ revisions: wasm +//@[wasm] compile-flags: --target wasm32-unknown-unknown -Ctarget-feature=+tail-call +//@[wasm] needs-llvm-components: webassembly +//@ revisions: wasip1 +//@[wasip1] compile-flags: --target wasm32-wasip1 -Ctarget-feature=+tail-call +//@[wasip1] needs-llvm-components: webassembly +// +// Failing cases (just zero support) +// +// //@ revisions: powerpc +// //@[powerpc] compile-flags: --target powerpc-unknown-linux-gnu +// //@[powerpc] needs-llvm-components: powerpc +// //@ revisions: aix +// //@[aix] compile-flags: --target powerpc64-ibm-aix +// //@[aix] needs-llvm-components: powerpc +// //@ revisions: csky +// //@[csky] compile-flags: --target csky-unknown-linux-gnuabiv2 +// //@[csky] needs-llvm-components: csky +// //@ revisions: mips +// //@[mips] compile-flags: --target mips-unknown-linux-gnu +// //@[mips] needs-llvm-components: mips +// //@ revisions: mips64 +// //@[mips64] compile-flags: --target mips64-unknown-linux-gnuabi64 +// //@[mips64] needs-llvm-components: mips +#![feature(no_core, explicit_tail_calls)] +#![expect(incomplete_features)] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::*; + +// The rust calling convention will pass this by-value. +struct PassedByVal { + a: u64, + b: u64, + c: u64, + d: u64, +} + +#[inline(never)] +fn callee(x: PassedByVal) -> PassedByVal { + x +} + +#[unsafe(no_mangle)] +fn byval(x: PassedByVal) -> PassedByVal { + become callee(x); +} From 52b4de34ec837c09e0ec4c1ce9f00ce8e8b032f8 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 27 Feb 2026 17:03:27 +1100 Subject: [PATCH 34/46] Abort after printing infinite type errors. Currently, `Representability::from_cycle_error` prints an "infinite size" error and then returns `Representability::Infinite`, which lets analysis continue. This commit changes it so it just aborts after printing the error. This has two benefits. First, the error messages are better. The error messages we get after continuing are mostly bad -- we usually get another cycle error, e.g. about drop checking or layout, which is not much use to the user, and then abort after that. The only exception is `issue-105231.rs` where a "conflicting implementations" error is now omitted, but there are three other errors before that one so it's no great loss. Second, it allows some simplifications: see the next commit. --- .../rustc_query_impl/src/from_cycle_error.rs | 4 ++- tests/ui/enum-discriminant/issue-72554.rs | 1 - tests/ui/enum-discriminant/issue-72554.stderr | 21 +++------------ tests/ui/infinite/infinite-struct.rs | 2 -- tests/ui/infinite/infinite-struct.stderr | 25 +++--------------- .../infinite/infinite-tag-type-recursion.rs | 1 - .../infinite-tag-type-recursion.stderr | 15 ++--------- .../query-cycle-printing-issue-151226.rs | 2 -- .../query-cycle-printing-issue-151226.stderr | 26 ++----------------- .../structs-enums/enum-rec/issue-17431-6.rs | 1 - .../enum-rec/issue-17431-6.stderr | 15 ++--------- tests/ui/traits/issue-105231.rs | 1 - tests/ui/traits/issue-105231.stderr | 16 ++---------- .../transmute_infinitely_recursive_type.rs | 1 - ...transmute_infinitely_recursive_type.stderr | 13 +++------- 15 files changed, 20 insertions(+), 124 deletions(-) diff --git a/compiler/rustc_query_impl/src/from_cycle_error.rs b/compiler/rustc_query_impl/src/from_cycle_error.rs index a13db9004d678..328bd4a02c8fd 100644 --- a/compiler/rustc_query_impl/src/from_cycle_error.rs +++ b/compiler/rustc_query_impl/src/from_cycle_error.rs @@ -117,8 +117,10 @@ impl<'tcx> FromCycleError<'tcx> for Representability { representable_ids.insert(def_id); } } + // We used to continue here, but the cycle error printed next is actually less useful than + // the error produced by `recursive_type_error`. let guar = recursive_type_error(tcx, item_and_field_ids, &representable_ids); - Representability::Infinite(guar) + guar.raise_fatal(); } } diff --git a/tests/ui/enum-discriminant/issue-72554.rs b/tests/ui/enum-discriminant/issue-72554.rs index 1fe9a5f4faa7d..54f7e9ac592eb 100644 --- a/tests/ui/enum-discriminant/issue-72554.rs +++ b/tests/ui/enum-discriminant/issue-72554.rs @@ -3,7 +3,6 @@ use std::collections::BTreeSet; #[derive(Hash)] pub enum ElemDerived { //~^ ERROR recursive type `ElemDerived` has infinite size - //~| ERROR cycle detected A(ElemDerived) } diff --git a/tests/ui/enum-discriminant/issue-72554.stderr b/tests/ui/enum-discriminant/issue-72554.stderr index 648680c6031da..381f24d351e9c 100644 --- a/tests/ui/enum-discriminant/issue-72554.stderr +++ b/tests/ui/enum-discriminant/issue-72554.stderr @@ -3,7 +3,7 @@ error[E0072]: recursive type `ElemDerived` has infinite size | LL | pub enum ElemDerived { | ^^^^^^^^^^^^^^^^^^^^ -... +LL | LL | A(ElemDerived) | ----------- recursive without indirection | @@ -12,21 +12,6 @@ help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle LL | A(Box) | ++++ + -error[E0391]: cycle detected when computing drop-check constraints for `ElemDerived` - --> $DIR/issue-72554.rs:4:1 - | -LL | pub enum ElemDerived { - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: ...which immediately requires computing drop-check constraints for `ElemDerived` again -note: cycle used when computing drop-check constraints for `Elem` - --> $DIR/issue-72554.rs:11:1 - | -LL | pub enum Elem { - | ^^^^^^^^^^^^^ - = 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 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0072, E0391. -For more information about an error, try `rustc --explain E0072`. +For more information about this error, try `rustc --explain E0072`. diff --git a/tests/ui/infinite/infinite-struct.rs b/tests/ui/infinite/infinite-struct.rs index d784455824604..f08e10f6bdbc0 100644 --- a/tests/ui/infinite/infinite-struct.rs +++ b/tests/ui/infinite/infinite-struct.rs @@ -1,7 +1,5 @@ struct Take(Take); //~^ ERROR has infinite size -//~| ERROR cycle -//~| ERROR reached the recursion limit finding the struct tail for `Take` // check that we don't hang trying to find the tail of a recursive struct (#79437) fn foo() -> Take { diff --git a/tests/ui/infinite/infinite-struct.stderr b/tests/ui/infinite/infinite-struct.stderr index 0d1ec4989aa53..b6c72b1de4695 100644 --- a/tests/ui/infinite/infinite-struct.stderr +++ b/tests/ui/infinite/infinite-struct.stderr @@ -10,7 +10,7 @@ LL | struct Take(Box); | ++++ + error[E0072]: recursive type `Foo` has infinite size - --> $DIR/infinite-struct.rs:12:1 + --> $DIR/infinite-struct.rs:10:1 | LL | struct Foo { | ^^^^^^^^^^ @@ -22,25 +22,6 @@ help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle LL | x: Bar>, | ++++ + -error: reached the recursion limit finding the struct tail for `Take` - --> $DIR/infinite-struct.rs:1:1 - | -LL | struct Take(Take); - | ^^^^^^^^^^^ - | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` - -error[E0391]: cycle detected when computing when `Take` needs drop - --> $DIR/infinite-struct.rs:1:1 - | -LL | struct Take(Take); - | ^^^^^^^^^^^ - | - = note: ...which immediately requires computing when `Take` needs drop again - = note: cycle used when computing whether `Take` needs drop - = 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 4 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0072, E0391. -For more information about an error, try `rustc --explain E0072`. +For more information about this error, try `rustc --explain E0072`. diff --git a/tests/ui/infinite/infinite-tag-type-recursion.rs b/tests/ui/infinite/infinite-tag-type-recursion.rs index 1b5cb55b4e4d1..87a9e08dd381a 100644 --- a/tests/ui/infinite/infinite-tag-type-recursion.rs +++ b/tests/ui/infinite/infinite-tag-type-recursion.rs @@ -1,5 +1,4 @@ enum MList { Cons(isize, MList), Nil } //~^ ERROR recursive type `MList` has infinite size -//~| ERROR cycle fn main() { let a = MList::Cons(10, MList::Cons(11, MList::Nil)); } diff --git a/tests/ui/infinite/infinite-tag-type-recursion.stderr b/tests/ui/infinite/infinite-tag-type-recursion.stderr index 8745224a45e13..4ca408260b84a 100644 --- a/tests/ui/infinite/infinite-tag-type-recursion.stderr +++ b/tests/ui/infinite/infinite-tag-type-recursion.stderr @@ -9,17 +9,6 @@ help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle LL | enum MList { Cons(isize, Box), Nil } | ++++ + -error[E0391]: cycle detected when computing when `MList` needs drop - --> $DIR/infinite-tag-type-recursion.rs:1:1 - | -LL | enum MList { Cons(isize, MList), Nil } - | ^^^^^^^^^^ - | - = note: ...which immediately requires computing when `MList` needs drop again - = note: cycle used when computing whether `MList` needs drop - = 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 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0072, E0391. -For more information about an error, try `rustc --explain E0072`. +For more information about this error, try `rustc --explain E0072`. diff --git a/tests/ui/query-system/query-cycle-printing-issue-151226.rs b/tests/ui/query-system/query-cycle-printing-issue-151226.rs index 9d0a20737c9fa..551321619ddd0 100644 --- a/tests/ui/query-system/query-cycle-printing-issue-151226.rs +++ b/tests/ui/query-system/query-cycle-printing-issue-151226.rs @@ -1,8 +1,6 @@ struct A(std::sync::OnceLock); //~^ ERROR recursive type `A` has infinite size -//~| ERROR cycle detected when computing layout of `A<()>` static B: A<()> = todo!(); -//~^ ERROR cycle occurred during layout computation fn main() {} diff --git a/tests/ui/query-system/query-cycle-printing-issue-151226.stderr b/tests/ui/query-system/query-cycle-printing-issue-151226.stderr index 7e574b5911a39..bb05cf5f915d7 100644 --- a/tests/ui/query-system/query-cycle-printing-issue-151226.stderr +++ b/tests/ui/query-system/query-cycle-printing-issue-151226.stderr @@ -9,28 +9,6 @@ help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle LL | struct A(Box>); | ++++ + -error[E0391]: cycle detected when computing layout of `A<()>` - | - = note: ...which requires computing layout of `std::sync::once_lock::OnceLock>`... - = note: ...which requires computing layout of `core::cell::UnsafeCell>>`... - = note: ...which requires computing layout of `core::mem::maybe_uninit::MaybeUninit>`... - = note: ...which requires computing layout of `core::mem::manually_drop::ManuallyDrop>`... - = note: ...which requires computing layout of `core::mem::maybe_dangling::MaybeDangling>`... - = note: ...which again requires computing layout of `A<()>`, completing the cycle -note: cycle used when checking that `B` is well-formed - --> $DIR/query-cycle-printing-issue-151226.rs:5:1 - | -LL | static B: A<()> = todo!(); - | ^^^^^^^^^^^^^^^ - = 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[E0080]: a cycle occurred during layout computation - --> $DIR/query-cycle-printing-issue-151226.rs:5:1 - | -LL | static B: A<()> = todo!(); - | ^^^^^^^^^^^^^^^ evaluation of `B` failed here - -error: aborting due to 3 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0072, E0080, E0391. -For more information about an error, try `rustc --explain E0072`. +For more information about this error, try `rustc --explain E0072`. diff --git a/tests/ui/structs-enums/enum-rec/issue-17431-6.rs b/tests/ui/structs-enums/enum-rec/issue-17431-6.rs index a3b510848dcd3..c89b69d3c2f2e 100644 --- a/tests/ui/structs-enums/enum-rec/issue-17431-6.rs +++ b/tests/ui/structs-enums/enum-rec/issue-17431-6.rs @@ -2,7 +2,6 @@ use std::cell::UnsafeCell; enum Foo { X(UnsafeCell>) } //~^ ERROR recursive type `Foo` has infinite size -//~| ERROR cycle detected impl Foo { fn bar(self) {} } diff --git a/tests/ui/structs-enums/enum-rec/issue-17431-6.stderr b/tests/ui/structs-enums/enum-rec/issue-17431-6.stderr index b192593d266b7..8939b603c7985 100644 --- a/tests/ui/structs-enums/enum-rec/issue-17431-6.stderr +++ b/tests/ui/structs-enums/enum-rec/issue-17431-6.stderr @@ -9,17 +9,6 @@ help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle LL | enum Foo { X(UnsafeCell>>) } | ++++ + -error[E0391]: cycle detected when computing when `Foo` needs drop - --> $DIR/issue-17431-6.rs:3:1 - | -LL | enum Foo { X(UnsafeCell>) } - | ^^^^^^^^ - | - = note: ...which immediately requires computing when `Foo` needs drop again - = note: cycle used when computing whether `Foo` needs drop - = 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 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0072, E0391. -For more information about an error, try `rustc --explain E0072`. +For more information about this error, try `rustc --explain E0072`. diff --git a/tests/ui/traits/issue-105231.rs b/tests/ui/traits/issue-105231.rs index 83c3158c106b6..9e47ded7c8c81 100644 --- a/tests/ui/traits/issue-105231.rs +++ b/tests/ui/traits/issue-105231.rs @@ -6,5 +6,4 @@ struct B(A>); trait Foo {} impl Foo for T where T: Send {} impl Foo for B {} -//~^ ERROR conflicting implementations of trait `Foo` for type `B` fn main() {} diff --git a/tests/ui/traits/issue-105231.stderr b/tests/ui/traits/issue-105231.stderr index b048548018a71..6732d4f5803c9 100644 --- a/tests/ui/traits/issue-105231.stderr +++ b/tests/ui/traits/issue-105231.stderr @@ -37,18 +37,6 @@ LL | struct B(A>); = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` = note: all type parameters must be used in a non-recursive way in order to constrain their variance -error[E0119]: conflicting implementations of trait `Foo` for type `B` - --> $DIR/issue-105231.rs:8:1 - | -LL | impl Foo for T where T: Send {} - | ------------------------------- first implementation here -LL | impl Foo for B {} - | ^^^^^^^^^^^^^^^^^^ conflicting implementation for `B` - | - = note: overflow evaluating the requirement `B: Send` - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_105231`) - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0072, E0119. -For more information about an error, try `rustc --explain E0072`. +For more information about this error, try `rustc --explain E0072`. diff --git a/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.rs b/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.rs index 8d291054365f9..d3a6ba94b68fb 100644 --- a/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.rs +++ b/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.rs @@ -1,4 +1,3 @@ -//~ ERROR: cycle detected //! Safe transmute did not handle cycle errors that could occur during //! layout computation. This test checks that we do not ICE in such //! situations (see #117491). diff --git a/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.stderr b/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.stderr index a96876a2c25a1..0b5689e1912f0 100644 --- a/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.stderr +++ b/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.stderr @@ -1,5 +1,5 @@ error[E0072]: recursive type `ExplicitlyPadded` has infinite size - --> $DIR/transmute_infinitely_recursive_type.rs:21:5 + --> $DIR/transmute_infinitely_recursive_type.rs:20:5 | LL | struct ExplicitlyPadded(ExplicitlyPadded); | ^^^^^^^^^^^^^^^^^^^^^^^ ---------------- recursive without indirection @@ -9,13 +9,6 @@ help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle LL | struct ExplicitlyPadded(Box); | ++++ + -error[E0391]: cycle detected when computing layout of `should_pad_explicitly_packed_field::ExplicitlyPadded` - | - = note: ...which immediately requires computing layout of `should_pad_explicitly_packed_field::ExplicitlyPadded` again - = note: cycle used when evaluating trait selection obligation `(): core::mem::transmutability::TransmuteFrom` - = 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 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0072, E0391. -For more information about an error, try `rustc --explain E0072`. +For more information about this error, try `rustc --explain E0072`. From ff3d308966724fb33303fd9a474027297a43f832 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 27 Feb 2026 17:38:59 +1100 Subject: [PATCH 35/46] Eliminate `Representability::Infinite`. This variant was a fallback value used to continue analysis after a `Representability` error, but it's no longer needed thanks to the previous commit. This means `Representability` can now be reduced to a unit type that exists just so `FromCycleError` can exist for the `representability` query. (There is potential for additional cleanups here, but those are beyond the scope of this PR.) This means the `representability`/`representability_adt_ty` queries no longer have a meaningful return value, i.e. they are check-only queries. So they now have a `check_` prefix added. And the `rtry!` macro is no longer needed. --- .../rustc_hir_analysis/src/check/wfcheck.rs | 2 +- compiler/rustc_middle/src/queries.rs | 17 ++-- compiler/rustc_middle/src/ty/adt.rs | 7 +- .../rustc_middle/src/ty/inhabitedness/mod.rs | 5 +- .../rustc_query_impl/src/from_cycle_error.rs | 4 +- .../rustc_ty_utils/src/representability.rs | 82 ++++++++++--------- compiler/rustc_ty_utils/src/ty.rs | 7 +- 7 files changed, 64 insertions(+), 60 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 0b6165c02baba..38025f74b2f69 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -997,7 +997,7 @@ fn check_type_defn<'tcx>( item: &hir::Item<'tcx>, all_sized: bool, ) -> Result<(), ErrorGuaranteed> { - let _ = tcx.representability(item.owner_id.def_id); + let _ = tcx.check_representability(item.owner_id.def_id); let adt_def = tcx.adt_def(item.owner_id); enter_wf_checking_ctxt(tcx, item.owner_id.def_id, |wfcx| { diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 932d0855c6016..4c7d68ebd3e09 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -593,18 +593,23 @@ rustc_queries! { } /// Checks whether a type is representable or infinitely sized - query representability(key: LocalDefId) -> rustc_middle::ty::Representability { + query check_representability(key: LocalDefId) -> rustc_middle::ty::Representability { desc { "checking if `{}` is representable", tcx.def_path_str(key) } - // infinitely sized types will cause a cycle + // Infinitely sized types will cause a cycle. The custom `FromCycleError` impl for + // `Representability` will print a custom error about the infinite size and then abort + // compilation. (In the past we recovered and continued, but in practice that leads to + // confusing subsequent error messages about cycles that then abort.) cycle_delay_bug - // we don't want recursive representability calls to be forced with + // We don't want recursive representability calls to be forced with // incremental compilation because, if a cycle occurs, we need the - // entire cycle to be in memory for diagnostics + // entire cycle to be in memory for diagnostics. This means we can't + // use `ensure_ok()` with this query. anon } - /// An implementation detail for the `representability` query - query representability_adt_ty(key: Ty<'tcx>) -> rustc_middle::ty::Representability { + /// An implementation detail for the `check_representability` query. See that query for more + /// details, particularly on the modifiers. + query check_representability_adt_ty(key: Ty<'tcx>) -> rustc_middle::ty::Representability { desc { "checking if `{}` is representable", key } cycle_delay_bug anon diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index c141eb0311dcf..6a51ea4deffee 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -741,8 +741,7 @@ impl<'tcx> AdtDef<'tcx> { } } +/// This type exists just so a `FromCycleError` impl can be made for the `check_representability` +/// query. #[derive(Clone, Copy, Debug, HashStable)] -pub enum Representability { - Representable, - Infinite(ErrorGuaranteed), -} +pub struct Representability; diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs index 5c4c1733be29e..b454689e243d2 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs @@ -61,10 +61,9 @@ pub(crate) fn provide(providers: &mut Providers) { /// requires calling [`InhabitedPredicate::instantiate`] fn inhabited_predicate_adt(tcx: TyCtxt<'_>, def_id: DefId) -> InhabitedPredicate<'_> { if let Some(def_id) = def_id.as_local() { - if matches!(tcx.representability(def_id), ty::Representability::Infinite(_)) { - return InhabitedPredicate::True; - } + let _ = tcx.check_representability(def_id); } + let adt = tcx.adt_def(def_id); InhabitedPredicate::any( tcx, diff --git a/compiler/rustc_query_impl/src/from_cycle_error.rs b/compiler/rustc_query_impl/src/from_cycle_error.rs index 328bd4a02c8fd..eb6942ba491ff 100644 --- a/compiler/rustc_query_impl/src/from_cycle_error.rs +++ b/compiler/rustc_query_impl/src/from_cycle_error.rs @@ -95,7 +95,7 @@ impl<'tcx> FromCycleError<'tcx> for Representability { let mut item_and_field_ids = Vec::new(); let mut representable_ids = FxHashSet::default(); for info in &cycle_error.cycle { - if info.frame.dep_kind == DepKind::representability + if info.frame.dep_kind == DepKind::check_representability && let Some(field_id) = info.frame.def_id && let Some(field_id) = field_id.as_local() && let Some(DefKind::Field) = info.frame.info.def_kind @@ -109,7 +109,7 @@ impl<'tcx> FromCycleError<'tcx> for Representability { } } for info in &cycle_error.cycle { - if info.frame.dep_kind == DepKind::representability_adt_ty + if info.frame.dep_kind == DepKind::check_representability_adt_ty && let Some(def_id) = info.frame.def_id_for_ty_in_cycle && let Some(def_id) = def_id.as_local() && !item_and_field_ids.iter().any(|&(id, _)| id == def_id) diff --git a/compiler/rustc_ty_utils/src/representability.rs b/compiler/rustc_ty_utils/src/representability.rs index 33d334092ba9c..1814e7604a2d1 100644 --- a/compiler/rustc_ty_utils/src/representability.rs +++ b/compiler/rustc_ty_utils/src/representability.rs @@ -6,69 +6,71 @@ use rustc_middle::ty::{self, Representability, Ty, TyCtxt}; use rustc_span::def_id::LocalDefId; pub(crate) fn provide(providers: &mut Providers) { - *providers = - Providers { representability, representability_adt_ty, params_in_repr, ..*providers }; -} - -macro_rules! rtry { - ($e:expr) => { - match $e { - e @ Representability::Infinite(_) => return e, - Representability::Representable => {} - } + *providers = Providers { + check_representability, + check_representability_adt_ty, + params_in_repr, + ..*providers }; } -fn representability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Representability { +fn check_representability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Representability { match tcx.def_kind(def_id) { DefKind::Struct | DefKind::Union | DefKind::Enum => { for variant in tcx.adt_def(def_id).variants() { for field in variant.fields.iter() { - rtry!(tcx.representability(field.did.expect_local())); + let _ = tcx.check_representability(field.did.expect_local()); } } - Representability::Representable } - DefKind::Field => representability_ty(tcx, tcx.type_of(def_id).instantiate_identity()), + DefKind::Field => { + check_representability_ty(tcx, tcx.type_of(def_id).instantiate_identity()); + } def_kind => bug!("unexpected {def_kind:?}"), } + Representability } -fn representability_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Representability { +fn check_representability_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) { match *ty.kind() { - ty::Adt(..) => tcx.representability_adt_ty(ty), + // This one must be a query rather than a vanilla `check_representability_adt_ty` call. See + // the comment on `check_representability_adt_ty` below for why. + ty::Adt(..) => { + let _ = tcx.check_representability_adt_ty(ty); + } // FIXME(#11924) allow zero-length arrays? - ty::Array(ty, _) => representability_ty(tcx, ty), + ty::Array(ty, _) => { + check_representability_ty(tcx, ty); + } ty::Tuple(tys) => { for ty in tys { - rtry!(representability_ty(tcx, ty)); + check_representability_ty(tcx, ty); } - Representability::Representable } - _ => Representability::Representable, + _ => {} } } -/* -The reason for this being a separate query is very subtle: -Consider this infinitely sized struct: `struct Foo(Box, Bar)`: -When calling representability(Foo), a query cycle will occur: - representability(Foo) - -> representability_adt_ty(Bar) - -> representability(Foo) -For the diagnostic output (in `Value::from_cycle_error`), we want to detect that -the `Foo` in the *second* field of the struct is culpable. This requires -traversing the HIR of the struct and calling `params_in_repr(Bar)`. But we can't -call params_in_repr for a given type unless it is known to be representable. -params_in_repr will cycle/panic on infinitely sized types. Looking at the query -cycle above, we know that `Bar` is representable because -representability_adt_ty(Bar<..>) is in the cycle and representability(Bar) is -*not* in the cycle. -*/ -fn representability_adt_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Representability { +// The reason for this being a separate query is very subtle. Consider this +// infinitely sized struct: `struct Foo(Box, Bar)`. When calling +// check_representability(Foo), a query cycle will occur: +// +// check_representability(Foo) +// -> check_representability_adt_ty(Bar) +// -> check_representability(Foo) +// +// For the diagnostic output (in `Value::from_cycle_error`), we want to detect +// that the `Foo` in the *second* field of the struct is culpable. This +// requires traversing the HIR of the struct and calling `params_in_repr(Bar)`. +// But we can't call params_in_repr for a given type unless it is known to be +// representable. params_in_repr will cycle/panic on infinitely sized types. +// Looking at the query cycle above, we know that `Bar` is representable +// because `check_representability_adt_ty(Bar<..>)` is in the cycle and +// `check_representability(Bar)` is *not* in the cycle. +fn check_representability_adt_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Representability { let ty::Adt(adt, args) = ty.kind() else { bug!("expected adt") }; if let Some(def_id) = adt.did().as_local() { - rtry!(tcx.representability(def_id)); + let _ = tcx.check_representability(def_id); } // At this point, we know that the item of the ADT type is representable; // but the type parameters may cause a cycle with an upstream type @@ -76,11 +78,11 @@ fn representability_adt_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Representab for (i, arg) in args.iter().enumerate() { if let ty::GenericArgKind::Type(ty) = arg.kind() { if params_in_repr.contains(i as u32) { - rtry!(representability_ty(tcx, ty)); + check_representability_ty(tcx, ty); } } } - Representability::Representable + Representability } fn params_in_repr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> DenseBitSet { diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index d6a29aba22b90..78f3a19228870 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -116,11 +116,10 @@ fn adt_sizedness_constraint<'tcx>( tcx: TyCtxt<'tcx>, (def_id, sizedness): (DefId, SizedTraitKind), ) -> Option>> { - if let Some(def_id) = def_id.as_local() - && let ty::Representability::Infinite(_) = tcx.representability(def_id) - { - return None; + if let Some(def_id) = def_id.as_local() { + let _ = tcx.check_representability(def_id); } + let def = tcx.adt_def(def_id); if !def.is_struct() { From cbd443d7293805720b82be0c7e85da527d52c193 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Thu, 12 Feb 2026 11:35:58 +0800 Subject: [PATCH 36/46] Remove unused features in tools --- src/tools/miri/src/lib.rs | 2 ++ src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs | 2 +- src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs | 3 ++- src/tools/test-float-parse/src/lib.rs | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 32cadddc33aa3..e5791d333fd78 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -41,6 +41,8 @@ clippy::len_zero, // We are not implementing queries here so it's fine rustc::potential_query_instability, + // FIXME: Unused features should be removed in the future + unused_features, )] #![warn( rust_2018_idioms, diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs index 68b3afc3e8414..e83ddb859498b 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs @@ -10,7 +10,7 @@ feature = "sysroot-abi", feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span) )] -#![allow(internal_features)] +#![allow(internal_features, unused_features)] #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #[cfg(feature = "in-rust-tree")] diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index 65de804404831..734cb4ecc1693 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -18,7 +18,8 @@ internal_features, clippy::disallowed_types, clippy::print_stderr, - unused_crate_dependencies + unused_crate_dependencies, + unused_features )] #![deny(deprecated_safe, clippy::undocumented_unsafe_blocks)] diff --git a/src/tools/test-float-parse/src/lib.rs b/src/tools/test-float-parse/src/lib.rs index 1321a3c335499..da83aeea3c132 100644 --- a/src/tools/test-float-parse/src/lib.rs +++ b/src/tools/test-float-parse/src/lib.rs @@ -1,5 +1,5 @@ -#![feature(f16)] #![feature(cfg_target_has_reliable_f16_f128)] +#![cfg_attr(target_has_reliable_f16, feature(f16))] #![expect(internal_features)] // reliable_f16_f128 mod traits; From 7ffaa416620a9032c7a8fc9e5837822b849553a3 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Fri, 20 Feb 2026 14:34:01 +0800 Subject: [PATCH 37/46] Remove unused features in library tests --- library/alloctests/tests/lib.rs | 1 - library/core/src/num/f16.rs | 6 +++++- library/core/src/num/f32.rs | 1 + library/core/src/num/f64.rs | 1 + library/coretests/tests/lib.rs | 1 - library/std/src/num/f16.rs | 1 + library/std/src/process.rs | 2 ++ 7 files changed, 10 insertions(+), 3 deletions(-) diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index 60b26126cf6be..699a5010282b0 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -1,6 +1,5 @@ #![feature(allocator_api)] #![feature(binary_heap_pop_if)] -#![feature(btree_merge)] #![feature(const_heap)] #![feature(deque_extend_front)] #![feature(iter_array_chunks)] diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index ef937fccb47f3..ff483f4062ac9 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -1521,7 +1521,11 @@ impl f16 { // Functions in this module fall into `core_float_math` // #[unstable(feature = "core_float_math", issue = "137578")] #[cfg(not(test))] -#[doc(test(attr(feature(cfg_target_has_reliable_f16_f128), expect(internal_features))))] +#[doc(test(attr( + feature(cfg_target_has_reliable_f16_f128), + expect(internal_features), + allow(unused_features) +)))] impl f16 { /// Returns the largest integer less than or equal to `self`. /// diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index aac81d48c1b45..63abb5adcbfb1 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -393,6 +393,7 @@ pub mod consts { pub const LN_10: f32 = 2.30258509299404568401799145468436421_f32; } +#[doc(test(attr(allow(unused_features))))] impl f32 { /// The radix or base of the internal representation of `f32`. #[stable(feature = "assoc_int_consts", since = "1.43.0")] diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index bacf429e77fab..1294e094e4a07 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -393,6 +393,7 @@ pub mod consts { pub const LN_10: f64 = 2.30258509299404568401799145468436421_f64; } +#[doc(test(attr(allow(unused_features))))] impl f64 { /// The radix or base of the internal representation of `f64`. #[stable(feature = "assoc_int_consts", since = "1.43.0")] diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 7cd946dc9a03f..d150d2f622d9d 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -51,7 +51,6 @@ #![feature(f16)] #![feature(f128)] #![feature(float_algebraic)] -#![feature(float_bits_const)] #![feature(float_exact_integer_constants)] #![feature(float_gamma)] #![feature(float_minimum_maximum)] diff --git a/library/std/src/num/f16.rs b/library/std/src/num/f16.rs index 318a0b3af86a2..7ca266c8a5f60 100644 --- a/library/std/src/num/f16.rs +++ b/library/std/src/num/f16.rs @@ -16,6 +16,7 @@ use crate::intrinsics; use crate::sys::cmath; #[cfg(not(test))] +#[doc(test(attr(allow(unused_features))))] impl f16 { /// Raises a number to a floating point power. /// diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 1199403b1d5ab..4b3bfa5335bf0 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -1380,6 +1380,7 @@ impl Output { /// # Examples /// /// ``` + /// # #![allow(unused_features)] /// #![feature(exit_status_error)] /// # #[cfg(all(unix, not(target_os = "android"), not(all(target_vendor = "apple", not(target_os = "macos")))))] { /// use std::process::Command; @@ -1959,6 +1960,7 @@ impl crate::sealed::Sealed for ExitStatusError {} pub struct ExitStatusError(imp::ExitStatusError); #[unstable(feature = "exit_status_error", issue = "84908")] +#[doc(test(attr(allow(unused_features))))] impl ExitStatusError { /// Reports the exit code, if applicable, from an `ExitStatusError`. /// From 0436634084b41b7af7d0a11459fad14a0b53ef59 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Sat, 28 Feb 2026 21:38:09 +0800 Subject: [PATCH 38/46] Remove unused features in tests --- .../add_private_fn_at_krate_root_cc/struct_point.rs | 1 - tests/incremental/change_add_field/struct_point.rs | 1 - tests/incremental/change_crate_dep_kind.rs | 2 +- tests/incremental/change_private_fn/struct_point.rs | 1 - tests/incremental/change_private_fn_cc/struct_point.rs | 1 - .../incremental/change_private_impl_method/struct_point.rs | 1 - .../change_private_impl_method_cc/struct_point.rs | 1 - .../change_pub_inherent_method_body/struct_point.rs | 1 - .../change_pub_inherent_method_sig/struct_point.rs | 1 - tests/ui-fulldeps/rustc_public/check_abi.rs | 1 - tests/ui-fulldeps/rustc_public/check_transform.rs | 1 - tests/ui/allocator/xcrate-use2.rs | 1 - tests/ui/associated-types/associated-types-impl-redirect.rs | 1 - .../associated-types-where-clause-impl-ambiguity.rs | 1 - tests/ui/async-await/async-drop/async-drop-initial.rs | 2 +- tests/ui/backtrace/line-tables-only.rs | 1 - tests/ui/borrowck/super-let-lifetime-and-drop.rs | 2 +- tests/ui/cfg/cfg_stmt_expr.rs | 1 - tests/ui/const-generics/transmute.rs | 1 - tests/ui/consts/const-fn-type-name.rs | 1 - tests/ui/consts/const-ptr-nonnull-rpass.rs | 2 +- tests/ui/consts/try-operator.rs | 1 - tests/ui/contracts/internal_machinery/lowering/basics.rs | 2 +- tests/ui/coroutine/control-flow.rs | 2 +- tests/ui/coroutine/missing_coroutine_attr_suggestion.fixed | 2 +- tests/ui/coroutine/missing_coroutine_attr_suggestion.rs | 2 +- tests/ui/coroutine/non-static-is-unpin.rs | 2 +- tests/ui/coroutine/other-attribute-on-gen.rs | 1 - tests/ui/coroutine/pin-box-coroutine.rs | 2 +- tests/ui/coroutine/xcrate.rs | 2 +- tests/ui/eii/default/call_default.rs | 1 - tests/ui/eii/default/call_default_panics.rs | 1 - tests/ui/eii/default/call_impl.rs | 1 - tests/ui/eii/linking/codegen_cross_crate.rs | 1 - tests/ui/eii/privacy1.rs | 1 - tests/ui/extern/extern-prelude-core.rs | 1 - .../feature-gate-explicit-extern-abis.current.fixed | 1 - .../feature-gate-explicit-extern-abis.current.stderr | 6 +++--- .../feature-gate-explicit-extern-abis.current_feature.fixed | 1 - ...feature-gate-explicit-extern-abis.current_feature.stderr | 6 +++--- .../feature-gate-explicit-extern-abis.future.fixed | 1 - .../feature-gate-explicit-extern-abis.future.stderr | 6 +++--- .../feature-gate-explicit-extern-abis.future_feature.stderr | 6 +++--- tests/ui/feature-gates/feature-gate-explicit-extern-abis.rs | 1 - tests/ui/float/classify-runtime-const.rs | 2 -- tests/ui/float/conv-bits-runtime-const.rs | 5 +++-- tests/ui/fmt/format-macro-no-std.rs | 1 - .../half-open-range-pats-semantics.rs | 4 ++-- tests/ui/hygiene/xcrate.rs | 1 - tests/ui/impl-trait/example-calendar.rs | 3 +-- tests/ui/intrinsics/intrinsic-alignment.rs | 2 +- tests/ui/io-checks/write-macro-error.rs | 1 - tests/ui/linkage-attr/raw-dylib/elf/as_needed.rs | 2 +- tests/ui/lint/lint-expr-stmt-attrs-for-early-lints.rs | 1 - tests/ui/lint/lint-unknown-feature.rs | 1 - .../lint-unnecessary-qualification-issue-121331.fixed | 2 +- .../lint-unnecessary-qualification-issue-121331.rs | 2 +- tests/ui/lint/unused/unused_attributes-must_use.fixed | 2 +- tests/ui/lint/unused/unused_attributes-must_use.rs | 2 +- tests/ui/lowering/issue-96847.rs | 1 - tests/ui/lowering/issue-96847.stderr | 2 +- tests/ui/macros/metavar_cross_edition_recursive_macros.rs | 1 - .../macros/rfc-2011-nicer-assert-messages/all-expr-kinds.rs | 2 +- .../all-not-available-cases.rs | 2 +- ...t-with-custom-errors-does-not-create-unnecessary-code.rs | 1 - ...ert-without-captures-does-not-create-unnecessary-code.rs | 2 +- .../feature-gate-generic_assert.rs | 2 +- tests/ui/macros/stringify.rs | 2 -- tests/ui/match/match-float.rs | 4 ++-- tests/ui/methods/supertrait-shadowing/out-of-scope.rs | 1 - tests/ui/nll/issue-48623-coroutine.rs | 2 +- tests/ui/overloaded/overloaded-calls-simple.rs | 2 +- tests/ui/sanitizer/memory-passing.rs | 1 - tests/ui/simd/monomorphize-shuffle-index.rs | 2 +- tests/ui/structs-enums/rec-align-u32.rs | 2 +- tests/ui/structs-enums/rec-align-u64.rs | 2 +- .../auxiliary/thread-local-extern-static.rs | 3 ++- tests/ui/threads-sendsync/thread-local-extern-static.rs | 3 ++- tests/ui/traits/alias/import-cross-crate.rs | 1 - tests/ui/traits/overlap-permitted-for-marker-traits.rs | 1 - tests/ui/type/typeid-consistency.rs | 1 - tests/ui/typeck/type-name-intrinsic-usage-61894.rs | 1 - 82 files changed, 51 insertions(+), 96 deletions(-) diff --git a/tests/incremental/add_private_fn_at_krate_root_cc/struct_point.rs b/tests/incremental/add_private_fn_at_krate_root_cc/struct_point.rs index 0367af8d53b8d..be1f27e7bae3e 100644 --- a/tests/incremental/add_private_fn_at_krate_root_cc/struct_point.rs +++ b/tests/incremental/add_private_fn_at_krate_root_cc/struct_point.rs @@ -9,7 +9,6 @@ //@ ignore-backends: gcc #![feature(rustc_attrs)] -#![feature(stmt_expr_attributes)] #![allow(dead_code)] #![crate_type = "rlib"] diff --git a/tests/incremental/change_add_field/struct_point.rs b/tests/incremental/change_add_field/struct_point.rs index fb363c9ce496f..024812bd4bebb 100644 --- a/tests/incremental/change_add_field/struct_point.rs +++ b/tests/incremental/change_add_field/struct_point.rs @@ -9,7 +9,6 @@ //@ ignore-backends: gcc #![feature(rustc_attrs)] -#![feature(stmt_expr_attributes)] #![allow(dead_code)] #![crate_type = "rlib"] diff --git a/tests/incremental/change_crate_dep_kind.rs b/tests/incremental/change_crate_dep_kind.rs index 4fd0c345aad21..d08e76c47527b 100644 --- a/tests/incremental/change_crate_dep_kind.rs +++ b/tests/incremental/change_crate_dep_kind.rs @@ -8,7 +8,7 @@ //@ build-pass (FIXME(62277): could be check-pass?) //@ ignore-backends: gcc -#![feature(panic_unwind)] +#![cfg_attr(cfail1, feature(panic_unwind))] // Turn the panic_unwind crate from an explicit into an implicit query: #[cfg(cfail1)] diff --git a/tests/incremental/change_private_fn/struct_point.rs b/tests/incremental/change_private_fn/struct_point.rs index cce26fa7a3e6e..2c2560309d27e 100644 --- a/tests/incremental/change_private_fn/struct_point.rs +++ b/tests/incremental/change_private_fn/struct_point.rs @@ -7,7 +7,6 @@ //@ ignore-backends: gcc #![feature(rustc_attrs)] -#![feature(stmt_expr_attributes)] #![allow(dead_code)] #![crate_type = "rlib"] diff --git a/tests/incremental/change_private_fn_cc/struct_point.rs b/tests/incremental/change_private_fn_cc/struct_point.rs index 87392ca857ed7..cfe26be225d30 100644 --- a/tests/incremental/change_private_fn_cc/struct_point.rs +++ b/tests/incremental/change_private_fn_cc/struct_point.rs @@ -9,7 +9,6 @@ #![crate_type = "rlib"] #![feature(rustc_attrs)] -#![feature(stmt_expr_attributes)] #![allow(dead_code)] #![rustc_partition_reused(module="struct_point-fn_calls_methods_in_same_impl", cfg="cfail2")] diff --git a/tests/incremental/change_private_impl_method/struct_point.rs b/tests/incremental/change_private_impl_method/struct_point.rs index dc0ba82c0a311..42793b53378d4 100644 --- a/tests/incremental/change_private_impl_method/struct_point.rs +++ b/tests/incremental/change_private_impl_method/struct_point.rs @@ -7,7 +7,6 @@ //@ ignore-backends: gcc #![feature(rustc_attrs)] -#![feature(stmt_expr_attributes)] #![allow(dead_code)] #![crate_type = "rlib"] diff --git a/tests/incremental/change_private_impl_method_cc/struct_point.rs b/tests/incremental/change_private_impl_method_cc/struct_point.rs index eb51af7209495..c554a2f3db96b 100644 --- a/tests/incremental/change_private_impl_method_cc/struct_point.rs +++ b/tests/incremental/change_private_impl_method_cc/struct_point.rs @@ -9,7 +9,6 @@ #![crate_type = "rlib"] #![feature(rustc_attrs)] -#![feature(stmt_expr_attributes)] #![allow(dead_code)] #![rustc_partition_reused(module="struct_point-fn_read_field", cfg="cfail2")] diff --git a/tests/incremental/change_pub_inherent_method_body/struct_point.rs b/tests/incremental/change_pub_inherent_method_body/struct_point.rs index b8e06d070a3c7..d024432266694 100644 --- a/tests/incremental/change_pub_inherent_method_body/struct_point.rs +++ b/tests/incremental/change_pub_inherent_method_body/struct_point.rs @@ -7,7 +7,6 @@ #![crate_type = "rlib"] #![feature(rustc_attrs)] -#![feature(stmt_expr_attributes)] #![allow(dead_code)] #![rustc_partition_codegened(module="struct_point-point", cfg="cfail2")] diff --git a/tests/incremental/change_pub_inherent_method_sig/struct_point.rs b/tests/incremental/change_pub_inherent_method_sig/struct_point.rs index 3672ec268010e..5c24199df6aa8 100644 --- a/tests/incremental/change_pub_inherent_method_sig/struct_point.rs +++ b/tests/incremental/change_pub_inherent_method_sig/struct_point.rs @@ -7,7 +7,6 @@ #![crate_type = "rlib"] #![feature(rustc_attrs)] -#![feature(stmt_expr_attributes)] #![allow(dead_code)] // These are expected to require codegen. diff --git a/tests/ui-fulldeps/rustc_public/check_abi.rs b/tests/ui-fulldeps/rustc_public/check_abi.rs index 8e97b773db567..d823e76b93cd0 100644 --- a/tests/ui-fulldeps/rustc_public/check_abi.rs +++ b/tests/ui-fulldeps/rustc_public/check_abi.rs @@ -6,7 +6,6 @@ //@ ignore-remote #![feature(rustc_private)] -#![feature(ascii_char, ascii_char_variants)] extern crate rustc_driver; extern crate rustc_hir; diff --git a/tests/ui-fulldeps/rustc_public/check_transform.rs b/tests/ui-fulldeps/rustc_public/check_transform.rs index f8aa40e474468..000f1f4cb365a 100644 --- a/tests/ui-fulldeps/rustc_public/check_transform.rs +++ b/tests/ui-fulldeps/rustc_public/check_transform.rs @@ -6,7 +6,6 @@ //@ ignore-remote #![feature(rustc_private)] -#![feature(ascii_char, ascii_char_variants)] extern crate rustc_hir; extern crate rustc_middle; diff --git a/tests/ui/allocator/xcrate-use2.rs b/tests/ui/allocator/xcrate-use2.rs index a48b0beeb07e3..4580d0f908bbc 100644 --- a/tests/ui/allocator/xcrate-use2.rs +++ b/tests/ui/allocator/xcrate-use2.rs @@ -5,7 +5,6 @@ //@ aux-build:helper.rs //@ no-prefer-dynamic -#![feature(allocator_api)] extern crate custom; extern crate custom_as_global; diff --git a/tests/ui/associated-types/associated-types-impl-redirect.rs b/tests/ui/associated-types/associated-types-impl-redirect.rs index 64cae31e5693b..11812c7212bb6 100644 --- a/tests/ui/associated-types/associated-types-impl-redirect.rs +++ b/tests/ui/associated-types/associated-types-impl-redirect.rs @@ -9,7 +9,6 @@ // for `ByRef`. The right answer was to consider the result ambiguous // until more type information was available. -#![feature(lang_items)] #![no_implicit_prelude] use std::marker::Sized; diff --git a/tests/ui/associated-types/associated-types-where-clause-impl-ambiguity.rs b/tests/ui/associated-types/associated-types-where-clause-impl-ambiguity.rs index 0de7617943c65..fd40438704420 100644 --- a/tests/ui/associated-types/associated-types-where-clause-impl-ambiguity.rs +++ b/tests/ui/associated-types/associated-types-where-clause-impl-ambiguity.rs @@ -8,7 +8,6 @@ // for `ByRef`. The right answer was to consider the result ambiguous // until more type information was available. -#![feature(lang_items)] #![no_implicit_prelude] use std::marker::Sized; diff --git a/tests/ui/async-await/async-drop/async-drop-initial.rs b/tests/ui/async-await/async-drop/async-drop-initial.rs index cd33c143fba01..427ed54fa6caa 100644 --- a/tests/ui/async-await/async-drop/async-drop-initial.rs +++ b/tests/ui/async-await/async-drop/async-drop-initial.rs @@ -5,7 +5,7 @@ // please consider modifying miri's async drop test at // `src/tools/miri/tests/pass/async-drop.rs`. -#![feature(async_drop, impl_trait_in_assoc_type)] +#![feature(async_drop)] #![allow(incomplete_features, dead_code)] //@ edition: 2021 diff --git a/tests/ui/backtrace/line-tables-only.rs b/tests/ui/backtrace/line-tables-only.rs index 7dac41119a324..b3ae8a1971b98 100644 --- a/tests/ui/backtrace/line-tables-only.rs +++ b/tests/ui/backtrace/line-tables-only.rs @@ -18,7 +18,6 @@ //@ needs-unwind //@ aux-build: line-tables-only-helper.rs -#![feature(backtrace_frames)] extern crate line_tables_only_helper; diff --git a/tests/ui/borrowck/super-let-lifetime-and-drop.rs b/tests/ui/borrowck/super-let-lifetime-and-drop.rs index 380470f792fe1..a103a6a804cc0 100644 --- a/tests/ui/borrowck/super-let-lifetime-and-drop.rs +++ b/tests/ui/borrowck/super-let-lifetime-and-drop.rs @@ -7,7 +7,7 @@ //@ [borrowck] check-fail #![allow(dropping_references)] -#![feature(super_let, stmt_expr_attributes)] +#![feature(super_let)] use std::convert::identity; diff --git a/tests/ui/cfg/cfg_stmt_expr.rs b/tests/ui/cfg/cfg_stmt_expr.rs index 361b159a354fd..128321a23320a 100644 --- a/tests/ui/cfg/cfg_stmt_expr.rs +++ b/tests/ui/cfg/cfg_stmt_expr.rs @@ -3,7 +3,6 @@ #![allow(unused_mut)] #![allow(unused_variables)] #![deny(non_snake_case)] -#![feature(stmt_expr_attributes)] fn main() { let a = 413; diff --git a/tests/ui/const-generics/transmute.rs b/tests/ui/const-generics/transmute.rs index e8ab8637932dd..6108139f3ce5c 100644 --- a/tests/ui/const-generics/transmute.rs +++ b/tests/ui/const-generics/transmute.rs @@ -1,6 +1,5 @@ //@ run-pass #![feature(generic_const_exprs)] -#![feature(transmute_generic_consts)] #![allow(incomplete_features)] fn ident(v: [[u32; H]; W]) -> [[u32; H]; W] { diff --git a/tests/ui/consts/const-fn-type-name.rs b/tests/ui/consts/const-fn-type-name.rs index 733ab79b7cdb8..5da86de8f67c8 100644 --- a/tests/ui/consts/const-fn-type-name.rs +++ b/tests/ui/consts/const-fn-type-name.rs @@ -1,7 +1,6 @@ //@ run-pass #![feature(core_intrinsics)] -#![feature(const_type_name)] #![allow(dead_code)] const fn type_name_wrapper(_: &T) -> &'static str { diff --git a/tests/ui/consts/const-ptr-nonnull-rpass.rs b/tests/ui/consts/const-ptr-nonnull-rpass.rs index 48ad72df63091..ebea036c16476 100644 --- a/tests/ui/consts/const-ptr-nonnull-rpass.rs +++ b/tests/ui/consts/const-ptr-nonnull-rpass.rs @@ -1,6 +1,6 @@ //@ run-pass -#![feature(ptr_internals, test)] +#![feature(test)] extern crate test; use test::black_box as b; // prevent promotion of the argument and const-propagation of the result diff --git a/tests/ui/consts/try-operator.rs b/tests/ui/consts/try-operator.rs index cd0bf8ea57167..54a6ed26241c6 100644 --- a/tests/ui/consts/try-operator.rs +++ b/tests/ui/consts/try-operator.rs @@ -1,7 +1,6 @@ //@ ignore-backends: gcc //@ run-pass -#![feature(try_trait_v2)] #![feature(const_trait_impl)] #![feature(const_try)] diff --git a/tests/ui/contracts/internal_machinery/lowering/basics.rs b/tests/ui/contracts/internal_machinery/lowering/basics.rs index 7b3a769af8258..f4f5dc5ab1056 100644 --- a/tests/ui/contracts/internal_machinery/lowering/basics.rs +++ b/tests/ui/contracts/internal_machinery/lowering/basics.rs @@ -1,6 +1,6 @@ //@ run-pass #![expect(incomplete_features)] -#![feature(contracts, cfg_contract_checks, contracts_internals, core_intrinsics)] +#![feature(contracts, contracts_internals, core_intrinsics)] extern crate core; diff --git a/tests/ui/coroutine/control-flow.rs b/tests/ui/coroutine/control-flow.rs index f64b6f7388366..fe611b1c1c319 100644 --- a/tests/ui/coroutine/control-flow.rs +++ b/tests/ui/coroutine/control-flow.rs @@ -3,7 +3,7 @@ //@ revisions: default nomiropt //@[nomiropt]compile-flags: -Z mir-opt-level=0 -#![feature(coroutines, coroutine_trait, stmt_expr_attributes)] +#![feature(coroutines, coroutine_trait)] use std::ops::{CoroutineState, Coroutine}; use std::pin::Pin; diff --git a/tests/ui/coroutine/missing_coroutine_attr_suggestion.fixed b/tests/ui/coroutine/missing_coroutine_attr_suggestion.fixed index 128f09a118439..9bda140ce58c5 100644 --- a/tests/ui/coroutine/missing_coroutine_attr_suggestion.fixed +++ b/tests/ui/coroutine/missing_coroutine_attr_suggestion.fixed @@ -1,6 +1,6 @@ //@ run-rustfix -#![feature(coroutines, gen_blocks, stmt_expr_attributes)] +#![feature(coroutines, stmt_expr_attributes)] fn main() { let _ = #[coroutine] || yield; diff --git a/tests/ui/coroutine/missing_coroutine_attr_suggestion.rs b/tests/ui/coroutine/missing_coroutine_attr_suggestion.rs index dc95259149609..d8af486e6d05a 100644 --- a/tests/ui/coroutine/missing_coroutine_attr_suggestion.rs +++ b/tests/ui/coroutine/missing_coroutine_attr_suggestion.rs @@ -1,6 +1,6 @@ //@ run-rustfix -#![feature(coroutines, gen_blocks, stmt_expr_attributes)] +#![feature(coroutines, stmt_expr_attributes)] fn main() { let _ = || yield; diff --git a/tests/ui/coroutine/non-static-is-unpin.rs b/tests/ui/coroutine/non-static-is-unpin.rs index b28bf1977145b..7dc036077f90c 100644 --- a/tests/ui/coroutine/non-static-is-unpin.rs +++ b/tests/ui/coroutine/non-static-is-unpin.rs @@ -3,7 +3,7 @@ //@[next] compile-flags: -Znext-solver //@ run-pass -#![feature(coroutines, coroutine_trait, stmt_expr_attributes)] +#![feature(coroutines)] #![allow(dropping_copy_types)] use std::marker::PhantomPinned; diff --git a/tests/ui/coroutine/other-attribute-on-gen.rs b/tests/ui/coroutine/other-attribute-on-gen.rs index 5f4584ee0226e..e13a0abcbfd63 100644 --- a/tests/ui/coroutine/other-attribute-on-gen.rs +++ b/tests/ui/coroutine/other-attribute-on-gen.rs @@ -2,7 +2,6 @@ //@ run-pass #![feature(gen_blocks)] #![feature(optimize_attribute)] -#![feature(stmt_expr_attributes)] #![feature(async_iterator)] #![allow(dead_code)] diff --git a/tests/ui/coroutine/pin-box-coroutine.rs b/tests/ui/coroutine/pin-box-coroutine.rs index d030f3ef214d1..d9674ed7341be 100644 --- a/tests/ui/coroutine/pin-box-coroutine.rs +++ b/tests/ui/coroutine/pin-box-coroutine.rs @@ -1,6 +1,6 @@ //@ run-pass -#![feature(coroutines, coroutine_trait, stmt_expr_attributes)] +#![feature(coroutines, coroutine_trait)] use std::ops::Coroutine; diff --git a/tests/ui/coroutine/xcrate.rs b/tests/ui/coroutine/xcrate.rs index 406152a0bf10c..6b4d81fc983f8 100644 --- a/tests/ui/coroutine/xcrate.rs +++ b/tests/ui/coroutine/xcrate.rs @@ -2,7 +2,7 @@ //@ aux-build:xcrate.rs -#![feature(coroutines, coroutine_trait)] +#![feature(coroutine_trait)] extern crate xcrate; diff --git a/tests/ui/eii/default/call_default.rs b/tests/ui/eii/default/call_default.rs index c13df48e99217..b60a1dd0b2156 100644 --- a/tests/ui/eii/default/call_default.rs +++ b/tests/ui/eii/default/call_default.rs @@ -7,7 +7,6 @@ //@ ignore-windows // Tests EIIs with default implementations. // When there's no explicit declaration, the default should be called from the declaring crate. -#![feature(extern_item_impls)] extern crate decl_with_default; diff --git a/tests/ui/eii/default/call_default_panics.rs b/tests/ui/eii/default/call_default_panics.rs index f71fddb71ba26..96b2742aa8e0a 100644 --- a/tests/ui/eii/default/call_default_panics.rs +++ b/tests/ui/eii/default/call_default_panics.rs @@ -23,7 +23,6 @@ // ``` // This is a simple test to make sure that we can unwind through these, // and that this wrapper function effectively doesn't show up in the trace. -#![feature(extern_item_impls)] extern crate decl_with_default_panics; diff --git a/tests/ui/eii/default/call_impl.rs b/tests/ui/eii/default/call_impl.rs index 94b2aa552a1e1..b887697574892 100644 --- a/tests/ui/eii/default/call_impl.rs +++ b/tests/ui/eii/default/call_impl.rs @@ -9,7 +9,6 @@ // Tests EIIs with default implementations. // When an explicit implementation is given in one dependency, and the declaration is in another, // the explicit implementation is preferred. -#![feature(extern_item_impls)] extern crate decl_with_default; extern crate impl1; diff --git a/tests/ui/eii/linking/codegen_cross_crate.rs b/tests/ui/eii/linking/codegen_cross_crate.rs index 4016712e7504a..2958a0f10521b 100644 --- a/tests/ui/eii/linking/codegen_cross_crate.rs +++ b/tests/ui/eii/linking/codegen_cross_crate.rs @@ -6,7 +6,6 @@ // FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 //@ ignore-windows // Tests whether calling EIIs works with the declaration in another crate. -#![feature(extern_item_impls)] extern crate codegen_cross_crate_other_crate as codegen; diff --git a/tests/ui/eii/privacy1.rs b/tests/ui/eii/privacy1.rs index 72aec83d2cee0..60bf36074e0ae 100644 --- a/tests/ui/eii/privacy1.rs +++ b/tests/ui/eii/privacy1.rs @@ -5,7 +5,6 @@ // FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows // Tests whether re-exports work. -#![feature(extern_item_impls)] extern crate other_crate_privacy1 as codegen; diff --git a/tests/ui/extern/extern-prelude-core.rs b/tests/ui/extern/extern-prelude-core.rs index 5108c02517c3d..05d3750ae6563 100644 --- a/tests/ui/extern/extern-prelude-core.rs +++ b/tests/ui/extern/extern-prelude-core.rs @@ -1,5 +1,4 @@ //@ run-pass -#![feature(lang_items)] #![no_std] extern crate std as other; diff --git a/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current.fixed b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current.fixed index 525f78d162fc0..a2b920880221f 100644 --- a/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current.fixed +++ b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current.fixed @@ -22,7 +22,6 @@ //@ [future_feature] compile-flags: -Z unstable-options #![cfg_attr(future_feature, feature(explicit_extern_abis))] -#![cfg_attr(current_feature, feature(explicit_extern_abis))] extern "C" fn _foo() {} //[current]~^ WARN `extern` declarations without an explicit ABI are deprecated diff --git a/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current.stderr b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current.stderr index cf927807c7c3c..f5cc09bef5e5d 100644 --- a/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current.stderr +++ b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current.stderr @@ -1,5 +1,5 @@ warning: `extern` declarations without an explicit ABI are deprecated - --> $DIR/feature-gate-explicit-extern-abis.rs:27:1 + --> $DIR/feature-gate-explicit-extern-abis.rs:26:1 | LL | extern fn _foo() {} | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` @@ -7,13 +7,13 @@ LL | extern fn _foo() {} = note: `#[warn(missing_abi)]` on by default warning: `extern` declarations without an explicit ABI are deprecated - --> $DIR/feature-gate-explicit-extern-abis.rs:33:8 + --> $DIR/feature-gate-explicit-extern-abis.rs:32:8 | LL | unsafe extern fn _bar() {} | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` warning: `extern` declarations without an explicit ABI are deprecated - --> $DIR/feature-gate-explicit-extern-abis.rs:39:8 + --> $DIR/feature-gate-explicit-extern-abis.rs:38:8 | LL | unsafe extern {} | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` diff --git a/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current_feature.fixed b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current_feature.fixed index 525f78d162fc0..a2b920880221f 100644 --- a/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current_feature.fixed +++ b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current_feature.fixed @@ -22,7 +22,6 @@ //@ [future_feature] compile-flags: -Z unstable-options #![cfg_attr(future_feature, feature(explicit_extern_abis))] -#![cfg_attr(current_feature, feature(explicit_extern_abis))] extern "C" fn _foo() {} //[current]~^ WARN `extern` declarations without an explicit ABI are deprecated diff --git a/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current_feature.stderr b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current_feature.stderr index cf927807c7c3c..f5cc09bef5e5d 100644 --- a/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current_feature.stderr +++ b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current_feature.stderr @@ -1,5 +1,5 @@ warning: `extern` declarations without an explicit ABI are deprecated - --> $DIR/feature-gate-explicit-extern-abis.rs:27:1 + --> $DIR/feature-gate-explicit-extern-abis.rs:26:1 | LL | extern fn _foo() {} | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` @@ -7,13 +7,13 @@ LL | extern fn _foo() {} = note: `#[warn(missing_abi)]` on by default warning: `extern` declarations without an explicit ABI are deprecated - --> $DIR/feature-gate-explicit-extern-abis.rs:33:8 + --> $DIR/feature-gate-explicit-extern-abis.rs:32:8 | LL | unsafe extern fn _bar() {} | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` warning: `extern` declarations without an explicit ABI are deprecated - --> $DIR/feature-gate-explicit-extern-abis.rs:39:8 + --> $DIR/feature-gate-explicit-extern-abis.rs:38:8 | LL | unsafe extern {} | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` diff --git a/tests/ui/feature-gates/feature-gate-explicit-extern-abis.future.fixed b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.future.fixed index 525f78d162fc0..a2b920880221f 100644 --- a/tests/ui/feature-gates/feature-gate-explicit-extern-abis.future.fixed +++ b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.future.fixed @@ -22,7 +22,6 @@ //@ [future_feature] compile-flags: -Z unstable-options #![cfg_attr(future_feature, feature(explicit_extern_abis))] -#![cfg_attr(current_feature, feature(explicit_extern_abis))] extern "C" fn _foo() {} //[current]~^ WARN `extern` declarations without an explicit ABI are deprecated diff --git a/tests/ui/feature-gates/feature-gate-explicit-extern-abis.future.stderr b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.future.stderr index cf927807c7c3c..f5cc09bef5e5d 100644 --- a/tests/ui/feature-gates/feature-gate-explicit-extern-abis.future.stderr +++ b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.future.stderr @@ -1,5 +1,5 @@ warning: `extern` declarations without an explicit ABI are deprecated - --> $DIR/feature-gate-explicit-extern-abis.rs:27:1 + --> $DIR/feature-gate-explicit-extern-abis.rs:26:1 | LL | extern fn _foo() {} | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` @@ -7,13 +7,13 @@ LL | extern fn _foo() {} = note: `#[warn(missing_abi)]` on by default warning: `extern` declarations without an explicit ABI are deprecated - --> $DIR/feature-gate-explicit-extern-abis.rs:33:8 + --> $DIR/feature-gate-explicit-extern-abis.rs:32:8 | LL | unsafe extern fn _bar() {} | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` warning: `extern` declarations without an explicit ABI are deprecated - --> $DIR/feature-gate-explicit-extern-abis.rs:39:8 + --> $DIR/feature-gate-explicit-extern-abis.rs:38:8 | LL | unsafe extern {} | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` diff --git a/tests/ui/feature-gates/feature-gate-explicit-extern-abis.future_feature.stderr b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.future_feature.stderr index 096a6f4341699..3fad1cc76dd7c 100644 --- a/tests/ui/feature-gates/feature-gate-explicit-extern-abis.future_feature.stderr +++ b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.future_feature.stderr @@ -1,5 +1,5 @@ error: `extern` declarations without an explicit ABI are disallowed - --> $DIR/feature-gate-explicit-extern-abis.rs:27:1 + --> $DIR/feature-gate-explicit-extern-abis.rs:26:1 | LL | extern fn _foo() {} | ^^^^^^ help: specify an ABI: `extern ""` @@ -7,7 +7,7 @@ LL | extern fn _foo() {} = help: prior to Rust 2024, a default ABI was inferred error: `extern` declarations without an explicit ABI are disallowed - --> $DIR/feature-gate-explicit-extern-abis.rs:33:8 + --> $DIR/feature-gate-explicit-extern-abis.rs:32:8 | LL | unsafe extern fn _bar() {} | ^^^^^^ help: specify an ABI: `extern ""` @@ -15,7 +15,7 @@ LL | unsafe extern fn _bar() {} = help: prior to Rust 2024, a default ABI was inferred error: `extern` declarations without an explicit ABI are disallowed - --> $DIR/feature-gate-explicit-extern-abis.rs:39:8 + --> $DIR/feature-gate-explicit-extern-abis.rs:38:8 | LL | unsafe extern {} | ^^^^^^ help: specify an ABI: `extern ""` diff --git a/tests/ui/feature-gates/feature-gate-explicit-extern-abis.rs b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.rs index 379c45f589907..2ddc817768a16 100644 --- a/tests/ui/feature-gates/feature-gate-explicit-extern-abis.rs +++ b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.rs @@ -22,7 +22,6 @@ //@ [future_feature] compile-flags: -Z unstable-options #![cfg_attr(future_feature, feature(explicit_extern_abis))] -#![cfg_attr(current_feature, feature(explicit_extern_abis))] extern fn _foo() {} //[current]~^ WARN `extern` declarations without an explicit ABI are deprecated diff --git a/tests/ui/float/classify-runtime-const.rs b/tests/ui/float/classify-runtime-const.rs index ca852ea2468bc..fb7361f9cbad5 100644 --- a/tests/ui/float/classify-runtime-const.rs +++ b/tests/ui/float/classify-runtime-const.rs @@ -6,8 +6,6 @@ // This tests the float classification functions, for regular runtime code and for const evaluation. -#![feature(f16)] -#![feature(f128)] use std::num::FpCategory::*; diff --git a/tests/ui/float/conv-bits-runtime-const.rs b/tests/ui/float/conv-bits-runtime-const.rs index 1373001b74dab..e7b306714bb85 100644 --- a/tests/ui/float/conv-bits-runtime-const.rs +++ b/tests/ui/float/conv-bits-runtime-const.rs @@ -3,9 +3,10 @@ // This tests the float classification functions, for regular runtime code and for const evaluation. -#![feature(f16)] -#![feature(f128)] #![feature(cfg_target_has_reliable_f16_f128)] +#![cfg_attr(target_has_reliable_f16, feature(f16))] +#![cfg_attr(target_has_reliable_f128, feature(f128))] + #![allow(unused_macro_rules)] // expect the unexpected (`target_has_reliable_*` are not "known" configs since they are unstable) #![expect(unexpected_cfgs)] diff --git a/tests/ui/fmt/format-macro-no-std.rs b/tests/ui/fmt/format-macro-no-std.rs index d096b4de01390..31c3a9a950668 100644 --- a/tests/ui/fmt/format-macro-no-std.rs +++ b/tests/ui/fmt/format-macro-no-std.rs @@ -4,7 +4,6 @@ //@ ignore-emscripten no no_std executables //@ ignore-wasm different `main` convention -#![feature(lang_items)] #![no_std] #![no_main] diff --git a/tests/ui/half-open-range-patterns/half-open-range-pats-semantics.rs b/tests/ui/half-open-range-patterns/half-open-range-pats-semantics.rs index df1b9e164c768..2f25f0078eecd 100644 --- a/tests/ui/half-open-range-patterns/half-open-range-pats-semantics.rs +++ b/tests/ui/half-open-range-patterns/half-open-range-pats-semantics.rs @@ -6,8 +6,8 @@ #![allow(unreachable_patterns)] #![feature(cfg_target_has_reliable_f16_f128)] -#![feature(f128)] -#![feature(f16)] +#![cfg_attr(target_has_reliable_f16, feature(f16))] +#![cfg_attr(target_has_reliable_f128, feature(f128))] macro_rules! yes { ($scrutinee:expr, $($t:tt)+) => { diff --git a/tests/ui/hygiene/xcrate.rs b/tests/ui/hygiene/xcrate.rs index 0dd9136e4d562..aa221cb5ad2a3 100644 --- a/tests/ui/hygiene/xcrate.rs +++ b/tests/ui/hygiene/xcrate.rs @@ -3,7 +3,6 @@ //@ aux-build:xcrate.rs -#![feature(decl_macro)] extern crate xcrate; diff --git a/tests/ui/impl-trait/example-calendar.rs b/tests/ui/impl-trait/example-calendar.rs index c3c01f0103669..972ee5f4b63b4 100644 --- a/tests/ui/impl-trait/example-calendar.rs +++ b/tests/ui/impl-trait/example-calendar.rs @@ -1,7 +1,6 @@ //@ run-pass -#![feature(fn_traits, - step_trait, +#![feature(step_trait, unboxed_closures, )] diff --git a/tests/ui/intrinsics/intrinsic-alignment.rs b/tests/ui/intrinsics/intrinsic-alignment.rs index 5fee6b0b39783..242eea1f8407d 100644 --- a/tests/ui/intrinsics/intrinsic-alignment.rs +++ b/tests/ui/intrinsics/intrinsic-alignment.rs @@ -1,6 +1,6 @@ //@ run-pass -#![feature(core_intrinsics, rustc_attrs)] +#![feature(rustc_attrs)] #[cfg(any( target_os = "aix", diff --git a/tests/ui/io-checks/write-macro-error.rs b/tests/ui/io-checks/write-macro-error.rs index 857ea0024e16c..90b9537b56da6 100644 --- a/tests/ui/io-checks/write-macro-error.rs +++ b/tests/ui/io-checks/write-macro-error.rs @@ -4,7 +4,6 @@ //@ run-pass //@ needs-unwind -#![feature(io_error_uncategorized)] use std::fmt; use std::io::{self, Error, Write}; diff --git a/tests/ui/linkage-attr/raw-dylib/elf/as_needed.rs b/tests/ui/linkage-attr/raw-dylib/elf/as_needed.rs index 48ca39300f41c..6f66e6c0f67fd 100644 --- a/tests/ui/linkage-attr/raw-dylib/elf/as_needed.rs +++ b/tests/ui/linkage-attr/raw-dylib/elf/as_needed.rs @@ -15,7 +15,7 @@ #![allow(incomplete_features)] #![feature(raw_dylib_elf)] -#![feature(native_link_modifiers_as_needed)] +#![cfg_attr(not(no_modifier), feature(native_link_modifiers_as_needed))] #[cfg_attr( as_needed, diff --git a/tests/ui/lint/lint-expr-stmt-attrs-for-early-lints.rs b/tests/ui/lint/lint-expr-stmt-attrs-for-early-lints.rs index eb110869e44e8..77ef6fab16cd1 100644 --- a/tests/ui/lint/lint-expr-stmt-attrs-for-early-lints.rs +++ b/tests/ui/lint/lint-expr-stmt-attrs-for-early-lints.rs @@ -1,6 +1,5 @@ //@ run-pass -#![feature(stmt_expr_attributes)] #![deny(unused_parens)] // Tests that lint attributes on statements/expressions are diff --git a/tests/ui/lint/lint-unknown-feature.rs b/tests/ui/lint/lint-unknown-feature.rs index 188617467974e..b598f0d0106b1 100644 --- a/tests/ui/lint/lint-unknown-feature.rs +++ b/tests/ui/lint/lint-unknown-feature.rs @@ -4,6 +4,5 @@ #![allow(stable_features)] // FIXME(#44232) we should warn that this isn't used. -#![feature(rust1)] fn main() {} diff --git a/tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.fixed b/tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.fixed index d554bbfcc98c9..a3d8b1d32a89c 100644 --- a/tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.fixed +++ b/tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.fixed @@ -2,7 +2,7 @@ //@ edition:2021 #![deny(unused_qualifications)] #![deny(unused_imports)] -#![feature(coroutines, coroutine_trait)] +#![feature(coroutine_trait)] use std::ops::{ Coroutine, diff --git a/tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.rs b/tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.rs index 4d79f5ab74530..2da45507e3e74 100644 --- a/tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.rs +++ b/tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.rs @@ -2,7 +2,7 @@ //@ edition:2021 #![deny(unused_qualifications)] #![deny(unused_imports)] -#![feature(coroutines, coroutine_trait)] +#![feature(coroutine_trait)] use std::ops::{ Coroutine, diff --git a/tests/ui/lint/unused/unused_attributes-must_use.fixed b/tests/ui/lint/unused/unused_attributes-must_use.fixed index fa596da95ccd1..082096f12080a 100644 --- a/tests/ui/lint/unused/unused_attributes-must_use.fixed +++ b/tests/ui/lint/unused/unused_attributes-must_use.fixed @@ -1,6 +1,6 @@ //@ run-rustfix -#![allow(dead_code, path_statements)] +#![allow(dead_code, path_statements, unused_features)] #![deny(unused_attributes, unused_must_use)] #![feature(asm_experimental_arch, stmt_expr_attributes, trait_alias)] diff --git a/tests/ui/lint/unused/unused_attributes-must_use.rs b/tests/ui/lint/unused/unused_attributes-must_use.rs index 3e72dd1e43833..b4b17476d0b1b 100644 --- a/tests/ui/lint/unused/unused_attributes-must_use.rs +++ b/tests/ui/lint/unused/unused_attributes-must_use.rs @@ -1,6 +1,6 @@ //@ run-rustfix -#![allow(dead_code, path_statements)] +#![allow(dead_code, path_statements, unused_features)] #![deny(unused_attributes, unused_must_use)] #![feature(asm_experimental_arch, stmt_expr_attributes, trait_alias)] diff --git a/tests/ui/lowering/issue-96847.rs b/tests/ui/lowering/issue-96847.rs index a1fd105d9dd4a..6ee40338043f6 100644 --- a/tests/ui/lowering/issue-96847.rs +++ b/tests/ui/lowering/issue-96847.rs @@ -3,7 +3,6 @@ // Test that this doesn't abort during AST lowering. In #96847 it did abort // because the attribute was being lowered twice. -#![feature(stmt_expr_attributes)] #![feature(lang_items)] fn main() { diff --git a/tests/ui/lowering/issue-96847.stderr b/tests/ui/lowering/issue-96847.stderr index 2cded32f9fb86..26bc6645ce6d4 100644 --- a/tests/ui/lowering/issue-96847.stderr +++ b/tests/ui/lowering/issue-96847.stderr @@ -1,5 +1,5 @@ error[E0522]: definition of an unknown lang item: `foo` - --> $DIR/issue-96847.rs:11:9 + --> $DIR/issue-96847.rs:10:9 | LL | #![lang="foo"] | ^^^^^^^^^^^^^^ definition of unknown lang item `foo` diff --git a/tests/ui/macros/metavar_cross_edition_recursive_macros.rs b/tests/ui/macros/metavar_cross_edition_recursive_macros.rs index ae1bc00236f0f..4382c00ab13cd 100644 --- a/tests/ui/macros/metavar_cross_edition_recursive_macros.rs +++ b/tests/ui/macros/metavar_cross_edition_recursive_macros.rs @@ -6,7 +6,6 @@ // This test captures the behavior of macro-generating-macros with fragment // specifiers across edition boundaries. -#![feature(macro_metavar_expr)] #![allow(incomplete_features)] extern crate metavar_2018; diff --git a/tests/ui/macros/rfc-2011-nicer-assert-messages/all-expr-kinds.rs b/tests/ui/macros/rfc-2011-nicer-assert-messages/all-expr-kinds.rs index de7dbb9052edb..b470cd622d03c 100644 --- a/tests/ui/macros/rfc-2011-nicer-assert-messages/all-expr-kinds.rs +++ b/tests/ui/macros/rfc-2011-nicer-assert-messages/all-expr-kinds.rs @@ -5,7 +5,7 @@ //@ needs-unwind Asserting on contents of error message #![allow(path_statements, unused_allocation)] -#![feature(core_intrinsics, generic_assert)] +#![feature(generic_assert)] macro_rules! test { ( diff --git a/tests/ui/macros/rfc-2011-nicer-assert-messages/all-not-available-cases.rs b/tests/ui/macros/rfc-2011-nicer-assert-messages/all-not-available-cases.rs index 1600fd0af3f30..2165037244dca 100644 --- a/tests/ui/macros/rfc-2011-nicer-assert-messages/all-not-available-cases.rs +++ b/tests/ui/macros/rfc-2011-nicer-assert-messages/all-not-available-cases.rs @@ -4,7 +4,7 @@ //@ run-pass //@ needs-unwind Asserting on contents of error message -#![feature(core_intrinsics, generic_assert)] +#![feature(generic_assert)] extern crate common; diff --git a/tests/ui/macros/rfc-2011-nicer-assert-messages/assert-with-custom-errors-does-not-create-unnecessary-code.rs b/tests/ui/macros/rfc-2011-nicer-assert-messages/assert-with-custom-errors-does-not-create-unnecessary-code.rs index 2a27164f9cba2..d75eb8b0d761b 100644 --- a/tests/ui/macros/rfc-2011-nicer-assert-messages/assert-with-custom-errors-does-not-create-unnecessary-code.rs +++ b/tests/ui/macros/rfc-2011-nicer-assert-messages/assert-with-custom-errors-does-not-create-unnecessary-code.rs @@ -3,7 +3,6 @@ //@ compile-flags: --test -Zpanic_abort_tests //@ run-pass -#![feature(core_intrinsics, generic_assert)] #[should_panic(expected = "Custom user message")] #[test] diff --git a/tests/ui/macros/rfc-2011-nicer-assert-messages/assert-without-captures-does-not-create-unnecessary-code.rs b/tests/ui/macros/rfc-2011-nicer-assert-messages/assert-without-captures-does-not-create-unnecessary-code.rs index 6e5f8d6cd12d4..be4456604f627 100644 --- a/tests/ui/macros/rfc-2011-nicer-assert-messages/assert-without-captures-does-not-create-unnecessary-code.rs +++ b/tests/ui/macros/rfc-2011-nicer-assert-messages/assert-without-captures-does-not-create-unnecessary-code.rs @@ -3,7 +3,7 @@ //@ run-pass //@ needs-unwind Asserting on contents of error message -#![feature(core_intrinsics, generic_assert)] +#![feature(generic_assert)] extern crate common; diff --git a/tests/ui/macros/rfc-2011-nicer-assert-messages/feature-gate-generic_assert.rs b/tests/ui/macros/rfc-2011-nicer-assert-messages/feature-gate-generic_assert.rs index 254d59076e531..a5ceb3d2ecd4c 100644 --- a/tests/ui/macros/rfc-2011-nicer-assert-messages/feature-gate-generic_assert.rs +++ b/tests/ui/macros/rfc-2011-nicer-assert-messages/feature-gate-generic_assert.rs @@ -4,7 +4,7 @@ // ignore-tidy-linelength //@ run-pass -#![feature(core_intrinsics, generic_assert)] +#![feature(generic_assert)] use std::fmt::{Debug, Formatter}; diff --git a/tests/ui/macros/stringify.rs b/tests/ui/macros/stringify.rs index 890b429b4ac7d..80c8d1cfe738d 100644 --- a/tests/ui/macros/stringify.rs +++ b/tests/ui/macros/stringify.rs @@ -9,12 +9,10 @@ #![feature(const_trait_impl)] #![feature(coroutines)] #![feature(decl_macro)] -#![feature(explicit_tail_calls)] #![feature(more_qualified_paths)] #![feature(never_patterns)] #![feature(trait_alias)] #![feature(try_blocks)] -#![feature(type_ascription)] #![feature(yeet_expr)] #![deny(unused_macros)] diff --git a/tests/ui/match/match-float.rs b/tests/ui/match/match-float.rs index 279bb5927ac45..504740add5371 100644 --- a/tests/ui/match/match-float.rs +++ b/tests/ui/match/match-float.rs @@ -3,8 +3,8 @@ // Makes sure we use `==` (not bitwise) semantics for float comparison. #![feature(cfg_target_has_reliable_f16_f128)] -#![feature(f128)] -#![feature(f16)] +#![cfg_attr(target_has_reliable_f16, feature(f16))] +#![cfg_attr(target_has_reliable_f128, feature(f128))] #[cfg(target_has_reliable_f16)] fn check_f16() { diff --git a/tests/ui/methods/supertrait-shadowing/out-of-scope.rs b/tests/ui/methods/supertrait-shadowing/out-of-scope.rs index bd263be59cc7d..8e0f5ba978e3a 100644 --- a/tests/ui/methods/supertrait-shadowing/out-of-scope.rs +++ b/tests/ui/methods/supertrait-shadowing/out-of-scope.rs @@ -1,7 +1,6 @@ //@ run-pass //@ check-run-results -#![feature(supertrait_item_shadowing)] #![allow(dead_code)] mod out_of_scope { diff --git a/tests/ui/nll/issue-48623-coroutine.rs b/tests/ui/nll/issue-48623-coroutine.rs index 63348a2047c07..57f579106b7a9 100644 --- a/tests/ui/nll/issue-48623-coroutine.rs +++ b/tests/ui/nll/issue-48623-coroutine.rs @@ -2,7 +2,7 @@ #![allow(path_statements)] #![allow(dead_code)] -#![feature(coroutines, coroutine_trait)] +#![feature(coroutines)] struct WithDrop; diff --git a/tests/ui/overloaded/overloaded-calls-simple.rs b/tests/ui/overloaded/overloaded-calls-simple.rs index 34b674357d89c..b53686aa94643 100644 --- a/tests/ui/overloaded/overloaded-calls-simple.rs +++ b/tests/ui/overloaded/overloaded-calls-simple.rs @@ -1,6 +1,6 @@ //@ run-pass -#![feature(lang_items, unboxed_closures, fn_traits)] +#![feature(unboxed_closures, fn_traits)] struct S1 { x: i32, diff --git a/tests/ui/sanitizer/memory-passing.rs b/tests/ui/sanitizer/memory-passing.rs index 6ac41b178fe7f..d41f62b310b05 100644 --- a/tests/ui/sanitizer/memory-passing.rs +++ b/tests/ui/sanitizer/memory-passing.rs @@ -13,7 +13,6 @@ // This test case intentionally limits the usage of the std, // since it will be linked with an uninstrumented version of it. -#![feature(core_intrinsics)] #![allow(invalid_value)] #![no_main] diff --git a/tests/ui/simd/monomorphize-shuffle-index.rs b/tests/ui/simd/monomorphize-shuffle-index.rs index ba952cdb0dc60..c86c2448cc683 100644 --- a/tests/ui/simd/monomorphize-shuffle-index.rs +++ b/tests/ui/simd/monomorphize-shuffle-index.rs @@ -4,12 +4,12 @@ //@ ignore-backends: gcc #![feature( repr_simd, - core_intrinsics, intrinsics, adt_const_params, unsized_const_params, generic_const_exprs )] +#![cfg_attr(old, feature(core_intrinsics))] #![allow(incomplete_features)] #[path = "../../auxiliary/minisimd.rs"] diff --git a/tests/ui/structs-enums/rec-align-u32.rs b/tests/ui/structs-enums/rec-align-u32.rs index 058f06732b927..b86cdebfe19ae 100644 --- a/tests/ui/structs-enums/rec-align-u32.rs +++ b/tests/ui/structs-enums/rec-align-u32.rs @@ -3,7 +3,7 @@ #![allow(unused_unsafe)] // Issue #2303 -#![feature(core_intrinsics, rustc_attrs)] +#![feature(rustc_attrs)] use std::mem; diff --git a/tests/ui/structs-enums/rec-align-u64.rs b/tests/ui/structs-enums/rec-align-u64.rs index 41b196dc5c222..e49726c7d438f 100644 --- a/tests/ui/structs-enums/rec-align-u64.rs +++ b/tests/ui/structs-enums/rec-align-u64.rs @@ -4,7 +4,7 @@ // Issue #2303 -#![feature(core_intrinsics, rustc_attrs)] +#![feature(rustc_attrs)] use std::mem; diff --git a/tests/ui/threads-sendsync/auxiliary/thread-local-extern-static.rs b/tests/ui/threads-sendsync/auxiliary/thread-local-extern-static.rs index 4d3c4e8accd88..4eda8fab1b63b 100644 --- a/tests/ui/threads-sendsync/auxiliary/thread-local-extern-static.rs +++ b/tests/ui/threads-sendsync/auxiliary/thread-local-extern-static.rs @@ -1,4 +1,5 @@ -#![feature(cfg_target_thread_local, thread_local)] +#![feature(cfg_target_thread_local)] +#![cfg_attr(target_thread_local, feature(thread_local))] #![crate_type = "lib"] #[cfg(target_thread_local)] diff --git a/tests/ui/threads-sendsync/thread-local-extern-static.rs b/tests/ui/threads-sendsync/thread-local-extern-static.rs index ce2a221ec2e41..0da4e398f335c 100644 --- a/tests/ui/threads-sendsync/thread-local-extern-static.rs +++ b/tests/ui/threads-sendsync/thread-local-extern-static.rs @@ -2,7 +2,8 @@ //@ ignore-windows FIXME(134939): thread_local + no_mangle doesn't work on Windows //@ aux-build:thread-local-extern-static.rs -#![feature(cfg_target_thread_local, thread_local)] +#![feature(cfg_target_thread_local)] +#![cfg_attr(target_thread_local, feature(thread_local))] #[cfg(target_thread_local)] extern crate thread_local_extern_static; diff --git a/tests/ui/traits/alias/import-cross-crate.rs b/tests/ui/traits/alias/import-cross-crate.rs index 65e7c90965b85..f77aa3cd05a3b 100644 --- a/tests/ui/traits/alias/import-cross-crate.rs +++ b/tests/ui/traits/alias/import-cross-crate.rs @@ -1,7 +1,6 @@ //@ run-pass //@ aux-build:greeter.rs -#![feature(trait_alias)] extern crate greeter; diff --git a/tests/ui/traits/overlap-permitted-for-marker-traits.rs b/tests/ui/traits/overlap-permitted-for-marker-traits.rs index c05e5fddae635..310516f4e4ace 100644 --- a/tests/ui/traits/overlap-permitted-for-marker-traits.rs +++ b/tests/ui/traits/overlap-permitted-for-marker-traits.rs @@ -4,7 +4,6 @@ // `MyMarker` if it is either `Debug` or `Display`. #![feature(marker_trait_attr)] -#![feature(negative_impls)] use std::fmt::{Debug, Display}; diff --git a/tests/ui/type/typeid-consistency.rs b/tests/ui/type/typeid-consistency.rs index 67ee1b6d839ab..fb22ae3ac3050 100644 --- a/tests/ui/type/typeid-consistency.rs +++ b/tests/ui/type/typeid-consistency.rs @@ -3,7 +3,6 @@ //@ run-pass #![allow(deprecated)] -#![feature(core_intrinsics)] //@ aux-build:typeid-consistency-aux1.rs //@ aux-build:typeid-consistency-aux2.rs diff --git a/tests/ui/typeck/type-name-intrinsic-usage-61894.rs b/tests/ui/typeck/type-name-intrinsic-usage-61894.rs index 8131bb273909d..f587e5fe53f97 100644 --- a/tests/ui/typeck/type-name-intrinsic-usage-61894.rs +++ b/tests/ui/typeck/type-name-intrinsic-usage-61894.rs @@ -1,7 +1,6 @@ // https://github.com/rust-lang/rust/issues/61894 //@ run-pass -#![feature(core_intrinsics)] use std::any::type_name; From b3434a216b860433acfb2bb4cbbbe6fee184502a Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Wed, 4 Mar 2026 05:13:41 +0000 Subject: [PATCH 39/46] Prepare for merging from rust-lang/rust This updates the rust-version file to d933cf483edf1605142ac6899ff32536c0ad8b22. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 3e265317484a6..90a48de0fcb00 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -ba1567989ee7774a1fb53aa680a8e4e8daa0f519 +d933cf483edf1605142ac6899ff32536c0ad8b22 From 91c7627c0f9df9624af546440bbbd0c5ba0cd268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Mon, 2 Mar 2026 09:08:05 +0100 Subject: [PATCH 40/46] Remove `cycle_fatal` query modifier This removes the `cycle_fatal` query modifier as it has no effect on its current users. The default `CycleErrorHandling::Error` mode does the same as `cycle_fatal` when the default impl of `FromCycleError` is used. The return types of queries using `cycle_fatal` however have no specialized `FromCycleError` impl. --- compiler/rustc_macros/src/query.rs | 9 --------- compiler/rustc_middle/src/queries.rs | 21 ++++---------------- compiler/rustc_middle/src/query/modifiers.rs | 5 ----- compiler/rustc_middle/src/query/plumbing.rs | 1 - compiler/rustc_query_impl/src/execution.rs | 4 ---- 5 files changed, 4 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 0a741d32ed61d..8f0bfed2035c8 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -144,7 +144,6 @@ struct QueryModifiers { arena_cache: Option, cache_on_disk_if: Option, cycle_delay_bug: Option, - cycle_fatal: Option, cycle_stash: Option, depth_limit: Option, desc: Desc, @@ -160,7 +159,6 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { let mut arena_cache = None; let mut cache_on_disk_if = None; let mut desc = None; - let mut cycle_fatal = None; let mut cycle_delay_bug = None; let mut cycle_stash = None; let mut no_hash = None; @@ -197,8 +195,6 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { try_insert!(cache_on_disk_if = CacheOnDiskIf { modifier, block }); } else if modifier == "arena_cache" { try_insert!(arena_cache = modifier); - } else if modifier == "cycle_fatal" { - try_insert!(cycle_fatal = modifier); } else if modifier == "cycle_delay_bug" { try_insert!(cycle_delay_bug = modifier); } else if modifier == "cycle_stash" { @@ -228,7 +224,6 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { arena_cache, cache_on_disk_if, desc, - cycle_fatal, cycle_delay_bug, cycle_stash, no_hash, @@ -248,7 +243,6 @@ fn make_modifiers_stream(query: &Query, modifiers: &QueryModifiers) -> proc_macr arena_cache, cache_on_disk_if, cycle_delay_bug, - cycle_fatal, cycle_stash, depth_limit, desc: _, @@ -266,8 +260,6 @@ fn make_modifiers_stream(query: &Query, modifiers: &QueryModifiers) -> proc_macr let cycle_error_handling = if cycle_delay_bug.is_some() { quote! { DelayBug } - } else if cycle_fatal.is_some() { - quote! { Fatal } } else if cycle_stash.is_some() { quote! { Stash } } else { @@ -407,7 +399,6 @@ fn add_to_analyzer_stream(query: &Query, analyzer_stream: &mut proc_macro2::Toke doc_link!( arena_cache, - cycle_fatal, cycle_delay_bug, cycle_stash, no_hash, diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 932d0855c6016..c5cdd7598a562 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -32,7 +32,6 @@ //! - `arena_cache`: Use an arena for in-memory caching of the query result. //! - `cache_on_disk_if { ... }`: Cache the query result to disk if the provided block evaluates to //! true. The query key identifier is available for use within the block, as is `tcx`. -//! - `cycle_fatal`: If a dependency cycle is detected, abort compilation with a fatal error. //! - `cycle_delay_bug`: If a dependency cycle is detected, emit a delayed bug instead of aborting immediately. //! - `cycle_stash`: If a dependency cycle is detected, stash the error for later handling. //! - `no_hash`: Do not hash the query result for incremental compilation; just mark as dirty if recomputed. @@ -149,11 +148,11 @@ use crate::{dep_graph, mir, thir}; // which memoizes and does dep-graph tracking, wrapping around the actual // `Providers` that the driver creates (using several `rustc_*` crates). // -// The result type of each query must implement `Clone`, and additionally -// `ty::query::from_cycle_error::FromCycleError`, which produces an appropriate +// The result type of each query must implement `Clone`. Additionally +// `ty::query::from_cycle_error::FromCycleError` can be implemented which produces an appropriate // placeholder (error) value if the query resulted in a query cycle. -// Queries marked with `cycle_fatal` do not need the latter implementation, -// as they will raise a fatal error on query cycles instead. +// Queries without a `FromCycleError` implementation will raise a fatal error on query +// cycles instead. rustc_queries! { /// Caches the expansion of a derive proc macro, e.g. `#[derive(Serialize)]`. /// The key is: @@ -587,7 +586,6 @@ rustc_queries! { } query is_panic_runtime(_: CrateNum) -> bool { - cycle_fatal desc { "checking if the crate is_panic_runtime" } separate_provide_extern } @@ -1318,7 +1316,6 @@ rustc_queries! { /// Return the set of (transitive) callees that may result in a recursive call to `key`, /// if we were able to walk all callees. query mir_callgraph_cyclic(key: LocalDefId) -> &'tcx Option> { - cycle_fatal arena_cache desc { "computing (transitive) callees of `{}` that may recurse", @@ -1329,7 +1326,6 @@ rustc_queries! { /// Obtain all the calls into other local functions query mir_inliner_callees(key: ty::InstanceKind<'tcx>) -> &'tcx [(DefId, GenericArgsRef<'tcx>)] { - cycle_fatal desc { "computing all local function calls in `{}`", tcx.def_path_str(key.def_id()), @@ -1824,31 +1820,26 @@ rustc_queries! { } query is_compiler_builtins(_: CrateNum) -> bool { - cycle_fatal desc { "checking if the crate is_compiler_builtins" } separate_provide_extern } query has_global_allocator(_: CrateNum) -> bool { // This query depends on untracked global state in CStore eval_always - cycle_fatal desc { "checking if the crate has_global_allocator" } separate_provide_extern } query has_alloc_error_handler(_: CrateNum) -> bool { // This query depends on untracked global state in CStore eval_always - cycle_fatal desc { "checking if the crate has_alloc_error_handler" } separate_provide_extern } query has_panic_handler(_: CrateNum) -> bool { - cycle_fatal desc { "checking if the crate has_panic_handler" } separate_provide_extern } query is_profiler_runtime(_: CrateNum) -> bool { - cycle_fatal desc { "checking if a crate is `#![profiler_runtime]`" } separate_provide_extern } @@ -1857,22 +1848,18 @@ rustc_queries! { cache_on_disk_if { true } } query required_panic_strategy(_: CrateNum) -> Option { - cycle_fatal desc { "getting a crate's required panic strategy" } separate_provide_extern } query panic_in_drop_strategy(_: CrateNum) -> PanicStrategy { - cycle_fatal desc { "getting a crate's configured panic-in-drop strategy" } separate_provide_extern } query is_no_builtins(_: CrateNum) -> bool { - cycle_fatal desc { "getting whether a crate has `#![no_builtins]`" } separate_provide_extern } query symbol_mangling_version(_: CrateNum) -> SymbolManglingVersion { - cycle_fatal desc { "getting a crate's symbol mangling version" } separate_provide_extern } diff --git a/compiler/rustc_middle/src/query/modifiers.rs b/compiler/rustc_middle/src/query/modifiers.rs index 44aaa6a3ce3d9..c305f65a9ef35 100644 --- a/compiler/rustc_middle/src/query/modifiers.rs +++ b/compiler/rustc_middle/src/query/modifiers.rs @@ -28,11 +28,6 @@ pub(crate) struct cache_on_disk_if; /// A cycle error results in a delay_bug call pub(crate) struct cycle_delay_bug; -/// # `cycle_fatal` query modifier -/// -/// A cycle error for this query aborting the compilation with a fatal error. -pub(crate) struct cycle_fatal; - /// # `cycle_stash` query modifier /// /// A cycle error results in a stashed cycle error that can be unstashed and canceled later diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 815c8a1baab61..ae348c668fb50 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -57,7 +57,6 @@ pub enum ActiveKeyStatus<'tcx> { #[derive(Copy, Clone)] pub enum CycleErrorHandling { Error, - Fatal, DelayBug, Stash, } diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index 323d40fdbaabb..34be511ad3121 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -131,10 +131,6 @@ fn mk_cycle<'tcx, C: QueryCache>( let guar = error.emit(); query.value_from_cycle_error(tcx, cycle_error, guar) } - CycleErrorHandling::Fatal => { - let guar = error.emit(); - guar.raise_fatal(); - } CycleErrorHandling::DelayBug => { let guar = error.delay_as_bug(); query.value_from_cycle_error(tcx, cycle_error, guar) From ee855c02aff1dfc5e609f2ff307885e8842f454c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 4 Mar 2026 11:32:19 +0100 Subject: [PATCH 41/46] Migrate `rustc_pattern_analysis` lint to `Diagnostic` --- compiler/rustc_pattern_analysis/src/errors.rs | 6 ++++-- compiler/rustc_pattern_analysis/src/lints.rs | 10 +++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_pattern_analysis/src/errors.rs b/compiler/rustc_pattern_analysis/src/errors.rs index b27e52f32bf27..25432d6ea5cdc 100644 --- a/compiler/rustc_pattern_analysis/src/errors.rs +++ b/compiler/rustc_pattern_analysis/src/errors.rs @@ -1,5 +1,5 @@ use rustc_errors::{Diag, EmissionGuarantee, Subdiagnostic}; -use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::Ty; use rustc_span::Span; @@ -131,10 +131,12 @@ pub(crate) struct NonExhaustiveOmittedPattern<'tcx> { pub uncovered: Uncovered, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("the lint level must be set on the whole match")] #[help("it no longer has any effect to set the lint level on an individual match arm")] pub(crate) struct NonExhaustiveOmittedPatternLintOnArm { + #[primary_span] + pub span: Span, #[label("remove this attribute")] pub lint_span: Span, #[suggestion( diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index 3da744dc8c029..5a8adf4841c40 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -92,17 +92,13 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>( let LevelAndSource { level, src, .. } = rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, arm.arm_data); if !matches!(level, rustc_session::lint::Level::Allow) { - let decorator = NonExhaustiveOmittedPatternLintOnArm { + rcx.tcx.dcx().emit_warn(NonExhaustiveOmittedPatternLintOnArm { + span: arm.pat.data().span, lint_span: src.span(), suggest_lint_on_match: rcx.whole_match_span.map(|span| span.shrink_to_lo()), lint_level: level.as_str(), lint_name: "non_exhaustive_omitted_patterns", - }; - - use rustc_errors::LintDiagnostic; - let mut err = rcx.tcx.dcx().struct_span_warn(arm.pat.data().span, ""); - decorator.decorate_lint(&mut err); - err.emit(); + }); } } } From c29a26e37d6a42b0a46fdecb1ababfc7b269c354 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 4 Mar 2026 12:03:39 +0100 Subject: [PATCH 42/46] Remove `LintDiagnostic` implementation on `rustc_middle::Deprecated` --- compiler/rustc_middle/src/middle/stability.rs | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index ff6baabbb29a0..628d358146846 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -4,7 +4,7 @@ use std::num::NonZero; use rustc_ast::NodeId; -use rustc_errors::{Applicability, Diag, EmissionGuarantee, LintBuffer, LintDiagnostic, msg}; +use rustc_errors::{Applicability, Diag, EmissionGuarantee, LintBuffer, msg}; use rustc_feature::GateIssue; use rustc_hir::attrs::{DeprecatedSince, Deprecation}; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -131,15 +131,8 @@ impl<'a, G: EmissionGuarantee> rustc_errors::Diagnostic<'a, G> for Deprecated { dcx: rustc_errors::DiagCtxtHandle<'a>, level: rustc_errors::Level, ) -> Diag<'a, G> { - let mut diag = Diag::new(dcx, level, ""); - self.decorate_lint(&mut diag); - diag - } -} - -impl<'a, G: EmissionGuarantee> LintDiagnostic<'a, G> for Deprecated { - fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>) { - diag.primary_message(match &self.since_kind { + let Self { sub, kind, path, note, since_kind } = self; + let mut diag = Diag::new(dcx, level, match &since_kind { DeprecatedSinceKind::InEffect => msg!( "use of deprecated {$kind} `{$path}`{$has_note -> [true] : {$note} @@ -160,21 +153,22 @@ impl<'a, G: EmissionGuarantee> LintDiagnostic<'a, G> for Deprecated { }" ) } - }); - diag.arg("kind", self.kind); - diag.arg("path", self.path); - if let DeprecatedSinceKind::InVersion(version) = self.since_kind { + }) + .with_arg("kind", kind) + .with_arg("path", path); + if let DeprecatedSinceKind::InVersion(version) = since_kind { diag.arg("version", version); } - if let Some(note) = self.note { + if let Some(note) = note { diag.arg("has_note", true); diag.arg("note", note); } else { diag.arg("has_note", false); } - if let Some(sub) = self.sub { + if let Some(sub) = sub { diag.subdiagnostic(sub); } + diag } } From 3d542091d1a85e2ae319ffa0dfbe134ec74d6091 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 4 Mar 2026 12:22:47 +0100 Subject: [PATCH 43/46] Remove unused `LintDiagnostic` trait --- compiler/rustc_errors/src/decorate_diag.rs | 2 +- compiler/rustc_errors/src/diagnostic.rs | 8 ----- compiler/rustc_errors/src/lib.rs | 2 +- .../diagnostic-derive-inline.rs | 29 +------------------ 4 files changed, 3 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_errors/src/decorate_diag.rs b/compiler/rustc_errors/src/decorate_diag.rs index 7e53b8195d5bc..a11082e296638 100644 --- a/compiler/rustc_errors/src/decorate_diag.rs +++ b/compiler/rustc_errors/src/decorate_diag.rs @@ -7,7 +7,7 @@ use rustc_lint_defs::{BuiltinLintDiag, Lint, LintId}; use crate::{Diag, DiagCtxtHandle, Diagnostic, Level}; -/// We can't implement `LintDiagnostic` for `BuiltinLintDiag`, because decorating some of its +/// We can't implement `Diagnostic` for `BuiltinLintDiag`, because decorating some of its /// variants requires types we don't have yet. So, handle that case separately. pub enum DecorateDiagCompat { Dynamic(Box FnOnce(DiagCtxtHandle<'a>, Level) -> Diag<'a, ()> + DynSend + 'static>), diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 888009bf9d4c5..f68e6000ddf9e 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -138,14 +138,6 @@ where fn add_to_diag(self, diag: &mut Diag<'_, G>); } -/// Trait implemented by lint types. This should not be implemented manually. Instead, use -/// `#[derive(LintDiagnostic)]` -- see [rustc_macros::LintDiagnostic]. -#[rustc_diagnostic_item = "LintDiagnostic"] -pub trait LintDiagnostic<'a, G: EmissionGuarantee> { - /// Decorate a lint with the information from this type. - fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>); -} - #[derive(Clone, Debug, Encodable, Decodable)] pub(crate) struct DiagLocation { file: Cow<'static, str>, diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 4ef4d58074639..743c90bd28843 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -40,7 +40,7 @@ pub use codes::*; pub use decorate_diag::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer}; pub use diagnostic::{ BugAbort, Diag, DiagInner, DiagStyledString, Diagnostic, EmissionGuarantee, FatalAbort, - LintDiagnostic, StringPart, Subdiag, Subdiagnostic, + StringPart, Subdiag, Subdiagnostic, }; pub use diagnostic_impls::{ DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter, diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs index 759a9412f7690..26d92126fe87a 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs @@ -20,7 +20,7 @@ use rustc_span::symbol::Ident; use rustc_span::Span; extern crate rustc_macros; -use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, Subdiagnostic}; extern crate rustc_middle; use rustc_middle::ty::Ty; @@ -495,18 +495,6 @@ struct LabelWithTrailingList { span: Span, } -#[derive(LintDiagnostic)] -#[diag("this is an example message")] -struct LintsGood {} - -#[derive(LintDiagnostic)] -#[diag("this is an example message")] -struct PrimarySpanOnLint { - #[primary_span] - //~^ ERROR `#[primary_span]` is not a valid attribute - span: Span, -} - #[derive(Diagnostic)] #[diag("this is an example message", code = E0123)] struct ErrorWithMultiSpan { @@ -542,13 +530,6 @@ struct WarnAttribute {} //~| ERROR cannot find attribute `lint` in this scope struct LintAttributeOnSessionDiag {} -#[derive(LintDiagnostic)] -#[lint("this is an example message", code = E0123)] -//~^ ERROR `#[lint(...)]` is not a valid attribute -//~| ERROR diagnostic message not specified -//~| ERROR cannot find attribute `lint` in this scope -struct LintAttributeOnLintDiag {} - #[derive(Diagnostic)] #[diag("this is an example message", code = E0123)] struct DuplicatedSuggestionCode { @@ -670,14 +651,6 @@ struct SubdiagnosticBadLitStr { note: Note, } -#[derive(LintDiagnostic)] -#[diag("this is an example message")] -struct SubdiagnosticEagerLint { - #[subdiagnostic(eager)] - //~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute - note: Note, -} - #[derive(Diagnostic)] #[diag("this is an example message")] struct SubdiagnosticEagerFormerlyCorrect { From ab960af49fdfe0dc8eeaeb776ad7005a565ce87b Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 4 Mar 2026 13:41:36 +0100 Subject: [PATCH 44/46] Remove `LintDiagnostic` derive proc-macro --- .../src/diagnostics/diagnostic.rs | 54 +------- .../src/diagnostics/diagnostic_builder.rs | 125 +++++++----------- compiler/rustc_macros/src/diagnostics/mod.rs | 36 +---- compiler/rustc_macros/src/lib.rs | 19 --- 4 files changed, 52 insertions(+), 182 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index dc8231e5f0b06..2f7c3cc6a46d2 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -4,7 +4,7 @@ use proc_macro2::TokenStream; use quote::quote; use synstructure::Structure; -use crate::diagnostics::diagnostic_builder::DiagnosticDeriveKind; +use crate::diagnostics::diagnostic_builder::each_variant; use crate::diagnostics::error::DiagnosticDeriveError; /// The central struct for constructing the `into_diag` method from an annotated struct. @@ -19,8 +19,7 @@ impl<'a> DiagnosticDerive<'a> { pub(crate) fn into_tokens(self) -> TokenStream { let DiagnosticDerive { mut structure } = self; - let kind = DiagnosticDeriveKind::Diagnostic; - let implementation = kind.each_variant(&mut structure, |mut builder, variant| { + let implementation = each_variant(&mut structure, |mut builder, variant| { let preamble = builder.preamble(variant); let body = builder.body(variant); @@ -64,52 +63,3 @@ impl<'a> DiagnosticDerive<'a> { }) } } - -/// The central struct for constructing the `decorate_lint` method from an annotated struct. -pub(crate) struct LintDiagnosticDerive<'a> { - structure: Structure<'a>, -} - -impl<'a> LintDiagnosticDerive<'a> { - pub(crate) fn new(structure: Structure<'a>) -> Self { - Self { structure } - } - - pub(crate) fn into_tokens(self) -> TokenStream { - let LintDiagnosticDerive { mut structure } = self; - let kind = DiagnosticDeriveKind::LintDiagnostic; - let implementation = kind.each_variant(&mut structure, |mut builder, variant| { - let preamble = builder.preamble(variant); - let body = builder.body(variant); - - let Some(message) = builder.primary_message() else { - return DiagnosticDeriveError::ErrorHandled.to_compile_error(); - }; - let message = message.diag_message(Some(variant)); - let primary_message = quote! { - diag.primary_message(#message); - }; - - let formatting_init = &builder.formatting_init; - quote! { - #primary_message - #preamble - #formatting_init - #body - diag - } - }); - - structure.gen_impl(quote! { - gen impl<'__a> rustc_errors::LintDiagnostic<'__a, ()> for @Self { - #[track_caller] - fn decorate_lint<'__b>( - self, - diag: &'__b mut rustc_errors::Diag<'__a, ()> - ) { - #implementation; - } - } - }) - } -} diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index de8ee42caf45c..e335037f2c4c9 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -18,20 +18,54 @@ use crate::diagnostics::utils::{ should_generate_arg, type_is_bool, type_is_unit, type_matches_path, }; -/// What kind of diagnostic is being derived - a fatal/error/warning or a lint? -#[derive(Clone, Copy, PartialEq, Eq)] -pub(crate) enum DiagnosticDeriveKind { - Diagnostic, - LintDiagnostic, +pub(crate) fn each_variant<'s, F>(structure: &mut Structure<'s>, f: F) -> TokenStream +where + F: for<'v> Fn(DiagnosticDeriveVariantBuilder, &VariantInfo<'v>) -> TokenStream, +{ + let ast = structure.ast(); + let span = ast.span().unwrap(); + match ast.data { + syn::Data::Struct(..) | syn::Data::Enum(..) => (), + syn::Data::Union(..) => { + span_err(span, "diagnostic derives can only be used on structs and enums").emit(); + } + } + + if matches!(ast.data, syn::Data::Enum(..)) { + for attr in &ast.attrs { + span_err(attr.span().unwrap(), "unsupported type attribute for diagnostic derive enum") + .emit(); + } + } + + structure.bind_with(|_| synstructure::BindStyle::Move); + let variants = structure.each_variant(|variant| { + let span = match structure.ast().data { + syn::Data::Struct(..) => span, + // There isn't a good way to get the span of the variant, so the variant's + // name will need to do. + _ => variant.ast().ident.span().unwrap(), + }; + let builder = DiagnosticDeriveVariantBuilder { + span, + field_map: build_field_mapping(variant), + formatting_init: TokenStream::new(), + message: None, + code: None, + }; + f(builder, variant) + }); + + quote! { + match self { + #variants + } + } } /// Tracks persistent information required for a specific variant when building up individual calls -/// to diagnostic methods for generated diagnostic derives - both `Diagnostic` for -/// fatal/errors/warnings and `LintDiagnostic` for lints. +/// to diagnostic methods for generated diagnostic derives. pub(crate) struct DiagnosticDeriveVariantBuilder { - /// The kind for the entire type. - pub kind: DiagnosticDeriveKind, - /// Initialization of format strings for code suggestions. pub formatting_init: TokenStream, @@ -51,60 +85,6 @@ pub(crate) struct DiagnosticDeriveVariantBuilder { pub code: SpannedOption<()>, } -impl DiagnosticDeriveKind { - /// Call `f` for the struct or for each variant of the enum, returning a `TokenStream` with the - /// tokens from `f` wrapped in an `match` expression. Emits errors for use of derive on unions - /// or attributes on the type itself when input is an enum. - pub(crate) fn each_variant<'s, F>(self, structure: &mut Structure<'s>, f: F) -> TokenStream - where - F: for<'v> Fn(DiagnosticDeriveVariantBuilder, &VariantInfo<'v>) -> TokenStream, - { - let ast = structure.ast(); - let span = ast.span().unwrap(); - match ast.data { - syn::Data::Struct(..) | syn::Data::Enum(..) => (), - syn::Data::Union(..) => { - span_err(span, "diagnostic derives can only be used on structs and enums").emit(); - } - } - - if matches!(ast.data, syn::Data::Enum(..)) { - for attr in &ast.attrs { - span_err( - attr.span().unwrap(), - "unsupported type attribute for diagnostic derive enum", - ) - .emit(); - } - } - - structure.bind_with(|_| synstructure::BindStyle::Move); - let variants = structure.each_variant(|variant| { - let span = match structure.ast().data { - syn::Data::Struct(..) => span, - // There isn't a good way to get the span of the variant, so the variant's - // name will need to do. - _ => variant.ast().ident.span().unwrap(), - }; - let builder = DiagnosticDeriveVariantBuilder { - kind: self, - span, - field_map: build_field_mapping(variant), - formatting_init: TokenStream::new(), - message: None, - code: None, - }; - f(builder, variant) - }); - - quote! { - match self { - #variants - } - } - } -} - impl DiagnosticDeriveVariantBuilder { pub(crate) fn primary_message(&self) -> Option<&Message> { match self.message.as_ref() { @@ -358,20 +338,11 @@ impl DiagnosticDeriveVariantBuilder { // `arg` call will not be generated. (Meta::Path(_), "skip_arg") => return Ok(quote! {}), (Meta::Path(_), "primary_span") => { - match self.kind { - DiagnosticDeriveKind::Diagnostic => { - report_error_if_not_applied_to_span(attr, &info)?; + report_error_if_not_applied_to_span(attr, &info)?; - return Ok(quote! { - diag.span(#binding); - }); - } - DiagnosticDeriveKind::LintDiagnostic => { - throw_invalid_attr!(attr, |diag| { - diag.help("the `primary_span` field attribute is not valid for lint diagnostics") - }) - } - } + return Ok(quote! { + diag.span(#binding); + }); } (Meta::Path(_), "subdiagnostic") => { return Ok(quote! { diag.subdiagnostic(#binding); }); diff --git a/compiler/rustc_macros/src/diagnostics/mod.rs b/compiler/rustc_macros/src/diagnostics/mod.rs index c0512e86bbcb4..a5828cd715a86 100644 --- a/compiler/rustc_macros/src/diagnostics/mod.rs +++ b/compiler/rustc_macros/src/diagnostics/mod.rs @@ -6,7 +6,7 @@ mod msg_macro; mod subdiagnostic; mod utils; -use diagnostic::{DiagnosticDerive, LintDiagnosticDerive}; +use diagnostic::DiagnosticDerive; pub(super) use msg_macro::msg_macro; use proc_macro2::TokenStream; use subdiagnostic::SubdiagnosticDerive; @@ -51,38 +51,6 @@ pub(super) fn diagnostic_derive(s: Structure<'_>) -> TokenStream { DiagnosticDerive::new(s).into_tokens() } -/// Implements `#[derive(LintDiagnostic)]`, which allows for lints to be specified as a struct, -/// independent from the actual lint emitting code. -/// -/// ```ignore (rust) -/// #[derive(LintDiagnostic)] -/// #[diag("unused attribute")] -/// pub(crate) struct UnusedAttribute { -/// #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] -/// pub this: Span, -/// #[note("attribute also specified here")] -/// pub other: Span, -/// #[warning( -/// "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" -/// )] -/// pub warning: bool, -/// } -/// ``` -/// -/// Then, later, to emit the error: -/// -/// ```ignore (rust) -/// cx.emit_span_lint(UNUSED_ATTRIBUTES, span, UnusedAttribute { -/// ... -/// }); -/// ``` -/// -/// See rustc dev guide for more examples on using the `#[derive(LintDiagnostic)]`: -/// -pub(super) fn lint_diagnostic_derive(s: Structure<'_>) -> TokenStream { - LintDiagnosticDerive::new(s).into_tokens() -} - /// Implements `#[derive(Subdiagnostic)]`, which allows for labels, notes, helps and /// suggestions to be specified as a structs or enums, independent from the actual diagnostics /// emitting code or diagnostic derives. @@ -99,7 +67,7 @@ pub(super) fn lint_diagnostic_derive(s: Structure<'_>) -> TokenStream { /// Then, later, use the subdiagnostic in a diagnostic: /// /// ```ignore (rust) -/// #[derive(LintDiagnostic)] +/// #[derive(Diagnostic)] /// #[diag("unused doc comment")] /// pub(crate) struct BuiltinUnusedDocComment<'a> { /// pub kind: &'a str, diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index d7f75e08bac82..44969908b3f41 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -196,25 +196,6 @@ decl_derive!( suggestion_hidden, suggestion_verbose)] => diagnostics::diagnostic_derive ); -decl_derive!( - [LintDiagnostic, attributes( - // struct attributes - diag, - help, - help_once, - note, - note_once, - warning, - // field attributes - skip_arg, - primary_span, - label, - subdiagnostic, - suggestion, - suggestion_short, - suggestion_hidden, - suggestion_verbose)] => diagnostics::lint_diagnostic_derive -); decl_derive!( [Subdiagnostic, attributes( // struct/variant attributes From 930bbe4b61e0a6fb991e666731a43cd19ac77ade Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 4 Mar 2026 13:59:38 +0100 Subject: [PATCH 45/46] Update `rustc-dev-guide` to remove mentions of `LintDiagnostic` --- .../src/diagnostics/diagnostic-structs.md | 13 ++++++------- .../rustc-dev-guide/src/diagnostics/translation.md | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-structs.md b/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-structs.md index a99f6a2849c32..a3a7a11258c8a 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-structs.md +++ b/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-structs.md @@ -1,6 +1,6 @@ # Diagnostic and subdiagnostic structs -rustc has three diagnostic traits that can be used to create diagnostics: -`Diagnostic`, `LintDiagnostic`, and `Subdiagnostic`. +rustc has two diagnostic traits that can be used to create diagnostics: +`Diagnostic` and `Subdiagnostic`. For simple diagnostics, derived impls can be used, e.g. `#[derive(Diagnostic)]`. They are only suitable for simple diagnostics that @@ -8,12 +8,12 @@ don't require much logic in deciding whether or not to add additional subdiagnos In cases where diagnostics require more complex or dynamic behavior, such as conditionally adding subdiagnostics, customizing the rendering logic, or selecting messages at runtime, you will need to manually implement -the corresponding trait (`Diagnostic`, `LintDiagnostic`, or `Subdiagnostic`). +the corresponding trait (`Diagnostic` or `Subdiagnostic`). This approach provides greater flexibility and is recommended for diagnostics that go beyond simple, static structures. Diagnostic can be translated into different languages. -## `#[derive(Diagnostic)]` and `#[derive(LintDiagnostic)]` +## `#[derive(Diagnostic)]` Consider the [definition][defn] of the "field already declared" diagnostic shown below: @@ -123,8 +123,8 @@ tcx.dcx().emit_err(FieldAlreadyDeclared { }); ``` -### Reference for `#[derive(Diagnostic)]` and `#[derive(LintDiagnostic)]` -`#[derive(Diagnostic)]` and `#[derive(LintDiagnostic)]` support the following attributes: +### Reference for `#[derive(Diagnostic)]` +`#[derive(Diagnostic)]` supports the following attributes: - `#[diag("message", code = "...")]` - _Applied to struct or enum variant._ @@ -171,7 +171,6 @@ tcx.dcx().emit_err(FieldAlreadyDeclared { - Adds the subdiagnostic represented by the subdiagnostic struct. - `#[primary_span]` (_Optional_) - _Applied to `Span` fields on `Subdiagnostic`s. - Not used for `LintDiagnostic`s._ - Indicates the primary span of the diagnostic. - `#[skip_arg]` (_Optional_) - _Applied to any field._ diff --git a/src/doc/rustc-dev-guide/src/diagnostics/translation.md b/src/doc/rustc-dev-guide/src/diagnostics/translation.md index 88526ec1c5c78..cf95727e2a673 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics/translation.md +++ b/src/doc/rustc-dev-guide/src/diagnostics/translation.md @@ -32,7 +32,7 @@ There are two ways of writing translatable diagnostics: deciding to emit subdiagnostics and can therefore be represented as diagnostic structs). See [the diagnostic and subdiagnostic structs documentation](./diagnostic-structs.md). 2. Using typed identifiers with `Diag` APIs (in - `Diagnostic` or `Subdiagnostic` or `LintDiagnostic` implementations). + `Diagnostic` or `Subdiagnostic` implementations). When adding or changing a translatable diagnostic, you don't need to worry about the translations. From 7b7682950d6808c844247999281921f15d5bbf0a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 4 Mar 2026 14:12:11 +0100 Subject: [PATCH 46/46] Remove mentions of `LintDiagnostic` --- compiler/rustc_middle/src/lint.rs | 4 ++-- compiler/rustc_pattern_analysis/src/errors.rs | 3 +-- .../ui/lint/decorate-ice/decorate-def-path-str-ice.rs | 11 +++++------ 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 7347fe6d9d5e0..223b47c9044be 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -492,8 +492,8 @@ pub fn lint_level( /// - [`TyCtxt::node_lint`] /// - `LintContext::opt_span_lint` /// -/// This function will replace `lint_level` once all `LintDiagnostic` items have been migrated to -/// `Diagnostic`. +/// This function will replace `lint_level` once all its callers have been replaced +/// with `diag_lint_level`. #[track_caller] pub fn diag_lint_level<'a, D: Diagnostic<'a, ()> + 'a>( sess: &'a Session, diff --git a/compiler/rustc_pattern_analysis/src/errors.rs b/compiler/rustc_pattern_analysis/src/errors.rs index 25432d6ea5cdc..514d1e2a97299 100644 --- a/compiler/rustc_pattern_analysis/src/errors.rs +++ b/compiler/rustc_pattern_analysis/src/errors.rs @@ -109,8 +109,7 @@ impl Subdiagnostic for GappedRange { fn add_to_diag(self, diag: &mut Diag<'_, G>) { let GappedRange { span, gap, first_range } = self; - // FIXME(mejrs) unfortunately `#[derive(LintDiagnostic)]` - // does not support `#[subdiagnostic(eager)]`... + // FIXME(mejrs) Use `#[subdiagnostic(eager)]` instead let message = format!( "this could appear to continue range `{first_range}`, but `{gap}` isn't matched by \ either of them" diff --git a/tests/ui/lint/decorate-ice/decorate-def-path-str-ice.rs b/tests/ui/lint/decorate-ice/decorate-def-path-str-ice.rs index 8116e36ed0d9a..3bb9882d348a3 100644 --- a/tests/ui/lint/decorate-ice/decorate-def-path-str-ice.rs +++ b/tests/ui/lint/decorate-ice/decorate-def-path-str-ice.rs @@ -3,17 +3,16 @@ // Previously, this test ICEs when the `unused_must_use` lint is suppressed via the combination of // `-A warnings` and `--cap-lints=warn`, because: // -// - Its lint diagnostic struct `UnusedDef` implements `LintDiagnostic` manually and in the impl +// - Its lint diagnostic struct `UnusedDef` implements `Diagnostic` manually and in the impl // `def_path_str` was called (which calls `trimmed_def_path`, which will produce a // `must_produce_diag` ICE if a trimmed def path is constructed but never emitted in a diagnostic // because it is expensive to compute). -// - A `LintDiagnostic` has a `decorate_lint` method which decorates a `Diag` with lint-specific -// information. This method is wrapped by a `decorate` closure in `TyCtxt` diagnostic emission -// machinery, and the `decorate` closure called as late as possible. -// - `decorate`'s invocation is delayed as late as possible until `lint_level` is called. +// - A `Diagnostic` has an `into_diag` method which generates a `Diag` with (potentially) +// lint-specific information. +// - The `into_diag` method is called as late as possible until `diag_lint_level` is called. // - If a lint's corresponding diagnostic is suppressed (to be effectively allow at the final // emission time) via `-A warnings` or `--cap-lints=allow` (or `-A warnings` + `--cap-lints=warn` -// like in this test case), `decorate` is still called and a diagnostic is still constructed -- +// like in this test case), `into_diag` is still called and a diagnostic is still constructed -- // but the diagnostic is never eventually emitted, triggering the aforementioned // `must_produce_diag` ICE due to use of `trimmed_def_path`. //