From d33b74b7bd4820703e192093a1fd9d18b318be3c Mon Sep 17 00:00:00 2001 From: Axel Rousselot Date: Mon, 6 Jul 2026 13:37:03 +0200 Subject: [PATCH 01/33] Add `desktop` method to `CommandExt` Adds the `desktop` method to the Windows `CommandExt` trait to enable setting the `lpDesktop` field of the STARTUPINFO passed to the Windows `CreateProcess` API call. --- library/std/src/os/windows/process.rs | 14 +++ library/std/src/sys/process/windows.rs | 11 +++ tests/ui/process/win-desktop.rs | 129 +++++++++++++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 tests/ui/process/win-desktop.rs diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index 3332714ae4bb7..ad22dd4820955 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -174,6 +174,15 @@ pub impl(self) trait CommandExt { #[stable(feature = "windows_process_extensions", since = "1.16.0")] fn creation_flags(&mut self, flags: u32) -> &mut process::Command; + /// Places the child process on the desktop named `desktop` by setting the + /// `lpDesktop` field of the [STARTUPINFO][1] passed to `CreateProcess`. + /// + /// The name may be a desktop or a `window-station\desktop` path. + /// + /// [1]: + #[unstable(feature = "windows_process_extensions_desktop", issue = "158852")] + fn desktop>(&mut self, desktop: S) -> &mut process::Command; + /// Sets the field `wShowWindow` of [STARTUPINFO][1] that is passed to `CreateProcess`. /// Allowed values are the ones listed in /// @@ -381,6 +390,11 @@ impl CommandExt for process::Command { self } + fn desktop>(&mut self, desktop: S) -> &mut process::Command { + self.as_inner_mut().desktop(desktop.as_ref()); + self + } + fn show_window(&mut self, cmd_show: u16) -> &mut process::Command { self.as_inner_mut().show_window(Some(cmd_show)); self diff --git a/library/std/src/sys/process/windows.rs b/library/std/src/sys/process/windows.rs index a747ef048d901..b6ecbc71cba85 100644 --- a/library/std/src/sys/process/windows.rs +++ b/library/std/src/sys/process/windows.rs @@ -162,6 +162,7 @@ pub struct Command { startupinfo_untrusted_source: bool, startupinfo_force_feedback: Option, inherit_handles: bool, + desktop: Option>, } pub enum Stdio { @@ -191,6 +192,7 @@ impl Command { startupinfo_untrusted_source: false, startupinfo_force_feedback: None, inherit_handles: true, + desktop: None, } } @@ -215,6 +217,7 @@ impl Command { pub fn creation_flags(&mut self, flags: u32) { self.flags = flags; } + pub fn show_window(&mut self, cmd_show: Option) { self.show_window = cmd_show; } @@ -239,6 +242,10 @@ impl Command { self.startupinfo_force_feedback = enabled; } + pub fn desktop(&mut self, desktop: &OsStr) { + self.desktop = Some(desktop.encode_wide().chain([0]).collect()); + } + pub fn get_program(&self) -> &OsStr { &self.program } @@ -391,6 +398,10 @@ impl Command { None => {} } + if let Some(desktop) = &mut self.desktop { + si.lpDesktop = desktop.as_mut_ptr(); + } + let si_ptr: *mut c::STARTUPINFOW; let mut si_ex; diff --git a/tests/ui/process/win-desktop.rs b/tests/ui/process/win-desktop.rs new file mode 100644 index 0000000000000..f5ffed42212c7 --- /dev/null +++ b/tests/ui/process/win-desktop.rs @@ -0,0 +1,129 @@ +// Tests `desktop` by creating a new desktop, spawning a child process onto it +// and checking that the child reports back the expected desktop name. + +//@ run-pass +//@ only-windows +//@ needs-subprocess +//@ edition: 2024 + +#![feature(windows_process_extensions_desktop)] + +use std::os::windows::process::CommandExt; +use std::process::{Command, Stdio}; +use std::{env, io, process}; + +fn main() { + if env::args().skip(1).any(|s| s == "--child") { + child(); + } else { + parent(); + } +} + +fn parent() { + let exe = env::current_exe().unwrap(); + + // Create a uniquely named desktop on the current window station and keep the + // handle alive so the desktop is not destroyed while the child runs. + let desktop_name = format!("rust-test-desktop-{}", process::id()); + let desktop_name_wide: Vec = desktop_name.encode_utf16().chain([0]).collect(); + let hdesk = unsafe { + CreateDesktopW( + desktop_name_wide.as_ptr(), + core::ptr::null(), + core::ptr::null(), + 0, + GENERIC_ALL, + core::ptr::null(), + ) + }; + assert!(!hdesk.is_null(), "CreateDesktopW failed: {:?}", io::Error::last_os_error()); + + // Spawning with `.desktop` should place the child on our new desktop. + let output = Command::new(&exe) + .arg("--child") + .desktop(&desktop_name) + .stdout(Stdio::piped()) + .output() + .unwrap(); + assert!(output.status.success(), "child failed: {:?}", output); + let reported = String::from_utf8(output.stdout).unwrap(); + assert!( + reported.trim().eq_ignore_ascii_case(&desktop_name), + "child ran on unexpected desktop: expected {:?}, got {:?}", + desktop_name, + reported.trim(), + ); + + // Without `.desktop` the child inherits the parent's desktop, which is + // not the one we just created. + let output = Command::new(&exe).arg("--child").stdout(Stdio::piped()).output().unwrap(); + assert!(output.status.success(), "child failed: {:?}", output); + let reported = String::from_utf8(output.stdout).unwrap(); + assert!( + !reported.trim().eq_ignore_ascii_case(&desktop_name), + "child unexpectedly ran on the created desktop {:?} without being asked to", + desktop_name, + ); + + unsafe { CloseDesktop(hdesk) }; +} + +/// Prints the name of the desktop the current process is running on. +fn child() { + let hdesk = unsafe { GetThreadDesktop(GetCurrentThreadId()) }; + assert!(!hdesk.is_null(), "GetThreadDesktop failed: {:?}", io::Error::last_os_error()); + + let mut buffer = [0u16; 256]; + let mut needed = 0u32; + let ret = unsafe { + GetUserObjectInformationW( + hdesk, + UOI_NAME, + buffer.as_mut_ptr().cast(), + size_of_val(&buffer) as u32, + &raw mut needed, + ) + }; + assert_ne!(ret, 0, "GetUserObjectInformationW failed: {:?}", io::Error::last_os_error()); + + let len = buffer.iter().position(|&c| c == 0).unwrap_or(buffer.len()); + let name = String::from_utf16(&buffer[..len]).unwrap(); + print!("{name}"); +} + +// Windows API +mod winapi { + use std::ffi::c_void; + use std::os::windows::raw::HANDLE; + + pub const GENERIC_ALL: u32 = 0x10000000; + pub const UOI_NAME: i32 = 2; + + #[link(name = "user32")] + unsafe extern "system" { + pub fn CreateDesktopW( + lpszDesktop: *const u16, + lpszDevice: *const u16, + pDevmode: *const c_void, + dwFlags: u32, + dwDesiredAccess: u32, + lpsa: *const c_void, + ) -> HANDLE; + pub fn CloseDesktop(hDesktop: HANDLE) -> i32; + pub fn GetThreadDesktop(dwThreadId: u32) -> HANDLE; + pub fn GetUserObjectInformationW( + hObj: HANDLE, + nIndex: i32, + pvInfo: *mut c_void, + nLength: u32, + lpnLengthNeeded: *mut u32, + ) -> i32; + } + + #[link(name = "kernel32")] + unsafe extern "system" { + pub fn GetCurrentThreadId() -> u32; + } +} +use winapi::*; From c266bb2f5307864efd38eea45343e0c650bc4470 Mon Sep 17 00:00:00 2001 From: Sa4dUs Date: Wed, 17 Jun 2026 16:19:15 +0200 Subject: [PATCH 02/33] get number of devices intrinisc --- compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs | 10 ++++++++++ compiler/rustc_codegen_llvm/src/intrinsic.rs | 9 ++++++++- compiler/rustc_codegen_ssa/src/mir/intrinsic.rs | 1 + compiler/rustc_hir_analysis/src/check/intrinsic.rs | 7 +++++++ compiler/rustc_span/src/symbol.rs | 1 + library/core/src/intrinsics/mod.rs | 5 +++++ 6 files changed, 32 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs index 3d0bb6fcc48fd..5fc4f71f951ad 100644 --- a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs +++ b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs @@ -197,6 +197,16 @@ fn generate_launcher<'ll>(cx: &CodegenCx<'ll, '_>) -> (&'ll llvm::Value, &'ll ll (tgt_decl, tgt_fn_ty) } +pub(crate) fn generate_decl<'ll>(cx: &CodegenCx<'ll, '_>) -> (&'ll llvm::Value, &'ll llvm::Type) { + let ti32 = cx.type_i32(); + let tgt_fn_ty = cx.type_func(&[], ti32); + let name = "omp_get_num_devices"; + let tgt_decl = declare_offload_fn(&cx, name, tgt_fn_ty); + let nounwind = llvm::AttributeKind::NoUnwind.create_attr(cx.llcx); + attributes::apply_to_llfn(tgt_decl, Function, &[nounwind]); + (tgt_decl, tgt_fn_ty) +} + // What is our @1 here? A magic global, used in our data_{begin/update/end}_mapper: // @0 = private unnamed_addr constant [23 x i8] c";unknown;unknown;0;0;;\00", align 1 // @1 = private unnamed_addr constant %struct.ident_t { i32 0, i32 2, i32 0, i32 22, ptr @0 }, align 8 diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index ba11ef29fb536..da230a6da9ac2 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -37,7 +37,7 @@ use crate::abi::FnAbiLlvmExt; use crate::builder::Builder; use crate::builder::autodiff::{adjust_activity_to_abi, generate_enzyme_call}; use crate::builder::gpu_offload::{ - OffloadKernelDims, gen_call_handling, gen_define_handling, register_offload, + OffloadKernelDims, gen_call_handling, gen_define_handling, register_offload, generate_decl, }; use crate::context::CodegenCx; use crate::declare::declare_raw_fn; @@ -241,6 +241,13 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { // offload *has* a return type, but somehow works without mentioning the place return IntrinsicResult::WroteIntoPlace; } + sym::offload_get_num_devices => { + let (fn_decl, fn_ty) = generate_decl(self.cx); + + let llval = self.call(fn_ty, None, None, fn_decl, &[], None, None); + + return IntrinsicResult::Operand(OperandValue::Immediate(llval)); + }, sym::is_val_statically_known => { if let OperandValue::Immediate(imm) = args[0].val { self.call_intrinsic( diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 77ab3bdce4689..23285a043af70 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -135,6 +135,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | sym::atomic_fence | sym::atomic_singlethreadfence | sym::caller_location + | sym::offload_get_num_devices | sym::return_address => {} _ => { span_bug!( diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 11d21744bd7ae..c96cd0a8c8a5f 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -168,6 +168,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::needs_drop | sym::non_exhaustive | sym::offload + | sym::offload_get_num_devices | sym::offset_of | sym::overflow_checks | sym::powf16 @@ -384,6 +385,12 @@ pub(crate) fn check_intrinsic_type( ], param(2), ), + sym::offload_get_num_devices => ( + 0, + 0, + vec![], + tcx.types.i32, + ), sym::offset => (2, 0, vec![param(0), param(1)], param(0)), sym::arith_offset => ( 1, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 45df107bf7469..b678f63495b82 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1468,6 +1468,7 @@ symbols! { of, off, offload, + offload_get_num_devices, offload_kernel, offset, offset_of, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 4e67199bba8c9..eefb1b9ed7bd8 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -3814,6 +3814,11 @@ pub const fn offload( args: T, ) -> R; +// TODO(Sa4dUs): add docs +#[rustc_nounwind] +#[rustc_intrinsic] +pub const fn offload_get_num_devices() -> i32; + /// Inform Miri that a given pointer definitely has a certain alignment. #[cfg(miri)] #[rustc_allow_const_fn_unstable(const_eval_select)] From efbb356b50c0d7e8c743ad1fbb9f4227b050b6fb Mon Sep 17 00:00:00 2001 From: Marcelo Dominguez Date: Thu, 13 Aug 2026 12:35:46 +0300 Subject: [PATCH 03/33] Add device arg to offload intrinisc --- .../src/builder/gpu_offload.rs | 12 +++-------- compiler/rustc_codegen_llvm/src/intrinsic.rs | 9 ++++++-- .../rustc_hir_analysis/src/check/intrinsic.rs | 8 ++----- library/core/src/intrinsics/mod.rs | 12 +++++++++-- .../codegen-llvm/gpu_offload/control_flow.rs | 1 + tests/codegen-llvm/gpu_offload/gpu_host.rs | 16 +++++++------- tests/codegen-llvm/gpu_offload/scalar_host.rs | 9 +++++++- .../codegen-llvm/gpu_offload/slice_device.rs | 2 +- tests/codegen-llvm/gpu_offload/slice_host.rs | 9 +++++++- tests/ui/offload/check_config.rs | 2 +- tests/ui/offload/duplicate_kernel.rs | 2 +- tests/ui/offload/non_tuple_args.rs | 2 +- tests/ui/offload/non_tuple_args.stderr | 2 +- tests/ui/offload/type_mismatch.rs | 21 ++++++++++++------- tests/ui/offload/type_mismatch.stderr | 12 +++++------ 15 files changed, 73 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs index 5fc4f71f951ad..4f8d918c72b51 100644 --- a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs +++ b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs @@ -601,6 +601,7 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>( offload_globals: &OffloadGlobals<'ll>, offload_dims: &OffloadKernelDims<'ll>, dyn_cache: &'ll Value, + device_id: &'ll Value, ) { let cx = builder.cx; let OffloadKernelGlobals { @@ -785,15 +786,8 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>( builder.store(value.2, ptr, value.0); } - let args = vec![ - s_ident_t, - // FIXME(offload) give users a way to select which GPU to use. - cx.get_const_i64(u64::MAX), // MAX == -1. - num_workgroups, - threads_per_block, - region_id, - a5, - ]; + let device_id = builder.sext(device_id, cx.type_i64()); + let args = vec![s_ident_t, device_id, num_workgroups, threads_per_block, region_id, a5]; builder.call(tgt_target_kernel_ty, None, None, tgt_decl, &args, None, None); // %41 = call i32 @__tgt_target_kernel(ptr @1, i64 -1, i32 2097152, i32 256, ptr @.kernel_1.region_id, ptr %kernel_args) diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index da230a6da9ac2..fbd9a04ab2687 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -37,7 +37,7 @@ use crate::abi::FnAbiLlvmExt; use crate::builder::Builder; use crate::builder::autodiff::{adjust_activity_to_abi, generate_enzyme_call}; use crate::builder::gpu_offload::{ - OffloadKernelDims, gen_call_handling, gen_define_handling, register_offload, generate_decl, + OffloadKernelDims, gen_call_handling, gen_define_handling, generate_decl, register_offload, }; use crate::context::CodegenCx; use crate::declare::declare_raw_fn; @@ -1858,7 +1858,11 @@ fn codegen_offload<'ll, 'tcx>( OperandValue::Immediate(val) => val, _ => panic!("unparsable"), }; - let args = get_args_from_tuple(bx, args[4], fn_target); + let device_id = match args[4].val { + OperandValue::Immediate(val) => val, + _ => panic!("unparsable"), + }; + let args = get_args_from_tuple(bx, args[5], fn_target); let target_symbol = mangle_offload_export(tcx, fn_target); let sig = tcx.fn_sig(fn_target.def_id()).instantiate(tcx, fn_target.args).skip_norm_wip(); @@ -1899,6 +1903,7 @@ fn codegen_offload<'ll, 'tcx>( offload_globals, &offload_dims, &dyn_cache, + &device_id, ); } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index c96cd0a8c8a5f..39e68b7541d08 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -381,16 +381,12 @@ pub(crate) fn check_intrinsic_type( Ty::new_array_with_const_len(tcx, tcx.types.u32, Const::from_target_usize(tcx, 3)), Ty::new_array_with_const_len(tcx, tcx.types.u32, Const::from_target_usize(tcx, 3)), tcx.types.u32, + tcx.types.i32, param(1), ], param(2), ), - sym::offload_get_num_devices => ( - 0, - 0, - vec![], - tcx.types.i32, - ), + sym::offload_get_num_devices => (0, 0, vec![], tcx.types.i32), sym::offset => (2, 0, vec![param(0), param(1)], param(0)), sym::arith_offset => ( 1, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index eefb1b9ed7bd8..c26c007a254e5 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -3781,13 +3781,15 @@ pub const fn autodiff(f: F, df: G, args: T) -> /// - `f`: The kernel function to offload. /// - `workgroup_dim`: A 3D size specifying the number of workgroups to launch. /// - `thread_dim`: A 3D size specifying the number of threads per workgroup. +/// - `dyn_cache`: The amount of dynamic shared memory to request for the kernel. +/// - `device_id`: The device to offload to. Use `-1` to select the default device. /// - `args`: A tuple of arguments forwarded to `f`. /// /// Example usage (pseudocode): /// /// ```rust,ignore (pseudocode) /// fn kernel(x: *mut [f64; 128]) { -/// core::intrinsics::offload(kernel_1, [256, 1, 1], [32, 1, 1], (x,)) +/// core::intrinsics::offload(kernel_1, [256, 1, 1], [32, 1, 1], 0, -1, (x,)) /// } /// /// #[cfg(target_os = "linux")] @@ -3811,10 +3813,16 @@ pub const fn offload( workgroup_dim: [u32; 3], thread_dim: [u32; 3], dyn_cache: u32, + device_id: i32, args: T, ) -> R; -// TODO(Sa4dUs): add docs +/// Returns the number of offload devices available on the system. +/// +/// Use this to discover which `device_id` values are valid to pass to +/// [`offload`]. Devices are numbered from `0` to the returned value minus one. +/// +/// Returns `0` if no offloading devices are present. #[rustc_nounwind] #[rustc_intrinsic] pub const fn offload_get_num_devices() -> i32; diff --git a/tests/codegen-llvm/gpu_offload/control_flow.rs b/tests/codegen-llvm/gpu_offload/control_flow.rs index da997de53a428..ef4881147e63b 100644 --- a/tests/codegen-llvm/gpu_offload/control_flow.rs +++ b/tests/codegen-llvm/gpu_offload/control_flow.rs @@ -33,6 +33,7 @@ unsafe fn main() { [256, 1, 1], [32, 1, 1], 0, + -1, (A.as_ptr() as *const [f32; 6],), ); } diff --git a/tests/codegen-llvm/gpu_offload/gpu_host.rs b/tests/codegen-llvm/gpu_offload/gpu_host.rs index 2bfaf89b45590..208417b98bcae 100644 --- a/tests/codegen-llvm/gpu_offload/gpu_host.rs +++ b/tests/codegen-llvm/gpu_offload/gpu_host.rs @@ -21,7 +21,7 @@ fn main() { } pub fn kernel_1(x: &mut [f32; 256], y: &[f32; 256]) { - core::intrinsics::offload(_kernel_1, [256, 1, 1], [32, 1, 1], 0, (x, y)) + core::intrinsics::offload(_kernel_1, [256, 1, 1], [32, 1, 1], 0, -1, (x, y)) } #[inline(never)] @@ -78,8 +78,10 @@ pub fn _kernel_1(x: &mut [f32; 256], y: &[f32; 256]) { // CHECK-NEXT: [[P32:%[^ ]+]] = getelementptr inbounds nuw i8, ptr %kernel_args, i64 32 // CHECK-NEXT: store ptr @.offload_maptypes.[[K]].kernel, ptr [[P32]], align 8 // CHECK-NEXT: [[P40:%[^ ]+]] = getelementptr inbounds nuw i8, ptr %kernel_args, i64 40 +// CHECK-NEXT: [[P64:%[^ ]+]] = getelementptr inbounds nuw i8, ptr %kernel_args, i64 64 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(24) [[P40]], i8 0, i64 24, i1 false) +// CHECK-NEXT: store i64 64, ptr [[P64]], align 8 // CHECK-NEXT: [[P72:%[^ ]+]] = getelementptr inbounds nuw i8, ptr %kernel_args, i64 72 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(32) [[P40]], i8 0, i64 32, i1 false) // CHECK-NEXT: store <4 x i32> , ptr [[P72]], align 8 // CHECK-NEXT: [[P88:%[^ ]+]] = getelementptr inbounds nuw i8, ptr %kernel_args, i64 88 // CHECK-NEXT: store i32 1, ptr [[P88]], align 8 @@ -95,17 +97,17 @@ pub fn _kernel_1(x: &mut [f32; 256], y: &[f32; 256]) { // CHECK: declare void @__tgt_register_lib(ptr) local_unnamed_addr // CHECK: declare void @__tgt_unregister_lib(ptr) local_unnamed_addr -// CHECK-LABEL: define internal void @.omp_offloading.descriptor_reg() section ".text.startup" { +// CHECK-LABEL: define internal void @.omp_offloading.descriptor_reg() section ".text.startup" !guid !{{[0-9]+}} { // CHECK-NEXT: entry: -// CHECK-NEXT: call void @__tgt_register_lib(ptr nonnull @.omp_offloading.descriptor) -// CHECK-NEXT: call void @__tgt_init_all_rtls() +// CHECK-NEXT: {{tail }}call void @__tgt_register_lib(ptr nonnull @.omp_offloading.descriptor) +// CHECK-NEXT: {{tail }}call void @__tgt_init_all_rtls() // CHECK-NEXT: %0 = {{tail }}call i32 @atexit(ptr nonnull @.omp_offloading.descriptor_unreg) // CHECK-NEXT: ret void // CHECK-NEXT: } -// CHECK-LABEL: define internal void @.omp_offloading.descriptor_unreg() section ".text.startup" { +// CHECK-LABEL: define internal void @.omp_offloading.descriptor_unreg() section ".text.startup" !guid !{{[0-9]+}} { // CHECK-NEXT: entry: -// CHECK-NEXT: call void @__tgt_unregister_lib(ptr nonnull @.omp_offloading.descriptor) +// CHECK-NEXT: {{tail }}call void @__tgt_unregister_lib(ptr nonnull @.omp_offloading.descriptor) // CHECK-NEXT: ret void // CHECK-NEXT: } diff --git a/tests/codegen-llvm/gpu_offload/scalar_host.rs b/tests/codegen-llvm/gpu_offload/scalar_host.rs index 66c910c439e46..950306c83a43f 100644 --- a/tests/codegen-llvm/gpu_offload/scalar_host.rs +++ b/tests/codegen-llvm/gpu_offload/scalar_host.rs @@ -28,7 +28,14 @@ fn main() { let mut x = 0.0f32; let k = core::hint::black_box(42.0f32); - core::intrinsics::offload::<_, _, ()>(foo, [1, 1, 1], [1, 1, 1], 0, (&mut x as *mut f32, k)); + core::intrinsics::offload::<_, _, ()>( + foo, + [1, 1, 1], + [1, 1, 1], + 0, + -1, + (&mut x as *mut f32, k), + ); } unsafe extern "C" { diff --git a/tests/codegen-llvm/gpu_offload/slice_device.rs b/tests/codegen-llvm/gpu_offload/slice_device.rs index 1abe04f8cc429..6e900c21ca7cb 100644 --- a/tests/codegen-llvm/gpu_offload/slice_device.rs +++ b/tests/codegen-llvm/gpu_offload/slice_device.rs @@ -15,7 +15,7 @@ extern crate minicore; // CHECK: ; Function Attrs // nvptx-NEXT: define ptx_kernel void @foo // amdgpu-NEXT: define amdgpu_kernel void @foo -// CHECK-SAME: ptr readnone captures(none) %dyn_ptr +// CHECK-SAME: ptr nofree readnone captures(none) %dyn_ptr // nvptx-SAME: [2 x i64] %0 // amdgpu-SAME: ptr noalias {{.*}} %0, i64 {{.*}} %1 // CHECK-NEXT: entry: diff --git a/tests/codegen-llvm/gpu_offload/slice_host.rs b/tests/codegen-llvm/gpu_offload/slice_host.rs index dfc7ec545630c..1b314d489612c 100644 --- a/tests/codegen-llvm/gpu_offload/slice_host.rs +++ b/tests/codegen-llvm/gpu_offload/slice_host.rs @@ -27,7 +27,14 @@ #[unsafe(no_mangle)] fn main() { let mut x = [0.0f32, 0.0, 0.0, 0.0]; - core::intrinsics::offload::<_, _, ()>(foo, [1, 1, 1], [1, 1, 1], 0, ((&mut x) as &mut [f32],)); + core::intrinsics::offload::<_, _, ()>( + foo, + [1, 1, 1], + [1, 1, 1], + 0, + -1, + ((&mut x) as &mut [f32],), + ); } unsafe extern "C" { diff --git a/tests/ui/offload/check_config.rs b/tests/ui/offload/check_config.rs index ff145f420e482..833ba39dffeca 100644 --- a/tests/ui/offload/check_config.rs +++ b/tests/ui/offload/check_config.rs @@ -17,7 +17,7 @@ fn main() { } fn kernel_1(x: &mut [f32; 256]) { - core::intrinsics::offload(_kernel_1, [1, 1, 1], [1, 1, 1], 0, (x,)) + core::intrinsics::offload(_kernel_1, [1, 1, 1], [1, 1, 1], 0, -1, (x,)) } fn _kernel_1(x: &mut [f32; 256]) {} diff --git a/tests/ui/offload/duplicate_kernel.rs b/tests/ui/offload/duplicate_kernel.rs index abde76137a37c..da667a0c0666a 100644 --- a/tests/ui/offload/duplicate_kernel.rs +++ b/tests/ui/offload/duplicate_kernel.rs @@ -18,5 +18,5 @@ fn kernel(_x: f32) {} fn main() { _RNvC19collision_kernels_a6kernel(0.0); - core::intrinsics::offload::<_, _, ()>(kernel, [1, 1, 1], [1, 1, 1], 0, (0.0f32,)); + core::intrinsics::offload::<_, _, ()>(kernel, [1, 1, 1], [1, 1, 1], 0, -1, (0.0f32,)); } diff --git a/tests/ui/offload/non_tuple_args.rs b/tests/ui/offload/non_tuple_args.rs index 0a07c99a26d34..14de21b2374a2 100644 --- a/tests/ui/offload/non_tuple_args.rs +++ b/tests/ui/offload/non_tuple_args.rs @@ -4,7 +4,7 @@ fn main() { // args_ty is not a tuple - core::intrinsics::offload::<_, _, ()>(kernel_0, [1, 1, 1], [1, 1, 1], 0, 42); + core::intrinsics::offload::<_, _, ()>(kernel_0, [1, 1, 1], [1, 1, 1], 0, -1, 42); //~^ ERROR `{integer}` is not a tuple } diff --git a/tests/ui/offload/non_tuple_args.stderr b/tests/ui/offload/non_tuple_args.stderr index 8b59d6828c6f2..90b0f16bec53e 100644 --- a/tests/ui/offload/non_tuple_args.stderr +++ b/tests/ui/offload/non_tuple_args.stderr @@ -1,7 +1,7 @@ error[E0277]: `{integer}` is not a tuple --> $DIR/non_tuple_args.rs:7:36 | -LL | core::intrinsics::offload::<_, _, ()>(kernel_0, [1, 1, 1], [1, 1, 1], 0, 42); +LL | core::intrinsics::offload::<_, _, ()>(kernel_0, [1, 1, 1], [1, 1, 1], 0, -1, 42); | ^ the nightly-only, unstable trait `std::marker::Tuple` is not implemented for `{integer}` | note: required by a bound in `offload` diff --git a/tests/ui/offload/type_mismatch.rs b/tests/ui/offload/type_mismatch.rs index 4079444a0aff1..a75f8358b7359 100644 --- a/tests/ui/offload/type_mismatch.rs +++ b/tests/ui/offload/type_mismatch.rs @@ -5,25 +5,32 @@ fn main() { // kernel_ty is not a function item let not_fn = 42; - core::intrinsics::offload::<_, _, ()>(not_fn, [1, 1, 1], [1, 1, 1], 0, ()); + core::intrinsics::offload::<_, _, ()>(not_fn, [1, 1, 1], [1, 1, 1], 0, -1, ()); //~^ ERROR expected a function item for the offload kernel, found `i32` // argument count mismatch - core::intrinsics::offload::<_, _, ()>(kernel_1, [1, 1, 1], [1, 1, 1], 0, ()); + core::intrinsics::offload::<_, _, ()>(kernel_1, [1, 1, 1], [1, 1, 1], 0, -1, ()); //~^ ERROR offload kernel expects 1 arguments, but 0 arguments were provided // argument type mismatch - core::intrinsics::offload::<_, _, ()>(kernel_1, [1, 1, 1], [1, 1, 1], 0, (42.0f64,)); + core::intrinsics::offload::<_, _, ()>(kernel_1, [1, 1, 1], [1, 1, 1], 0, -1, (42.0f64,)); //~^ ERROR type mismatch in offload kernel argument 0: expected `f32`, found `f64` // return type mismatch - let _: f64 = core::intrinsics::offload::<_, _, f64>(kernel_0, [1, 1, 1], [1, 1, 1], 0, ()); + let _: f64 = core::intrinsics::offload::<_, _, f64>(kernel_0, [1, 1, 1], [1, 1, 1], 0, -1, ()); //~^ ERROR offload kernel return type mismatch: kernel returns `()`, but offload call expects `f64` // multiple argument type mismatch - core::intrinsics::offload::<_, _, ()>(kernel_2, [1, 1, 1], [1, 1, 1], 0, (42.0f64, 42.0f64)); - //~^ ERROR type mismatch in offload kernel argument 0: expected `f32`, found `f64` - //~| ERROR type mismatch in offload kernel argument 1: expected `f32`, found `f64` + core::intrinsics::offload::<_, _, ()>( + //~^ ERROR type mismatch in offload kernel argument 0: expected `f32`, found `f64` + //~| ERROR type mismatch in offload kernel argument 1: expected `f32`, found `f64` + kernel_2, + [1, 1, 1], + [1, 1, 1], + 0, + -1, + (42.0f64, 42.0f64), + ); } fn kernel_0() {} diff --git a/tests/ui/offload/type_mismatch.stderr b/tests/ui/offload/type_mismatch.stderr index 8cf160ca09486..808768e7cd4f3 100644 --- a/tests/ui/offload/type_mismatch.stderr +++ b/tests/ui/offload/type_mismatch.stderr @@ -1,37 +1,37 @@ error: expected a function item for the offload kernel, found `i32` --> $DIR/type_mismatch.rs:8:5 | -LL | core::intrinsics::offload::<_, _, ()>(not_fn, [1, 1, 1], [1, 1, 1], 0, ()); +LL | core::intrinsics::offload::<_, _, ()>(not_fn, [1, 1, 1], [1, 1, 1], 0, -1, ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: offload kernel expects 1 arguments, but 0 arguments were provided --> $DIR/type_mismatch.rs:12:5 | -LL | core::intrinsics::offload::<_, _, ()>(kernel_1, [1, 1, 1], [1, 1, 1], 0, ()); +LL | core::intrinsics::offload::<_, _, ()>(kernel_1, [1, 1, 1], [1, 1, 1], 0, -1, ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: type mismatch in offload kernel argument 0: expected `f32`, found `f64` --> $DIR/type_mismatch.rs:16:5 | -LL | core::intrinsics::offload::<_, _, ()>(kernel_1, [1, 1, 1], [1, 1, 1], 0, (42.0f64,)); +LL | core::intrinsics::offload::<_, _, ()>(kernel_1, [1, 1, 1], [1, 1, 1], 0, -1, (42.0f64,)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: offload kernel return type mismatch: kernel returns `()`, but offload call expects `f64` --> $DIR/type_mismatch.rs:20:18 | -LL | let _: f64 = core::intrinsics::offload::<_, _, f64>(kernel_0, [1, 1, 1], [1, 1, 1], 0, ()); +LL | let _: f64 = core::intrinsics::offload::<_, _, f64>(kernel_0, [1, 1, 1], [1, 1, 1], 0, -1, ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: type mismatch in offload kernel argument 0: expected `f32`, found `f64` --> $DIR/type_mismatch.rs:24:5 | -LL | core::intrinsics::offload::<_, _, ()>(kernel_2, [1, 1, 1], [1, 1, 1], 0, (42.0f64, 42.0f64)); +LL | core::intrinsics::offload::<_, _, ()>( | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: type mismatch in offload kernel argument 1: expected `f32`, found `f64` --> $DIR/type_mismatch.rs:24:5 | -LL | core::intrinsics::offload::<_, _, ()>(kernel_2, [1, 1, 1], [1, 1, 1], 0, (42.0f64, 42.0f64)); +LL | core::intrinsics::offload::<_, _, ()>( | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 6 previous errors From ed486fb5181457e5dce84e7d6cb551e31e06e033 Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Sun, 16 Aug 2026 14:40:02 +0100 Subject: [PATCH 04/33] Add a regression test for a panicking proc macro used as an inner attribute --- tests/ui/proc-macro/auxiliary/panicking-attribute.rs | 8 ++++++++ tests/ui/proc-macro/panicking-inner-attribute-macro.rs | 8 ++++++++ .../proc-macro/panicking-inner-attribute-macro.stderr | 10 ++++++++++ 3 files changed, 26 insertions(+) create mode 100644 tests/ui/proc-macro/auxiliary/panicking-attribute.rs create mode 100644 tests/ui/proc-macro/panicking-inner-attribute-macro.rs create mode 100644 tests/ui/proc-macro/panicking-inner-attribute-macro.stderr diff --git a/tests/ui/proc-macro/auxiliary/panicking-attribute.rs b/tests/ui/proc-macro/auxiliary/panicking-attribute.rs new file mode 100644 index 0000000000000..f5544030b8ec5 --- /dev/null +++ b/tests/ui/proc-macro/auxiliary/panicking-attribute.rs @@ -0,0 +1,8 @@ +extern crate proc_macro; + +use proc_macro::TokenStream; + +#[proc_macro_attribute] +pub fn tester(_: TokenStream, _: TokenStream) -> TokenStream { + panic!(); +} diff --git a/tests/ui/proc-macro/panicking-inner-attribute-macro.rs b/tests/ui/proc-macro/panicking-inner-attribute-macro.rs new file mode 100644 index 0000000000000..2fef1cb83135a --- /dev/null +++ b/tests/ui/proc-macro/panicking-inner-attribute-macro.rs @@ -0,0 +1,8 @@ +//! Regression test for . + +//@ proc-macro: panicking-attribute.rs +//@ compile-flags: --crate-type=lib + +#![feature(custom_inner_attributes)] +#![panicking_attribute::tester] +//~^ ERROR custom attribute panicked diff --git a/tests/ui/proc-macro/panicking-inner-attribute-macro.stderr b/tests/ui/proc-macro/panicking-inner-attribute-macro.stderr new file mode 100644 index 0000000000000..cbfb29ad1ef89 --- /dev/null +++ b/tests/ui/proc-macro/panicking-inner-attribute-macro.stderr @@ -0,0 +1,10 @@ +error: custom attribute panicked + --> $DIR/panicking-inner-attribute-macro.rs:7:1 + | +LL | #![panicking_attribute::tester] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: explicit panic + +error: aborting due to 1 previous error + From 652eb8425c3177c2b4617f08c91fbcfefa42a795 Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Sun, 16 Aug 2026 17:10:01 +0100 Subject: [PATCH 05/33] Add a regression test for a `macro_rules!` generated by another crate's `macro_rules!` --- .../nested-macro-rules-definition.rs | 15 +++++++++++ .../cross-crate-nested-macro-rules-span.rs | 18 +++++++++++++ ...cross-crate-nested-macro-rules-span.stderr | 27 +++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 tests/ui/macros/auxiliary/nested-macro-rules-definition.rs create mode 100644 tests/ui/macros/cross-crate-nested-macro-rules-span.rs create mode 100644 tests/ui/macros/cross-crate-nested-macro-rules-span.stderr diff --git a/tests/ui/macros/auxiliary/nested-macro-rules-definition.rs b/tests/ui/macros/auxiliary/nested-macro-rules-definition.rs new file mode 100644 index 0000000000000..cb980fd3d9274 --- /dev/null +++ b/tests/ui/macros/auxiliary/nested-macro-rules-definition.rs @@ -0,0 +1,15 @@ +pub struct ProjectileCreated; +pub struct NotificationChannel(std::marker::PhantomData); + +// The inner `macro_rules!` is what later reports a span from this crate while the +// diagnostic is being rendered against the downstream crate's source. +macro_rules! define_trigger_system { + ($(( $field:ident, $ty:ident, $channel:ident )),* $(,)?) => { + #[macro_export] + macro_rules! all_trigger_fields { + ($submacro:ident) => { $submacro!($( ( $field, $ty, $channel ) ),*) } + } + }; +} + +define_trigger_system!((projectile_created, ProjectileCreated, NotificationChannel),); diff --git a/tests/ui/macros/cross-crate-nested-macro-rules-span.rs b/tests/ui/macros/cross-crate-nested-macro-rules-span.rs new file mode 100644 index 0000000000000..9353398093ff8 --- /dev/null +++ b/tests/ui/macros/cross-crate-nested-macro-rules-span.rs @@ -0,0 +1,18 @@ +//! Regression test for . + +//@ aux-build: nested-macro-rules-definition.rs + +extern crate nested_macro_rules_definition; +use nested_macro_rules_definition::*; + +macro_rules! make_event_subscription { + ($(( $field:ident, $ty:ident, $channel:ident )),*) => { + pub struct EventSubscription($($channel::ReaderId),*); + //~^ ERROR ambiguous associated type + }; +} + +all_trigger_fields!(make_event_subscription); +//~^ ERROR macros that expand to items must be delimited with braces or followed by a semicolon + +fn main() {} diff --git a/tests/ui/macros/cross-crate-nested-macro-rules-span.stderr b/tests/ui/macros/cross-crate-nested-macro-rules-span.stderr new file mode 100644 index 0000000000000..edf3d575b972d --- /dev/null +++ b/tests/ui/macros/cross-crate-nested-macro-rules-span.stderr @@ -0,0 +1,27 @@ +error: macros that expand to items must be delimited with braces or followed by a semicolon + --> $DIR/cross-crate-nested-macro-rules-span.rs:15:1 + | +LL | all_trigger_fields!(make_event_subscription); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `all_trigger_fields` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0223]: ambiguous associated type + --> $DIR/cross-crate-nested-macro-rules-span.rs:10:40 + | +LL | pub struct EventSubscription($($channel::ReaderId),*); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | all_trigger_fields!(make_event_subscription); + | -------------------------------------------- in this macro invocation + | + = note: this error originates in the macro `make_event_subscription` which comes from the expansion of the macro `all_trigger_fields` (in Nightly builds, run with -Z macro-backtrace for more info) +help: if there were a trait named `Example` with associated type `ReaderId` implemented for `nested_macro_rules_definition::NotificationChannel`, you could use the fully-qualified path + | +LL - pub struct EventSubscription($($channel::ReaderId),*); +LL + pub struct EventSubscription($( as Example>::ReaderId),*); + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0223`. From d9d3a215af9af24d4be9d474f9cbed89363f9188 Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Sun, 16 Aug 2026 17:15:19 +0100 Subject: [PATCH 06/33] Add a regression test for full LTO against `rustc_private` --- tests/ui-fulldeps/lto-with-rustc-private.rs | 14 ++++++++++++++ tests/ui-fulldeps/lto-with-rustc-private.stderr | 2 ++ 2 files changed, 16 insertions(+) create mode 100644 tests/ui-fulldeps/lto-with-rustc-private.rs create mode 100644 tests/ui-fulldeps/lto-with-rustc-private.stderr diff --git a/tests/ui-fulldeps/lto-with-rustc-private.rs b/tests/ui-fulldeps/lto-with-rustc-private.rs new file mode 100644 index 0000000000000..7695d5b6ea1cc --- /dev/null +++ b/tests/ui-fulldeps/lto-with-rustc-private.rs @@ -0,0 +1,14 @@ +//! Regression test for . + +//@ build-fail +//@ compile-flags: -Clto +//@ normalize-stderr: "error: crate .* required.*\n( .*\n)*\n" -> "" +//@ normalize-stderr: "aborting due to [0-9]+" -> "aborting due to NUMBER" +//@ dont-require-annotations: ERROR + +#![feature(rustc_private)] + +extern crate rustc_errors; +//~? ERROR crate `rustc_errors` required to be available in rlib format + +fn main() {} diff --git a/tests/ui-fulldeps/lto-with-rustc-private.stderr b/tests/ui-fulldeps/lto-with-rustc-private.stderr new file mode 100644 index 0000000000000..58577ffffb3f6 --- /dev/null +++ b/tests/ui-fulldeps/lto-with-rustc-private.stderr @@ -0,0 +1,2 @@ +error: aborting due to NUMBER previous errors + From f18340b169b91ad11abb55447f464544b2ac4005 Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Sun, 16 Aug 2026 17:38:22 +0100 Subject: [PATCH 07/33] Add a regression test for eliding the middle of a span containing hard tabs --- .../elided-span-with-hard-tabs.rs | 11 +++++++++++ .../elided-span-with-hard-tabs.stderr | 16 ++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 tests/ui/diagnostic-width/elided-span-with-hard-tabs.rs create mode 100644 tests/ui/diagnostic-width/elided-span-with-hard-tabs.stderr diff --git a/tests/ui/diagnostic-width/elided-span-with-hard-tabs.rs b/tests/ui/diagnostic-width/elided-span-with-hard-tabs.rs new file mode 100644 index 0000000000000..d455da13fef83 --- /dev/null +++ b/tests/ui/diagnostic-width/elided-span-with-hard-tabs.rs @@ -0,0 +1,11 @@ +//! Regression test for . + +// The panic happens while the JSON emitter fills in its `rendered` field, which is the +// path `cargo` takes, so this has to be checked with the default JSON error format. +//@ compile-flags: --diagnostic-width=30 +// ignore-tidy-file-tab + +fn main() { + let _: &[u8] = [0, 0]; + //~^ ERROR mismatched types +} diff --git a/tests/ui/diagnostic-width/elided-span-with-hard-tabs.stderr b/tests/ui/diagnostic-width/elided-span-with-hard-tabs.stderr new file mode 100644 index 0000000000000..6415675e5a69d --- /dev/null +++ b/tests/ui/diagnostic-width/elided-span-with-hard-tabs.stderr @@ -0,0 +1,16 @@ +error[E0308]: mismatched types + --> $DIR/elided-span-with-hard-tabs.rs:9:20 + | +LL | ..._: &[u8] = [0, ... 0]; + | ----- ^^^^^^^^...^^^^^^^^ expected `&[u8]`, found `[{integer}; 2]` + | | + | expected due to this + | +help: consider borrowing here + | +LL | let _: &[u8] = &[0, 0]; + | + + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. From c83b0a459335fe196088557fa0b02872ca7a3240 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sun, 16 Aug 2026 19:06:14 -0400 Subject: [PATCH 08/33] std: Add a test for operations that depend on overflow checks I wasn't able to find a test that verifies different behavior based on whether or not overflow checks are available, so add one here. --- tests/ui/std/overflow-check-ops.rs | 42 ++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 tests/ui/std/overflow-check-ops.rs diff --git a/tests/ui/std/overflow-check-ops.rs b/tests/ui/std/overflow-check-ops.rs new file mode 100644 index 0000000000000..9f79bb6505753 --- /dev/null +++ b/tests/ui/std/overflow-check-ops.rs @@ -0,0 +1,42 @@ +//! Verify the behavior differences between enabling and disabling overflow checks. + +//@ run-pass +//@ needs-unwind +//@ revisions: ERROR WRAP +//@[ERROR] compile-flags: -C overflow-checks=true +//@[WRAP] compile-flags: -C overflow-checks=false + +#![feature(cfg_overflow_checks)] + +use std::hint::black_box as bb; +use std::{assert_matches, fmt, panic}; + +#[track_caller] +fn check(func: fn() -> T, wrapping_res: T, name: &str) { + let type_name = std::any::type_name::(); + let res = panic::catch_unwind(func); + if cfg!(overflow_checks) { + assert_matches!(res, Err(_), "{type_name} {name}"); + } else { + assert_eq!(res.unwrap(), wrapping_res, "{type_name} {name}"); + } +} + +fn main() { + check(|| bb(u32::MAX) + bb(1), 0, "add"); + check(|| bb(0u32) - bb(1), u32::MAX, "sub"); + check(|| bb(u32::MAX) * bb(2), u32::MAX << 1, "mul"); + check(|| bb(1u32) << bb(32), 1, "shl"); + check(|| bb(u32::MAX) >> bb(32), u32::MAX, "shr"); + check(|| bb(u32::MAX).pow(bb(2)), 1, "pow"); + check(|| bb(u32::MAX).next_power_of_two(), 0, "next_power_of_two"); + + check(|| bb(i32::MAX) + bb(1), i32::MIN, "add"); + check(|| bb(i32::MIN) - bb(1), i32::MAX, "sub"); + check(|| bb(i32::MAX) * bb(2), i32::MAX << 1, "mul"); + check(|| -bb(i32::MIN), i32::MIN, "neg"); + check(|| bb(i32::MIN).abs(), i32::MIN, "abs"); + check(|| bb(1) << bb(32), 1, "shl"); + check(|| bb(i32::MAX) >> bb(32), i32::MAX, "shr"); + check(|| bb(i32::MAX).pow(bb(2)), 1, "pow"); +} From dd69e6bd7cbe5c4ad6ab85f131f8724c9275d95e Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 18 Aug 2026 14:27:44 +0200 Subject: [PATCH 09/33] std: don't panic on long-elapsed deadlines for `sleep_until` --- library/std/src/sys/thread/unix.rs | 24 ++++++++++++++++++++++-- library/std/src/thread/tests.rs | 9 +++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/library/std/src/sys/thread/unix.rs b/library/std/src/sys/thread/unix.rs index 2dbb0314cb271..831c4e0787841 100644 --- a/library/std/src/sys/thread/unix.rs +++ b/library/std/src/sys/thread/unix.rs @@ -650,6 +650,26 @@ pub fn sleep(dur: Duration) { pub fn sleep_until(deadline: crate::time::Instant) { use crate::time::Instant; + let timespec = deadline.into_inner().into_timespec(); + if timespec.tv_sec < 0 { + // `clock_nanosleep` fails with EINVAL if + // > The tp argument to clock_settime() is outside the range for the + // > given clock ID. + // + // This specification allows *any* clock range, which means we'd + // theoretically have to detect whether the time point is in the + // future (and block indefinitely) or the past (and return immediately) + // when encountering `EINVAL`. But since all existing implementations + // interpret this as saying that negative `tv_sec` values are unsupported, + // we can just test that and return – given that POSIX specifies that + // `CLOCK_MONOTONIC` measures the time "since an unspecified amount + // in the past" negative values are definitely in the past. If you + // observe any platform returning `EINVAL` for more cases, please + // file a bug; we'd need to add logic handling `EINVAL` when it + // occurs. + return; + } + #[cfg(all( target_os = "linux", target_env = "gnu", @@ -672,7 +692,7 @@ pub fn sleep_until(deadline: crate::time::Instant) { } if let Some(clock_nanosleep) = __clock_nanosleep_time64.get() { - let ts = deadline.into_inner().into_timespec().to_timespec64(); + let ts = timespec.to_timespec64(); loop { let r = unsafe { clock_nanosleep( @@ -700,7 +720,7 @@ pub fn sleep_until(deadline: crate::time::Instant) { } } - let Some(ts) = deadline.into_inner().into_timespec().to_timespec() else { + let Some(ts) = timespec.to_timespec() else { // The deadline is further in the future then can be passed to // clock_nanosleep. We have to use Self::sleep instead. This might // happen on 32 bit platforms, especially closer to 2038. diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs index 78b6f7c35e8db..e88ca92218dc8 100644 --- a/library/std/src/thread/tests.rs +++ b/library/std/src/thread/tests.rs @@ -333,6 +333,15 @@ fn sleep_ms_smoke() { thread::sleep(Duration::from_millis(2)); } +#[test] +fn sleep_until_elapsed() { + // UNIX's `clock_nanosleep` doesn't like timeouts that are too far back. + // Test that `sleep_until` returns immediately instead of panicking. + // Going 10 years back should be enough to trigger any errors. + let earlier = Instant::now() - Duration::from_secs(10 * 365 * 24 * 3600); + thread::sleep_until(earlier); +} + #[test] fn test_size_of_option_thread_id() { assert_eq!(size_of::>(), size_of::()); From d448db857bab7cb03ca9c82f23acfcfa86f0b6c8 Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Sun, 16 Aug 2026 17:55:07 +0100 Subject: [PATCH 10/33] Add a regression test for wrapping arithmetic under MIR optimisations --- .../numbers-arithmetic/wrapping-ops-under-mir-opts.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/ui/numbers-arithmetic/wrapping-ops-under-mir-opts.rs diff --git a/tests/ui/numbers-arithmetic/wrapping-ops-under-mir-opts.rs b/tests/ui/numbers-arithmetic/wrapping-ops-under-mir-opts.rs new file mode 100644 index 0000000000000..ab0e48f51be6f --- /dev/null +++ b/tests/ui/numbers-arithmetic/wrapping-ops-under-mir-opts.rs @@ -0,0 +1,11 @@ +//! Regression test for . + +//@ run-pass +//@ compile-flags: -Zmir-opt-level=2 -Coverflow-checks=on + +fn main() { + assert_eq!(1_u32.wrapping_sub(2), u32::MAX); + assert_eq!(u32::MAX.wrapping_add(2), 1); + assert_eq!(i32::MIN.wrapping_sub(1), i32::MAX); + assert_eq!(2_u32.wrapping_mul(u32::MAX), u32::MAX - 1); +} From ceeaac0ba45fd7d63e7d3763c2bd36df301be6f9 Mon Sep 17 00:00:00 2001 From: zakrad <49591476+zakrad@users.noreply.github.com> Date: Wed, 19 Aug 2026 01:15:32 +0330 Subject: [PATCH 11/33] Add regression test for nested RPIT not-an-iterator ICE --- .../nested-rpit-not-iterator-ice-159559.rs | 19 +++++++++++++++++++ ...nested-rpit-not-iterator-ice-159559.stderr | 12 ++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 tests/ui/impl-trait/nested-rpit-not-iterator-ice-159559.rs create mode 100644 tests/ui/impl-trait/nested-rpit-not-iterator-ice-159559.stderr diff --git a/tests/ui/impl-trait/nested-rpit-not-iterator-ice-159559.rs b/tests/ui/impl-trait/nested-rpit-not-iterator-ice-159559.rs new file mode 100644 index 0000000000000..03842bc36db7a --- /dev/null +++ b/tests/ui/impl-trait/nested-rpit-not-iterator-ice-159559.rs @@ -0,0 +1,19 @@ +//! Regression test for . +//! Reporting the `E0277` for the unsatisfied `IntoIterator` bound on the +//! nested opaque type used to ICE ("Normalizing ... without wrapping in a +//! `Binder`") in the RPIT method-chain suggestion when the return type +//! captures a lifetime. + +trait Cap<'a> {} + +impl Cap<'_> for T {} + +fn fail_late_bound<'a>( + a: &u8, + _: &'a u8, +) -> impl IntoIterator + IntoIterator>> { + //~^ ERROR `&u8` is not an iterator + [a] +} + +fn main() {} diff --git a/tests/ui/impl-trait/nested-rpit-not-iterator-ice-159559.stderr b/tests/ui/impl-trait/nested-rpit-not-iterator-ice-159559.stderr new file mode 100644 index 0000000000000..4ba03ed69999c --- /dev/null +++ b/tests/ui/impl-trait/nested-rpit-not-iterator-ice-159559.stderr @@ -0,0 +1,12 @@ +error[E0277]: `&u8` is not an iterator + --> $DIR/nested-rpit-not-iterator-ice-159559.rs:14:31 + | +LL | ) -> impl IntoIterator + IntoIterator>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `&u8` is not an iterator + | + = help: the trait `Iterator` is not implemented for `&u8` + = note: required for `&u8` to implement `IntoIterator` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. From 8ac67c56c1a3ecab81dfc4180c4f83bb970f0b0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 19 Aug 2026 13:29:51 +0200 Subject: [PATCH 12/33] Cleanup: Move impl of `#[rustc_dump_object_lifetime_defaults]` --- .../rustc_hir_analysis/src/collect/dump.rs | 25 +++++++++++++++++++ compiler/rustc_hir_analysis/src/lib.rs | 11 +++++--- compiler/rustc_passes/src/check_attr.rs | 22 +--------------- 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/collect/dump.rs b/compiler/rustc_hir_analysis/src/collect/dump.rs index b1b8b513f3b35..772bbe4a6b579 100644 --- a/compiler/rustc_hir_analysis/src/collect/dump.rs +++ b/compiler/rustc_hir_analysis/src/collect/dump.rs @@ -3,6 +3,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::{find_attr, intravisit}; use rustc_middle::hir::nested_filter; +use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt, Unnormalized}; use rustc_span::sym; @@ -25,6 +26,30 @@ pub(crate) fn generics(tcx: TyCtxt<'_>) { } } +pub(crate) fn object_lifetime_defaults(tcx: TyCtxt<'_>) { + for def_id in tcx.hir_crate_items(()).definitions() { + if def_id == hir::def_id::CRATE_DEF_ID { + continue; + } + + if !find_attr!(tcx, def_id, RustcDumpObjectLifetimeDefaults) { + continue; + } + + for param in &tcx.generics_of(def_id).own_params { + let ty::GenericParamDefKind::Type { .. } = param.kind else { continue }; + let default = tcx.object_lifetime_default(param.def_id); + let repr = match default { + ObjectLifetimeDefault::Empty => "Empty".to_owned(), + ObjectLifetimeDefault::Static => "'static".to_owned(), + ObjectLifetimeDefault::Param(def_id) => tcx.item_name(def_id).to_string(), + ObjectLifetimeDefault::Ambiguous => "Ambiguous".to_owned(), + }; + tcx.dcx().span_err(tcx.def_span(param.def_id), repr); + } + } +} + pub(crate) fn opaque_hidden_types(tcx: TyCtxt<'_>) { if !find_attr!(tcx, crate, RustcDumpHiddenTypeOfOpaques) { return; diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 572200dbd7634..7121b4b654cfe 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -203,13 +203,16 @@ pub fn check_crate(tcx: TyCtxt<'_>) { if tcx.features().rustc_attrs() { tcx.sess.time("dumping_rustc_attr_data", || { - outlives::dump::inferred_outlives(tcx); - variance::dump::variances(tcx); - collect::dump::generics(tcx); - collect::dump::opaque_hidden_types(tcx); + // tidy-alphabetical-start collect::dump::clauses_and_item_bounds(tcx); collect::dump::def_parents(tcx); + collect::dump::generics(tcx); + collect::dump::object_lifetime_defaults(tcx); + collect::dump::opaque_hidden_types(tcx); collect::dump::vtables(tcx); + outlives::dump::inferred_outlives(tcx); + variance::dump::variances(tcx); + // tidy-alphabetical-end }); } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index d9099cadaece8..1fe16d294433c 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -30,7 +30,6 @@ use rustc_hir::{ }; use rustc_macros::Diagnostic; use rustc_middle::hir::nested_filter; -use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault; use rustc_middle::query::Providers; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::error::{ExpectedFound, TypeError}; @@ -195,9 +194,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::Deprecated { span: attr_span, .. } => { self.check_deprecated(hir_id, *attr_span, target) } - AttributeKind::RustcDumpObjectLifetimeDefaults => { - self.check_dump_object_lifetime_defaults(hir_id); - } AttributeKind::Naked(..) => self.check_naked(hir_id, target), AttributeKind::NonExhaustive(attr_span) => { self.check_non_exhaustive(*attr_span, span, target, item) @@ -337,6 +333,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::RustcDumpInferredOutlives => (), AttributeKind::RustcDumpItemBounds => (), AttributeKind::RustcDumpLayout(..) => (), + AttributeKind::RustcDumpObjectLifetimeDefaults => (), AttributeKind::RustcDumpSymbolName(..) => (), AttributeKind::RustcDumpUserArgs => (), AttributeKind::RustcDumpVariances => (), @@ -785,23 +782,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - /// Debugging aid for the `object_lifetime_default` query. - fn check_dump_object_lifetime_defaults(&self, hir_id: HirId) { - let tcx = self.tcx; - let Some(owner_id) = hir_id.as_owner() else { return }; - for param in &tcx.generics_of(owner_id.def_id).own_params { - let ty::GenericParamDefKind::Type { .. } = param.kind else { continue }; - let default = tcx.object_lifetime_default(param.def_id); - let repr = match default { - ObjectLifetimeDefault::Empty => "Empty".to_owned(), - ObjectLifetimeDefault::Static => "'static".to_owned(), - ObjectLifetimeDefault::Param(def_id) => tcx.item_name(def_id).to_string(), - ObjectLifetimeDefault::Ambiguous => "Ambiguous".to_owned(), - }; - tcx.dcx().span_err(tcx.def_span(param.def_id), repr); - } - } - /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. fn check_non_exhaustive( &self, From eae69d81bc730cdde441312201d3a00f3b87ef34 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 19 Aug 2026 14:49:31 +0200 Subject: [PATCH 13/33] Add file path to some archive build errors This would have been useful to have while debugging a problem in cg_clif. --- .../rustc_codegen_ssa/src/back/archive.rs | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index c4107b4a60f27..6c4575caebd8e 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -623,8 +623,9 @@ impl<'a> ArArchiveBuilder<'a> { io::Error::new( io::ErrorKind::InvalidData, format!( - "archive member at offset {start} with size {} \ + "archive member of {} at offset {start} with size {} \ exceeds archive size {} in `{}`", + src_archive.0.display(), file_range.1, archive_data.len(), src_archive.0.display(), @@ -642,11 +643,18 @@ impl<'a> ArArchiveBuilder<'a> { } } ArchiveEntrySource::File(file) => unsafe { - let mmap = Mmap::map( - File::open(file) - .map_err(|err| io_error_context("failed to open object file", err))?, - ) - .map_err(|err| io_error_context("failed to map object file", err))?; + let mmap = Mmap::map(File::open(&file).map_err(|err| { + io_error_context( + &format!("failed to open object file {}", file.display()), + err, + ) + })?) + .map_err(|err| { + io_error_context( + &format!("failed to map object file {}", file.display()), + err, + ) + })?; if entry.kind == ArchiveEntryKind::RustObj && let Some(sym) = &symbols { From f151cb926f92e4a9de37baa01c167aba4ba50ff5 Mon Sep 17 00:00:00 2001 From: Rohan Singla Date: Wed, 8 Jul 2026 05:09:45 +0530 Subject: [PATCH 14/33] diagnostics: fix `let x: vec![]` suggestion pointing into stdlib When a macro call like `vec![]` appears in type position, the compiler's "use `=` if you meant to assign" suggestion was pointing into the macro definition in stdlib instead of the user's own code. The suggestion span was computed as `stmt.pat.span.between(hir_ty.span)`. After expansion `hir_ty.span` lies inside the macro body, so that span crossed syntax contexts and the renderer displayed the stdlib location. Compute the span via `find_ancestor_in_same_ctxt` instead, so the pattern and the type are compared in a common syntax context. When no common context exists -- e.g. the `let` comes from a macro body while the pattern is a call-site metavariable -- no suggestion is emitted at all. --- .../src/hir_ty_lowering/errors.rs | 18 +++++++++ .../src/hir_ty_lowering/mod.rs | 40 ++++++++++--------- .../let-binding-init-expr-as-ty.rs | 11 +++++ .../let-binding-init-expr-as-ty.stderr | 35 +++++++++++++--- 4 files changed, 79 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index e5dbae16d07d4..7d07139a9077e 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -2055,3 +2055,21 @@ fn assoc_tag_str(assoc_tag: ty::AssocTag) -> &'static str { ty::AssocTag::Type => "type", } } + +/// Computes the `pat.between(ty)` span for the "use `=`" suggestion on `let pat: ty`. +/// Returns `None` if `pat` and `ty` are in incompatible macro contexts (e.g. `pat` is a +/// metavariable from the call site while `ty` lives in the macro body), in which case no +/// suggestion is emitted. +pub(crate) fn eq_ctxt_suggestion_span(pat: Span, ty: Span) -> Option { + if let Some(ty2) = ty.find_ancestor_in_same_ctxt(pat) + && pat.hi() <= ty2.lo() + { + return Some(pat.between(ty2)); + } + if let Some(pat2) = pat.find_ancestor_in_same_ctxt(ty) + && pat2.hi() <= ty.lo() + { + return Some(pat2.between(ty)); + } + None +} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index c65e9bdbd211e..8256ef6442057 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -56,7 +56,9 @@ use tracing::{debug, instrument}; use crate::check::check_abi; use crate::check_c_variadic_abi; use crate::diagnostics::{self, BadReturnTypeNotation, NoFieldOnType, NoVariantNamed}; -use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint}; +use crate::hir_ty_lowering::errors::{ + GenericsArgsErrExtend, eq_ctxt_suggestion_span, prohibit_assoc_item_constraint, +}; use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args}; use crate::middle::resolve_bound_vars as rbv; @@ -3302,18 +3304,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .next() { // `let x: S::new(valid_in_ty_ctxt);` -> `let x = S::new(valid_in_ty_ctxt);` - let err = tcx - .dcx() - .struct_span_err( - hir_ty.span, - "expected type, found associated function call", - ) - .with_span_suggestion_verbose( - stmt.pat.span.between(hir_ty.span), + let mut err = tcx.dcx().struct_span_err( + hir_ty.span, + "expected type, found associated function call", + ); + if let Some(between) = eq_ctxt_suggestion_span(stmt.pat.span, hir_ty.span) { + err.span_suggestion_verbose( + between, "use `=` if you meant to assign", - " = ".to_string(), + " = ", Applicability::MaybeIncorrect, ); + } self.dcx().try_steal_replace_and_emit_err( hir_ty.span, StashKey::ReturnTypeNotation, @@ -3328,18 +3330,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { { // `let x: i32::something(valid_in_ty_ctxt);` -> `let x = i32::something(valid_in_ty_ctxt);` // FIXME: Check that `something` is a valid function in `i32`. - let err = tcx - .dcx() - .struct_span_err( - hir_ty.span, - "expected type, found associated function call", - ) - .with_span_suggestion_verbose( - stmt.pat.span.between(hir_ty.span), + let mut err = tcx.dcx().struct_span_err( + hir_ty.span, + "expected type, found associated function call", + ); + if let Some(between) = eq_ctxt_suggestion_span(stmt.pat.span, hir_ty.span) { + err.span_suggestion_verbose( + between, "use `=` if you meant to assign", - " = ".to_string(), + " = ", Applicability::MaybeIncorrect, ); + } self.dcx().try_steal_replace_and_emit_err( hir_ty.span, StashKey::ReturnTypeNotation, diff --git a/tests/ui/suggestions/let-binding-init-expr-as-ty.rs b/tests/ui/suggestions/let-binding-init-expr-as-ty.rs index 22240d02d7fd2..b70bf5572a57c 100644 --- a/tests/ui/suggestions/let-binding-init-expr-as-ty.rs +++ b/tests/ui/suggestions/let-binding-init-expr-as-ty.rs @@ -28,6 +28,17 @@ fn main() { //~^ ERROR return type notation is experimental let x: S::new(()); //~ ERROR expected type, found associated function call + // Macros — suggestion must point at user code, not the macro definition (#158492) + let x: vec![]; //~ ERROR expected type, found associated function call + + // When the `let` is inside a macro, no suggestion should be emitted at the call site + macro_rules! make { + ($pat:pat) => { + let $pat: Vec::new(); //~ ERROR expected type, found associated function call + }; + } + make!(_); + // Literals let x: 42; //~ ERROR expected type, found `42` let x: ""; //~ ERROR expected type, found `""` diff --git a/tests/ui/suggestions/let-binding-init-expr-as-ty.stderr b/tests/ui/suggestions/let-binding-init-expr-as-ty.stderr index c096fd8c5556e..35198467409e8 100644 --- a/tests/ui/suggestions/let-binding-init-expr-as-ty.stderr +++ b/tests/ui/suggestions/let-binding-init-expr-as-ty.stderr @@ -1,5 +1,5 @@ error: expected type, found `42` - --> $DIR/let-binding-init-expr-as-ty.rs:32:12 + --> $DIR/let-binding-init-expr-as-ty.rs:43:12 | LL | let x: 42; | - ^^ expected type @@ -13,7 +13,7 @@ LL + let x = 42; | error: expected type, found `""` - --> $DIR/let-binding-init-expr-as-ty.rs:33:12 + --> $DIR/let-binding-init-expr-as-ty.rs:44:12 | LL | let x: ""; | - ^^ expected type @@ -40,7 +40,7 @@ LL + let foo = i32::from_be(num); | error[E0573]: cannot find type `bar` in this scope - --> $DIR/let-binding-init-expr-as-ty.rs:36:12 + --> $DIR/let-binding-init-expr-as-ty.rs:47:12 | LL | let x: bar(); | ^^^ not found in this scope @@ -53,7 +53,7 @@ LL + let x = bar(); | error[E0573]: cannot find type `bar` in this scope - --> $DIR/let-binding-init-expr-as-ty.rs:37:12 + --> $DIR/let-binding-init-expr-as-ty.rs:48:12 | LL | let x: bar; | ^^^ not found in this scope @@ -61,7 +61,7 @@ LL | let x: bar; = note: a function named `bar` exists in another namespace error[E0573]: cannot find type `x` in this scope - --> $DIR/let-binding-init-expr-as-ty.rs:40:12 + --> $DIR/let-binding-init-expr-as-ty.rs:51:12 | LL | struct K(S::new(())); | --------------------- similarly named struct `K` defined here @@ -158,7 +158,30 @@ LL - let x: S::new(()); LL + let x = S::new(()); | -error: aborting due to 13 previous errors +error: expected type, found associated function call + --> $DIR/let-binding-init-expr-as-ty.rs:32:12 + | +LL | let x: vec![]; + | ^^^^^^ + | +help: use `=` if you meant to assign + | +LL - let x: vec![]; +LL + let x = vec![]; + | + +error: expected type, found associated function call + --> $DIR/let-binding-init-expr-as-ty.rs:37:23 + | +LL | let $pat: Vec::new(); + | ^^^^^^^^^^ +... +LL | make!(_); + | -------- in this macro invocation + | + = note: this error originates in the macro `make` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 15 previous errors Some errors have detailed explanations: E0573, E0658. For more information about an error, try `rustc --explain E0573`. From d827d4476c5b864fd2a65e2fc1842fbabbd80e8a Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Wed, 19 Aug 2026 18:29:40 +0000 Subject: [PATCH 15/33] Uplift rustfmt macro formatting fix --- src/tools/rustfmt/src/macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs index e4c05d58004a7..8bfd99f2f7c9f 100644 --- a/src/tools/rustfmt/src/macros.rs +++ b/src/tools/rustfmt/src/macros.rs @@ -454,7 +454,7 @@ pub(crate) fn rewrite_macro_def( }; let mut header = if def.macro_rules { - let pos = context.snippet_provider.span_after(span, "macro_rules!"); + let pos = context.snippet_provider.span_after(span, "!"); vec![HeaderPart::new("macro_rules!", span.with_hi(pos))] } else { let macro_lo = context.snippet_provider.span_before(span, "macro"); From 3777642175b402d20d3957ba98ce49b31ed424fb Mon Sep 17 00:00:00 2001 From: Marcelo Dominguez Date: Fri, 14 Aug 2026 18:04:13 +0300 Subject: [PATCH 16/33] Update with macro and some tweaks --- .../src/builder/gpu_offload.rs | 6 +- compiler/rustc_codegen_llvm/src/intrinsic.rs | 9 +-- library/core/src/offload.md | 3 +- library/core/src/offload/mod.rs | 62 +++++++++++++------ .../codegen-llvm/gpu_offload/control_flow.rs | 16 +++-- .../codegen-llvm/gpu_offload/device_check.rs | 30 +++++++++ tests/codegen-llvm/gpu_offload/gpu_host.rs | 13 ++-- tests/codegen-llvm/gpu_offload/scalar_host.rs | 14 ++--- tests/codegen-llvm/gpu_offload/slice_host.rs | 14 ++--- .../offload-generic-manifest/generic.rs | 12 +++- tests/ui/offload/check_config.rs | 7 ++- tests/ui/offload/offload_macro.rs | 3 + tests/ui/offload/offload_macro.stderr | 10 ++- tests/ui/offload/offload_negative_device.rs | 12 ++++ .../ui/offload/offload_negative_device.stderr | 19 ++++++ 15 files changed, 168 insertions(+), 62 deletions(-) create mode 100644 tests/codegen-llvm/gpu_offload/device_check.rs create mode 100644 tests/ui/offload/offload_negative_device.rs create mode 100644 tests/ui/offload/offload_negative_device.stderr diff --git a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs index 4f8d918c72b51..d20a73e8e6825 100644 --- a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs +++ b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs @@ -197,7 +197,11 @@ fn generate_launcher<'ll>(cx: &CodegenCx<'ll, '_>) -> (&'ll llvm::Value, &'ll ll (tgt_decl, tgt_fn_ty) } -pub(crate) fn generate_decl<'ll>(cx: &CodegenCx<'ll, '_>) -> (&'ll llvm::Value, &'ll llvm::Type) { +/// Declares the `omp_get_num_devices` runtime function and returns the +/// declaration together with its type. +pub(crate) fn declare_omp_get_num_devices<'ll>( + cx: &CodegenCx<'ll, '_>, +) -> (&'ll llvm::Value, &'ll llvm::Type) { let ti32 = cx.type_i32(); let tgt_fn_ty = cx.type_func(&[], ti32); let name = "omp_get_num_devices"; diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index fbd9a04ab2687..a5bb595d9b2c6 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -37,7 +37,7 @@ use crate::abi::FnAbiLlvmExt; use crate::builder::Builder; use crate::builder::autodiff::{adjust_activity_to_abi, generate_enzyme_call}; use crate::builder::gpu_offload::{ - OffloadKernelDims, gen_call_handling, gen_define_handling, generate_decl, register_offload, + self, OffloadKernelDims, declare_omp_get_num_devices, register_offload, }; use crate::context::CodegenCx; use crate::declare::declare_raw_fn; @@ -242,7 +242,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { return IntrinsicResult::WroteIntoPlace; } sym::offload_get_num_devices => { - let (fn_decl, fn_ty) = generate_decl(self.cx); + let (fn_decl, fn_ty) = declare_omp_get_num_devices(self.cx); let llval = self.call(fn_ty, None, None, fn_decl, &[], None, None); @@ -1893,8 +1893,9 @@ fn codegen_offload<'ll, 'tcx>( } }; register_offload(cx); - let offload_data = gen_define_handling(&cx, &metadata, target_symbol, offload_globals); - gen_call_handling( + let offload_data = + gpu_offload::gen_define_handling(&cx, &metadata, target_symbol, offload_globals); + gpu_offload::gen_call_handling( bx, &offload_data, &args, diff --git a/library/core/src/offload.md b/library/core/src/offload.md index 985a93a4294fa..726d0c7af1928 100644 --- a/library/core/src/offload.md +++ b/library/core/src/offload.md @@ -21,7 +21,8 @@ fn kernel(x: *mut [f64; 256]) { ``` To launch an offloaded kernel, use the `offload!` macro. It lets you specify the kernel, the -workgroup and thread dimensions, and the arguments to forward to the device. +workgroup and thread dimensions, the device to offload to, and the arguments to forward to the +device. ```rust,ignore (optional component) let mut x = [0.0f64; 256]; diff --git a/library/core/src/offload/mod.rs b/library/core/src/offload/mod.rs index 17ff74f0bbfbb..3d85621361209 100644 --- a/library/core/src/offload/mod.rs +++ b/library/core/src/offload/mod.rs @@ -19,6 +19,9 @@ pub use crate::offload; /// Defaults to `[1, 1, 1]`. /// - `dyn_cache`: The amount of dynamic shared memory, in bytes, to allocate for the kernel. /// Defaults to `0`. +/// - `device`: The index of the device to offload to. Must be `>= 0`. If omitted, the +/// default device is used. Use [`crate::intrinsics::offload_get_num_devices`] to discover +/// which device ids are valid. /// /// Each argument may only be specified once. /// @@ -43,61 +46,82 @@ macro_rules! offload { workgroup_dim = ([1, 1, 1]); thread_dim = ([1, 1, 1]); dyn_cache = (0); + device = NONE; args = NONE ) }; - (@munch [kernel = $val:expr $(, $rest_f:ident = $rest_v:expr)*]; kernel = NONE; workgroup_dim = $w:tt; thread_dim = $t:tt; dyn_cache = $d:tt; args = $a:tt) => { - $crate::offload!(@munch [$($rest_f = $rest_v),*]; kernel = (SOME $val); workgroup_dim = $w; thread_dim = $t; dyn_cache = $d; args = $a) + (@munch [kernel = $val:expr $(, $rest_f:ident = $rest_v:expr)*]; kernel = NONE; workgroup_dim = $w:tt; thread_dim = $t:tt; dyn_cache = $d:tt; device = $device:tt; args = $a:tt) => { + $crate::offload!(@munch [$($rest_f = $rest_v),*]; kernel = (SOME $val); workgroup_dim = $w; thread_dim = $t; dyn_cache = $d; device = $device; args = $a) }; - (@munch [kernel = $val:expr $(, $rest_f:ident = $rest_v:expr)*]; kernel = (SOME $old:expr); workgroup_dim = $w:tt; thread_dim = $t:tt; dyn_cache = $d:tt; args = $a:tt) => { + (@munch [kernel = $val:expr $(, $rest_f:ident = $rest_v:expr)*]; kernel = (SOME $old:expr); workgroup_dim = $w:tt; thread_dim = $t:tt; dyn_cache = $d:tt; device = $device:tt; args = $a:tt) => { compile_error!("duplicate field `kernel`") }; - (@munch [workgroup_dim = $val:expr $(, $rest_f:ident = $rest_v:expr)*]; kernel = $k:tt; workgroup_dim = ([1, 1, 1]); thread_dim = $t:tt; dyn_cache = $d:tt; args = $a:tt) => { - $crate::offload!(@munch [$($rest_f = $rest_v),*]; kernel = $k; workgroup_dim = (SOME $val); thread_dim = $t; dyn_cache = $d; args = $a) + (@munch [workgroup_dim = $val:expr $(, $rest_f:ident = $rest_v:expr)*]; kernel = $k:tt; workgroup_dim = ([1, 1, 1]); thread_dim = $t:tt; dyn_cache = $d:tt; device = $device:tt; args = $a:tt) => { + $crate::offload!(@munch [$($rest_f = $rest_v),*]; kernel = $k; workgroup_dim = (SOME $val); thread_dim = $t; dyn_cache = $d; device = $device; args = $a) }; - (@munch [workgroup_dim = $val:expr $(, $rest_f:ident = $rest_v:expr)*]; kernel = $k:tt; workgroup_dim = (SOME $old:expr); thread_dim = $t:tt; dyn_cache = $d:tt; args = $a:tt) => { + (@munch [workgroup_dim = $val:expr $(, $rest_f:ident = $rest_v:expr)*]; kernel = $k:tt; workgroup_dim = (SOME $old:expr); thread_dim = $t:tt; dyn_cache = $d:tt; device = $device:tt; args = $a:tt) => { compile_error!("duplicate field `workgroup_dim`") }; - (@munch [thread_dim = $val:expr $(, $rest_f:ident = $rest_v:expr)*]; kernel = $k:tt; workgroup_dim = $w:tt; thread_dim = ([1, 1, 1]); dyn_cache = $d:tt; args = $a:tt) => { - $crate::offload!(@munch [$($rest_f = $rest_v),*]; kernel = $k; workgroup_dim = $w; thread_dim = (SOME $val); dyn_cache = $d; args = $a) + (@munch [thread_dim = $val:expr $(, $rest_f:ident = $rest_v:expr)*]; kernel = $k:tt; workgroup_dim = $w:tt; thread_dim = ([1, 1, 1]); dyn_cache = $d:tt; device = $device:tt; args = $a:tt) => { + $crate::offload!(@munch [$($rest_f = $rest_v),*]; kernel = $k; workgroup_dim = $w; thread_dim = (SOME $val); dyn_cache = $d; device = $device; args = $a) }; - (@munch [thread_dim = $val:expr $(, $rest_f:ident = $rest_v:expr)*]; kernel = $k:tt; workgroup_dim = $w:tt; thread_dim = (SOME $old:expr); dyn_cache = $d:tt; args = $a:tt) => { + (@munch [thread_dim = $val:expr $(, $rest_f:ident = $rest_v:expr)*]; kernel = $k:tt; workgroup_dim = $w:tt; thread_dim = (SOME $old:expr); dyn_cache = $d:tt; device = $device:tt; args = $a:tt) => { compile_error!("duplicate field `thread_dim`") }; - (@munch [dyn_cache = $val:expr $(, $rest_f:ident = $rest_v:expr)*]; kernel = $k:tt; workgroup_dim = $w:tt; thread_dim = $t:tt; dyn_cache = (0); args = $a:tt) => { - $crate::offload!(@munch [$($rest_f = $rest_v),*]; kernel = $k; workgroup_dim = $w; thread_dim = $t; dyn_cache = (SOME $val); args = $a) + (@munch [dyn_cache = $val:expr $(, $rest_f:ident = $rest_v:expr)*]; kernel = $k:tt; workgroup_dim = $w:tt; thread_dim = $t:tt; dyn_cache = (0); device = $device:tt; args = $a:tt) => { + $crate::offload!(@munch [$($rest_f = $rest_v),*]; kernel = $k; workgroup_dim = $w; thread_dim = $t; dyn_cache = (SOME $val); device = $device; args = $a) }; - (@munch [dyn_cache = $val:expr $(, $rest_f:ident = $rest_v:expr)*]; kernel = $k:tt; workgroup_dim = $w:tt; thread_dim = $t:tt; dyn_cache = (SOME $old:expr); args = $a:tt) => { + (@munch [dyn_cache = $val:expr $(, $rest_f:ident = $rest_v:expr)*]; kernel = $k:tt; workgroup_dim = $w:tt; thread_dim = $t:tt; dyn_cache = (SOME $old:expr); device = $device:tt; args = $a:tt) => { compile_error!("duplicate field `dyn_cache`") }; - (@munch [args = $val:expr $(, $rest_f:ident = $rest_v:expr)*]; kernel = $k:tt; workgroup_dim = $w:tt; thread_dim = $t:tt; dyn_cache = $d:tt; args = NONE) => { - $crate::offload!(@munch [$($rest_f = $rest_v),*]; kernel = $k; workgroup_dim = $w; thread_dim = $t; dyn_cache = $d; args = (SOME $val)) + (@munch [device = $val:expr $(, $rest_f:ident = $rest_v:expr)*]; kernel = $k:tt; workgroup_dim = $w:tt; thread_dim = $t:tt; dyn_cache = $d:tt; device = NONE; args = $a:tt) => { + $crate::offload!(@munch [$($rest_f = $rest_v),*]; kernel = $k; workgroup_dim = $w; thread_dim = $t; dyn_cache = $d; device = (SOME $val); args = $a) }; - (@munch [args = $val:expr $(, $rest_f:ident = $rest_v:expr)*]; kernel = $k:tt; workgroup_dim = $w:tt; thread_dim = $t:tt; dyn_cache = $d:tt; args = (SOME $old:expr)) => { + (@munch [device = $val:expr $(, $rest_f:ident = $rest_v:expr)*]; kernel = $k:tt; workgroup_dim = $w:tt; thread_dim = $t:tt; dyn_cache = $d:tt; device = (SOME $old:expr); args = $a:tt) => { + compile_error!("duplicate field `device`") + }; + (@munch [args = $val:expr $(, $rest_f:ident = $rest_v:expr)*]; kernel = $k:tt; workgroup_dim = $w:tt; thread_dim = $t:tt; dyn_cache = $d:tt; device = $device:tt; args = NONE) => { + $crate::offload!(@munch [$($rest_f = $rest_v),*]; kernel = $k; workgroup_dim = $w; thread_dim = $t; dyn_cache = $d; device = $device; args = (SOME $val)) + }; + (@munch [args = $val:expr $(, $rest_f:ident = $rest_v:expr)*]; kernel = $k:tt; workgroup_dim = $w:tt; thread_dim = $t:tt; dyn_cache = $d:tt; device = $device:tt; args = (SOME $old:expr)) => { compile_error!("duplicate field `args`") }; - (@munch [$invalid:ident = $val:expr $(, $rest_f:ident = $rest_v:expr)*]; kernel = $k:tt; workgroup_dim = $w:tt; thread_dim = $t:tt; dyn_cache = $d:tt; args = $a:tt) => { + (@munch [$invalid:ident = $val:expr $(, $rest_f:ident = $rest_v:expr)*]; kernel = $k:tt; workgroup_dim = $w:tt; thread_dim = $t:tt; dyn_cache = $d:tt; device = $device:tt; args = $a:tt) => { compile_error!(concat!("unknown field `", stringify!($invalid), "`")) }; - (@munch []; kernel = NONE; workgroup_dim = $w:tt; thread_dim = $t:tt; dyn_cache = $d:tt; args = $a:tt) => { + (@munch []; kernel = NONE; workgroup_dim = $w:tt; thread_dim = $t:tt; dyn_cache = $d:tt; device = $device:tt; args = $a:tt) => { compile_error!("missing `kernel`") }; - (@munch []; kernel = $k:tt; workgroup_dim = $w:tt; thread_dim = $t:tt; dyn_cache = $d:tt; args = NONE) => { + (@munch []; kernel = $k:tt; workgroup_dim = $w:tt; thread_dim = $t:tt; dyn_cache = $d:tt; device = $device:tt; args = NONE) => { compile_error!("missing `args`") }; - (@munch []; kernel = (SOME $kernel:expr); workgroup_dim = $w:tt; thread_dim = $t:tt; dyn_cache = $d:tt; args = (SOME $args:expr)) => { + (@munch []; kernel = (SOME $kernel:expr); workgroup_dim = $w:tt; thread_dim = $t:tt; dyn_cache = $d:tt; device = $device:tt; args = (SOME $args:expr)) => { $crate::intrinsics::offload::<_, _, ()>( $kernel, $crate::offload!(@value $w), $crate::offload!(@value $t), $crate::offload!(@value $d), + $crate::offload!(@device $device), $args, ) }; (@value (SOME $val:expr)) => { $val }; (@value ($val:expr)) => { $val }; + + // if `device` is omitted (`NONE), we use the OpenMP default device (`-1`) + (@device NONE) => { -1 }; + (@device (SOME $val:expr)) => { { + const { $crate::assert!($val >= 0, "offload device must be non-negative; omit `device` to use the default device") }; + let device: i32 = $val; + $crate::assert!( + device < $crate::intrinsics::offload_get_num_devices(), + "offload device {} is not available", + device, + ); + device + } }; } diff --git a/tests/codegen-llvm/gpu_offload/control_flow.rs b/tests/codegen-llvm/gpu_offload/control_flow.rs index ef4881147e63b..8cafeda3395ce 100644 --- a/tests/codegen-llvm/gpu_offload/control_flow.rs +++ b/tests/codegen-llvm/gpu_offload/control_flow.rs @@ -6,8 +6,8 @@ // contains control flow. #![feature(abi_gpu_kernel)] +#![feature(gpu_offload)] #![feature(rustc_attrs)] -#![feature(core_intrinsics)] #![no_main] // CHECK: @.offload_sizes.[[K:[^ ]*foo]] = private unnamed_addr constant @@ -28,14 +28,12 @@ unsafe fn main() { let A = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0]; for i in 0..100 { - core::intrinsics::offload::<_, _, ()>( - foo, - [256, 1, 1], - [32, 1, 1], - 0, - -1, - (A.as_ptr() as *const [f32; 6],), - ); + core::offload::offload! { + kernel = foo, + workgroup_dim = [256, 1, 1], + thread_dim = [32, 1, 1], + args = (A.as_ptr() as *const [f32; 6],), + } } } diff --git a/tests/codegen-llvm/gpu_offload/device_check.rs b/tests/codegen-llvm/gpu_offload/device_check.rs new file mode 100644 index 0000000000000..4eaa324a662a4 --- /dev/null +++ b/tests/codegen-llvm/gpu_offload/device_check.rs @@ -0,0 +1,30 @@ +//@ compile-flags: -Zoffload=Test -Zunstable-options -C opt-level=0 -Clto=fat +//@ no-prefer-dynamic +//@ needs-offload + +// This test verifies that selecting an unavailable `device` in the `offload` macro panics. + +#![feature(gpu_offload)] +#![no_main] + +#[unsafe(no_mangle)] +fn main() { + core::offload::offload! { + kernel = kernel, + device = 99, + args = (), + } +} + +#[unsafe(no_mangle)] +fn kernel() {} + +// CHECK-LABEL: define{{( dso_local)?}} void @main() +// CHECK: store i32 99, ptr %device, align 4 +// CHECK-NEXT: %{{[0-9_]+}} = call i32 @omp_get_num_devices() +// CHECK-NEXT: %{{[0-9_]+}} = load i32, ptr %device, align 4 +// CHECK-NEXT: %{{[0-9_]+}} = icmp slt i32 %{{[0-9_]+}}, %{{[0-9_]+}} +// CHECK-NEXT: br i1 %{{[0-9_]+}}, label %bb{{[0-9]+}}, label %bb{{[0-9]+}} +// CHECK: call void @{{.*}}panic_fmt +// CHECK: unreachable +// CHECK: call i32 @__tgt_target_kernel diff --git a/tests/codegen-llvm/gpu_offload/gpu_host.rs b/tests/codegen-llvm/gpu_offload/gpu_host.rs index 208417b98bcae..45fcf6cf5c3ce 100644 --- a/tests/codegen-llvm/gpu_offload/gpu_host.rs +++ b/tests/codegen-llvm/gpu_offload/gpu_host.rs @@ -7,8 +7,8 @@ // Better documentation to what each global or variable means is available in the gpu offload code, // or the LLVM offload documentation. +#![feature(gpu_offload)] #![feature(rustc_attrs)] -#![feature(core_intrinsics)] #![no_main] #[unsafe(no_mangle)] @@ -21,7 +21,12 @@ fn main() { } pub fn kernel_1(x: &mut [f32; 256], y: &[f32; 256]) { - core::intrinsics::offload(_kernel_1, [256, 1, 1], [32, 1, 1], 0, -1, (x, y)) + core::offload::offload! { + kernel = _kernel_1, + workgroup_dim = [256, 1, 1], + thread_dim = [32, 1, 1], + args = (x, y), + } } #[inline(never)] @@ -97,7 +102,7 @@ pub fn _kernel_1(x: &mut [f32; 256], y: &[f32; 256]) { // CHECK: declare void @__tgt_register_lib(ptr) local_unnamed_addr // CHECK: declare void @__tgt_unregister_lib(ptr) local_unnamed_addr -// CHECK-LABEL: define internal void @.omp_offloading.descriptor_reg() section ".text.startup" !guid !{{[0-9]+}} { +// CHECK-LABEL: define internal void @.omp_offloading.descriptor_reg() section ".text.startup" // CHECK-NEXT: entry: // CHECK-NEXT: {{tail }}call void @__tgt_register_lib(ptr nonnull @.omp_offloading.descriptor) // CHECK-NEXT: {{tail }}call void @__tgt_init_all_rtls() @@ -105,7 +110,7 @@ pub fn _kernel_1(x: &mut [f32; 256], y: &[f32; 256]) { // CHECK-NEXT: ret void // CHECK-NEXT: } -// CHECK-LABEL: define internal void @.omp_offloading.descriptor_unreg() section ".text.startup" !guid !{{[0-9]+}} { +// CHECK-LABEL: define internal void @.omp_offloading.descriptor_unreg() section ".text.startup" // CHECK-NEXT: entry: // CHECK-NEXT: {{tail }}call void @__tgt_unregister_lib(ptr nonnull @.omp_offloading.descriptor) // CHECK-NEXT: ret void diff --git a/tests/codegen-llvm/gpu_offload/scalar_host.rs b/tests/codegen-llvm/gpu_offload/scalar_host.rs index 950306c83a43f..807d08ddf1893 100644 --- a/tests/codegen-llvm/gpu_offload/scalar_host.rs +++ b/tests/codegen-llvm/gpu_offload/scalar_host.rs @@ -6,8 +6,8 @@ // the kernel as i64 #![feature(abi_gpu_kernel)] +#![feature(gpu_offload)] #![feature(rustc_attrs)] -#![feature(core_intrinsics)] #![no_main] // CHECK: define{{( dso_local)?}} void @main() @@ -28,14 +28,10 @@ fn main() { let mut x = 0.0f32; let k = core::hint::black_box(42.0f32); - core::intrinsics::offload::<_, _, ()>( - foo, - [1, 1, 1], - [1, 1, 1], - 0, - -1, - (&mut x as *mut f32, k), - ); + core::offload::offload! { + kernel = foo, + args = (&mut x as *mut f32, k), + } } unsafe extern "C" { diff --git a/tests/codegen-llvm/gpu_offload/slice_host.rs b/tests/codegen-llvm/gpu_offload/slice_host.rs index 1b314d489612c..ad47d2e76360a 100644 --- a/tests/codegen-llvm/gpu_offload/slice_host.rs +++ b/tests/codegen-llvm/gpu_offload/slice_host.rs @@ -5,8 +5,8 @@ // This test verifies that offload is properly handling slices passing them properly to the device #![feature(abi_gpu_kernel)] +#![feature(gpu_offload)] #![feature(rustc_attrs)] -#![feature(core_intrinsics)] #![no_main] // CHECK: @anon.[[ID:.*]].0 = private unnamed_addr constant [23 x i8] c";unknown;unknown;0;0;;\00", align 1 @@ -27,14 +27,10 @@ #[unsafe(no_mangle)] fn main() { let mut x = [0.0f32, 0.0, 0.0, 0.0]; - core::intrinsics::offload::<_, _, ()>( - foo, - [1, 1, 1], - [1, 1, 1], - 0, - -1, - ((&mut x) as &mut [f32],), - ); + core::offload::offload! { + kernel = foo, + args = ((&mut x) as &mut [f32],), + } } unsafe extern "C" { diff --git a/tests/run-make/offload-generic-manifest/generic.rs b/tests/run-make/offload-generic-manifest/generic.rs index eb356ad05c574..a6b8a7368858d 100644 --- a/tests/run-make/offload-generic-manifest/generic.rs +++ b/tests/run-make/offload-generic-manifest/generic.rs @@ -1,4 +1,4 @@ -#![feature(core_intrinsics, rustc_attrs)] +#![feature(gpu_offload, rustc_attrs)] #![allow(internal_features)] #![cfg_attr(device, no_main)] @@ -7,6 +7,12 @@ fn kernel(x: T) {} #[cfg(not(device))] fn main() { - core::intrinsics::offload::<_, _, ()>(kernel::, [1, 1, 1], [1, 1, 1], 0, (0.0f32,)); - core::intrinsics::offload::<_, _, ()>(kernel::, [1, 1, 1], [1, 1, 1], 0, (0i32,)); + core::offload::offload! { + kernel = kernel::, + args = (0.0f32,), + } + core::offload::offload! { + kernel = kernel::, + args = (0i32,), + } } diff --git a/tests/ui/offload/check_config.rs b/tests/ui/offload/check_config.rs index 833ba39dffeca..63388ce69ba62 100644 --- a/tests/ui/offload/check_config.rs +++ b/tests/ui/offload/check_config.rs @@ -9,7 +9,7 @@ //[fail]~? ERROR: using the offload feature requires -Z offload= //[fail]~? ERROR: using the offload feature requires -C lto=fat -#![feature(core_intrinsics)] +#![feature(gpu_offload)] fn main() { let mut x = [3.0; 256]; @@ -17,7 +17,10 @@ fn main() { } fn kernel_1(x: &mut [f32; 256]) { - core::intrinsics::offload(_kernel_1, [1, 1, 1], [1, 1, 1], 0, -1, (x,)) + core::offload::offload! { + kernel = _kernel_1, + args = (x,), + } } fn _kernel_1(x: &mut [f32; 256]) {} diff --git a/tests/ui/offload/offload_macro.rs b/tests/ui/offload/offload_macro.rs index 468820f08c291..4480f7dd9c80b 100644 --- a/tests/ui/offload/offload_macro.rs +++ b/tests/ui/offload/offload_macro.rs @@ -26,4 +26,7 @@ fn main() { core::offload::offload! { kernel = kernel, args = (), dyn_cache = 0, dyn_cache = 8 } //~^ ERROR duplicate field `dyn_cache` + + core::offload::offload! { kernel = kernel, args = (), device = 0, device = 1 } + //~^ ERROR duplicate field `device` } diff --git a/tests/ui/offload/offload_macro.stderr b/tests/ui/offload/offload_macro.stderr index cd85afeab373e..e2517a2f8cca1 100644 --- a/tests/ui/offload/offload_macro.stderr +++ b/tests/ui/offload/offload_macro.stderr @@ -62,5 +62,13 @@ LL | core::offload::offload! { kernel = kernel, args = (), dyn_cache = 0, dy | = note: this error originates in the macro `$crate::offload` which comes from the expansion of the macro `core::offload::offload` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 8 previous errors +error: duplicate field `device` + --> $DIR/offload_macro.rs:30:5 + | +LL | core::offload::offload! { kernel = kernel, args = (), device = 0, device = 1 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `$crate::offload` which comes from the expansion of the macro `core::offload::offload` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 9 previous errors diff --git a/tests/ui/offload/offload_negative_device.rs b/tests/ui/offload/offload_negative_device.rs new file mode 100644 index 0000000000000..b47dc5f085315 --- /dev/null +++ b/tests/ui/offload/offload_negative_device.rs @@ -0,0 +1,12 @@ +//@ compile-flags: -Zunstable-options -Zoffload=Test -Clto=fat --emit=llvm-ir -Zdeduplicate-diagnostics=yes +//@ no-prefer-dynamic +//@ needs-offload + +#![feature(gpu_offload)] + +fn kernel() {} + +fn main() { + core::offload::offload! { kernel = kernel, args = (), device = -1 } + //~^ ERROR evaluation panicked: offload device must be non-negative; omit `device` to use the default device +} diff --git a/tests/ui/offload/offload_negative_device.stderr b/tests/ui/offload/offload_negative_device.stderr new file mode 100644 index 0000000000000..4be386dd436da --- /dev/null +++ b/tests/ui/offload/offload_negative_device.stderr @@ -0,0 +1,19 @@ +error[E0080]: evaluation panicked: offload device must be non-negative; omit `device` to use the default device + --> $DIR/offload_negative_device.rs:10:5 + | +LL | core::offload::offload! { kernel = kernel, args = (), device = -1 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `main::{constant#0}` failed here + | + = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `core::offload::offload` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/offload_negative_device.rs:10:5 + | +LL | core::offload::offload! { kernel = kernel, args = (), device = -1 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this note originates in the macro `$crate::offload` which comes from the expansion of the macro `core::offload::offload` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. From f11d76f5e6f98ef03cd7b3237d2f34a6a8b05150 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Wed, 19 Aug 2026 21:56:11 +0200 Subject: [PATCH 17/33] Allow running EC2 jobs locally --- src/ci/citool/src/jobs.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ci/citool/src/jobs.rs b/src/ci/citool/src/jobs.rs index 8b4f66c85761a..9800f14d8fa39 100644 --- a/src/ci/citool/src/jobs.rs +++ b/src/ci/citool/src/jobs.rs @@ -44,7 +44,7 @@ impl Job { } fn is_linux(&self) -> bool { - self.os.contains("ubuntu") + self.os.contains("ubuntu") || self.os.contains("linux") } } @@ -414,7 +414,10 @@ pub fn find_linux_job<'a>(jobs: &'a [Job], name: &str) -> anyhow::Result<&'a Job )); }; if !job.is_linux() { - return Err(anyhow::anyhow!("Only Linux jobs can be executed locally")); + return Err(anyhow::anyhow!( + "Only Linux jobs can be executed locally, os `{}` is not linux", + job.os + )); } Ok(job) From 6c99f69a2838ede0bdb24d9a90a4b586328e4939 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 19 Aug 2026 21:39:48 +1000 Subject: [PATCH 18/33] Fix obvious copy/paste bug in `State::fmt_diff_with` --- compiler/rustc_const_eval/src/check_consts/resolver.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_const_eval/src/check_consts/resolver.rs b/compiler/rustc_const_eval/src/check_consts/resolver.rs index 29b6e26d950d5..fa2ff057f64f2 100644 --- a/compiler/rustc_const_eval/src/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/check_consts/resolver.rs @@ -309,7 +309,7 @@ impl DebugWithContext for State { if self.borrow != old.borrow { f.write_str("borrow: ")?; - self.qualif.fmt_diff_with(&old.borrow, ctxt, f)?; + self.borrow.fmt_diff_with(&old.borrow, ctxt, f)?; f.write_str("\n")?; } From dffe2777a0963d1fb01fb3c67e85068f1510b2fa Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 19 Aug 2026 21:49:16 +1000 Subject: [PATCH 19/33] Remove unnecessary arg from `FlowSensitiveAnalysis::new` --- compiler/rustc_const_eval/src/check_consts/check.rs | 6 +++--- compiler/rustc_const_eval/src/check_consts/resolver.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 7648bf4eb241d..b79286f030be5 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -69,7 +69,7 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> { let needs_drop = self.needs_drop.get_or_insert_with(|| { let ConstCx { tcx, body, .. } = *ccx; - FlowSensitiveAnalysis::new(NeedsDrop, ccx) + FlowSensitiveAnalysis::new(ccx) .iterate_to_fixpoint(tcx, body, None) .into_results_cursor(body) }); @@ -98,7 +98,7 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> { let needs_non_const_drop = self.needs_non_const_drop.get_or_insert_with(|| { let ConstCx { tcx, body, .. } = *ccx; - FlowSensitiveAnalysis::new(NeedsNonConstDrop, ccx) + FlowSensitiveAnalysis::new(ccx) .iterate_to_fixpoint(tcx, body, None) .into_results_cursor(body) }); @@ -127,7 +127,7 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> { let has_mut_interior = self.has_mut_interior.get_or_insert_with(|| { let ConstCx { tcx, body, .. } = *ccx; - FlowSensitiveAnalysis::new(HasMutInterior, ccx) + FlowSensitiveAnalysis::new(ccx) .iterate_to_fixpoint(tcx, body, None) .into_results_cursor(body) }); diff --git a/compiler/rustc_const_eval/src/check_consts/resolver.rs b/compiler/rustc_const_eval/src/check_consts/resolver.rs index fa2ff057f64f2..4fc8c62ccf80a 100644 --- a/compiler/rustc_const_eval/src/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/check_consts/resolver.rs @@ -247,7 +247,7 @@ impl<'mir, 'tcx, Q> FlowSensitiveAnalysis<'mir, 'tcx, Q> where Q: Qualif, { - pub(super) fn new(_: Q, ccx: &'mir ConstCx<'mir, 'tcx>) -> Self { + pub(super) fn new(ccx: &'mir ConstCx<'mir, 'tcx>) -> Self { FlowSensitiveAnalysis { ccx, _qualif: PhantomData } } From c7ead52dcd58af93a3daac4ba2af452fd0795a2b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 19 Aug 2026 22:08:31 +1000 Subject: [PATCH 20/33] Factor out duplicated code in `Qualifs` --- .../src/check_consts/check.rs | 103 ++++++------------ 1 file changed, 31 insertions(+), 72 deletions(-) diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index b79286f030be5..b1efe62e57a04 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -49,69 +49,11 @@ pub(crate) struct Qualifs<'mir, 'tcx> { } impl<'mir, 'tcx> Qualifs<'mir, 'tcx> { - /// Returns `true` if `local` is `NeedsDrop` at the given `Location`. - /// - /// Only updates the cursor if absolutely necessary - pub(crate) fn needs_drop( - &mut self, - ccx: &'mir ConstCx<'mir, 'tcx>, - local: Local, - location: Location, - ) -> bool { - let ty = ccx.body.local_decls[local].ty; - // Peeking into opaque types causes cycles if the current function declares said opaque - // type. Thus we avoid short circuiting on the type and instead run the more expensive - // analysis that looks at the actual usage within this function - if !ty.has_opaque_types() && !NeedsDrop::in_any_value_of_ty(ccx, ty) { - return false; - } - - let needs_drop = self.needs_drop.get_or_insert_with(|| { - let ConstCx { tcx, body, .. } = *ccx; - - FlowSensitiveAnalysis::new(ccx) - .iterate_to_fixpoint(tcx, body, None) - .into_results_cursor(body) - }); - - needs_drop.seek_before_primary_effect(location); - needs_drop.get().contains(local) - } - - /// Returns `true` if `local` is `NeedsNonConstDrop` at the given `Location`. - /// - /// Only updates the cursor if absolutely necessary - pub(crate) fn needs_non_const_drop( - &mut self, - ccx: &'mir ConstCx<'mir, 'tcx>, - local: Local, - location: Location, - ) -> bool { - let ty = ccx.body.local_decls[local].ty; - // Peeking into opaque types causes cycles if the current function declares said opaque - // type. Thus we avoid short circuiting on the type and instead run the more expensive - // analysis that looks at the actual usage within this function - if !ty.has_opaque_types() && !NeedsNonConstDrop::in_any_value_of_ty(ccx, ty) { - return false; - } - - let needs_non_const_drop = self.needs_non_const_drop.get_or_insert_with(|| { - let ConstCx { tcx, body, .. } = *ccx; - - FlowSensitiveAnalysis::new(ccx) - .iterate_to_fixpoint(tcx, body, None) - .into_results_cursor(body) - }); - - needs_non_const_drop.seek_before_primary_effect(location); - needs_non_const_drop.get().contains(local) - } - - /// Returns `true` if `local` is `HasMutInterior` at the given `Location`. + /// Does `Q` hold for the `local` at the given `Location`? /// /// Only updates the cursor if absolutely necessary. - fn has_mut_interior( - &mut self, + fn in_local( + qualif_results: &mut Option>, ccx: &'mir ConstCx<'mir, 'tcx>, local: Local, location: Location, @@ -119,12 +61,12 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> { let ty = ccx.body.local_decls[local].ty; // Peeking into opaque types causes cycles if the current function declares said opaque // type. Thus we avoid short circuiting on the type and instead run the more expensive - // analysis that looks at the actual usage within this function - if !ty.has_opaque_types() && !HasMutInterior::in_any_value_of_ty(ccx, ty) { + // analysis that looks at the actual usage within this function. + if !ty.has_opaque_types() && !Q::in_any_value_of_ty(ccx, ty) { return false; } - let has_mut_interior = self.has_mut_interior.get_or_insert_with(|| { + let qualif_results = qualif_results.get_or_insert_with(|| { let ConstCx { tcx, body, .. } = *ccx; FlowSensitiveAnalysis::new(ccx) @@ -132,8 +74,8 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> { .into_results_cursor(body) }); - has_mut_interior.seek_before_primary_effect(location); - has_mut_interior.get().contains(local) + qualif_results.seek_before_primary_effect(location); + qualif_results.get().contains(local) } fn in_return_place( @@ -161,9 +103,19 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> { let return_loc = ccx.body.terminator_loc(return_block); ConstQualifs { - needs_drop: self.needs_drop(ccx, RETURN_PLACE, return_loc), - needs_non_const_drop: self.needs_non_const_drop(ccx, RETURN_PLACE, return_loc), - has_mut_interior: self.has_mut_interior(ccx, RETURN_PLACE, return_loc), + needs_drop: Self::in_local(&mut self.needs_drop, ccx, RETURN_PLACE, return_loc), + needs_non_const_drop: Self::in_local( + &mut self.needs_non_const_drop, + ccx, + RETURN_PLACE, + return_loc, + ), + has_mut_interior: Self::in_local( + &mut self.has_mut_interior, + ccx, + RETURN_PLACE, + return_loc, + ), tainted_by_errors, } } @@ -435,7 +387,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { let ty_of_dropped_place = dropped_place.ty(self.body, self.tcx).ty; let needs_drop = if let Some(local) = dropped_place.as_local() { - self.qualifs.needs_drop(self.ccx, local, location) + Qualifs::in_local(&mut self.qualifs.needs_drop, self.ccx, local, location) } else { qualifs::NeedsDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place) }; @@ -448,7 +400,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { let needs_non_const_drop = if let Some(local) = dropped_place.as_local() { // Use the span where the local was declared as the span of the drop error. err_span = self.body.local_decls[local].source_info.span; - self.qualifs.needs_non_const_drop(self.ccx, local, location) + Qualifs::in_local(&mut self.qualifs.needs_non_const_drop, self.ccx, local, location) } else { qualifs::NeedsNonConstDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place) }; @@ -602,7 +554,14 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { | Rvalue::RawPtr(RawPtrKind::Const, place) => { let borrowed_place_has_mut_interior = qualifs::in_place::( self.ccx, - &mut |local| self.qualifs.has_mut_interior(self.ccx, local, location), + &mut |local| { + Qualifs::in_local( + &mut self.qualifs.has_mut_interior, + self.ccx, + local, + location, + ) + }, place.as_ref(), ); From 6229d6d70ef5d4d220f7db9b8d2cdab08b369253 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 20 Aug 2026 08:49:34 +1000 Subject: [PATCH 21/33] Remove defaults for `Qualif` assoc consts There are only three impls of `Qualif`, and the extra explicitness is clearer. --- compiler/rustc_const_eval/src/check_consts/qualifs.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index b2b8a567860e0..daac2d5176258 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -45,10 +45,10 @@ pub trait Qualif { const ANALYSIS_NAME: &'static str; /// Whether this `Qualif` is cleared when a local is moved from. - const IS_CLEARED_ON_MOVE: bool = false; + const IS_CLEARED_ON_MOVE: bool; /// Whether this `Qualif` might be evaluated after the promotion and can encounter a promoted. - const ALLOW_PROMOTED: bool = false; + const ALLOW_PROMOTED: bool; /// Extracts the field of `ConstQualifs` that corresponds to this `Qualif`. fn in_qualifs(qualifs: &ConstQualifs) -> bool; @@ -79,6 +79,8 @@ pub struct HasMutInterior; impl Qualif for HasMutInterior { const ANALYSIS_NAME: &'static str = "flow_has_mut_interior"; + const IS_CLEARED_ON_MOVE: bool = false; + const ALLOW_PROMOTED: bool = false; fn in_qualifs(qualifs: &ConstQualifs) -> bool { qualifs.has_mut_interior From bb9cd32e38d137738e880a958f5048df228e285f Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 18 Aug 2026 16:23:09 +1000 Subject: [PATCH 22/33] Pre-adjust visibility of pub items in `lib.rs` --- src/bootstrap/src/core/builder/mod.rs | 2 +- src/bootstrap/src/lib.rs | 42 ++++++++++++++------------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 6f30f3b56f8b8..3d581fe1e1061 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -40,7 +40,7 @@ mod tests; /// Builds and performs different [`Self::kind`]s of stuff and actions, taking /// into account build configuration from e.g. bootstrap.toml. -pub struct Builder<'a> { +pub(crate) struct Builder<'a> { /// Build configuration from e.g. bootstrap.toml. pub build: &'a Build, diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 92e36155ffa22..b24ffe0740089 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -53,7 +53,7 @@ pub mod cli_main; mod core; mod utils; -pub enum GitRepo { +pub(crate) enum GitRepo { Rustc, Llvm, } @@ -68,7 +68,7 @@ pub enum GitRepo { /// although most functions are implemented as free functions rather than /// methods specifically on this structure itself (to make it easier to /// organize). -pub struct Build { +pub(crate) struct Build { /// User-specified configuration from `bootstrap.toml`. config: Config, @@ -130,7 +130,7 @@ pub struct Build { /// When building Rust various objects are handled differently. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub enum DependencyType { +pub(crate) enum DependencyType { /// Libraries originating from proc-macros. Host, /// Typical Rust libraries. @@ -144,7 +144,7 @@ pub enum DependencyType { /// These entries currently correspond to the various output directories of the /// build system, with each mod generating output in a different directory. #[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)] -pub enum Mode { +pub(crate) enum Mode { /// Build the standard library, placing output in the "stageN-std" directory. Std, @@ -193,7 +193,7 @@ pub enum Mode { } impl Mode { - pub fn must_support_dlopen(&self) -> bool { + pub(crate) fn must_support_dlopen(&self) -> bool { match self { Mode::Std | Mode::Codegen => true, Mode::ToolBootstrap @@ -208,7 +208,7 @@ impl Mode { /// When `rust.rust_remap_debuginfo` is requested, the compiler needs to know how to /// opportunistically unremap compiler vs non-compiler sources. We use two schemes, /// [`RemapScheme::Compiler`] and [`RemapScheme::NonCompiler`]. -pub enum RemapScheme { +pub(crate) enum RemapScheme { /// The [`RemapScheme::Compiler`] scheme will remap to `/rustc-dev/{hash}`. Compiler, /// The [`RemapScheme::NonCompiler`] scheme will remap to `/rustc/{hash}`. @@ -216,13 +216,13 @@ pub enum RemapScheme { } #[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)] -pub enum CLang { +pub(crate) enum CLang { C, Cxx, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum FileType { +pub(crate) enum FileType { /// An executable binary file (like a `.exe`). Executable, /// A native, binary library file (like a `.so`, `.dll`, `.a`, `.lib` or `.o`). @@ -235,14 +235,14 @@ pub enum FileType { impl FileType { /// Get Unix permissions appropriate for this file type. - pub fn perms(self) -> u32 { + pub(crate) fn perms(self) -> u32 { match self { FileType::Executable | FileType::Script => 0o755, FileType::Regular | FileType::NativeLibrary => 0o644, } } - pub fn could_have_split_debuginfo(self) -> bool { + pub(crate) fn could_have_split_debuginfo(self) -> bool { match self { FileType::Executable | FileType::NativeLibrary => true, FileType::Script | FileType::Regular => false, @@ -515,7 +515,7 @@ impl Build { fields(submodule = submodule), ), )] - pub fn require_submodule(&self, submodule: &str, err_hint: Option<&str>) { + pub(crate) fn require_submodule(&self, submodule: &str, err_hint: Option<&str>) { if self.rust_info().is_from_tarball() { return; } @@ -589,7 +589,7 @@ impl Build { /// Executes the entire build, as configured by the flags and configuration. #[cfg_attr(feature = "tracing", instrument(level = "debug", name = "Build::build", skip_all))] - pub fn build(&mut self) { + pub(crate) fn build(&mut self) { trace!("setting up job management"); unsafe { crate::utils::job::setup(self); @@ -1558,7 +1558,7 @@ impl Build { /// If `src` is a symlink, `src` will be resolved to the actual path /// and copied to `dst` instead of the symlink itself. #[track_caller] - pub fn resolve_symlink_and_copy(&self, src: &Path, dst: &Path) { + pub(crate) fn resolve_symlink_and_copy(&self, src: &Path, dst: &Path) { self.copy_link_internal(src, dst, true); } @@ -1567,7 +1567,7 @@ impl Build { /// You can neither rely on this being a copy nor it being a link, /// so do not write to dst. #[track_caller] - pub fn copy_link(&self, src: &Path, dst: &Path, file_type: FileType) { + pub(crate) fn copy_link(&self, src: &Path, dst: &Path, file_type: FileType) { self.copy_link_internal(src, dst, false); if file_type.could_have_split_debuginfo() @@ -1636,7 +1636,7 @@ impl Build { /// when this function is called. /// Will attempt to use hard links if possible and fall back to copying. #[track_caller] - pub fn cp_link_r(&self, src: &Path, dst: &Path) { + pub(crate) fn cp_link_r(&self, src: &Path, dst: &Path) { if self.config.dry_run() { return; } @@ -1659,7 +1659,7 @@ impl Build { /// Unwanted files or directories can be skipped /// by returning `false` from the filter function. #[track_caller] - pub fn cp_link_filtered(&self, src: &Path, dst: &Path, filter: &dyn Fn(&Path) -> bool) { + pub(crate) fn cp_link_filtered(&self, src: &Path, dst: &Path, filter: &dyn Fn(&Path) -> bool) { // Immediately recurse with an empty relative path self.cp_link_filtered_recurse(src, dst, Path::new(""), filter) } @@ -1826,11 +1826,12 @@ to download LLVM rather than building it. self.config.ninja_in_file } - pub fn colored_stdout R>(&self, f: F) -> R { + pub(crate) fn colored_stdout R>(&self, f: F) -> R { self.colored_stream_inner(StandardStream::stdout, self.config.stdout_is_tty, f) } - pub fn colored_stderr R>(&self, f: F) -> R { + #[expect(dead_code, reason = "symmetric with `colored_stdout`")] + pub(crate) fn colored_stderr R>(&self, f: F) -> R { self.colored_stream_inner(StandardStream::stderr, self.config.stderr_is_tty, f) } @@ -1851,12 +1852,13 @@ to download LLVM rather than building it. result } - pub fn report_summary(&self, path: &Path, start_time: Instant) { + #[cfg_attr(not(feature = "tracing"), expect(dead_code))] + pub(crate) fn report_summary(&self, path: &Path, start_time: Instant) { self.config.exec_ctx.profiler().report_summary(path, start_time); } #[cfg(feature = "tracing")] - pub fn report_step_graph(self, directory: &Path) { + pub(crate) fn report_step_graph(self, directory: &Path) { self.step_graph.into_inner().store_to_dot_files(directory); } } From 651d501b45e06ac966537763a983e3ea6ac576cd Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 17 Aug 2026 13:56:38 +1000 Subject: [PATCH 23/33] Pre-adjust visibility of default-visibility items in `lib.rs` Items with implicit `pub(self)` visibility in the crate root are effectively `pub(crate)`, which causes friction when trying to move them elsewhere. --- src/bootstrap/src/lib.rs | 238 +++++++++++++++++++++------------------ 1 file changed, 131 insertions(+), 107 deletions(-) diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index b24ffe0740089..1fe73fb78f819 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -70,62 +70,62 @@ pub(crate) enum GitRepo { /// organize). pub(crate) struct Build { /// User-specified configuration from `bootstrap.toml`. - config: Config, + pub(crate) config: Config, // Version information - version: String, + pub(crate) version: String, // Properties derived from the above configuration - src: PathBuf, - out: PathBuf, - bootstrap_out: PathBuf, - cargo_info: GitInfo, - rust_analyzer_info: GitInfo, - clippy_info: GitInfo, - miri_info: GitInfo, - rustfmt_info: GitInfo, - enzyme_info: GitInfo, - in_tree_llvm_info: GitInfo, - in_tree_gcc_info: GitInfo, - local_rebuild: bool, - fail_fast: bool, - test_target: TestTarget, - verbosity: usize, + pub(crate) src: PathBuf, + pub(crate) out: PathBuf, + pub(crate) bootstrap_out: PathBuf, + pub(crate) cargo_info: GitInfo, + pub(crate) rust_analyzer_info: GitInfo, + pub(crate) clippy_info: GitInfo, + pub(crate) miri_info: GitInfo, + pub(crate) rustfmt_info: GitInfo, + pub(crate) enzyme_info: GitInfo, + pub(crate) in_tree_llvm_info: GitInfo, + pub(crate) in_tree_gcc_info: GitInfo, + pub(crate) local_rebuild: bool, + pub(crate) fail_fast: bool, + pub(crate) test_target: TestTarget, + pub(crate) verbosity: usize, /// Build triple for the pre-compiled snapshot compiler. - host_target: TargetSelection, + pub(crate) host_target: TargetSelection, /// Which triples to produce a compiler toolchain for. - hosts: Vec, + pub(crate) hosts: Vec, /// Which triples to build libraries (core/alloc/std/test/proc_macro) for. - targets: Vec, + pub(crate) targets: Vec, - initial_rustc: PathBuf, - initial_rustdoc: PathBuf, - initial_cargo: PathBuf, - initial_lld: PathBuf, - initial_relative_libdir: PathBuf, - initial_sysroot: PathBuf, + pub(crate) initial_rustc: PathBuf, + pub(crate) initial_rustdoc: PathBuf, + pub(crate) initial_cargo: PathBuf, + pub(crate) initial_lld: PathBuf, + pub(crate) initial_relative_libdir: PathBuf, + pub(crate) initial_sysroot: PathBuf, // Runtime state filled in later on // C/C++ compilers and archiver for all targets - cc: HashMap, - cxx: HashMap, - ar: HashMap, - ranlib: HashMap, - wasi_sdk_path: Option, + pub(crate) cc: HashMap, + pub(crate) cxx: HashMap, + pub(crate) ar: HashMap, + pub(crate) ranlib: HashMap, + pub(crate) wasi_sdk_path: Option, // Miscellaneous // allow bidirectional lookups: both name -> path and path -> name - crates: HashMap, - crate_paths: HashMap, - is_sudo: bool, - prerelease_version: Cell>, + pub(crate) crates: HashMap, + pub(crate) crate_paths: HashMap, + pub(crate) is_sudo: bool, + pub(crate) prerelease_version: Cell>, #[cfg(feature = "build-metrics")] - metrics: crate::utils::metrics::BuildMetrics, + pub(crate) metrics: crate::utils::metrics::BuildMetrics, #[cfg(feature = "tracing")] - step_graph: std::cell::RefCell, + pub(crate) step_graph: std::cell::RefCell, } /// When building Rust various objects are handled differently. @@ -253,9 +253,11 @@ impl FileType { macro_rules! forward { ( $( $fn:ident( $($param:ident: $ty:ty),* ) $( -> $ret:ty)? ),+ $(,)? ) => { impl Build { - $( fn $fn(&self, $($param: $ty),* ) $( -> $ret)? { - self.config.$fn( $($param),* ) - } )+ + $( + pub(crate) fn $fn(&self, $($param: $ty),* ) $( -> $ret)? { + self.config.$fn( $($param),* ) + } + )+ } } } @@ -271,7 +273,7 @@ forward! { /// An alternative way of specifying what target and stage is involved in some bootstrap activity. /// Ideally using a `Compiler` directly should be preferred. -struct TargetAndStage { +pub(crate) struct TargetAndStage { target: TargetSelection, stage: u32, } @@ -550,7 +552,7 @@ impl Build { /// If any submodule has been initialized already, sync it unconditionally. /// This avoids contributors checking in a submodule change by accident. - fn update_existing_submodules(&self) { + pub(crate) fn update_existing_submodules(&self) { // Avoid running git when there isn't a git checkout, or the user has // explicitly disabled submodules in `bootstrap.toml`. if !self.config.submodules() { @@ -670,13 +672,13 @@ impl Build { self.metrics.persist(self); } - fn rust_info(&self) -> &GitInfo { + pub(crate) fn rust_info(&self) -> &GitInfo { &self.config.rust_info } /// Gets the space-separated set of activated features for the standard library. /// This can be configured with the `std-features` key in bootstrap.toml. - fn std_features(&self, target: TargetSelection) -> String { + pub(crate) fn std_features(&self, target: TargetSelection) -> String { let mut features: BTreeSet<&str> = self.config.rust_std_features.iter().map(|s| s.as_str()).collect(); @@ -703,7 +705,12 @@ impl Build { } /// Gets the space-separated set of activated features for the compiler. - fn rustc_features(&self, kind: Kind, target: TargetSelection, crates: &[String]) -> String { + pub(crate) fn rustc_features( + &self, + kind: Kind, + target: TargetSelection, + crates: &[String], + ) -> String { let possible_features_by_crates: HashSet<_> = crates .iter() .flat_map(|krate| &self.crates[krate].features) @@ -753,7 +760,7 @@ impl Build { /// Component directory that Cargo will produce output into (e.g. /// release/debug) - fn cargo_dir(&self, mode: Mode) -> &'static str { + pub(crate) fn cargo_dir(&self, mode: Mode) -> &'static str { match (mode, self.config.rust_optimize.is_release()) { (Mode::Std, _) => "dist", (_, true) => "release", @@ -761,7 +768,7 @@ impl Build { } } - fn tools_dir(&self, build_compiler: Compiler) -> PathBuf { + pub(crate) fn tools_dir(&self, build_compiler: Compiler) -> PathBuf { let out = self .out .join(build_compiler.host) @@ -774,7 +781,7 @@ impl Build { /// stage when being built with a particular build compiler. /// /// The mode indicates what the root directory is for. - fn stage_out(&self, build_compiler: Compiler, mode: Mode) -> PathBuf { + pub(crate) fn stage_out(&self, build_compiler: Compiler, mode: Mode) -> PathBuf { use std::fmt::Write; fn bootstrap_tool() -> (Option, &'static str) { @@ -814,64 +821,69 @@ impl Build { /// Returns the root output directory for all Cargo output in a given stage, /// running a particular compiler, whether or not we're building the /// standard library, and targeting the specified architecture. - fn cargo_out(&self, build_compiler: Compiler, mode: Mode, target: TargetSelection) -> PathBuf { + pub(crate) fn cargo_out( + &self, + build_compiler: Compiler, + mode: Mode, + target: TargetSelection, + ) -> PathBuf { self.stage_out(build_compiler, mode).join(target).join(self.cargo_dir(mode)) } /// Output directory for all documentation for a target - fn doc_out(&self, target: TargetSelection) -> PathBuf { + pub(crate) fn doc_out(&self, target: TargetSelection) -> PathBuf { self.out.join(target).join("doc") } /// Output directory for all JSON-formatted documentation for a target - fn json_doc_out(&self, target: TargetSelection) -> PathBuf { + pub(crate) fn json_doc_out(&self, target: TargetSelection) -> PathBuf { self.out.join(target).join("json-doc") } - fn test_out(&self, target: TargetSelection) -> PathBuf { + pub(crate) fn test_out(&self, target: TargetSelection) -> PathBuf { self.out.join(target).join("test") } /// Output directory for all documentation for a target - fn compiler_doc_out(&self, target: TargetSelection) -> PathBuf { + pub(crate) fn compiler_doc_out(&self, target: TargetSelection) -> PathBuf { self.out.join(target).join("compiler-doc") } /// Output directory for some generated md crate documentation for a target (temporary) - fn md_doc_out(&self, target: TargetSelection) -> PathBuf { + pub(crate) fn md_doc_out(&self, target: TargetSelection) -> PathBuf { self.out.join(target).join("md-doc") } /// Path to the vendored Rust crates. - fn vendored_crates_path(&self) -> Option { + pub(crate) fn vendored_crates_path(&self) -> Option { if self.config.vendor { Some(self.src.join(VENDOR_DIR)) } else { None } } /// Directory for libraries built from C/C++ code and shared between stages. - fn native_dir(&self, target: TargetSelection) -> PathBuf { + pub(crate) fn native_dir(&self, target: TargetSelection) -> PathBuf { self.out.join(target).join("native") } /// Root output directory for rust_test_helpers library compiled for /// `target` - fn test_helpers_out(&self, target: TargetSelection) -> PathBuf { + pub(crate) fn test_helpers_out(&self, target: TargetSelection) -> PathBuf { self.native_dir(target).join("rust-test-helpers") } /// Adds the `RUST_TEST_THREADS` env var if necessary - fn add_rust_test_threads(&self, cmd: &mut BootstrapCommand) { + pub(crate) fn add_rust_test_threads(&self, cmd: &mut BootstrapCommand) { if env::var_os("RUST_TEST_THREADS").is_none() { cmd.env("RUST_TEST_THREADS", self.jobs().to_string()); } } /// Returns the libdir of the snapshot compiler. - fn rustc_snapshot_libdir(&self) -> PathBuf { + pub(crate) fn rustc_snapshot_libdir(&self) -> PathBuf { self.rustc_snapshot_sysroot().join(libdir(self.config.host_target)) } /// Returns the sysroot of the snapshot compiler. - fn rustc_snapshot_sysroot(&self) -> &Path { + pub(crate) fn rustc_snapshot_sysroot(&self) -> &Path { static SYSROOT_CACHE: OnceLock = OnceLock::new(); SYSROOT_CACHE.get_or_init(|| { command(&self.initial_rustc) @@ -885,7 +897,7 @@ impl Build { }) } - fn info(&self, msg: &str) { + pub(crate) fn info(&self, msg: &str) { match self.config.get_dry_run() { DryRun::SelfCheck => (), DryRun::Disabled | DryRun::UserSelected => { @@ -907,7 +919,7 @@ impl Build { /// [`Step`]: crate::core::builder::Step #[must_use = "Groups should not be dropped until the Step finishes running"] #[track_caller] - fn msg( + pub(crate) fn msg( &self, action: impl Into, what: impl Display, @@ -964,7 +976,7 @@ impl Build { /// [`Step`]: crate::core::builder::Step #[must_use = "Groups should not be dropped until the Step finishes running"] #[track_caller] - fn msg_test( + pub(crate) fn msg_test( &self, what: impl Display, target: TargetSelection, @@ -980,7 +992,7 @@ impl Build { /// [`Step`]: crate::core::builder::Step #[must_use = "Groups should not be dropped until the Step finishes running"] #[track_caller] - fn msg_unstaged( + pub(crate) fn msg_unstaged( &self, action: impl Into, what: impl Display, @@ -992,7 +1004,7 @@ impl Build { } #[track_caller] - fn group(&self, msg: &str) -> Option { + pub(crate) fn group(&self, msg: &str) -> Option { match self.config.get_dry_run() { DryRun::SelfCheck => None, DryRun::Disabled | DryRun::UserSelected => Some(gha::group(msg)), @@ -1001,13 +1013,17 @@ impl Build { /// Returns the number of parallel jobs that have been configured for this /// build. - fn jobs(&self) -> u32 { + pub(crate) fn jobs(&self) -> u32 { self.config.jobs.unwrap_or_else(|| { std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) as u32 }) } - fn debuginfo_map_to(&self, which: GitRepo, remap_scheme: RemapScheme) -> Option { + pub(crate) fn debuginfo_map_to( + &self, + which: GitRepo, + remap_scheme: RemapScheme, + ) -> Option { if !self.config.rust_remap_debuginfo { return None; } @@ -1039,7 +1055,7 @@ impl Build { } /// Returns the path to the C compiler for the target specified. - fn cc(&self, target: TargetSelection) -> PathBuf { + pub(crate) fn cc(&self, target: TargetSelection) -> PathBuf { if self.config.dry_run() { return PathBuf::new(); } @@ -1047,18 +1063,18 @@ impl Build { } /// Returns the internal `cc::Tool` for the C compiler. - fn cc_tool(&self, target: TargetSelection) -> cc::Tool { + pub(crate) fn cc_tool(&self, target: TargetSelection) -> cc::Tool { self.cc[&target].clone() } /// Returns the internal `cc::Tool` for the C++ compiler. - fn cxx_tool(&self, target: TargetSelection) -> cc::Tool { + pub(crate) fn cxx_tool(&self, target: TargetSelection) -> cc::Tool { self.cxx[&target].clone() } /// Returns C flags that `cc-rs` thinks should be enabled for the /// specified target by default. - fn cc_handled_cflags(&self, target: TargetSelection, c: CLang) -> Vec { + pub(crate) fn cc_handled_cflags(&self, target: TargetSelection, c: CLang) -> Vec { if self.config.dry_run() { return Vec::new(); } @@ -1077,7 +1093,7 @@ impl Build { } /// Returns extra C flags that `cc-rs` doesn't handle. - fn cc_unhandled_cflags( + pub(crate) fn cc_unhandled_cflags( &self, target: TargetSelection, which: GitRepo, @@ -1113,7 +1129,7 @@ impl Build { } /// Returns the path to the `ar` archive utility for the target specified. - fn ar(&self, target: TargetSelection) -> Option { + pub(crate) fn ar(&self, target: TargetSelection) -> Option { if self.config.dry_run() { return None; } @@ -1121,7 +1137,7 @@ impl Build { } /// Returns the path to the `ranlib` utility for the target specified. - fn ranlib(&self, target: TargetSelection) -> Option { + pub(crate) fn ranlib(&self, target: TargetSelection) -> Option { if self.config.dry_run() { return None; } @@ -1129,7 +1145,7 @@ impl Build { } /// Returns the path to the C++ compiler for the target specified. - fn cxx(&self, target: TargetSelection) -> Result { + pub(crate) fn cxx(&self, target: TargetSelection) -> Result { if self.config.dry_run() { return Ok(PathBuf::new()); } @@ -1140,7 +1156,7 @@ impl Build { } /// Returns the path to the linker for the given target if it needs to be overridden. - fn linker(&self, target: TargetSelection) -> Option { + pub(crate) fn linker(&self, target: TargetSelection) -> Option { if self.config.dry_run() { return Some(PathBuf::new()); } @@ -1172,12 +1188,12 @@ impl Build { // Is LLD configured directly through `-Clinker`? // Only MSVC targets use LLD directly at the moment. - fn is_lld_direct_linker(&self, target: TargetSelection) -> bool { + pub(crate) fn is_lld_direct_linker(&self, target: TargetSelection) -> bool { target.is_msvc() } /// Returns if this target should statically link the C runtime, if specified - fn crt_static(&self, target: TargetSelection) -> Option { + pub(crate) fn crt_static(&self, target: TargetSelection) -> Option { if target.contains("pc-windows-msvc") { Some(true) } else { @@ -1189,7 +1205,7 @@ impl Build { /// /// If this is a native target (host is also musl) and no musl-root is given, /// it falls back to the system toolchain in /usr. - fn musl_root(&self, target: TargetSelection) -> Option<&Path> { + pub(crate) fn musl_root(&self, target: TargetSelection) -> Option<&Path> { let configured_root = self .config .target_config @@ -1206,7 +1222,7 @@ impl Build { } /// Returns the "musl libdir" for this `target`. - fn musl_libdir(&self, target: TargetSelection) -> Option { + pub(crate) fn musl_libdir(&self, target: TargetSelection) -> Option { self.config .target_config .get(&target) @@ -1220,7 +1236,7 @@ impl Build { /// This first consults `wasi-root` as configured in per-target /// configuration, and failing that it assumes that `$WASI_SDK_PATH` is /// set in the environment, and failing that `None` is returned. - fn wasi_libdir(&self, target: TargetSelection) -> Option { + pub(crate) fn wasi_libdir(&self, target: TargetSelection) -> Option { let configured = self.config.target_config.get(&target).and_then(|t| t.wasi_root.as_ref()).map(|p| &**p); if let Some(path) = configured { @@ -1235,13 +1251,13 @@ impl Build { } /// Returns `true` if this is a no-std `target`, if defined - fn no_std(&self, target: TargetSelection) -> Option { + pub(crate) fn no_std(&self, target: TargetSelection) -> Option { self.config.target_config.get(&target).map(|t| t.no_std) } /// Returns `true` if the target will be tested using the `remote-test-client` /// and `remote-test-server` binaries. - fn remote_tested(&self, target: TargetSelection) -> bool { + pub(crate) fn remote_tested(&self, target: TargetSelection) -> bool { self.qemu_rootfs(target).is_some() || target.contains("android") || env::var_os("TEST_DEVICE_ADDR").is_some() @@ -1252,7 +1268,7 @@ impl Build { /// /// An example of this would be a WebAssembly runtime when testing the wasm /// targets. - fn runner(&self, target: TargetSelection) -> Option { + pub(crate) fn runner(&self, target: TargetSelection) -> Option { let configured_runner = self.config.target_config.get(&target).and_then(|t| t.runner.as_ref()).map(|p| &**p); if let Some(runner) = configured_runner { @@ -1301,7 +1317,7 @@ impl Build { /// /// This requires that both the `extended` key is set and the `tools` key is /// either unset or specifically contains the specified tool. - fn tool_enabled(&self, tool: &str) -> bool { + pub(crate) fn tool_enabled(&self, tool: &str) -> bool { if !self.config.extended { return false; } @@ -1316,12 +1332,12 @@ impl Build { /// /// If `Some` is returned then that means that tests for this target are /// emulated with QEMU and binaries will need to be shipped to the emulator. - fn qemu_rootfs(&self, target: TargetSelection) -> Option<&Path> { + pub(crate) fn qemu_rootfs(&self, target: TargetSelection) -> Option<&Path> { self.config.target_config.get(&target).and_then(|t| t.qemu_rootfs.as_ref()).map(|p| &**p) } /// Temporary directory that extended error information is emitted to. - fn extended_error_dir(&self) -> PathBuf { + pub(crate) fn extended_error_dir(&self) -> PathBuf { self.out.join("tmp/extended-error-metadata") } @@ -1343,7 +1359,7 @@ impl Build { /// /// When all of these conditions are met the build will lift artifacts from /// the previous stage forward. - fn force_use_stage1(&self, stage: u32, target: TargetSelection) -> bool { + pub(crate) fn force_use_stage1(&self, stage: u32, target: TargetSelection) -> bool { !self.config.full_bootstrap && !self.config.download_rustc() && stage >= 2 @@ -1355,7 +1371,7 @@ impl Build { /// /// When we download the pre-compiled version of rustc and compiler stage is >= 2, /// it should be forced to use a stage2 compiler. - fn force_use_stage2(&self, stage: u32) -> bool { + pub(crate) fn force_use_stage2(&self, stage: u32) -> bool { self.config.download_rustc() && stage >= 2 } @@ -1364,7 +1380,7 @@ impl Build { /// /// For example on nightly this returns "a.b.c-nightly", on beta it returns /// "a.b.c-beta.1" and on stable it just returns "a.b.c". - fn release(&self, num: &str) -> String { + pub(crate) fn release(&self, num: &str) -> String { match &self.config.channel[..] { "stable" => num.to_string(), "beta" => { @@ -1415,7 +1431,7 @@ impl Build { } /// Returns the value of `release` above for Rust itself. - fn rust_release(&self) -> String { + pub(crate) fn rust_release(&self) -> String { self.release(&self.version) } @@ -1424,7 +1440,7 @@ impl Build { /// The package version is typically what shows up in the names of tarballs. /// For channels like beta/nightly it's just the channel name, otherwise it's the release /// version. - fn rust_package_vers(&self) -> String { + pub(crate) fn rust_package_vers(&self) -> String { match &self.config.channel[..] { "stable" => self.version.to_string(), "beta" => "beta".to_string(), @@ -1438,7 +1454,7 @@ impl Build { /// /// Note that this is a descriptive string which includes the commit date, /// sha, version, etc. - fn rust_version(&self) -> String { + pub(crate) fn rust_version(&self) -> String { let mut version = self.rust_info().version(self, &self.version); if let Some(ref s) = self.config.description && !s.is_empty() @@ -1451,12 +1467,12 @@ impl Build { } /// Returns the full commit hash. - fn rust_sha(&self) -> Option<&str> { + pub(crate) fn rust_sha(&self) -> Option<&str> { self.rust_info().sha() } /// Returns the `a.b.c` version that the given package is at. - fn release_num(&self, package: &str) -> String { + pub(crate) fn release_num(&self, package: &str) -> String { if self.config.dry_run() { return "0.0.0 (dry-run)".into(); } @@ -1475,14 +1491,18 @@ impl Build { /// Returns `true` if unstable features should be enabled for the compiler /// we're building. - fn unstable_features(&self) -> bool { + pub(crate) fn unstable_features(&self) -> bool { !matches!(&self.config.channel[..], "stable" | "beta") } /// Returns a Vec of all the dependencies of the given root crate, /// including transitive dependencies and the root itself. Only includes /// "local" crates (those in the local source tree, not from a registry). - fn in_tree_crates(&self, root: &str, target: Option) -> Vec<&Crate> { + pub(crate) fn in_tree_crates( + &self, + root: &str, + target: Option, + ) -> Vec<&Crate> { let mut ret = Vec::new(); let mut list = vec![root.to_owned()]; let mut visited = HashSet::new(); @@ -1520,7 +1540,7 @@ impl Build { ret } - fn read_stamp_file(&self, stamp: &BuildStamp) -> Vec<(PathBuf, DependencyType)> { + pub(crate) fn read_stamp_file(&self, stamp: &BuildStamp) -> Vec<(PathBuf, DependencyType)> { if self.config.dry_run() { return Vec::new(); } @@ -1691,13 +1711,13 @@ impl Build { } } - fn copy_link_to_folder(&self, src: &Path, dest_folder: &Path) { + pub(crate) fn copy_link_to_folder(&self, src: &Path, dest_folder: &Path) { let file_name = src.file_name().unwrap(); let dest = dest_folder.join(file_name); self.copy_link(src, &dest, FileType::Regular); } - fn install(&self, src: &Path, dstdir: &Path, file_type: FileType) { + pub(crate) fn install(&self, src: &Path, dstdir: &Path, file_type: FileType) { if self.config.dry_run() { return; } @@ -1722,7 +1742,7 @@ impl Build { } } - fn read(&self, path: &Path) -> String { + pub(crate) fn read(&self, path: &Path) -> String { if self.config.dry_run() { return String::new(); } @@ -1730,7 +1750,7 @@ impl Build { } #[track_caller] - fn create_dir(&self, dir: &Path) { + pub(crate) fn create_dir(&self, dir: &Path) { if self.config.dry_run() { return; } @@ -1741,7 +1761,7 @@ impl Build { t!(fs::create_dir_all(dir)) } - fn remove_dir(&self, dir: &Path) { + pub(crate) fn remove_dir(&self, dir: &Path) { if self.config.dry_run() { return; } @@ -1754,7 +1774,7 @@ impl Build { /// Make sure that `dir` will be an empty existing directory after this function ends. /// If it existed before, it will be first deleted. - fn clear_dir(&self, dir: &Path) { + pub(crate) fn clear_dir(&self, dir: &Path) { if self.config.dry_run() { return; } @@ -1766,7 +1786,7 @@ impl Build { self.create_dir(dir); } - fn read_dir(&self, dir: &Path) -> impl Iterator { + pub(crate) fn read_dir(&self, dir: &Path) -> impl Iterator { let iter = match fs::read_dir(dir) { Ok(v) => v, Err(_) if self.config.dry_run() => return vec![].into_iter(), @@ -1775,7 +1795,11 @@ impl Build { iter.map(|e| t!(e)).collect::>().into_iter() } - fn symlink_file, Q: AsRef>(&self, src: P, link: Q) -> io::Result<()> { + pub(crate) fn symlink_file, Q: AsRef>( + &self, + src: P, + link: Q, + ) -> io::Result<()> { #[cfg(unix)] use std::os::unix::fs::symlink as symlink_file; #[cfg(windows)] @@ -1785,7 +1809,7 @@ impl Build { /// Returns if config.ninja is enabled, and checks for ninja existence, /// exiting with a nicer error message if not. - fn ninja(&self) -> bool { + pub(crate) fn ninja(&self) -> bool { let mut cmd_finder = crate::core::sanity::Finder::new(); if self.config.ninja_in_file { From 72e9e0e65a97a587d4a43ab23242e1bca437d517 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 17 Aug 2026 14:03:42 +1000 Subject: [PATCH 24/33] Pre-adjust imports from the crate root --- src/bootstrap/src/cli_main.rs | 3 ++- src/bootstrap/src/core/build_steps/check.rs | 2 +- src/bootstrap/src/core/build_steps/clean.rs | 2 +- src/bootstrap/src/core/build_steps/clippy.rs | 2 +- src/bootstrap/src/core/build_steps/compile.rs | 3 ++- src/bootstrap/src/core/build_steps/dist.rs | 3 ++- src/bootstrap/src/core/build_steps/doc.rs | 2 +- src/bootstrap/src/core/build_steps/llvm.rs | 3 ++- src/bootstrap/src/core/build_steps/run.rs | 2 +- src/bootstrap/src/core/build_steps/test.rs | 2 +- src/bootstrap/src/core/build_steps/tool.rs | 2 +- src/bootstrap/src/core/builder/cargo.rs | 2 +- src/bootstrap/src/core/builder/cli_paths/tests.rs | 2 +- src/bootstrap/src/core/builder/mod.rs | 3 ++- src/bootstrap/src/core/compiler.rs | 2 +- src/bootstrap/src/core/config/config.rs | 2 +- src/bootstrap/src/core/config/flags.rs | 2 +- src/bootstrap/src/core/metadata.rs | 2 +- src/bootstrap/src/core/mod.rs | 3 +++ src/bootstrap/src/core/sanity.rs | 2 +- src/bootstrap/src/utils/build_stamp.rs | 2 +- src/bootstrap/src/utils/cc_detect.rs | 2 +- src/bootstrap/src/utils/cc_detect/tests.rs | 2 +- src/bootstrap/src/utils/channel.rs | 2 +- src/bootstrap/src/utils/job.rs | 10 +++++----- src/bootstrap/src/utils/metrics.rs | 2 +- src/bootstrap/src/utils/tarball.rs | 2 +- 27 files changed, 38 insertions(+), 30 deletions(-) diff --git a/src/bootstrap/src/cli_main.rs b/src/bootstrap/src/cli_main.rs index c11e1478f4d42..8a74b2e598283 100644 --- a/src/bootstrap/src/cli_main.rs +++ b/src/bootstrap/src/cli_main.rs @@ -16,11 +16,12 @@ use std::{env, process}; use crate::core::builder::StepStack; use crate::core::config::flags::{Flags, Subcommand}; use crate::core::config::{ChangeId, Config}; +use crate::core::session::Build; +use crate::debug; use crate::utils::change_tracker::{ CONFIG_CHANGE_HISTORY, find_recent_config_change_ids, human_readable_changes, }; use crate::utils::helpers::t; -use crate::{Build, debug}; fn is_tracing_enabled() -> bool { cfg!(feature = "tracing") diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 265999711eb87..4a75cdbb1562f 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -3,7 +3,6 @@ use std::fs; use std::path::{Path, PathBuf}; -use crate::Mode; use crate::core::backend::CodegenBackendKind; use crate::core::build_steps::compile::{ ArtifactKeepMode, add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, @@ -20,6 +19,7 @@ use crate::core::builder::{ }; use crate::core::compiler::Compiler; use crate::core::config::TargetSelection; +use crate::core::session::Mode; use crate::utils::build_stamp::{self, BuildStamp}; use crate::utils::helpers::t; diff --git a/src/bootstrap/src/core/build_steps/clean.rs b/src/bootstrap/src/core/build_steps/clean.rs index a5c7398d11302..23f12bbb63e72 100644 --- a/src/bootstrap/src/core/build_steps/clean.rs +++ b/src/bootstrap/src/core/build_steps/clean.rs @@ -14,9 +14,9 @@ use crate::core::builder::{ }; use crate::core::compiler::Compiler; use crate::core::config::flags::Subcommand; +use crate::core::session::{Build, Mode}; use crate::utils::build_stamp::BuildStamp; use crate::utils::helpers::t; -use crate::{Build, Mode}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CleanAll {} diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index 2ca775f92d683..dc3e3efb80ee5 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -14,7 +14,6 @@ //! (as usual) a massive undertaking/refactoring. use super::tool::{SourceType, prepare_tool_cargo}; -use crate::Mode; use crate::core::build_steps::check::{CompilerForCheck, prepare_compiler_for_check}; use crate::core::build_steps::compile::{ ArtifactKeepMode, run_cargo, rustc_cargo, std_cargo, std_crates_for_make_run, @@ -26,6 +25,7 @@ use crate::core::builder::{ use crate::core::compiler::Compiler; use crate::core::config::TargetSelection; use crate::core::config::flags::Subcommand; +use crate::core::session::Mode; use crate::utils::build_stamp::{self, BuildStamp}; use crate::utils::helpers; diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index c008ff5090a9c..fcf8be30c9dd1 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -33,13 +33,14 @@ use crate::core::config::toml::target::DefaultLinuxLinkerOverride; use crate::core::config::{ Allocator, CompilerBuiltins, DebuginfoLevel, LlvmLibunwind, RustcLto, TargetSelection, }; +use crate::core::session::{CLang, DependencyType, FileType, GitRepo, Mode}; use crate::utils::build_stamp; use crate::utils::build_stamp::BuildStamp; use crate::utils::exec::command; use crate::utils::helpers::{ self, exe, get_clang_cl_resource_dir, is_debug_info, is_dylib, symlink_dir, t, up_to_date, }; -use crate::{CLang, DependencyType, FileType, GitRepo, Mode, debug, trace}; +use crate::{debug, trace}; /// Build a standard library for the given `target` using the given `build_compiler`. #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index b5118281fab74..5c86d117767c8 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -38,6 +38,8 @@ use crate::core::builder::{ }; use crate::core::compiler::Compiler; use crate::core::config::{GccCiMode, TargetSelection}; +use crate::core::session::{DependencyType, FileType, Mode}; +use crate::trace; use crate::utils::build_stamp::{self, BuildStamp}; use crate::utils::channel::{self, Info}; use crate::utils::exec::{BootstrapCommand, command}; @@ -45,7 +47,6 @@ use crate::utils::helpers::{ exe, is_dylib, move_file, t, target_supports_cranelift_backend, timeit, }; use crate::utils::tarball::{GeneratedTarball, OverlayKind, Tarball}; -use crate::{DependencyType, FileType, Mode, trace}; pub(crate) const LLVM_TOOLS: &[&str] = &[ "llvm-cov", // used to generate coverage report diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index adff654fe88e8..b80a0b0ba27c8 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -21,8 +21,8 @@ use crate::core::builder::{ }; use crate::core::compiler::Compiler; use crate::core::config::{Config, TargetSelection}; +use crate::core::session::{FileType, Mode}; use crate::utils::helpers::{submodule_path_of, symlink_dir, t, up_to_date}; -use crate::{FileType, Mode}; macro_rules! book { ($($name:ident, $path:expr, $book_name:expr, $lang:expr ;)+) => { diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 323cce1da51ab..5d188bcd25570 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -21,12 +21,13 @@ use crate::core::builder::{ Builder, CommandLineStep, Kind, RunConfig, ShouldRun, Step, StepMetadata, }; use crate::core::config::{Config, LlvmCiMode, LlvmPgoGenerationMode, TargetSelection}; +use crate::core::session::{CLang, GitRepo}; +use crate::trace; use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash}; use crate::utils::exec::command; use crate::utils::helpers::{ self, exe, get_clang_cl_resource_dir, libdir, t, unhashed_basename, up_to_date, }; -use crate::{CLang, GitRepo, trace}; /// Path where a file containing the link type (dynamic or static) is stored in the LLVM CI tarball. pub const LLVM_CI_LINK_TYPE_PATH: &str = "link-type.txt"; diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index 82a132d0b5288..243b09acaa308 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -8,7 +8,6 @@ use std::path::PathBuf; use build_helper::git::get_git_untracked_files; use clap_complete::{Generator, shells}; -use crate::Mode; use crate::core::build_steps::dist::distdir; use crate::core::build_steps::test; use crate::core::build_steps::tool::{self, RustcPrivateCompilers, SourceType, Tool}; @@ -16,6 +15,7 @@ use crate::core::build_steps::vendor::{VENDOR_DIR, Vendor, default_paths_to_vend use crate::core::builder::{Builder, CommandLineStep, Kind, RunConfig, ShouldRun, StepMetadata}; use crate::core::config::TargetSelection; use crate::core::config::flags::{get_completion, top_level_help}; +use crate::core::session::Mode; use crate::utils::exec::command; use crate::utils::helpers::{self, t}; diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index a4eefe51c420e..be3dc2954086c 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -38,6 +38,7 @@ use crate::core::builder::{ use crate::core::compiler::Compiler; use crate::core::config::TargetSelection; use crate::core::config::flags::{Subcommand, get_completion, top_level_help}; +use crate::core::session::{CLang, GitRepo, Mode}; use crate::core::{android, debuggers}; use crate::utils::build_stamp::{self, BuildStamp}; use crate::utils::exec::{BootstrapCommand, command}; @@ -47,7 +48,6 @@ use crate::utils::helpers::{ target_supports_cranelift_backend, up_to_date, }; use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests}; -use crate::{CLang, GitRepo, Mode}; mod compiletest; pub mod failed_tests; diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index b67d1b1bd49e7..4e94a422fd153 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -23,9 +23,9 @@ use crate::core::builder::{ }; use crate::core::compiler::Compiler; use crate::core::config::{Allocator, DebuginfoLevel, RustcLto, TargetSelection}; +use crate::core::session::{FileType, Mode}; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{self, add_dylib_path, exe, t}; -use crate::{FileType, Mode}; #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum SourceType { diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index f5c28c8e0445e..7b621a2ecc834 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -11,10 +11,10 @@ use crate::core::compiler::Compiler; use crate::core::config::flags::{Color, Subcommand}; use crate::core::config::toml::pgo::PgoConfig; use crate::core::config::{CompressDebuginfo, Config, DryRun, SplitDebuginfo, TargetSelection}; +use crate::core::session::{CLang, GitRepo, Mode, RemapScheme}; use crate::utils::build_stamp; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{self, LldThreads, check_cfg_arg, envify, linker_flags, t}; -use crate::{CLang, GitRepo, Mode, RemapScheme}; /// Extra `--check-cfg` to add when building the compiler or tools /// (Mode restriction, config name, config values (if any)) diff --git a/src/bootstrap/src/core/builder/cli_paths/tests.rs b/src/bootstrap/src/core/builder/cli_paths/tests.rs index e18a274c75f49..3a92bf37bdf0a 100644 --- a/src/bootstrap/src/core/builder/cli_paths/tests.rs +++ b/src/bootstrap/src/core/builder/cli_paths/tests.rs @@ -2,8 +2,8 @@ use std::collections::{BTreeSet, HashSet}; use std::path::PathBuf; use std::sync::{Arc, Mutex}; -use crate::Build; use crate::core::builder::{Builder, CommandLineStepDescription}; +use crate::core::session::Build; use crate::utils::tests::TestCtx; fn render_steps_for_cli_args(args_str: &str) -> String { diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 3d581fe1e1061..98fceeae9df5c 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -25,12 +25,13 @@ use crate::core::compiler::Compiler; use crate::core::config::flags::Subcommand; use crate::core::config::{DryRun, TargetSelection}; use crate::core::metadata::Crate; +use crate::core::session::Build; +use crate::trace; use crate::utils::build_stamp::BuildStamp; use crate::utils::cache::Cache; use crate::utils::exec::{BootstrapCommand, ExecutionContext, command}; use crate::utils::helpers::{self, LldThreads, add_dylib_path, exe, libdir, linker_args, t}; use crate::utils::tracing::format_location; -use crate::{Build, trace}; mod cargo; mod cli_paths; diff --git a/src/bootstrap/src/core/compiler.rs b/src/bootstrap/src/core/compiler.rs index a57c60465f24c..5602e8ffd1efd 100644 --- a/src/bootstrap/src/core/compiler.rs +++ b/src/bootstrap/src/core/compiler.rs @@ -1,7 +1,7 @@ use std::hash::{Hash, Hasher}; -use crate::Build; use crate::core::config::TargetSelection; +use crate::core::session::Build; /// A structure representing a Rust compiler. /// diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 8ca1c74b929e3..f74def6a61d8b 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1845,7 +1845,7 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to /// /// This *does not* update the submodule if `bootstrap.toml` explicitly says /// not to, or if we're not in a git repository (like a plain source - /// tarball). Typically [`crate::Build::require_submodule`] should be + /// tarball). Typically [`crate::core::session::Build::require_submodule`] should be /// used instead to provide a nice error to the user if the submodule is /// missing. #[cfg_attr( diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index 56c2541161cec..da479251c68ab 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -10,7 +10,6 @@ use clap_complete::Generator; #[cfg(feature = "tracing")] use tracing::instrument; -use crate::Build; use crate::core::backend::CodegenBackendKind; use crate::core::build_steps::perf::PerfArgs; use crate::core::build_steps::setup::Profile; @@ -18,6 +17,7 @@ use crate::core::build_steps::test::TestTarget; use crate::core::builder::{Builder, Kind}; use crate::core::config::Config; use crate::core::config::target_selection::{TargetSelectionList, target_selection_list}; +use crate::core::session::Build; use crate::utils::helpers; #[derive(Copy, Clone, Default, Debug, ValueEnum)] diff --git a/src/bootstrap/src/core/metadata.rs b/src/bootstrap/src/core/metadata.rs index a3b52e1071d24..5e88277008971 100644 --- a/src/bootstrap/src/core/metadata.rs +++ b/src/bootstrap/src/core/metadata.rs @@ -11,7 +11,7 @@ use std::path::PathBuf; use serde_derive::Deserialize; -use crate::Build; +use crate::core::session::Build; use crate::utils::exec::command; use crate::utils::helpers::t; diff --git a/src/bootstrap/src/core/mod.rs b/src/bootstrap/src/core/mod.rs index d6db6c701cc35..3c79dad5b00b6 100644 --- a/src/bootstrap/src/core/mod.rs +++ b/src/bootstrap/src/core/mod.rs @@ -8,3 +8,6 @@ pub(crate) mod debuggers; pub(crate) mod download; pub(crate) mod metadata; pub(crate) mod sanity; +pub(crate) mod session { + pub(crate) use crate::*; +} diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index a8359ad34fa50..456019fc96977 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -14,11 +14,11 @@ use std::ffi::{OsStr, OsString}; use std::path::PathBuf; use std::{env, fs}; -use crate::Build; use crate::core::build_steps::tool; use crate::core::builder::Builder; use crate::core::config::flags::Subcommand; use crate::core::config::{CompilerBuiltins, DebuggerPath, Target}; +use crate::core::session::Build; use crate::utils::exec::command; use crate::utils::helpers::{self, t}; diff --git a/src/bootstrap/src/utils/build_stamp.rs b/src/bootstrap/src/utils/build_stamp.rs index d27d5fa2cf420..36a3d0772e5ad 100644 --- a/src/bootstrap/src/utils/build_stamp.rs +++ b/src/bootstrap/src/utils/build_stamp.rs @@ -7,11 +7,11 @@ use std::{fs, io}; use sha2::digest::Digest; -use crate::Mode; use crate::core::backend::CodegenBackendKind; use crate::core::builder::Builder; use crate::core::compiler::Compiler; use crate::core::config::TargetSelection; +use crate::core::session::Mode; use crate::utils::helpers::{self, hex_encode, mtime, t}; #[cfg(test)] diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs index 977b4a31eadc4..e753ee71683fd 100644 --- a/src/bootstrap/src/utils/cc_detect.rs +++ b/src/bootstrap/src/utils/cc_detect.rs @@ -27,8 +27,8 @@ use std::path::{Path, PathBuf}; use crate::core::config::flags::Subcommand; use crate::core::config::{CompressDebuginfo, TargetSelection}; +use crate::core::session::{Build, CLang, GitRepo}; use crate::utils::exec::{BootstrapCommand, command}; -use crate::{Build, CLang, GitRepo}; /// Creates and configures a new [`cc::Build`] instance for the given target. fn new_cc_build(build: &Build, target: TargetSelection) -> cc::Build { diff --git a/src/bootstrap/src/utils/cc_detect/tests.rs b/src/bootstrap/src/utils/cc_detect/tests.rs index a2f35e6a1030d..716407cb0cb1c 100644 --- a/src/bootstrap/src/utils/cc_detect/tests.rs +++ b/src/bootstrap/src/utils/cc_detect/tests.rs @@ -2,8 +2,8 @@ use std::iter; use std::path::PathBuf; use super::*; -use crate::Build; use crate::core::config::{Target, TargetSelection}; +use crate::core::session::Build; use crate::utils::tests::TestCtx; #[test] diff --git a/src/bootstrap/src/utils/channel.rs b/src/bootstrap/src/utils/channel.rs index 21b4257e54d0b..ebb40edf9b262 100644 --- a/src/bootstrap/src/utils/channel.rs +++ b/src/bootstrap/src/utils/channel.rs @@ -10,7 +10,7 @@ use std::path::Path; use super::exec::ExecutionContext; use super::helpers; -use crate::Build; +use crate::core::session::Build; use crate::utils::helpers::t; #[derive(Clone, Default)] diff --git a/src/bootstrap/src/utils/job.rs b/src/bootstrap/src/utils/job.rs index 887deb41ca8bc..942ac6c80e4ee 100644 --- a/src/bootstrap/src/utils/job.rs +++ b/src/bootstrap/src/utils/job.rs @@ -1,11 +1,13 @@ #[cfg(windows)] pub use for_windows::*; +use crate::core::session::Build; + #[cfg(any(target_os = "haiku", target_os = "hermit", not(any(unix, windows))))] -pub unsafe fn setup(_build: &mut crate::Build) {} +pub unsafe fn setup(_build: &mut Build) {} #[cfg(all(unix, not(target_os = "haiku")))] -pub unsafe fn setup(build: &mut crate::Build) { +pub unsafe fn setup(build: &mut Build) { if build.config.low_priority { unsafe { libc::setpriority(libc::PRIO_PGRP as _, 0, 10); @@ -58,9 +60,7 @@ mod for_windows { use windows::Win32::System::Threading::{BELOW_NORMAL_PRIORITY_CLASS, GetCurrentProcess}; use windows::core::PCWSTR; - use crate::Build; - - pub unsafe fn setup(build: &mut Build) { + pub unsafe fn setup(build: &mut super::Build) { // SAFETY: pretty much everything below is unsafe unsafe { // Enable the Windows Error Reporting dialog which msys disables, diff --git a/src/bootstrap/src/utils/metrics.rs b/src/bootstrap/src/utils/metrics.rs index e685c64733c66..a309b1d53b8e9 100644 --- a/src/bootstrap/src/utils/metrics.rs +++ b/src/bootstrap/src/utils/metrics.rs @@ -16,8 +16,8 @@ use build_helper::metrics::{ }; use sysinfo::{CpuRefreshKind, RefreshKind, System}; -use crate::Build; use crate::core::builder::{Builder, Step}; +use crate::core::session::Build; use crate::utils::helpers::t; // Update this number whenever a breaking change is made to the build metrics. diff --git a/src/bootstrap/src/utils/tarball.rs b/src/bootstrap/src/utils/tarball.rs index 41ad6b022ac18..3ba7dbdb984c8 100644 --- a/src/bootstrap/src/utils/tarball.rs +++ b/src/bootstrap/src/utils/tarball.rs @@ -7,10 +7,10 @@ use std::path::{Path, PathBuf}; -use crate::FileType; use crate::core::build_steps::dist::distdir; use crate::core::builder::{Builder, Kind}; use crate::core::config::BUILDER_CONFIG_FILENAME; +use crate::core::session::FileType; use crate::utils::channel; use crate::utils::exec::BootstrapCommand; use crate::utils::helpers::{self, move_file, t}; From f068a507f43170b5f853077a0050ff47600c89d6 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 18 Aug 2026 17:00:44 +1000 Subject: [PATCH 25/33] Temporarily rename `lib.rs` to `session.rs` This intermediate commit helps to preserve line history. --- src/bootstrap/Cargo.toml | 2 +- src/bootstrap/src/{lib.rs => session.rs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/bootstrap/src/{lib.rs => session.rs} (100%) diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 4dcf87e3290a2..8379b441008cb 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -10,7 +10,7 @@ build-metrics = ["dep:sysinfo", "build_helper/metrics"] tracing = ["dep:tracing", "dep:tracing-chrome", "dep:tracing-subscriber", "dep:chrono"] [lib] -path = "src/lib.rs" +path = "src/session.rs" doctest = false [[bin]] diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/session.rs similarity index 100% rename from src/bootstrap/src/lib.rs rename to src/bootstrap/src/session.rs From 91374a526c3b755b0e1e0ce78afb5e413bd933b7 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 18 Aug 2026 17:00:44 +1000 Subject: [PATCH 26/33] Move all non-module items out of the crate root --- src/bootstrap/Cargo.toml | 2 +- src/bootstrap/src/core/mod.rs | 4 +--- src/bootstrap/src/{ => core}/session.rs | 30 +++---------------------- src/bootstrap/src/lib.rs | 26 +++++++++++++++++++++ 4 files changed, 31 insertions(+), 31 deletions(-) rename src/bootstrap/src/{ => core}/session.rs (98%) create mode 100644 src/bootstrap/src/lib.rs diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 8379b441008cb..4dcf87e3290a2 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -10,7 +10,7 @@ build-metrics = ["dep:sysinfo", "build_helper/metrics"] tracing = ["dep:tracing", "dep:tracing-chrome", "dep:tracing-subscriber", "dep:chrono"] [lib] -path = "src/session.rs" +path = "src/lib.rs" doctest = false [[bin]] diff --git a/src/bootstrap/src/core/mod.rs b/src/bootstrap/src/core/mod.rs index 3c79dad5b00b6..c130051a8c7c4 100644 --- a/src/bootstrap/src/core/mod.rs +++ b/src/bootstrap/src/core/mod.rs @@ -8,6 +8,4 @@ pub(crate) mod debuggers; pub(crate) mod download; pub(crate) mod metadata; pub(crate) mod sanity; -pub(crate) mod session { - pub(crate) use crate::*; -} +pub(crate) mod session; diff --git a/src/bootstrap/src/session.rs b/src/bootstrap/src/core/session.rs similarity index 98% rename from src/bootstrap/src/session.rs rename to src/bootstrap/src/core/session.rs index 1fe73fb78f819..3e6668258c641 100644 --- a/src/bootstrap/src/session.rs +++ b/src/bootstrap/src/core/session.rs @@ -1,26 +1,3 @@ -//! Implementation of bootstrap, the Rust build system. -//! -//! This module, and its descendants, are the implementation of the Rust build -//! system. Most of this build system is backed by Cargo but the outer layer -//! here serves as the ability to orchestrate calling Cargo, sequencing Cargo -//! builds, building artifacts like LLVM, etc. The goals of bootstrap are: -//! -//! * To be an easily understandable, easily extensible, and maintainable build -//! system. -//! * Leverage standard tools in the Rust ecosystem to build the compiler, aka -//! crates.io and Cargo. -//! * A standard interface to build across all platforms, including MSVC -//! -//! ## Further information -//! -//! More documentation can be found in each respective module below, and you can -//! also check out the `src/bootstrap/README.md` file for more information. - -// tidy-alphabetical-start -#![allow(clippy::assertions_on_constants, reason = "false positive for `assert!(cfg!(..))`")] -#![allow(clippy::map_clone, reason = "false positive for `|x: &&Foo| Foo::clone(x)`")] -// tidy-alphabetical-end - use std::cell::Cell; use std::collections::{BTreeSet, HashMap, HashSet}; use std::fmt::Display; @@ -42,16 +19,15 @@ use crate::core::compiler::Compiler; use crate::core::config::flags::{self, Subcommand}; use crate::core::config::{BootstrapOverrideLld, Config, DryRun, LlvmLibunwind, TargetSelection}; use crate::core::metadata::Crate; +#[cfg(feature = "tracing")] +use crate::trace_io; use crate::utils::build_stamp::BuildStamp; use crate::utils::channel::GitInfo; use crate::utils::exec::{BootstrapCommand, ExecutionContext, command}; use crate::utils::helpers::{ self, dir_is_empty, exe, libdir, set_file_times, split_debuginfo, symlink_dir, t, }; - -pub mod cli_main; -mod core; -mod utils; +use crate::{debug, trace}; pub(crate) enum GitRepo { Rustc, diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs new file mode 100644 index 0000000000000..cafe81af4d56e --- /dev/null +++ b/src/bootstrap/src/lib.rs @@ -0,0 +1,26 @@ +//! Implementation of bootstrap, the Rust build system. +//! +//! This module, and its descendants, are the implementation of the Rust build +//! system. Most of this build system is backed by Cargo but the outer layer +//! here serves as the ability to orchestrate calling Cargo, sequencing Cargo +//! builds, building artifacts like LLVM, etc. The goals of bootstrap are: +//! +//! * To be an easily understandable, easily extensible, and maintainable build +//! system. +//! * Leverage standard tools in the Rust ecosystem to build the compiler, aka +//! crates.io and Cargo. +//! * A standard interface to build across all platforms, including MSVC +//! +//! ## Further information +//! +//! More documentation can be found in each respective module below, and you can +//! also check out the `src/bootstrap/README.md` file for more information. + +// tidy-alphabetical-start +#![allow(clippy::assertions_on_constants, reason = "false positive for `assert!(cfg!(..))`")] +#![allow(clippy::map_clone, reason = "false positive for `|x: &&Foo| Foo::clone(x)`")] +// tidy-alphabetical-end + +pub mod cli_main; +mod core; +mod utils; From 988eccabd44847eb05342c8bfca8a53f617d4bed Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sun, 16 Aug 2026 19:06:14 -0400 Subject: [PATCH 27/33] core: Make funnel shifts panic only if overflow checks are enabled Change from the existing behavior of panicking unconditionally to panicking only if debug assertions are enabled, wrapping otherwise. This is more consistent with other operations, and gives slightly better codegen [1]. Suggested in the stabilization PR, RUST-161015. [1]: https://rust.godbolt.org/z/Yz81zhbno --- library/core/src/num/uint_macros.rs | 54 +++++++++++++++++----- library/coretests/tests/lib.rs | 1 + library/coretests/tests/num/uint_macros.rs | 14 ++++++ tests/ui/std/overflow-check-ops.rs | 3 ++ 4 files changed, 60 insertions(+), 12 deletions(-) diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 1664ce83aef72..6fac5cc11e1e4 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -515,8 +515,13 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `n` is greater than or equal to the number of - /// bits in `self`. + /// ## Overflow behavior + /// + /// If overflow checks are enabled (default in debug mode), this function will panic if `n` + /// is greater than or equal to the number of bits in `self`. If overflow checks are + /// disabled (default in release mode), there is no panic; instead, the value is shifted + /// by `n % Self::BITS`. + // FIXME(wrapping_funnel_shifts): link to `wrapping_funnel_shl` when stable. /// /// # Examples /// @@ -543,21 +548,31 @@ macro_rules! uint_impl { /// /// ```should_panic /// #![feature(funnel_shifts)] + /// # #![feature(cfg_overflow_checks)] + /// # #[cfg(overflow_checks)] { /// #[doc = concat!("let a = ", stringify!($SelfT), "::MAX;")] /// // Okay #[doc = concat!("let _ = a.rotate_left(", stringify!($SelfT), "::BITS);")] - /// // Panics + /// // Panics (only when overflow checks are enabled) #[doc = concat!("let _ = a.funnel_shl(a, ", stringify!($SelfT), "::BITS);")] + /// # } + /// # #[cfg(not(overflow_checks))] panic!("fulfill should_panic"); /// ``` #[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")] #[unstable(feature = "funnel_shifts", issue = "145686")] #[must_use = "this returns the result of the operation, without modifying the original"] #[inline(always)] + #[rustc_inherit_overflow_checks] pub const fn funnel_shl(self, right: Self, n: u32) -> Self { - assert!(n < Self::BITS, "attempt to funnel shift left with overflow"); - // SAFETY: just checked that `shift` is in-range - unsafe { self.unchecked_funnel_shl(right, n) } + if intrinsics::overflow_checks() { + assert!(n < Self::BITS, "attempt to funnel shift left with overflow"); + } + // SAFETY: `n` is wrapped to within range + unsafe { + let n = n & (Self::BITS - 1); + self.unchecked_funnel_shl(right, n) + } } /// Performs a right funnel shift. @@ -571,8 +586,13 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `n` is greater than or equal to the number of - /// bits in `self`. + /// ## Overflow behavior + /// + /// If overflow checks are enabled (default in debug mode), this function will panic if `n` + /// is greater than or equal to the number of bits in `self`. If overflow checks are + /// disabled (default in release mode), there is no panic; instead, the value is shifted + /// by `n % Self::BITS`. + // FIXME(wrapping_funnel_shifts): link to `wrapping_funnel_shr` when stable. /// /// # Examples /// @@ -599,21 +619,31 @@ macro_rules! uint_impl { /// /// ```should_panic /// #![feature(funnel_shifts)] + /// # #![feature(cfg_overflow_checks)] + /// # #[cfg(overflow_checks)] { /// #[doc = concat!("let a = ", stringify!($SelfT), "::MAX;")] /// // Okay #[doc = concat!("let _ = a.rotate_right(", stringify!($SelfT), "::BITS);")] - /// // Panics + /// // Panics (only when overflow checks are enabled) #[doc = concat!("let _ = a.funnel_shr(a, ", stringify!($SelfT), "::BITS);")] + /// # } + /// # #[cfg(not(overflow_checks))] panic!("fulfill should_panic"); /// ``` #[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")] #[unstable(feature = "funnel_shifts", issue = "145686")] #[must_use = "this returns the result of the operation, without modifying the original"] #[inline(always)] + #[rustc_inherit_overflow_checks] pub const fn funnel_shr(self, right: Self, n: u32) -> Self { - assert!(n < Self::BITS, "attempt to funnel shift right with overflow"); - // SAFETY: just checked that `shift` is in-range - unsafe { self.unchecked_funnel_shr(right, n) } + if intrinsics::overflow_checks() { + assert!(n < Self::BITS, "attempt to funnel shift right with overflow"); + } + // SAFETY: `n` is wrapped to within range + unsafe { + let n = n & (Self::BITS - 1); + self.unchecked_funnel_shr(right, n) + } } /// Unchecked funnel shift left. diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 35327264ccd77..3d23e7bb7ee24 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -11,6 +11,7 @@ #![feature(borrowed_buf_init)] #![feature(bstr)] #![feature(casefold)] +#![feature(cfg_overflow_checks)] #![feature(cfg_target_has_reliable_f16_f128)] #![feature(char_internals)] #![feature(clone_to_uninit)] diff --git a/library/coretests/tests/num/uint_macros.rs b/library/coretests/tests/num/uint_macros.rs index 8189776807915..37ad8a6b74185 100644 --- a/library/coretests/tests/num/uint_macros.rs +++ b/library/coretests/tests/num/uint_macros.rs @@ -216,17 +216,31 @@ macro_rules! uint_module { } #[test] + #[cfg(overflow_checks)] #[should_panic = "attempt to funnel shift left with overflow"] fn test_funnel_shl_overflow() { let _ = <$T>::funnel_shl(A, B, $T::BITS); } #[test] + #[cfg(overflow_checks)] #[should_panic = "attempt to funnel shift right with overflow"] fn test_funnel_shr_overflow() { let _ = <$T>::funnel_shr(A, B, $T::BITS); } + #[test] + #[cfg(not(overflow_checks))] + fn test_funnel_shl_overflow() { + let _ = <$T>::funnel_shl(A, B, A); + } + + #[test] + #[cfg(not(overflow_checks))] + fn test_funnel_shr_overflow() { + let _ = <$T>::funnel_shr(A, B, B); + } + #[test] fn test_funnel_shifts_runtime() { for i in 0..$T::BITS - 1 { diff --git a/tests/ui/std/overflow-check-ops.rs b/tests/ui/std/overflow-check-ops.rs index 9f79bb6505753..458c1b121664e 100644 --- a/tests/ui/std/overflow-check-ops.rs +++ b/tests/ui/std/overflow-check-ops.rs @@ -7,6 +7,7 @@ //@[WRAP] compile-flags: -C overflow-checks=false #![feature(cfg_overflow_checks)] +#![feature(funnel_shifts)] use std::hint::black_box as bb; use std::{assert_matches, fmt, panic}; @@ -28,6 +29,8 @@ fn main() { check(|| bb(u32::MAX) * bb(2), u32::MAX << 1, "mul"); check(|| bb(1u32) << bb(32), 1, "shl"); check(|| bb(u32::MAX) >> bb(32), u32::MAX, "shr"); + check(|| bb(1234u32).funnel_shl(4567, bb(32)), 1234, "funnel_shl"); + check(|| bb(1234u32).funnel_shr(4567, bb(32)), 4567, "funnel_shr"); check(|| bb(u32::MAX).pow(bb(2)), 1, "pow"); check(|| bb(u32::MAX).next_power_of_two(), 0, "next_power_of_two"); From 0ff906ae755fd8a5a12215ecc105e2b97650e4a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 20 Aug 2026 10:47:44 +0200 Subject: [PATCH 28/33] Configure LLM policy URL for triagebot --- triagebot.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/triagebot.toml b/triagebot.toml index a842b2a07c15f..4990c72ffce6c 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1581,6 +1581,7 @@ cc = ["@rust-lang/wg-const-eval"] [assign] warn_non_default_branch.enable = true contributing_url = "https://rustc-dev-guide.rust-lang.org/getting-started.html" +llm_policy_url = "https://forge.rust-lang.org/policies/llm-usage.html" [[assign.warn_non_default_branch.exceptions]] title = "[beta" From ac623ff4034caeee28f423ae7800e8516cbc0078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Sanz=20Gonz=C3=A1lez?= <80487270+hsanzg@users.noreply.github.com> Date: Wed, 19 Aug 2026 19:40:05 +0200 Subject: [PATCH 29/33] Double-word align `_Unwind_Exception` --- library/unwind/src/types.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/library/unwind/src/types.rs b/library/unwind/src/types.rs index 7634052c93f33..d745b836e822b 100644 --- a/library/unwind/src/types.rs +++ b/library/unwind/src/types.rs @@ -42,6 +42,12 @@ pub const unwinder_private_data_size: usize = cfg_select! { }; #[repr(C)] +// The Itanium C++ ABI requires this type to have "double-word" alignment, +// which libunwind and libgcc interpret as the maximum alignment of any +// scalar type on the current target. +#[cfg_attr(target_pointer_width = "16", repr(align(4)))] +#[cfg_attr(target_pointer_width = "32", repr(align(8)))] +#[cfg_attr(target_pointer_width = "64", repr(align(16)))] pub struct _Unwind_Exception { pub exception_class: _Unwind_Exception_Class, pub exception_cleanup: _Unwind_Exception_Cleanup_Fn, From ed151ed962581e8e995ea70b55ea1affa7c4e466 Mon Sep 17 00:00:00 2001 From: joboet Date: Sat, 14 Feb 2026 14:58:58 +0100 Subject: [PATCH 30/33] std: update definitions on Fuchsia --- library/std/src/sys/pal/unix/fuchsia.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/library/std/src/sys/pal/unix/fuchsia.rs b/library/std/src/sys/pal/unix/fuchsia.rs index c118dee624764..8b0e9cb1ff10a 100644 --- a/library/std/src/sys/pal/unix/fuchsia.rs +++ b/library/std/src/sys/pal/unix/fuchsia.rs @@ -9,12 +9,12 @@ use crate::io; // Time // ////////// -pub type zx_time_t = i64; +pub type zx_instant_mono_t = i64; -pub const ZX_TIME_INFINITE: zx_time_t = i64::MAX; +pub const ZX_TIME_INFINITE: zx_instant_mono_t = i64::MAX; unsafe extern "C" { - pub safe fn zx_clock_get_monotonic() -> zx_time_t; + pub safe fn zx_clock_get_monotonic() -> zx_instant_mono_t; } ///////////// @@ -62,7 +62,7 @@ unsafe extern "C" { pub fn zx_object_wait_one( handle: zx_handle_t, signals: zx_signals_t, - timeout: zx_time_t, + deadline: zx_instant_mono_t, pending: *mut zx_signals_t, ) -> zx_status_t; @@ -70,7 +70,7 @@ unsafe extern "C" { value_ptr: *const zx_futex_t, current_value: zx_futex_t, new_futex_owner: zx_handle_t, - deadline: zx_time_t, + deadline: zx_instant_mono_t, ) -> zx_status_t; pub fn zx_futex_wake(value_ptr: *const zx_futex_t, wake_count: u32) -> zx_status_t; pub fn zx_futex_wake_single_owner(value_ptr: *const zx_futex_t) -> zx_status_t; @@ -117,7 +117,7 @@ pub type zx_info_process_flags_t = u32; #[repr(C)] pub struct zx_info_process_t { pub return_code: i64, - pub start_time: zx_time_t, + pub start_time: zx_instant_mono_t, pub flags: zx_info_process_flags_t, pub reserved1: u32, } From 95d6b37a456fb809406edb327a8ce358e6d41d16 Mon Sep 17 00:00:00 2001 From: joboet Date: Sat, 14 Feb 2026 15:04:06 +0100 Subject: [PATCH 31/33] std: implement `sleep_until` for Fuchsia --- library/std/src/sys/pal/unix/fuchsia.rs | 1 + library/std/src/sys/thread/mod.rs | 4 +++- library/std/src/sys/thread/unix.rs | 10 ++++++++++ library/std/src/sys/time/unix.rs | 5 +++++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/pal/unix/fuchsia.rs b/library/std/src/sys/pal/unix/fuchsia.rs index 8b0e9cb1ff10a..f9dfd52a610bb 100644 --- a/library/std/src/sys/pal/unix/fuchsia.rs +++ b/library/std/src/sys/pal/unix/fuchsia.rs @@ -15,6 +15,7 @@ pub const ZX_TIME_INFINITE: zx_instant_mono_t = i64::MAX; unsafe extern "C" { pub safe fn zx_clock_get_monotonic() -> zx_instant_mono_t; + pub safe fn zx_nanosleep(deadline: zx_instant_mono_t) -> zx_status_t; } ///////////// diff --git a/library/std/src/sys/thread/mod.rs b/library/std/src/sys/thread/mod.rs index 9816981c7fc88..1ae0da23fe5e1 100644 --- a/library/std/src/sys/thread/mod.rs +++ b/library/std/src/sys/thread/mod.rs @@ -73,6 +73,7 @@ cfg_select! { target_os = "vxworks", target_os = "wasi", target_vendor = "apple", + target_os = "fuchsia", ))] pub use unix::sleep_until; #[expect(dead_code)] @@ -134,7 +135,8 @@ cfg_select! { target_os = "wasi", target_vendor = "apple", target_os = "motor", - target_os = "vexos" + target_os = "vexos", + target_os = "fuchsia", )))] pub fn sleep_until(deadline: crate::time::Instant) { use crate::time::Instant; diff --git a/library/std/src/sys/thread/unix.rs b/library/std/src/sys/thread/unix.rs index 2dbb0314cb271..4988b32a431df 100644 --- a/library/std/src/sys/thread/unix.rs +++ b/library/std/src/sys/thread/unix.rs @@ -778,6 +778,16 @@ pub fn sleep_until(deadline: crate::time::Instant) { } } +#[cfg(target_os = "fuchsia")] +pub fn sleep_until(deadline: crate::time::Instant) { + use crate::sys::pal::fuchsia::{zx_cvt, zx_nanosleep}; + + let deadline = deadline.into_inner().into_deadline(); + if let Err(error) = zx_cvt(zx_nanosleep(deadline)) { + panic!("zx_nanosleep failed: {error}"); + } +} + pub fn yield_now() { let ret = unsafe { libc::sched_yield() }; debug_assert_eq!(ret, 0); diff --git a/library/std/src/sys/time/unix.rs b/library/std/src/sys/time/unix.rs index 944cb552cad9e..d84256df0cd53 100644 --- a/library/std/src/sys/time/unix.rs +++ b/library/std/src/sys/time/unix.rs @@ -123,6 +123,11 @@ impl Instant { // 126 bits. Some((nanos * u128::from(timebase.denom)).div_ceil(u128::from(timebase.numer))) } + + #[cfg(target_os = "fuchsia")] + pub fn into_deadline(self) -> crate::sys::pal::fuchsia::zx_instant_mono_t { + self.t.tv_sec.saturating_mul(1_000_000_000).saturating_add(self.t.tv_nsec.as_inner().into()) + } } impl AsInner for Instant { From 1c0a0d9a1229869297c760fc73adc98c267dc47c Mon Sep 17 00:00:00 2001 From: joboet Date: Thu, 21 May 2026 15:48:26 +0200 Subject: [PATCH 32/33] std: update `sleep_until` syscall table --- library/std/src/thread/functions.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/library/std/src/thread/functions.rs b/library/std/src/thread/functions.rs index 355a00c2a95ad..6a940f9b677b6 100644 --- a/library/std/src/thread/functions.rs +++ b/library/std/src/thread/functions.rs @@ -313,19 +313,21 @@ pub fn sleep(dur: Duration) { /// /// | Platform | System call | /// |-----------|----------------------------------------------------------------------| -/// | Linux | [clock_nanosleep] (Monotonic Clock) | -/// | BSD except OpenBSD | [clock_nanosleep] (Monotonic Clock) | -/// | Android | [clock_nanosleep] (Monotonic Clock) | -/// | Solaris | [clock_nanosleep] (Monotonic Clock) | -/// | Illumos | [clock_nanosleep] (Monotonic Clock) | -/// | Dragonfly | [clock_nanosleep] (Monotonic Clock) | -/// | Hurd | [clock_nanosleep] (Monotonic Clock) | -/// | Vxworks | [clock_nanosleep] (Monotonic Clock) | +/// | Linux | [`clock_nanosleep`] (Monotonic Clock) | +/// | BSD except OpenBSD | [`clock_nanosleep`] (Monotonic Clock) | +/// | Android | [`clock_nanosleep`] (Monotonic Clock) | +/// | Solaris | [`clock_nanosleep`] (Monotonic Clock) | +/// | Illumos | [`clock_nanosleep`] (Monotonic Clock) | +/// | Dragonfly | [`clock_nanosleep`] (Monotonic Clock) | +/// | Hurd | [`clock_nanosleep`] (Monotonic Clock) | +/// | Vxworks | [`clock_nanosleep`] (Monotonic Clock) | /// | Apple | `mach_wait_until` | +/// | Fuchsia | [`zx_nanosleep`] | /// | Other | `sleep_until` uses [`sleep`] and does not issue a syscall itself | /// /// [currently]: crate::io#platform-specific-behavior -/// [clock_nanosleep]: https://linux.die.net/man/3/clock_nanosleep +/// [`clock_nanosleep`]: https://linux.die.net/man/3/clock_nanosleep +/// [`zx_nanosleep`]: https://fuchsia.dev/reference/syscalls/nanosleep /// /// **Disclaimer:** These system calls might change over time. /// From 2d28ce9ee7a5fcad7e09641d7c2738a7ecaeb931 Mon Sep 17 00:00:00 2001 From: joboet Date: Thu, 20 Aug 2026 13:37:44 +0200 Subject: [PATCH 33/33] std: add a comment about elapsed deadlines for `sleep_until` --- library/std/src/thread/functions.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/library/std/src/thread/functions.rs b/library/std/src/thread/functions.rs index 355a00c2a95ad..918c266cdbd34 100644 --- a/library/std/src/thread/functions.rs +++ b/library/std/src/thread/functions.rs @@ -295,9 +295,10 @@ pub fn sleep(dur: Duration) { /// Puts the current thread to sleep until the specified deadline has passed. /// -/// The thread may still be asleep after the deadline specified due to -/// scheduling specifics or platform-dependent functionality. It will never -/// wake before. +/// If the deadline has already passed at the time this function is called, it +/// will return immediately. Note that the thread may still be asleep after the +/// deadline specified due to scheduling specifics or platform-dependent +/// functionality. It will never wake before. /// /// This function is blocking, and should not be used in `async` functions. ///