From 65c17223c1fa4792be28f533c17f680d9eef0e66 Mon Sep 17 00:00:00 2001 From: is57primenumber <58158444+is57primenumber@users.noreply.github.com> Date: Wed, 17 Dec 2025 02:30:19 +0900 Subject: [PATCH 01/28] add CSE optimization tests for iterating over slice --- tests/codegen-llvm/slice_cse_optimization.rs | 46 ++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 tests/codegen-llvm/slice_cse_optimization.rs diff --git a/tests/codegen-llvm/slice_cse_optimization.rs b/tests/codegen-llvm/slice_cse_optimization.rs new file mode 100644 index 000000000000..2b1851d8ae44 --- /dev/null +++ b/tests/codegen-llvm/slice_cse_optimization.rs @@ -0,0 +1,46 @@ +//! Various iterating method over slice correctly optimized using common subexpression elimination. +//! Checks function has memory(argmem: read) attribute. +//! Regression test for . +//@ compile-flags: -O + +#![crate_type = "lib"] +// CHECK-LABEL: @has_zero_iter +// CHECK-SAME: #[[ATTR:[0-9]+]] +#[inline(never)] +#[unsafe(no_mangle)] +pub fn has_zero_iter(xs: &[u8]) -> bool { + xs.iter().any(|&x| x == 0) +} + +// CHECK-LABEL: @has_zero_ptr +// CHECK-SAME: #[[ATTR]] +#[inline(never)] +#[unsafe(no_mangle)] +fn has_zero_ptr(xs: &[u8]) -> bool { + let range = xs.as_ptr_range(); + let mut start = range.start; + let end = range.end; + while start < end { + unsafe { + if *start == 0 { + return true; + } + start = start.add(1); + } + } + false +} +// CHECK-LABEL: @has_zero_for +// CHECK-SAME: #[[ATTR]] +#[inline(never)] +#[unsafe(no_mangle)] +fn has_zero_for(xs: &[u8]) -> bool { + for x in xs { + if *x == 0 { + return true; + } + } + false +} + +// CHECK: attributes #[[ATTR]] = { {{.*}}memory(argmem: read){{.*}} } From eb101b1d2a2b7ee1407935ba8c5bee36ac9ee31d Mon Sep 17 00:00:00 2001 From: Paul Mabileau Date: Thu, 18 Dec 2025 12:55:04 +0100 Subject: [PATCH 02/28] Fix(lib/win/thread): Ensure `Sleep`'s usage passes over the requested duration under Win7 Fixes #149935. See the added comment for more details. Signed-off-by: Paul Mabileau --- library/std/src/sys/thread/windows.rs | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/library/std/src/sys/thread/windows.rs b/library/std/src/sys/thread/windows.rs index 6a21b11e0312..ea18572489ee 100644 --- a/library/std/src/sys/thread/windows.rs +++ b/library/std/src/sys/thread/windows.rs @@ -8,7 +8,7 @@ use crate::sys::pal::time::WaitableTimer; use crate::sys::pal::{dur2timeout, to_u16s}; use crate::sys::{FromInner, c, stack_overflow}; use crate::thread::ThreadInit; -use crate::time::Duration; +use crate::time::{Duration, Instant}; use crate::{io, ptr}; pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024; @@ -120,11 +120,28 @@ pub fn sleep(dur: Duration) { timer.set(dur)?; timer.wait() } + // Directly forward to `Sleep` for its zero duration behavior when indeed + // zero in order to skip the `Instant::now` calls, useless in this case. + if dur.is_zero() { + unsafe { c::Sleep(0) }; // Attempt to use high-precision sleep (Windows 10, version 1803+). - // On error fallback to the standard `Sleep` function. - // Also preserves the zero duration behavior of `Sleep`. - if dur.is_zero() || high_precision_sleep(dur).is_err() { - unsafe { c::Sleep(dur2timeout(dur)) } + // On error, fallback to the standard `Sleep` function. + } else if high_precision_sleep(dur).is_err() { + let start = Instant::now(); + unsafe { c::Sleep(dur2timeout(dur)) }; + + // See #149935: `Sleep` under Windows 7 and probably 8 as well seems a + // bit buggy for us as it can last less than the requested time while + // our API is meant to guarantee that. This is fixed by measuring the + // effective time difference and if needed, sleeping a bit more in + // order to ensure the duration is always exceeded. A fixed single + // millisecond works because `Sleep` operates based on a system-wide + // (until Windows 10 2004 that makes it process-local) interrupt timer + // that counts in "tick" units of ~15ms by default: a 1ms timeout + // therefore passes the next tick boundary. + if start.elapsed() < dur { + unsafe { c::Sleep(1) }; + } } } From 3df0dc880376ef16076851046c40f2ad7374b63c Mon Sep 17 00:00:00 2001 From: The 8472 Date: Sat, 10 Jan 2026 14:21:33 +0100 Subject: [PATCH 03/28] mark rust_dealloc as captures(address) Co-authored-by: Ralf Jung --- compiler/rustc_codegen_llvm/src/attributes.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index a25ce9e5a90a..bf6bb81b53b0 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -517,7 +517,16 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( to_add.push(llvm::CreateAllocKindAttr(cx.llcx, AllocKindFlags::Free)); // applies to argument place instead of function place let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx); - attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]); + let attrs: &[_] = if llvm_util::get_version() >= (21, 0, 0) { + // "Does not capture provenance" means "if the function call stashes the pointer somewhere, + // accessing that pointer after the function returns is UB". That is definitely the case here since + // freeing will destroy the provenance. + let captures_addr = AttributeKind::CapturesAddress.create_attr(cx.llcx); + &[allocated_pointer, captures_addr] + } else { + &[allocated_pointer] + }; + attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), attrs); } if let Some(align) = codegen_fn_attrs.alignment { llvm::set_alignment(llfn, align); From 2b8f4a562f24afcb45f4f12b109c13beb5b45f75 Mon Sep 17 00:00:00 2001 From: The 8472 Date: Sun, 29 Sep 2024 00:27:50 +0200 Subject: [PATCH 04/28] avoid phi node for pointers flowing into Vec appends --- library/alloc/src/slice.rs | 11 ++++--- library/alloc/src/vec/mod.rs | 6 +++- .../lib-optimizations/append-elements.rs | 33 +++++++++++++++++++ 3 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 tests/codegen-llvm/lib-optimizations/append-elements.rs diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index e7d0fc3454ee..634747ca3f84 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -444,13 +444,16 @@ impl [T] { impl ConvertVec for T { #[inline] fn to_vec(s: &[Self], alloc: A) -> Vec { - let mut v = Vec::with_capacity_in(s.len(), alloc); + let len = s.len(); + let mut v = Vec::with_capacity_in(len, alloc); // SAFETY: // allocated above with the capacity of `s`, and initialize to `s.len()` in // ptr::copy_to_non_overlapping below. - unsafe { - s.as_ptr().copy_to_nonoverlapping(v.as_mut_ptr(), s.len()); - v.set_len(s.len()); + if len > 0 { + unsafe { + s.as_ptr().copy_to_nonoverlapping(v.as_mut_ptr(), len); + v.set_len(len); + } } v } diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 379e964f0a0c..ac86399df7ab 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -2818,7 +2818,11 @@ impl Vec { let count = other.len(); self.reserve(count); let len = self.len(); - unsafe { ptr::copy_nonoverlapping(other as *const T, self.as_mut_ptr().add(len), count) }; + if count > 0 { + unsafe { + ptr::copy_nonoverlapping(other as *const T, self.as_mut_ptr().add(len), count) + }; + } self.len += count; } diff --git a/tests/codegen-llvm/lib-optimizations/append-elements.rs b/tests/codegen-llvm/lib-optimizations/append-elements.rs new file mode 100644 index 000000000000..b8657104d665 --- /dev/null +++ b/tests/codegen-llvm/lib-optimizations/append-elements.rs @@ -0,0 +1,33 @@ +//@ compile-flags: -O -Zmerge-functions=disabled +//@ needs-deterministic-layouts +//@ min-llvm-version: 21 +#![crate_type = "lib"] + +//! Check that a temporary intermediate allocations can eliminated and replaced +//! with memcpy forwarding. +//! This requires Vec code to be structured in a way that avoids phi nodes from the +//! zero-capacity length flowing into the memcpy arguments. + +// CHECK-LABEL: @vec_append_with_temp_alloc +// CHECK-SAME: ptr{{.*}}[[DST:%[a-z]+]]{{.*}}ptr{{.*}}[[SRC:%[a-z]+]] +#[no_mangle] +pub fn vec_append_with_temp_alloc(dst: &mut Vec, src: &[u8]) { + // CHECK-NOT: call void @llvm.memcpy + // CHECK: call void @llvm.memcpy.{{.*}}[[DST]].i{{.*}}[[SRC]] + // CHECK-NOT: call void @llvm.memcpy + let temp = src.to_vec(); + dst.extend(&temp); + // CHECK: ret +} + +// CHECK-LABEL: @string_append_with_temp_alloc +// CHECK-SAME: ptr{{.*}}[[DST:%[a-z]+]]{{.*}}ptr{{.*}}[[SRC:%[a-z]+]] +#[no_mangle] +pub fn string_append_with_temp_alloc(dst: &mut String, src: &str) { + // CHECK-NOT: call void @llvm.memcpy + // CHECK: call void @llvm.memcpy.{{.*}}[[DST]].i{{.*}}[[SRC]] + // CHECK-NOT: call void @llvm.memcpy + let temp = src.to_string(); + dst.push_str(&temp); + // CHECK: ret +} From e3f198ec05bdae302caf792cb4ab2e07d834d7d9 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Tue, 20 Jan 2026 16:18:56 +0800 Subject: [PATCH 05/28] LoongArch: Fix direct-access-external-data test On LoongArch targets, `-Cdirect-access-external-data` defaults to `no`. Since copy relocations are not supported, `dso_local` is not emitted under `-Crelocation-model=static`, unlike on other targets. --- .../direct-access-external-data.rs | 1 + .../loongarch/direct-access-external-data.rs | 47 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 tests/codegen-llvm/loongarch/direct-access-external-data.rs diff --git a/tests/codegen-llvm/direct-access-external-data.rs b/tests/codegen-llvm/direct-access-external-data.rs index 73dc08dc2b57..a151bb6012e1 100644 --- a/tests/codegen-llvm/direct-access-external-data.rs +++ b/tests/codegen-llvm/direct-access-external-data.rs @@ -1,3 +1,4 @@ +//@ ignore-loongarch64 (handles dso_local differently) //@ ignore-powerpc64 (handles dso_local differently) //@ ignore-apple (handles dso_local differently) diff --git a/tests/codegen-llvm/loongarch/direct-access-external-data.rs b/tests/codegen-llvm/loongarch/direct-access-external-data.rs new file mode 100644 index 000000000000..de495d7fe9a7 --- /dev/null +++ b/tests/codegen-llvm/loongarch/direct-access-external-data.rs @@ -0,0 +1,47 @@ +//@ only-loongarch64 + +//@ revisions: DEFAULT PIE DIRECT INDIRECT +//@ [DEFAULT] compile-flags: -C relocation-model=static +//@ [PIE] compile-flags: -C relocation-model=pie +//@ [DIRECT] compile-flags: -C relocation-model=pie -Z direct-access-external-data=yes +//@ [INDIRECT] compile-flags: -C relocation-model=static -Z direct-access-external-data=no + +#![crate_type = "rlib"] +#![feature(linkage)] + +unsafe extern "C" { + // CHECK: @VAR = external + // DEFAULT-NOT: dso_local + // PIE-NOT: dso_local + // DIRECT-SAME: dso_local + // INDIRECT-NOT: dso_local + // CHECK-SAME: global i32 + safe static VAR: i32; + + // When "linkage" is used, we generate an indirection global. + // Check dso_local is still applied to the actual global. + // CHECK: @EXTERNAL = external + // DEFAULT-NOT: dso_local + // PIE-NOT: dso_local + // DIRECT-SAME: dso_local + // INDIRECT-NOT: dso_local + // CHECK-SAME: global i8 + #[linkage = "external"] + safe static EXTERNAL: *const u32; + + // CHECK: @WEAK = extern_weak + // DEFAULT-NOT: dso_local + // PIE-NOT: dso_local + // DIRECT-SAME: dso_local + // INDIRECT-NOT: dso_local + // CHECK-SAME: global i8 + #[linkage = "extern_weak"] + safe static WEAK: *const u32; +} + +#[no_mangle] +pub fn refer() { + core::hint::black_box(VAR); + core::hint::black_box(EXTERNAL); + core::hint::black_box(WEAK); +} From d977471ce22c379cd7f8a53d907aecab67d05195 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Tue, 20 Jan 2026 17:09:52 +0800 Subject: [PATCH 06/28] LoongArch: Fix call-llvm-intrinsics test --- tests/codegen-llvm/loongarch-abi/call-llvm-intrinsics.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/codegen-llvm/loongarch-abi/call-llvm-intrinsics.rs b/tests/codegen-llvm/loongarch-abi/call-llvm-intrinsics.rs index 9a50f7b8e3a5..36eb2dde7afb 100644 --- a/tests/codegen-llvm/loongarch-abi/call-llvm-intrinsics.rs +++ b/tests/codegen-llvm/loongarch-abi/call-llvm-intrinsics.rs @@ -23,9 +23,7 @@ pub fn do_call() { unsafe { // Ensure that we `call` LLVM intrinsics instead of trying to `invoke` them - // CHECK: store float 4.000000e+00, ptr %{{.}}, align 4 - // CHECK: load float, ptr %{{.}}, align 4 - // CHECK: call float @llvm.sqrt.f32(float %{{.}} + // CHECK: call float @llvm.sqrt.f32(float 4.000000e+00) sqrt(4.0); } } From bc0cce1595ee614fd91898657b4402570880cc48 Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Sun, 9 Nov 2025 20:48:55 +0300 Subject: [PATCH 07/28] ptr_aligment_type: add more APIs --- library/alloc/src/rc.rs | 12 +- library/alloc/src/sync.rs | 12 +- library/core/src/alloc/layout.rs | 136 +++++++++++++----- library/core/src/mem/mod.rs | 5 + library/core/src/ptr/alignment.rs | 74 ++++++++++ ...ace.PreCodegen.after.32bit.panic-abort.mir | 56 +++++--- ...ce.PreCodegen.after.32bit.panic-unwind.mir | 56 +++++--- ...ace.PreCodegen.after.64bit.panic-abort.mir | 56 +++++--- ...ce.PreCodegen.after.64bit.panic-unwind.mir | 56 +++++--- tests/mir-opt/pre-codegen/drop_boxed_slice.rs | 2 +- tests/ui/thir-print/offset_of.stdout | 18 +-- 11 files changed, 330 insertions(+), 153 deletions(-) diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index f58ebd488d7c..cec41524325e 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -252,7 +252,7 @@ use core::intrinsics::abort; #[cfg(not(no_global_oom_handling))] use core::iter; use core::marker::{PhantomData, Unsize}; -use core::mem::{self, ManuallyDrop, align_of_val_raw}; +use core::mem::{self, ManuallyDrop}; use core::num::NonZeroUsize; use core::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, LegacyReceiver}; #[cfg(not(no_global_oom_handling))] @@ -3845,15 +3845,15 @@ unsafe fn data_offset(ptr: *const T) -> usize { // Because RcInner is repr(C), it will always be the last field in memory. // SAFETY: since the only unsized types possible are slices, trait objects, // and extern types, the input safety requirement is currently enough to - // satisfy the requirements of align_of_val_raw; this is an implementation + // satisfy the requirements of Alignment::of_val_raw; this is an implementation // detail of the language that must not be relied upon outside of std. - unsafe { data_offset_align(Alignment::new_unchecked(align_of_val_raw(ptr))) } + unsafe { data_offset_alignment(Alignment::of_val_raw(ptr)) } } #[inline] -fn data_offset_align(align: Alignment) -> usize { +fn data_offset_alignment(alignment: Alignment) -> usize { let layout = Layout::new::>(); - layout.size() + layout.padding_needed_for(align) + layout.size() + layout.padding_needed_for(alignment) } /// A uniquely owned [`Rc`]. @@ -4478,7 +4478,7 @@ impl UniqueRcUninit { /// Returns the pointer to be written into to initialize the [`Rc`]. fn data_ptr(&mut self) -> *mut T { - let offset = data_offset_align(self.layout_for_value.alignment()); + let offset = data_offset_alignment(self.layout_for_value.alignment()); unsafe { self.ptr.as_ptr().byte_add(offset) as *mut T } } diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index fc44a468c8a4..a5e4fab916ab 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -19,7 +19,7 @@ use core::intrinsics::abort; #[cfg(not(no_global_oom_handling))] use core::iter; use core::marker::{PhantomData, Unsize}; -use core::mem::{self, ManuallyDrop, align_of_val_raw}; +use core::mem::{self, ManuallyDrop}; use core::num::NonZeroUsize; use core::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, LegacyReceiver}; #[cfg(not(no_global_oom_handling))] @@ -4206,15 +4206,15 @@ unsafe fn data_offset(ptr: *const T) -> usize { // Because ArcInner is repr(C), it will always be the last field in memory. // SAFETY: since the only unsized types possible are slices, trait objects, // and extern types, the input safety requirement is currently enough to - // satisfy the requirements of align_of_val_raw; this is an implementation + // satisfy the requirements of Alignment::of_val_raw; this is an implementation // detail of the language that must not be relied upon outside of std. - unsafe { data_offset_align(Alignment::new_unchecked(align_of_val_raw(ptr))) } + unsafe { data_offset_alignment(Alignment::of_val_raw(ptr)) } } #[inline] -fn data_offset_align(align: Alignment) -> usize { +fn data_offset_alignment(alignment: Alignment) -> usize { let layout = Layout::new::>(); - layout.size() + layout.padding_needed_for(align) + layout.size() + layout.padding_needed_for(alignment) } /// A unique owning pointer to an [`ArcInner`] **that does not imply the contents are initialized,** @@ -4258,7 +4258,7 @@ impl UniqueArcUninit { /// Returns the pointer to be written into to initialize the [`Arc`]. fn data_ptr(&mut self) -> *mut T { - let offset = data_offset_align(self.layout_for_value.alignment()); + let offset = data_offset_alignment(self.layout_for_value.alignment()); unsafe { self.ptr.as_ptr().byte_add(offset) as *mut T } } diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs index 3a2111350a4e..4bffdd17696f 100644 --- a/library/core/src/alloc/layout.rs +++ b/library/core/src/alloc/layout.rs @@ -67,15 +67,16 @@ impl Layout { #[inline] const fn is_size_align_valid(size: usize, align: usize) -> bool { - let Some(align) = Alignment::new(align) else { return false }; - if size > Self::max_size_for_align(align) { - return false; - } - true + let Some(alignment) = Alignment::new(align) else { return false }; + Self::is_size_alignment_valid(size, alignment) + } + + const fn is_size_alignment_valid(size: usize, alignment: Alignment) -> bool { + size <= Self::max_size_for_alignment(alignment) } #[inline(always)] - const fn max_size_for_align(align: Alignment) -> usize { + const fn max_size_for_alignment(alignment: Alignment) -> usize { // (power-of-two implies align != 0.) // Rounded up size is: @@ -93,18 +94,28 @@ impl Layout { // SAFETY: the maximum possible alignment is `isize::MAX + 1`, // so the subtraction cannot overflow. - unsafe { unchecked_sub(isize::MAX as usize + 1, align.as_usize()) } + unsafe { unchecked_sub(isize::MAX as usize + 1, alignment.as_usize()) } } - /// Internal helper constructor to skip revalidating alignment validity. + /// Constructs a `Layout` from a given `size` and `alignment`, + /// or returns `LayoutError` if any of the following conditions + /// are not met: + /// + /// * `size`, when rounded up to the nearest multiple of `alignment`, + /// must not overflow `isize` (i.e., the rounded value must be + /// less than or equal to `isize::MAX`). + #[unstable(feature = "ptr_alignment_type", issue = "102070")] #[inline] - const fn from_size_alignment(size: usize, align: Alignment) -> Result { - if size > Self::max_size_for_align(align) { - return Err(LayoutError); + pub const fn from_size_alignment( + size: usize, + alignment: Alignment, + ) -> Result { + if Layout::is_size_alignment_valid(size, alignment) { + // SAFETY: Layout::size invariants checked above. + Ok(Layout { size, align: alignment }) + } else { + Err(LayoutError) } - - // SAFETY: Layout::size invariants checked above. - Ok(Layout { size, align }) } /// Creates a layout, bypassing all checks. @@ -132,6 +143,30 @@ impl Layout { unsafe { Layout { size, align: mem::transmute(align) } } } + /// Creates a layout, bypassing all checks. + /// + /// # Safety + /// + /// This function is unsafe as it does not verify the preconditions from + /// [`Layout::from_size_alignment`]. + #[unstable(feature = "ptr_alignment_type", issue = "102070")] + #[must_use] + #[inline] + #[track_caller] + pub const unsafe fn from_size_alignment_unchecked(size: usize, alignment: Alignment) -> Self { + assert_unsafe_precondition!( + check_library_ub, + "Layout::from_size_alignment_unchecked requires \ + that the rounded-up allocation size does not exceed isize::MAX", + ( + size: usize = size, + alignment: Alignment = alignment, + ) => Layout::is_size_alignment_valid(size, alignment) + ); + // SAFETY: the caller is required to uphold the preconditions. + Layout { size, align: alignment } + } + /// The minimum size in bytes for a memory block of this layout. #[stable(feature = "alloc_layout", since = "1.28.0")] #[rustc_const_stable(feature = "const_alloc_layout_size_align", since = "1.50.0")] @@ -153,6 +188,16 @@ impl Layout { self.align.as_usize() } + /// The minimum byte alignment for a memory block of this layout. + /// + /// The returned alignment is guaranteed to be a power of two. + #[unstable(feature = "ptr_alignment_type", issue = "102070")] + #[must_use = "this returns the minimum alignment, without modifying the layout"] + #[inline] + pub const fn alignment(&self) -> Alignment { + self.align + } + /// Constructs a `Layout` suitable for holding a value of type `T`. #[stable(feature = "alloc_layout", since = "1.28.0")] #[rustc_const_stable(feature = "alloc_layout_const_new", since = "1.42.0")] @@ -170,9 +215,9 @@ impl Layout { #[must_use] #[inline] pub const fn for_value(t: &T) -> Self { - let (size, align) = (size_of_val(t), align_of_val(t)); + let (size, alignment) = (size_of_val(t), Alignment::of_val(t)); // SAFETY: see rationale in `new` for why this is using the unsafe variant - unsafe { Layout::from_size_align_unchecked(size, align) } + unsafe { Layout::from_size_alignment_unchecked(size, alignment) } } /// Produces layout describing a record that could be used to @@ -204,11 +249,12 @@ impl Layout { /// [extern type]: ../../unstable-book/language-features/extern-types.html #[unstable(feature = "layout_for_ptr", issue = "69835")] #[must_use] + #[inline] pub const unsafe fn for_value_raw(t: *const T) -> Self { // SAFETY: we pass along the prerequisites of these functions to the caller - let (size, align) = unsafe { (mem::size_of_val_raw(t), mem::align_of_val_raw(t)) }; + let (size, alignment) = unsafe { (mem::size_of_val_raw(t), Alignment::of_val_raw(t)) }; // SAFETY: see rationale in `new` for why this is using the unsafe variant - unsafe { Layout::from_size_align_unchecked(size, align) } + unsafe { Layout::from_size_alignment_unchecked(size, alignment) } } /// Creates a `NonNull` that is dangling, but well-aligned for this Layout. @@ -243,13 +289,33 @@ impl Layout { #[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")] #[inline] pub const fn align_to(&self, align: usize) -> Result { - if let Some(align) = Alignment::new(align) { - Layout::from_size_alignment(self.size, Alignment::max(self.align, align)) + if let Some(alignment) = Alignment::new(align) { + self.adjust_alignment_to(alignment) } else { Err(LayoutError) } } + /// Creates a layout describing the record that can hold a value + /// of the same layout as `self`, but that also is aligned to + /// alignment `alignment`. + /// + /// If `self` already meets the prescribed alignment, then returns + /// `self`. + /// + /// Note that this method does not add any padding to the overall + /// size, regardless of whether the returned layout has a different + /// alignment. In other words, if `K` has size 16, `K.align_to(32)` + /// will *still* have size 16. + /// + /// Returns an error if the combination of `self.size()` and the given + /// `alignment` violates the conditions listed in [`Layout::from_size_alignment`]. + #[unstable(feature = "ptr_alignment_type", issue = "102070")] + #[inline] + pub const fn adjust_alignment_to(&self, alignment: Alignment) -> Result { + Layout::from_size_alignment(self.size, Alignment::max(self.align, alignment)) + } + /// Returns the amount of padding we must insert after `self` /// to ensure that the following address will satisfy `alignment`. /// @@ -267,7 +333,7 @@ impl Layout { #[must_use = "this returns the padding needed, without modifying the `Layout`"] #[inline] pub const fn padding_needed_for(&self, alignment: Alignment) -> usize { - let len_rounded_up = self.size_rounded_up_to_custom_align(alignment); + let len_rounded_up = self.size_rounded_up_to_custom_alignment(alignment); // SAFETY: Cannot overflow because the rounded-up value is never less unsafe { unchecked_sub(len_rounded_up, self.size) } } @@ -277,7 +343,7 @@ impl Layout { /// This can return at most `Alignment::MAX` (aka `isize::MAX + 1`) /// because the original size is at most `isize::MAX`. #[inline] - const fn size_rounded_up_to_custom_align(&self, align: Alignment) -> usize { + const fn size_rounded_up_to_custom_alignment(&self, alignment: Alignment) -> usize { // SAFETY: // Rounded up value is: // size_rounded_up = (size + align - 1) & !(align - 1); @@ -297,7 +363,7 @@ impl Layout { // (Size 0 Align MAX is already aligned, so stays the same, but things like // Size 1 Align MAX or Size isize::MAX Align 2 round up to `isize::MAX + 1`.) unsafe { - let align_m1 = unchecked_sub(align.as_usize(), 1); + let align_m1 = unchecked_sub(alignment.as_usize(), 1); unchecked_add(self.size, align_m1) & !align_m1 } } @@ -317,10 +383,10 @@ impl Layout { // > `size`, when rounded up to the nearest multiple of `align`, // > must not overflow isize (i.e., the rounded value must be // > less than or equal to `isize::MAX`) - let new_size = self.size_rounded_up_to_custom_align(self.align); + let new_size = self.size_rounded_up_to_custom_alignment(self.align); // SAFETY: padded size is guaranteed to not exceed `isize::MAX`. - unsafe { Layout::from_size_align_unchecked(new_size, self.align()) } + unsafe { Layout::from_size_alignment_unchecked(new_size, self.alignment()) } } /// Creates a layout describing the record for `n` instances of @@ -426,8 +492,8 @@ impl Layout { #[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")] #[inline] pub const fn extend(&self, next: Self) -> Result<(Self, usize), LayoutError> { - let new_align = Alignment::max(self.align, next.align); - let offset = self.size_rounded_up_to_custom_align(next.align); + let new_alignment = Alignment::max(self.align, next.align); + let offset = self.size_rounded_up_to_custom_alignment(next.align); // SAFETY: `offset` is at most `isize::MAX + 1` (such as from aligning // to `Alignment::MAX`) and `next.size` is at most `isize::MAX` (from the @@ -435,7 +501,7 @@ impl Layout { // `isize::MAX + 1 + isize::MAX`, which is `usize::MAX`, and cannot overflow. let new_size = unsafe { unchecked_add(offset, next.size) }; - if let Ok(layout) = Layout::from_size_alignment(new_size, new_align) { + if let Ok(layout) = Layout::from_size_alignment(new_size, new_alignment) { Ok((layout, offset)) } else { Err(LayoutError) @@ -496,7 +562,7 @@ impl Layout { #[inline] const fn inner(element_layout: Layout, n: usize) -> Result { - let Layout { size: element_size, align } = element_layout; + let Layout { size: element_size, align: alignment } = element_layout; // We need to check two things about the size: // - That the total size won't overflow a `usize`, and @@ -504,7 +570,7 @@ impl Layout { // By using division we can check them both with a single threshold. // That'd usually be a bad idea, but thankfully here the element size // and alignment are constants, so the compiler will fold all of it. - if element_size != 0 && n > Layout::max_size_for_align(align) / element_size { + if element_size != 0 && n > Layout::max_size_for_alignment(alignment) / element_size { return Err(LayoutError); } @@ -517,17 +583,9 @@ impl Layout { // SAFETY: We just checked above that the `array_size` will not // exceed `isize::MAX` even when rounded up to the alignment. // And `Alignment` guarantees it's a power of two. - unsafe { Ok(Layout::from_size_align_unchecked(array_size, align.as_usize())) } + unsafe { Ok(Layout::from_size_alignment_unchecked(array_size, alignment)) } } } - - /// Perma-unstable access to `align` as `Alignment` type. - #[unstable(issue = "none", feature = "std_internals")] - #[doc(hidden)] - #[inline] - pub const fn alignment(&self) -> Alignment { - self.align - } } #[stable(feature = "alloc_layout", since = "1.28.0")] diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 1671c8219de1..7c486875a826 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -9,6 +9,7 @@ use crate::alloc::Layout; use crate::clone::TrivialClone; use crate::marker::{Destruct, DiscriminantKind}; use crate::panic::const_assert; +use crate::ptr::Alignment; use crate::{clone, cmp, fmt, hash, intrinsics, ptr}; mod manually_drop; @@ -1257,6 +1258,10 @@ pub trait SizedTypeProperties: Sized { #[lang = "mem_align_const"] const ALIGN: usize = intrinsics::align_of::(); + #[doc(hidden)] + #[unstable(feature = "ptr_alignment_type", issue = "102070")] + const ALIGNMENT: Alignment = Alignment::of::(); + /// `true` if this type requires no storage. /// `false` if its [size](size_of) is greater than zero. /// diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs index bc7d3a1de715..42c95e6c9cd2 100644 --- a/library/core/src/ptr/alignment.rs +++ b/library/core/src/ptr/alignment.rs @@ -1,5 +1,6 @@ #![allow(clippy::enum_clike_unportable_variant)] +use crate::marker::MetaSized; use crate::num::NonZero; use crate::ub_checks::assert_unsafe_precondition; use crate::{cmp, fmt, hash, mem, num}; @@ -50,6 +51,79 @@ impl Alignment { const { Alignment::new(align_of::()).unwrap() } } + /// Returns the [ABI]-required minimum alignment of the type of the value that `val` points to. + /// + /// Every reference to a value of the type `T` must be a multiple of this number. + /// + /// [ABI]: https://en.wikipedia.org/wiki/Application_binary_interface + /// + /// # Examples + /// + /// ``` + /// #![feature(ptr_alignment_type)] + /// use std::ptr::Alignment; + /// + /// assert_eq!(Alignment::of_val(&5i32).as_usize(), 4); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "ptr_alignment_type", issue = "102070")] + pub const fn of_val(val: &T) -> Self { + let align = mem::align_of_val(val); + // SAFETY: `align_of_val` returns valid alignment + unsafe { Alignment::new_unchecked(align) } + } + + /// Returns the [ABI]-required minimum alignment of the type of the value that `val` points to. + /// + /// Every reference to a value of the type `T` must be a multiple of this number. + /// + /// [ABI]: https://en.wikipedia.org/wiki/Application_binary_interface + /// + /// # Safety + /// + /// This function is only safe to call if the following conditions hold: + /// + /// - If `T` is `Sized`, this function is always safe to call. + /// - If the unsized tail of `T` is: + /// - a [slice], then the length of the slice tail must be an initialized + /// integer, and the size of the *entire value* + /// (dynamic tail length + statically sized prefix) must fit in `isize`. + /// For the special case where the dynamic tail length is 0, this function + /// is safe to call. + /// - a [trait object], then the vtable part of the pointer must point + /// to a valid vtable acquired by an unsizing coercion, and the size + /// of the *entire value* (dynamic tail length + statically sized prefix) + /// must fit in `isize`. + /// - an (unstable) [extern type], then this function is always safe to + /// call, but may panic or otherwise return the wrong value, as the + /// extern type's layout is not known. This is the same behavior as + /// [`Alignment::of_val`] on a reference to a type with an extern type tail. + /// - otherwise, it is conservatively not allowed to call this function. + /// + /// [trait object]: ../../book/ch17-02-trait-objects.html + /// [extern type]: ../../unstable-book/language-features/extern-types.html + /// + /// # Examples + /// + /// ``` + /// #![feature(ptr_alignment_type)] + /// #![feature(layout_for_ptr)] + /// use std::ptr::Alignment; + /// + /// assert_eq!(unsafe { Alignment::of_val_raw(&5i32) }.as_usize(), 4); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "ptr_alignment_type", issue = "102070")] + // #[unstable(feature = "layout_for_ptr", issue = "69835")] + pub const unsafe fn of_val_raw(val: *const T) -> Self { + // SAFETY: precondition propagated to the caller + let align = unsafe { mem::align_of_val_raw(val) }; + // SAFETY: `align_of_val_raw` returns valid alignment + unsafe { Alignment::new_unchecked(align) } + } + /// Creates an `Alignment` from a `usize`, or returns `None` if it's /// not a power of two. /// diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir index 9202814adec7..6ffadd4c51db 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir @@ -12,32 +12,32 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { scope 3 { let _8: std::ptr::alignment::AlignmentEnum; scope 4 { - scope 12 (inlined Layout::size) { + scope 17 (inlined Layout::size) { } - scope 13 (inlined std::ptr::Unique::<[T]>::cast::) { - scope 14 (inlined NonNull::<[T]>::cast::) { - scope 15 (inlined NonNull::<[T]>::as_ptr) { + scope 18 (inlined std::ptr::Unique::<[T]>::cast::) { + scope 19 (inlined NonNull::<[T]>::cast::) { + scope 20 (inlined NonNull::<[T]>::as_ptr) { } } } - scope 16 (inlined as From>>::from) { - scope 17 (inlined std::ptr::Unique::::as_non_null_ptr) { + scope 21 (inlined as From>>::from) { + scope 22 (inlined std::ptr::Unique::::as_non_null_ptr) { } } - scope 18 (inlined ::deallocate) { - scope 19 (inlined std::alloc::Global::deallocate_impl) { - scope 20 (inlined std::alloc::Global::deallocate_impl_runtime) { + scope 23 (inlined ::deallocate) { + scope 24 (inlined std::alloc::Global::deallocate_impl) { + scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) { let mut _9: *mut u8; - scope 21 (inlined Layout::size) { + scope 26 (inlined Layout::size) { } - scope 22 (inlined NonNull::::as_ptr) { + scope 27 (inlined NonNull::::as_ptr) { } - scope 23 (inlined std::alloc::dealloc) { + scope 28 (inlined std::alloc::dealloc) { let mut _10: usize; - scope 24 (inlined Layout::size) { + scope 29 (inlined Layout::size) { } - scope 25 (inlined Layout::align) { - scope 26 (inlined std::ptr::Alignment::as_usize) { + scope 30 (inlined Layout::align) { + scope 31 (inlined std::ptr::Alignment::as_usize) { } } } @@ -51,15 +51,25 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 7 (inlined Layout::for_value_raw::<[T]>) { let mut _5: usize; - let mut _6: usize; + let mut _7: std::ptr::Alignment; scope 8 { - scope 11 (inlined #[track_caller] Layout::from_size_align_unchecked) { - let mut _7: std::ptr::Alignment; + scope 16 (inlined #[track_caller] Layout::from_size_alignment_unchecked) { } } scope 9 (inlined size_of_val_raw::<[T]>) { } - scope 10 (inlined align_of_val_raw::<[T]>) { + scope 10 (inlined std::ptr::Alignment::of_val_raw::<[T]>) { + let _6: usize; + scope 11 { + scope 13 (inlined #[track_caller] std::ptr::Alignment::new_unchecked) { + scope 14 (inlined core::ub_checks::check_language_ub) { + scope 15 (inlined core::ub_checks::check_language_ub::runtime) { + } + } + } + } + scope 12 (inlined align_of_val_raw::<[T]>) { + } } } } @@ -72,17 +82,17 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { StorageLive(_4); _3 = copy _2 as *mut [T] (Transmute); _4 = copy _2 as *const [T] (Transmute); - StorageLive(_6); + StorageLive(_7); _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable]; } bb1: { + StorageLive(_6); _6 = const ::ALIGN; - StorageLive(_7); _7 = copy _6 as std::ptr::Alignment (Transmute); - _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); - StorageDead(_7); StorageDead(_6); + _8 = copy (_7.0: std::ptr::alignment::AlignmentEnum); + StorageDead(_7); StorageDead(_4); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir index 9202814adec7..6ffadd4c51db 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir @@ -12,32 +12,32 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { scope 3 { let _8: std::ptr::alignment::AlignmentEnum; scope 4 { - scope 12 (inlined Layout::size) { + scope 17 (inlined Layout::size) { } - scope 13 (inlined std::ptr::Unique::<[T]>::cast::) { - scope 14 (inlined NonNull::<[T]>::cast::) { - scope 15 (inlined NonNull::<[T]>::as_ptr) { + scope 18 (inlined std::ptr::Unique::<[T]>::cast::) { + scope 19 (inlined NonNull::<[T]>::cast::) { + scope 20 (inlined NonNull::<[T]>::as_ptr) { } } } - scope 16 (inlined as From>>::from) { - scope 17 (inlined std::ptr::Unique::::as_non_null_ptr) { + scope 21 (inlined as From>>::from) { + scope 22 (inlined std::ptr::Unique::::as_non_null_ptr) { } } - scope 18 (inlined ::deallocate) { - scope 19 (inlined std::alloc::Global::deallocate_impl) { - scope 20 (inlined std::alloc::Global::deallocate_impl_runtime) { + scope 23 (inlined ::deallocate) { + scope 24 (inlined std::alloc::Global::deallocate_impl) { + scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) { let mut _9: *mut u8; - scope 21 (inlined Layout::size) { + scope 26 (inlined Layout::size) { } - scope 22 (inlined NonNull::::as_ptr) { + scope 27 (inlined NonNull::::as_ptr) { } - scope 23 (inlined std::alloc::dealloc) { + scope 28 (inlined std::alloc::dealloc) { let mut _10: usize; - scope 24 (inlined Layout::size) { + scope 29 (inlined Layout::size) { } - scope 25 (inlined Layout::align) { - scope 26 (inlined std::ptr::Alignment::as_usize) { + scope 30 (inlined Layout::align) { + scope 31 (inlined std::ptr::Alignment::as_usize) { } } } @@ -51,15 +51,25 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 7 (inlined Layout::for_value_raw::<[T]>) { let mut _5: usize; - let mut _6: usize; + let mut _7: std::ptr::Alignment; scope 8 { - scope 11 (inlined #[track_caller] Layout::from_size_align_unchecked) { - let mut _7: std::ptr::Alignment; + scope 16 (inlined #[track_caller] Layout::from_size_alignment_unchecked) { } } scope 9 (inlined size_of_val_raw::<[T]>) { } - scope 10 (inlined align_of_val_raw::<[T]>) { + scope 10 (inlined std::ptr::Alignment::of_val_raw::<[T]>) { + let _6: usize; + scope 11 { + scope 13 (inlined #[track_caller] std::ptr::Alignment::new_unchecked) { + scope 14 (inlined core::ub_checks::check_language_ub) { + scope 15 (inlined core::ub_checks::check_language_ub::runtime) { + } + } + } + } + scope 12 (inlined align_of_val_raw::<[T]>) { + } } } } @@ -72,17 +82,17 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { StorageLive(_4); _3 = copy _2 as *mut [T] (Transmute); _4 = copy _2 as *const [T] (Transmute); - StorageLive(_6); + StorageLive(_7); _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable]; } bb1: { + StorageLive(_6); _6 = const ::ALIGN; - StorageLive(_7); _7 = copy _6 as std::ptr::Alignment (Transmute); - _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); - StorageDead(_7); StorageDead(_6); + _8 = copy (_7.0: std::ptr::alignment::AlignmentEnum); + StorageDead(_7); StorageDead(_4); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir index 9202814adec7..6ffadd4c51db 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir @@ -12,32 +12,32 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { scope 3 { let _8: std::ptr::alignment::AlignmentEnum; scope 4 { - scope 12 (inlined Layout::size) { + scope 17 (inlined Layout::size) { } - scope 13 (inlined std::ptr::Unique::<[T]>::cast::) { - scope 14 (inlined NonNull::<[T]>::cast::) { - scope 15 (inlined NonNull::<[T]>::as_ptr) { + scope 18 (inlined std::ptr::Unique::<[T]>::cast::) { + scope 19 (inlined NonNull::<[T]>::cast::) { + scope 20 (inlined NonNull::<[T]>::as_ptr) { } } } - scope 16 (inlined as From>>::from) { - scope 17 (inlined std::ptr::Unique::::as_non_null_ptr) { + scope 21 (inlined as From>>::from) { + scope 22 (inlined std::ptr::Unique::::as_non_null_ptr) { } } - scope 18 (inlined ::deallocate) { - scope 19 (inlined std::alloc::Global::deallocate_impl) { - scope 20 (inlined std::alloc::Global::deallocate_impl_runtime) { + scope 23 (inlined ::deallocate) { + scope 24 (inlined std::alloc::Global::deallocate_impl) { + scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) { let mut _9: *mut u8; - scope 21 (inlined Layout::size) { + scope 26 (inlined Layout::size) { } - scope 22 (inlined NonNull::::as_ptr) { + scope 27 (inlined NonNull::::as_ptr) { } - scope 23 (inlined std::alloc::dealloc) { + scope 28 (inlined std::alloc::dealloc) { let mut _10: usize; - scope 24 (inlined Layout::size) { + scope 29 (inlined Layout::size) { } - scope 25 (inlined Layout::align) { - scope 26 (inlined std::ptr::Alignment::as_usize) { + scope 30 (inlined Layout::align) { + scope 31 (inlined std::ptr::Alignment::as_usize) { } } } @@ -51,15 +51,25 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 7 (inlined Layout::for_value_raw::<[T]>) { let mut _5: usize; - let mut _6: usize; + let mut _7: std::ptr::Alignment; scope 8 { - scope 11 (inlined #[track_caller] Layout::from_size_align_unchecked) { - let mut _7: std::ptr::Alignment; + scope 16 (inlined #[track_caller] Layout::from_size_alignment_unchecked) { } } scope 9 (inlined size_of_val_raw::<[T]>) { } - scope 10 (inlined align_of_val_raw::<[T]>) { + scope 10 (inlined std::ptr::Alignment::of_val_raw::<[T]>) { + let _6: usize; + scope 11 { + scope 13 (inlined #[track_caller] std::ptr::Alignment::new_unchecked) { + scope 14 (inlined core::ub_checks::check_language_ub) { + scope 15 (inlined core::ub_checks::check_language_ub::runtime) { + } + } + } + } + scope 12 (inlined align_of_val_raw::<[T]>) { + } } } } @@ -72,17 +82,17 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { StorageLive(_4); _3 = copy _2 as *mut [T] (Transmute); _4 = copy _2 as *const [T] (Transmute); - StorageLive(_6); + StorageLive(_7); _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable]; } bb1: { + StorageLive(_6); _6 = const ::ALIGN; - StorageLive(_7); _7 = copy _6 as std::ptr::Alignment (Transmute); - _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); - StorageDead(_7); StorageDead(_6); + _8 = copy (_7.0: std::ptr::alignment::AlignmentEnum); + StorageDead(_7); StorageDead(_4); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir index 9202814adec7..6ffadd4c51db 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir @@ -12,32 +12,32 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { scope 3 { let _8: std::ptr::alignment::AlignmentEnum; scope 4 { - scope 12 (inlined Layout::size) { + scope 17 (inlined Layout::size) { } - scope 13 (inlined std::ptr::Unique::<[T]>::cast::) { - scope 14 (inlined NonNull::<[T]>::cast::) { - scope 15 (inlined NonNull::<[T]>::as_ptr) { + scope 18 (inlined std::ptr::Unique::<[T]>::cast::) { + scope 19 (inlined NonNull::<[T]>::cast::) { + scope 20 (inlined NonNull::<[T]>::as_ptr) { } } } - scope 16 (inlined as From>>::from) { - scope 17 (inlined std::ptr::Unique::::as_non_null_ptr) { + scope 21 (inlined as From>>::from) { + scope 22 (inlined std::ptr::Unique::::as_non_null_ptr) { } } - scope 18 (inlined ::deallocate) { - scope 19 (inlined std::alloc::Global::deallocate_impl) { - scope 20 (inlined std::alloc::Global::deallocate_impl_runtime) { + scope 23 (inlined ::deallocate) { + scope 24 (inlined std::alloc::Global::deallocate_impl) { + scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) { let mut _9: *mut u8; - scope 21 (inlined Layout::size) { + scope 26 (inlined Layout::size) { } - scope 22 (inlined NonNull::::as_ptr) { + scope 27 (inlined NonNull::::as_ptr) { } - scope 23 (inlined std::alloc::dealloc) { + scope 28 (inlined std::alloc::dealloc) { let mut _10: usize; - scope 24 (inlined Layout::size) { + scope 29 (inlined Layout::size) { } - scope 25 (inlined Layout::align) { - scope 26 (inlined std::ptr::Alignment::as_usize) { + scope 30 (inlined Layout::align) { + scope 31 (inlined std::ptr::Alignment::as_usize) { } } } @@ -51,15 +51,25 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 7 (inlined Layout::for_value_raw::<[T]>) { let mut _5: usize; - let mut _6: usize; + let mut _7: std::ptr::Alignment; scope 8 { - scope 11 (inlined #[track_caller] Layout::from_size_align_unchecked) { - let mut _7: std::ptr::Alignment; + scope 16 (inlined #[track_caller] Layout::from_size_alignment_unchecked) { } } scope 9 (inlined size_of_val_raw::<[T]>) { } - scope 10 (inlined align_of_val_raw::<[T]>) { + scope 10 (inlined std::ptr::Alignment::of_val_raw::<[T]>) { + let _6: usize; + scope 11 { + scope 13 (inlined #[track_caller] std::ptr::Alignment::new_unchecked) { + scope 14 (inlined core::ub_checks::check_language_ub) { + scope 15 (inlined core::ub_checks::check_language_ub::runtime) { + } + } + } + } + scope 12 (inlined align_of_val_raw::<[T]>) { + } } } } @@ -72,17 +82,17 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { StorageLive(_4); _3 = copy _2 as *mut [T] (Transmute); _4 = copy _2 as *const [T] (Transmute); - StorageLive(_6); + StorageLive(_7); _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable]; } bb1: { + StorageLive(_6); _6 = const ::ALIGN; - StorageLive(_7); _7 = copy _6 as std::ptr::Alignment (Transmute); - _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); - StorageDead(_7); StorageDead(_6); + _8 = copy (_7.0: std::ptr::alignment::AlignmentEnum); + StorageDead(_7); StorageDead(_4); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.rs b/tests/mir-opt/pre-codegen/drop_boxed_slice.rs index 83b10f8bd688..dddcffdd4843 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.rs +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.rs @@ -11,7 +11,7 @@ pub unsafe fn generic_in_place(ptr: *mut Box<[T]>) { // CHECK: [[SIZE:_.+]] = std::intrinsics::size_of_val::<[T]> // CHECK: [[ALIGN:_.+]] = const ::ALIGN; // CHECK: [[B:_.+]] = copy [[ALIGN]] as std::ptr::Alignment (Transmute); - // CHECK: [[C:_.+]] = move ([[B]].0: std::ptr::alignment::AlignmentEnum); + // CHECK: [[C:_.+]] = copy ([[B]].0: std::ptr::alignment::AlignmentEnum); // CHECK: [[D:_.+]] = discriminant([[C]]); // CHECK: = alloc::alloc::__rust_dealloc({{.+}}, move [[SIZE]], move [[D]]) -> std::ptr::drop_in_place(ptr) diff --git a/tests/ui/thir-print/offset_of.stdout b/tests/ui/thir-print/offset_of.stdout index b3791a2446cb..5666a5972f37 100644 --- a/tests/ui/thir-print/offset_of.stdout +++ b/tests/ui/thir-print/offset_of.stdout @@ -68,7 +68,7 @@ body: ) else_block: None hir_id: HirId(DefId(offset_of::concrete).10) - span: $DIR/offset_of.rs:37:5: 1440:57 (#0) + span: $DIR/offset_of.rs:37:5: 1445:57 (#0) } } Stmt { @@ -117,7 +117,7 @@ body: ) else_block: None hir_id: HirId(DefId(offset_of::concrete).20) - span: $DIR/offset_of.rs:38:5: 1440:57 (#0) + span: $DIR/offset_of.rs:38:5: 1445:57 (#0) } } Stmt { @@ -166,7 +166,7 @@ body: ) else_block: None hir_id: HirId(DefId(offset_of::concrete).30) - span: $DIR/offset_of.rs:39:5: 1440:57 (#0) + span: $DIR/offset_of.rs:39:5: 1445:57 (#0) } } Stmt { @@ -215,7 +215,7 @@ body: ) else_block: None hir_id: HirId(DefId(offset_of::concrete).40) - span: $DIR/offset_of.rs:40:5: 1440:57 (#0) + span: $DIR/offset_of.rs:40:5: 1445:57 (#0) } } Stmt { @@ -264,7 +264,7 @@ body: ) else_block: None hir_id: HirId(DefId(offset_of::concrete).50) - span: $DIR/offset_of.rs:41:5: 1440:57 (#0) + span: $DIR/offset_of.rs:41:5: 1445:57 (#0) } } ] @@ -864,7 +864,7 @@ body: ) else_block: None hir_id: HirId(DefId(offset_of::generic).12) - span: $DIR/offset_of.rs:45:5: 1440:57 (#0) + span: $DIR/offset_of.rs:45:5: 1445:57 (#0) } } Stmt { @@ -913,7 +913,7 @@ body: ) else_block: None hir_id: HirId(DefId(offset_of::generic).24) - span: $DIR/offset_of.rs:46:5: 1440:57 (#0) + span: $DIR/offset_of.rs:46:5: 1445:57 (#0) } } Stmt { @@ -962,7 +962,7 @@ body: ) else_block: None hir_id: HirId(DefId(offset_of::generic).36) - span: $DIR/offset_of.rs:47:5: 1440:57 (#0) + span: $DIR/offset_of.rs:47:5: 1445:57 (#0) } } Stmt { @@ -1011,7 +1011,7 @@ body: ) else_block: None hir_id: HirId(DefId(offset_of::generic).48) - span: $DIR/offset_of.rs:48:5: 1440:57 (#0) + span: $DIR/offset_of.rs:48:5: 1445:57 (#0) } } ] From 76438f032a1dd57e624f326f126b639f1c2a7e68 Mon Sep 17 00:00:00 2001 From: Jamie Hill-Daniel Date: Thu, 14 Aug 2025 13:32:31 +0000 Subject: [PATCH 08/28] Add codegen test for issue 138497 --- ...sue-138497-nonzero-remove-trailing-zeroes.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 tests/codegen-llvm/issues/issue-138497-nonzero-remove-trailing-zeroes.rs diff --git a/tests/codegen-llvm/issues/issue-138497-nonzero-remove-trailing-zeroes.rs b/tests/codegen-llvm/issues/issue-138497-nonzero-remove-trailing-zeroes.rs new file mode 100644 index 000000000000..77cdbaf2bfe5 --- /dev/null +++ b/tests/codegen-llvm/issues/issue-138497-nonzero-remove-trailing-zeroes.rs @@ -0,0 +1,17 @@ +//! This test checks that removing trailing zeroes from a `NonZero`, +//! then creating a new `NonZero` from the result does not panic. + +//@ min-llvm-version: 21 +//@ compile-flags: -O -Zmerge-functions=disabled +#![crate_type = "lib"] + +use std::num::NonZero; + +// CHECK-LABEL: @remove_trailing_zeros +#[no_mangle] +pub fn remove_trailing_zeros(x: NonZero) -> NonZero { + // CHECK: %[[TRAILING:[a-z0-9_-]+]] = {{.*}} call {{.*}} i8 @llvm.cttz.i8(i8 %x, i1 true) + // CHECK-NEXT: %[[RET:[a-z0-9_-]+]] = lshr exact i8 %x, %[[TRAILING]] + // CHECK-NEXT: ret i8 %[[RET]] + NonZero::new(x.get() >> x.trailing_zeros()).unwrap() +} From fdc7cfde551c46ae67b9902fa6d63821223fe161 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 18 Jan 2026 17:14:52 +0100 Subject: [PATCH 09/28] x86 soft-float feature: mark it as forbidden rather than unstable --- compiler/rustc_target/src/target_features.rs | 4 +--- ...mpatible-target-feature-attribute.x86.stderr | 2 +- ...i-incompatible-target-feature-flag-enable.rs | 3 ++- ...atible-target-feature-flag-enable.x86.stderr | 5 +++-- tests/ui/target-feature/x86-soft-float-cfg.rs | 17 +++++++++++++++++ 5 files changed, 24 insertions(+), 7 deletions(-) create mode 100644 tests/ui/target-feature/x86-soft-float-cfg.rs diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 4eba426dda59..02a91442d864 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -466,9 +466,7 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("sha512", Stable, &["avx2"]), ("sm3", Stable, &["avx"]), ("sm4", Stable, &["avx2"]), - // This cannot actually be toggled, the ABI always fixes it, so it'd make little sense to - // stabilize. It must be in this list for the ABI check to be able to use it. - ("soft-float", Stability::Unstable(sym::x87_target_feature), &[]), + ("soft-float", Stability::Forbidden { reason: "use a soft-float target instead" }, &[]), ("sse", Stable, &[]), ("sse2", Stable, &["sse"]), ("sse3", Stable, &["sse2"]), diff --git a/tests/ui/target-feature/abi-incompatible-target-feature-attribute.x86.stderr b/tests/ui/target-feature/abi-incompatible-target-feature-attribute.x86.stderr index fd9d693525cc..0fab0f003375 100644 --- a/tests/ui/target-feature/abi-incompatible-target-feature-attribute.x86.stderr +++ b/tests/ui/target-feature/abi-incompatible-target-feature-attribute.x86.stderr @@ -1,4 +1,4 @@ -error: target feature `soft-float` cannot be enabled with `#[target_feature]`: this feature is incompatible with the target ABI +error: target feature `soft-float` cannot be enabled with `#[target_feature]`: use a soft-float target instead --> $DIR/abi-incompatible-target-feature-attribute.rs:17:32 | LL | #[cfg_attr(x86, target_feature(enable = "soft-float"))] #[cfg_attr(riscv, target_feature(enable = "d"))] diff --git a/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs index 0cdaf3358d25..c6370f01c47d 100644 --- a/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs +++ b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs @@ -19,4 +19,5 @@ extern crate minicore; use minicore::*; //~? WARN must be disabled to ensure that the ABI of the current target can be implemented correctly -//~? WARN unstable feature specified for `-Ctarget-feature` +//[riscv]~? WARN unstable feature specified for `-Ctarget-feature` +//[x86]~? WARN use a soft-float target instead diff --git a/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.x86.stderr b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.x86.stderr index 90a9665fb41b..a8395f2d4690 100644 --- a/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.x86.stderr +++ b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.x86.stderr @@ -1,6 +1,7 @@ -warning: unstable feature specified for `-Ctarget-feature`: `soft-float` +warning: target feature `soft-float` cannot be enabled with `-Ctarget-feature`: use a soft-float target instead | - = note: this feature is not stably supported; its behavior can change in the future + = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116344 warning: target feature `soft-float` must be disabled to ensure that the ABI of the current target can be implemented correctly | diff --git a/tests/ui/target-feature/x86-soft-float-cfg.rs b/tests/ui/target-feature/x86-soft-float-cfg.rs new file mode 100644 index 000000000000..8718338ca105 --- /dev/null +++ b/tests/ui/target-feature/x86-soft-float-cfg.rs @@ -0,0 +1,17 @@ +//! The soft-float target feature is *not* exposed as `cfg` on x86. +//@ revisions: soft hard +//@[hard] compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib +//@[hard] needs-llvm-components: x86 +//@[soft] compile-flags: --target=x86_64-unknown-none --crate-type=lib +//@[soft] needs-llvm-components: x86 +//@ check-pass +//@ ignore-backends: gcc +//@ add-minicore +#![feature(no_core)] +#![no_core] +#![allow(unexpected_cfgs)] + +// The compile_error macro does not exist, so if the `cfg` evaluates to `true` this +// complains about the missing macro rather than showing the error... but that's good enough. +#[cfg(target_feature = "soft-float")] +compile_error!("the soft-float feature should NOT be exposed in `cfg`"); From bed40af305e84b422c68caa2b5828547ce459e4a Mon Sep 17 00:00:00 2001 From: joboet Date: Thu, 11 Dec 2025 00:31:46 +0100 Subject: [PATCH 10/28] std: avoid tearing `dbg!` prints --- library/std/src/lib.rs | 4 +- library/std/src/macros.rs | 77 ++++++++++++++++++++++++++++----------- 2 files changed, 59 insertions(+), 22 deletions(-) diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 03451214ccb5..8c50c39e7b50 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -468,7 +468,9 @@ extern crate std as realstd; // The standard macros that are not built-in to the compiler. #[macro_use] -mod macros; +#[doc(hidden)] +#[unstable(feature = "std_internals", issue = "none")] +pub mod macros; // The runtime entry point and a few unstable public functions used by the // compiler diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index 25e2b7ea1370..0bb14552432d 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -347,35 +347,70 @@ macro_rules! eprintln { /// [`debug!`]: https://docs.rs/log/*/log/macro.debug.html /// [`log`]: https://crates.io/crates/log #[macro_export] +#[allow_internal_unstable(std_internals)] #[cfg_attr(not(test), rustc_diagnostic_item = "dbg_macro")] #[stable(feature = "dbg_macro", since = "1.32.0")] macro_rules! dbg { - // NOTE: We cannot use `concat!` to make a static string as a format argument - // of `eprintln!` because `file!` could contain a `{` or - // `$val` expression could be a block (`{ .. }`), in which case the `eprintln!` - // will be malformed. () => { $crate::eprintln!("[{}:{}:{}]", $crate::file!(), $crate::line!(), $crate::column!()) }; - ($val:expr $(,)?) => { + ($($val:expr),+ $(,)?) => { + $crate::macros::dbg_internal!(() () ($($val),+)) + }; +} + +/// Internal macro that processes a list of expressions and produces a chain of +/// nested `match`es, one for each expression, before finally calling `eprint!` +/// with the collected information and returning all the evaluated expressions +/// in a tuple. +/// +/// E.g. `dbg_internal!(() () (1, 2))` expands into +/// ```rust, ignore +/// match 1 { +/// tmp_1 => match 2 { +/// tmp_2 => { +/// eprint!("...", &tmp_1, &tmp_2, /* some other arguments */); +/// (tmp_1, tmp_2) +/// } +/// } +/// } +/// ``` +/// +/// This is necessary so that `dbg!` outputs don't get torn, see #136703. +#[doc(hidden)] +#[rustc_macro_transparency = "semiopaque"] +pub macro dbg_internal { + (($($piece:literal),+) ($($processed:expr => $bound:expr),+) ()) => {{ + $crate::eprint!( + $crate::concat!($($piece),+), + $( + $crate::stringify!($processed), + // The `&T: Debug` check happens here (not in the format literal desugaring) + // to avoid format literal related messages and suggestions. + &&$bound as &dyn $crate::fmt::Debug + ),+, + // The location returned here is that of the macro invocation, so + // it will be the same for all expressions. Thus, label these + // arguments so that they can be reused in every piece of the + // formatting template. + file=$crate::file!(), + line=$crate::line!(), + column=$crate::column!() + ); + // Comma separate the variables only when necessary so that this will + // not yield a tuple for a single expression, but rather just parenthesize + // the expression. + ($($bound),+) + }}, + (($($piece:literal),*) ($($processed:expr => $bound:expr),*) ($val:expr $(,$rest:expr)*)) => { // Use of `match` here is intentional because it affects the lifetimes // of temporaries - https://stackoverflow.com/a/48732525/1063961 match $val { - tmp => { - $crate::eprintln!("[{}:{}:{}] {} = {:#?}", - $crate::file!(), - $crate::line!(), - $crate::column!(), - $crate::stringify!($val), - // The `&T: Debug` check happens here (not in the format literal desugaring) - // to avoid format literal related messages and suggestions. - &&tmp as &dyn $crate::fmt::Debug, - ); - tmp - } + tmp => $crate::macros::dbg_internal!( + ($($piece,)* "[{file}:{line}:{column}] {} = {:#?}\n") + ($($processed => $bound,)* $val => tmp) + ($($rest),*) + ), } - }; - ($($val:expr),+ $(,)?) => { - ($($crate::dbg!($val)),+,) - }; + }, } From f80c137f7b518ce9973a509e25a39520a6c9ec89 Mon Sep 17 00:00:00 2001 From: joboet Date: Fri, 12 Dec 2025 13:23:48 +0100 Subject: [PATCH 11/28] update `dbg!` clippy lint --- .../clippy/clippy_lints/src/dbg_macro.rs | 80 ++++++++++++------- 1 file changed, 52 insertions(+), 28 deletions(-) diff --git a/src/tools/clippy/clippy_lints/src/dbg_macro.rs b/src/tools/clippy/clippy_lints/src/dbg_macro.rs index 152516baf734..9197870cb695 100644 --- a/src/tools/clippy/clippy_lints/src/dbg_macro.rs +++ b/src/tools/clippy/clippy_lints/src/dbg_macro.rs @@ -5,7 +5,7 @@ use clippy_utils::macros::{MacroCall, macro_backtrace}; use clippy_utils::source::snippet_with_applicability; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{Closure, ClosureKind, CoroutineKind, Expr, ExprKind, LetStmt, LocalSource, Node, Stmt, StmtKind}; +use rustc_hir::{Arm, Closure, ClosureKind, CoroutineKind, Expr, ExprKind, LetStmt, LocalSource, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::{Span, SyntaxContext, sym}; @@ -90,33 +90,27 @@ impl LateLintPass<'_> for DbgMacro { (macro_call.span, String::from("()")) } }, - // dbg!(1) - ExprKind::Match(val, ..) => ( - macro_call.span, - snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability) - .to_string(), - ), - // dbg!(2, 3) - ExprKind::Tup( - [ - Expr { - kind: ExprKind::Match(first, ..), - .. - }, - .., - Expr { - kind: ExprKind::Match(last, ..), - .. - }, - ], - ) => { - let snippet = snippet_with_applicability( - cx, - first.span.source_callsite().to(last.span.source_callsite()), - "..", - &mut applicability, - ); - (macro_call.span, format!("({snippet})")) + ExprKind::Match(first, arms, _) => { + let vals = collect_vals(first, arms); + let suggestion = match vals.as_slice() { + // dbg!(1) => 1 + &[val] => { + snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability) + .to_string() + } + // dbg!(2, 3) => (2, 3) + &[first, .., last] => { + let snippet = snippet_with_applicability( + cx, + first.span.source_callsite().to(last.span.source_callsite()), + "..", + &mut applicability, + ); + format!("({snippet})") + } + _ => unreachable!(), + }; + (macro_call.span, suggestion) }, _ => unreachable!(), }; @@ -169,3 +163,33 @@ fn is_async_move_desugar<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx fn first_dbg_macro_in_expansion(cx: &LateContext<'_>, span: Span) -> Option { macro_backtrace(span).find(|mc| cx.tcx.is_diagnostic_item(sym::dbg_macro, mc.def_id)) } + +/// Extracts all value expressions from the `match`-tree generated by `dbg!`. +/// +/// E.g. from +/// ```rust, ignore +/// match 1 { +/// tmp_1 => match 2 { +/// tmp_2 => { +/// /* printing */ +/// (tmp_1, tmp_2) +/// } +/// } +/// } +/// ``` +/// this extracts `1` and `2`. +fn collect_vals<'hir>(first: &'hir Expr<'hir>, mut arms: &'hir [Arm<'hir>]) -> Vec<&'hir Expr<'hir>> { + let mut vals = vec![first]; + loop { + let [arm] = arms else { unreachable!("dbg! macro expansion only has single-arm matches") }; + + match is_async_move_desugar(arm.body).unwrap_or(arm.body).peel_drop_temps().kind { + ExprKind::Block(..) => return vals, + ExprKind::Match(val, a, _) => { + vals.push(val); + arms = a; + } + _ => unreachable!("dbg! macro expansion only results in block or match expressions"), + } + } +} From ee24f22b7c6654ef8ed4fc5b7f01f5661b0d1c5b Mon Sep 17 00:00:00 2001 From: joboet Date: Sat, 13 Dec 2025 10:48:15 +0100 Subject: [PATCH 12/28] update UI tests --- .../fail/dangling_pointers/dangling_primitive.stderr | 2 +- .../fail/function_calls/return_pointer_on_unwind.stderr | 2 +- tests/ui/delegation/ice-line-bounds-issue-148732.stderr | 4 ++-- .../mismatched_types/mismatched-types-issue-126222.stderr | 8 ++++---- tests/ui/modules/issue-107649.stderr | 2 +- .../rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr | 2 +- .../rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr | 2 +- tests/ui/typeck/closure-ty-mismatch-issue-128561.stderr | 2 +- .../suggest-clone-in-macro-issue-139253.stderr | 4 ++-- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_primitive.stderr b/src/tools/miri/tests/fail/dangling_pointers/dangling_primitive.stderr index 354cb882fd9f..24a807afd73d 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/dangling_primitive.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/dangling_primitive.stderr @@ -16,7 +16,7 @@ help: ALLOC was deallocated here: | LL | }; | ^ - = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr index d653ec3a069c..845b4f977ca3 100644 --- a/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr +++ b/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr @@ -11,7 +11,7 @@ LL | dbg!(x.0); | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) Uninitialized memory occurred at ALLOC[0x0..0x4], in this allocation: ALLOC (stack variable, size: 132, align: 4) { diff --git a/tests/ui/delegation/ice-line-bounds-issue-148732.stderr b/tests/ui/delegation/ice-line-bounds-issue-148732.stderr index f332bc6a7a21..f34ac0ea306c 100644 --- a/tests/ui/delegation/ice-line-bounds-issue-148732.stderr +++ b/tests/ui/delegation/ice-line-bounds-issue-148732.stderr @@ -4,7 +4,7 @@ error[E0106]: missing lifetime specifier LL | dbg!(b); | ^^^^^^^ expected named lifetime parameter | - = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0425]: cannot find function `a` in this scope --> $DIR/ice-line-bounds-issue-148732.rs:1:7 @@ -37,7 +37,7 @@ LL | dbg!(b); | ^^^^^^^ the trait `Debug` is not implemented for fn item `fn() {b}` | = help: use parentheses to call this function: `b()` - = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 4 previous errors diff --git a/tests/ui/mismatched_types/mismatched-types-issue-126222.stderr b/tests/ui/mismatched_types/mismatched-types-issue-126222.stderr index 2a8f9867abb8..6843cb65a8cd 100644 --- a/tests/ui/mismatched_types/mismatched-types-issue-126222.stderr +++ b/tests/ui/mismatched_types/mismatched-types-issue-126222.stderr @@ -4,7 +4,7 @@ error[E0308]: mismatched types LL | x => dbg!(x), | ^^^^^^^ expected `()`, found integer | - = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) help: you might have meant to return this value | LL | x => return dbg!(x), @@ -16,7 +16,7 @@ error[E0308]: mismatched types LL | dbg!(x) | ^^^^^^^ expected `()`, found integer | - = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) help: you might have meant to return this value | LL | return dbg!(x) @@ -28,7 +28,7 @@ error[E0308]: mismatched types LL | _ => dbg!(1) | ^^^^^^^ expected `()`, found integer | - = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) help: you might have meant to return this value | LL | _ => return dbg!(1) @@ -40,7 +40,7 @@ error[E0308]: mismatched types LL | _ => {dbg!(1)} | ^^^^^^^ expected `()`, found integer | - = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) help: you might have meant to return this value | LL | _ => {return dbg!(1)} diff --git a/tests/ui/modules/issue-107649.stderr b/tests/ui/modules/issue-107649.stderr index 49d7cb4e0aad..45cb29d10ec2 100644 --- a/tests/ui/modules/issue-107649.stderr +++ b/tests/ui/modules/issue-107649.stderr @@ -5,7 +5,7 @@ error[E0277]: `Dummy` doesn't implement `Debug` | ^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for `Dummy` | = note: add `#[derive(Debug)]` to `Dummy` or manually `impl Debug for Dummy` - = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Dummy` with `#[derive(Debug)]` --> $DIR/auxiliary/dummy_lib.rs:2:1 | diff --git a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr index f515cb62c7cd..fdf5115303ba 100644 --- a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr +++ b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr @@ -8,7 +8,7 @@ LL | let _ = dbg!(a); LL | let _ = dbg!(a); | ^^^^^^^ value used here after move | - = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider borrowing instead of transferring ownership | LL | let _ = dbg!(&a); diff --git a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr index 4e0ae9184150..2c4ce2676b07 100644 --- a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr +++ b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr @@ -5,7 +5,7 @@ LL | let _: NotDebug = dbg!(NotDebug); | ^^^^^^^^^^^^^^ the trait `Debug` is not implemented for `NotDebug` | = note: add `#[derive(Debug)]` to `NotDebug` or manually `impl Debug for NotDebug` - = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `NotDebug` with `#[derive(Debug)]` | LL + #[derive(Debug)] diff --git a/tests/ui/typeck/closure-ty-mismatch-issue-128561.stderr b/tests/ui/typeck/closure-ty-mismatch-issue-128561.stderr index 31acc5bb10ec..0907489f8e8a 100644 --- a/tests/ui/typeck/closure-ty-mismatch-issue-128561.stderr +++ b/tests/ui/typeck/closure-ty-mismatch-issue-128561.stderr @@ -15,7 +15,7 @@ error[E0308]: mismatched types LL | b"abc".iter().for_each(|x| dbg!(x)); | ^^^^^^^ expected `()`, found `&u8` | - = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0308]: mismatched types --> $DIR/closure-ty-mismatch-issue-128561.rs:8:9 diff --git a/tests/ui/typeck/suggestions/suggest-clone-in-macro-issue-139253.stderr b/tests/ui/typeck/suggestions/suggest-clone-in-macro-issue-139253.stderr index 59e56f672374..972c2ced0037 100644 --- a/tests/ui/typeck/suggestions/suggest-clone-in-macro-issue-139253.stderr +++ b/tests/ui/typeck/suggestions/suggest-clone-in-macro-issue-139253.stderr @@ -26,7 +26,7 @@ error[E0308]: mismatched types LL | let c: S = dbg!(field); | ^^^^^^^^^^^ expected `S`, found `&S` | - = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider using clone here | LL | let c: S = dbg!(field).clone(); @@ -38,7 +38,7 @@ error[E0308]: mismatched types LL | let c: S = dbg!(dbg!(field)); | ^^^^^^^^^^^^^^^^^ expected `S`, found `&S` | - = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider using clone here | LL | let c: S = dbg!(dbg!(field)).clone(); From 22b3f598821a7262e06f57542cdf146108709df2 Mon Sep 17 00:00:00 2001 From: Alan Egerton Date: Fri, 23 Jan 2026 22:20:09 +0000 Subject: [PATCH 13/28] Fix suppression of `unused_assignment` in binding of `unused_variable` Unused assignments to an unused variable should trigger only the `unused_variables` lint and not also the `unused_assignments` lint. This was previously implemented by checking whether the span of the assignee was within the span of the binding pattern, however that failed to capture situations was imported from elsewhere (eg from the input tokenstream of a proc-macro that generates the binding pattern). By comparing the span of the assignee to those of the variable introductions instead, a reported stable-to-stable regression is resolved. This fix also impacted some other preexisting tests, which had (undesirably) been triggering both the `unused_variables` and `unused_assignments` lints on the same initializing assignment; those tests have therefore now been updated to expect only the former lint. --- compiler/rustc_mir_transform/src/liveness.rs | 2 +- ...orthand-field-patterns-in-pattern-macro.rs | 4 ++-- ...and-field-patterns-in-pattern-macro.stderr | 12 ++++++---- .../auxiliary/unused_assignment_proc_macro.rs | 23 +++++++++++++++++++ tests/ui/lint/unused/unused_assignment.rs | 21 +++++++++++++++++ tests/ui/lint/unused/unused_assignment.stderr | 15 ++++++++++++ .../pattern/bindings-after-at/bind-by-copy.rs | 1 - .../bindings-after-at/bind-by-copy.stderr | 12 ++-------- 8 files changed, 72 insertions(+), 18 deletions(-) create mode 100644 tests/ui/lint/unused/auxiliary/unused_assignment_proc_macro.rs create mode 100644 tests/ui/lint/unused/unused_assignment.rs create mode 100644 tests/ui/lint/unused/unused_assignment.stderr diff --git a/compiler/rustc_mir_transform/src/liveness.rs b/compiler/rustc_mir_transform/src/liveness.rs index 1d1ba455a81e..0d3d162109da 100644 --- a/compiler/rustc_mir_transform/src/liveness.rs +++ b/compiler/rustc_mir_transform/src/liveness.rs @@ -986,7 +986,7 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> { // warn twice, for the unused local and for the unused assignment. Therefore, we remove // from the list of assignments the ones that happen at the definition site. statements.retain(|source_info, _| { - source_info.span.find_ancestor_inside(binding.pat_span).is_none() + !binding.introductions.iter().any(|intro| intro.span == source_info.span) }); // Extra assignments that we recognize thanks to the initialization span. We need to diff --git a/tests/ui/lint/issue-49588-non-shorthand-field-patterns-in-pattern-macro.rs b/tests/ui/lint/issue-49588-non-shorthand-field-patterns-in-pattern-macro.rs index 5b5843a8ddbb..570b559eb61a 100644 --- a/tests/ui/lint/issue-49588-non-shorthand-field-patterns-in-pattern-macro.rs +++ b/tests/ui/lint/issue-49588-non-shorthand-field-patterns-in-pattern-macro.rs @@ -1,5 +1,5 @@ //@ run-pass -#![allow(unused_variables)] +#![warn(unused)] #![deny(non_shorthand_field_patterns)] pub struct Value { pub value: A } @@ -13,5 +13,5 @@ macro_rules! pat { fn main() { let pat!(value) = Value { value: () }; - //~^ WARN value assigned to `value` is never read + //~^ WARN unused variable: `value` } diff --git a/tests/ui/lint/issue-49588-non-shorthand-field-patterns-in-pattern-macro.stderr b/tests/ui/lint/issue-49588-non-shorthand-field-patterns-in-pattern-macro.stderr index ba7d3515b0d8..3a68ec212b4a 100644 --- a/tests/ui/lint/issue-49588-non-shorthand-field-patterns-in-pattern-macro.stderr +++ b/tests/ui/lint/issue-49588-non-shorthand-field-patterns-in-pattern-macro.stderr @@ -1,11 +1,15 @@ -warning: value assigned to `value` is never read +warning: unused variable: `value` --> $DIR/issue-49588-non-shorthand-field-patterns-in-pattern-macro.rs:15:14 | LL | let pat!(value) = Value { value: () }; - | ^^^^^ + | ^^^^^ help: if this is intentional, prefix it with an underscore: `_value` | - = help: maybe it is overwritten before being read? - = note: `#[warn(unused_assignments)]` (part of `#[warn(unused)]`) on by default +note: the lint level is defined here + --> $DIR/issue-49588-non-shorthand-field-patterns-in-pattern-macro.rs:2:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` warning: 1 warning emitted diff --git a/tests/ui/lint/unused/auxiliary/unused_assignment_proc_macro.rs b/tests/ui/lint/unused/auxiliary/unused_assignment_proc_macro.rs new file mode 100644 index 000000000000..41cfefbaff54 --- /dev/null +++ b/tests/ui/lint/unused/auxiliary/unused_assignment_proc_macro.rs @@ -0,0 +1,23 @@ +#![feature(proc_macro_quote)] + +extern crate proc_macro; +use proc_macro::*; + +#[proc_macro_derive(Drop)] +pub fn generate(ts: TokenStream) -> TokenStream { + let mut ts = ts.into_iter(); + let _pub = ts.next(); + let _struct = ts.next(); + let name = ts.next().unwrap(); + let TokenTree::Group(fields) = ts.next().unwrap() else { panic!() }; + let mut fields = fields.stream().into_iter(); + let field = fields.next().unwrap(); + + quote! { + impl Drop for $name { + fn drop(&mut self) { + let Self { $field } = self; + } + } + } +} diff --git a/tests/ui/lint/unused/unused_assignment.rs b/tests/ui/lint/unused/unused_assignment.rs new file mode 100644 index 000000000000..f7b8ec94bc3b --- /dev/null +++ b/tests/ui/lint/unused/unused_assignment.rs @@ -0,0 +1,21 @@ +// Unused assignments to an unused variable should trigger only the `unused_variables` lint and not +// also the `unused_assignments` lint. This test covers the situation where the span of the unused +// variable identifier comes from a different scope to the binding pattern - here, from a proc +// macro's input tokenstream (whereas the binding pattern is generated within the proc macro +// itself). +// +// Regression test for https://github.com/rust-lang/rust/issues/151514 +// +//@ check-pass +//@ proc-macro: unused_assignment_proc_macro.rs +#![warn(unused)] + +extern crate unused_assignment_proc_macro; +use unused_assignment_proc_macro::Drop; + +#[derive(Drop)] +pub struct S { + a: (), //~ WARN unused variable: `a` +} + +fn main() {} diff --git a/tests/ui/lint/unused/unused_assignment.stderr b/tests/ui/lint/unused/unused_assignment.stderr new file mode 100644 index 000000000000..1f0619ecf142 --- /dev/null +++ b/tests/ui/lint/unused/unused_assignment.stderr @@ -0,0 +1,15 @@ +warning: unused variable: `a` + --> $DIR/unused_assignment.rs:18:5 + | +LL | a: (), + | ^ help: try ignoring the field: `a: _` + | +note: the lint level is defined here + --> $DIR/unused_assignment.rs:11:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` + +warning: 1 warning emitted + diff --git a/tests/ui/pattern/bindings-after-at/bind-by-copy.rs b/tests/ui/pattern/bindings-after-at/bind-by-copy.rs index d766411e4f98..3b6f2a9b08f7 100644 --- a/tests/ui/pattern/bindings-after-at/bind-by-copy.rs +++ b/tests/ui/pattern/bindings-after-at/bind-by-copy.rs @@ -53,7 +53,6 @@ pub fn main() { } match (E::E { a: 10, e: C { c: 20 } }) { mut x @ E::E{ a, e: C { mut c } } => { - //~^ WARN value assigned to `a` is never read x = E::NotE; //~^ WARN value assigned to `x` is never read c += 30; diff --git a/tests/ui/pattern/bindings-after-at/bind-by-copy.stderr b/tests/ui/pattern/bindings-after-at/bind-by-copy.stderr index d775b69ef0a5..d0128950ddda 100644 --- a/tests/ui/pattern/bindings-after-at/bind-by-copy.stderr +++ b/tests/ui/pattern/bindings-after-at/bind-by-copy.stderr @@ -20,20 +20,12 @@ LL | y.d.c = 30; = help: maybe it is overwritten before being read? warning: value assigned to `x` is never read - --> $DIR/bind-by-copy.rs:57:13 + --> $DIR/bind-by-copy.rs:56:13 | LL | x = E::NotE; | ^^^^^^^^^^^ | = help: maybe it is overwritten before being read? -warning: value assigned to `a` is never read - --> $DIR/bind-by-copy.rs:55:23 - | -LL | mut x @ E::E{ a, e: C { mut c } } => { - | ^ - | - = help: maybe it is overwritten before being read? - -warning: 4 warnings emitted +warning: 3 warnings emitted From 764ac2b84f240db58ecc2012739d6719e9db8cb2 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 23 Jan 2026 15:54:13 -0800 Subject: [PATCH 14/28] relnotes: fix 1.93's `as_mut_array` methods The links are correct, but text typoed `as_array_mut`. --- RELEASES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 10a400fda5c3..424e12ceec05 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -62,9 +62,9 @@ Stabilized APIs - [`::unchecked_shl`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.unchecked_shl) - [`::unchecked_shr`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.unchecked_shr) - [`<[T]>::as_array`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.as_array) -- [`<[T]>::as_array_mut`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.as_mut_array) +- [`<[T]>::as_mut_array`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.as_mut_array) - [`<*const [T]>::as_array`](https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.as_array) -- [`<*mut [T]>::as_array_mut`](https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.as_mut_array) +- [`<*mut [T]>::as_mut_array`](https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.as_mut_array) - [`VecDeque::pop_front_if`](https://doc.rust-lang.org/stable/std/collections/struct.VecDeque.html#method.pop_front_if) - [`VecDeque::pop_back_if`](https://doc.rust-lang.org/stable/std/collections/struct.VecDeque.html#method.pop_back_if) - [`Duration::from_nanos_u128`](https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.from_nanos_u128) From 4b25ccdb916d7346dfd717f2ebefc9ece49f5508 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 24 Jan 2026 18:06:11 +1100 Subject: [PATCH 15/28] Rename `DepKindStruct` to `DepKindVTable` --- compiler/rustc_interface/src/callbacks.rs | 2 +- compiler/rustc_interface/src/passes.rs | 2 +- compiler/rustc_middle/src/arena.rs | 2 +- compiler/rustc_middle/src/dep_graph/mod.rs | 6 +- compiler/rustc_middle/src/ty/context.rs | 8 +-- compiler/rustc_query_impl/src/lib.rs | 2 +- compiler/rustc_query_impl/src/plumbing.rs | 60 +++++++++++-------- .../src/dep_graph/dep_node.rs | 4 +- .../rustc_query_system/src/dep_graph/mod.rs | 24 ++++---- compiler/rustc_query_system/src/query/job.rs | 2 +- 10 files changed, 60 insertions(+), 52 deletions(-) diff --git a/compiler/rustc_interface/src/callbacks.rs b/compiler/rustc_interface/src/callbacks.rs index 7c6b7157f71a..3d8d5d59b118 100644 --- a/compiler/rustc_interface/src/callbacks.rs +++ b/compiler/rustc_interface/src/callbacks.rs @@ -72,7 +72,7 @@ fn def_id_debug(def_id: rustc_hir::def_id::DefId, f: &mut fmt::Formatter<'_>) -> pub fn dep_kind_debug(kind: DepKind, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { tls::with_opt(|opt_tcx| { if let Some(tcx) = opt_tcx { - write!(f, "{}", tcx.dep_kind_info(kind).name) + write!(f, "{}", tcx.dep_kind_vtable(kind).name) } else { default_dep_kind_debug(kind, f) } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 3228a0499acc..60b45f7391b5 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -992,7 +992,7 @@ pub fn create_and_enter_global_ctxt FnOnce(TyCtxt<'tcx>) -> T>( hir_arena, untracked, dep_graph, - rustc_query_impl::query_callbacks(arena), + rustc_query_impl::make_dep_kind_vtables(arena), rustc_query_impl::query_system( providers.queries, providers.extern_queries, diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 0bdc1bfd45ee..0f254aaa9fa0 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -104,7 +104,7 @@ macro_rules! arena_types { [decode] is_late_bound_map: rustc_data_structures::fx::FxIndexSet, [decode] impl_source: rustc_middle::traits::ImplSource<'tcx, ()>, - [] dep_kind: rustc_middle::dep_graph::DepKindStruct<'tcx>, + [] dep_kind_vtable: rustc_middle::dep_graph::DepKindVTable<'tcx>, [decode] trait_impl_trait_tys: rustc_data_structures::unord::UnordMap< diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs index 049e868879e9..f28ba10d52e2 100644 --- a/compiler/rustc_middle/src/dep_graph/mod.rs +++ b/compiler/rustc_middle/src/dep_graph/mod.rs @@ -18,7 +18,7 @@ pub use rustc_query_system::dep_graph::{ pub type DepGraph = rustc_query_system::dep_graph::DepGraph; -pub type DepKindStruct<'tcx> = rustc_query_system::dep_graph::DepKindStruct>; +pub type DepKindVTable<'tcx> = rustc_query_system::dep_graph::DepKindVTable>; pub struct DepsType; @@ -79,8 +79,8 @@ impl<'tcx> DepContext for TyCtxt<'tcx> { } #[inline] - fn dep_kind_info(&self, dk: DepKind) -> &DepKindStruct<'tcx> { - &self.query_kinds[dk.as_usize()] + fn dep_kind_vtable(&self, dk: DepKind) -> &DepKindVTable<'tcx> { + &self.dep_kind_vtables[dk.as_usize()] } fn with_reduced_queries(self, f: impl FnOnce() -> T) -> T { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 5ba5a3c3d4dc..f015d0edc56c 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -59,7 +59,7 @@ use rustc_type_ir::{ use tracing::{debug, instrument}; use crate::arena::Arena; -use crate::dep_graph::{DepGraph, DepKindStruct}; +use crate::dep_graph::{DepGraph, DepKindVTable}; use crate::infer::canonical::{CanonicalParamEnvCache, CanonicalVarKind, CanonicalVarKinds}; use crate::lint::lint_level; use crate::metadata::ModChild; @@ -1580,7 +1580,7 @@ pub struct GlobalCtxt<'tcx> { untracked: Untracked, pub query_system: QuerySystem<'tcx>, - pub(crate) query_kinds: &'tcx [DepKindStruct<'tcx>], + pub(crate) dep_kind_vtables: &'tcx [DepKindVTable<'tcx>], // Internal caches for metadata decoding. No need to track deps on this. pub ty_rcache: Lock>>, @@ -1801,7 +1801,7 @@ impl<'tcx> TyCtxt<'tcx> { hir_arena: &'tcx WorkerLocal>, untracked: Untracked, dep_graph: DepGraph, - query_kinds: &'tcx [DepKindStruct<'tcx>], + dep_kind_vtables: &'tcx [DepKindVTable<'tcx>], query_system: QuerySystem<'tcx>, hooks: crate::hooks::Providers, current_gcx: CurrentGcx, @@ -1831,7 +1831,7 @@ impl<'tcx> TyCtxt<'tcx> { consts: common_consts, untracked, query_system, - query_kinds, + dep_kind_vtables, ty_rcache: Default::default(), selection_cache: Default::default(), evaluation_cache: Default::default(), diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index c9abc4bdcdfc..57027e937a4a 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -9,7 +9,7 @@ use rustc_data_structures::stable_hasher::HashStable; use rustc_data_structures::sync::AtomicU64; use rustc_middle::arena::Arena; -use rustc_middle::dep_graph::{self, DepKind, DepKindStruct, DepNodeIndex}; +use rustc_middle::dep_graph::{self, DepKind, DepKindVTable, DepNodeIndex}; use rustc_middle::query::erase::{Erase, erase, restore}; use rustc_middle::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; use rustc_middle::query::plumbing::{DynamicQuery, QuerySystem, QuerySystemFns}; diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 7479a992e297..246152f5390c 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -12,7 +12,7 @@ use rustc_hir::limit::Limit; use rustc_index::Idx; use rustc_middle::bug; use rustc_middle::dep_graph::{ - self, DepContext, DepKind, DepKindStruct, DepNode, DepNodeIndex, SerializedDepNodeIndex, + self, DepContext, DepKind, DepKindVTable, DepNode, DepNodeIndex, SerializedDepNodeIndex, dep_kinds, }; use rustc_middle::query::Key; @@ -489,14 +489,17 @@ where } } -pub(crate) fn query_callback<'tcx, Q>(is_anon: bool, is_eval_always: bool) -> DepKindStruct<'tcx> +pub(crate) fn make_dep_kind_vtable_for_query<'tcx, Q>( + is_anon: bool, + is_eval_always: bool, +) -> DepKindVTable<'tcx> where Q: QueryConfigRestored<'tcx>, { let fingerprint_style = >>::Key::fingerprint_style(); if is_anon || !fingerprint_style.reconstructible() { - return DepKindStruct { + return DepKindVTable { is_anon, is_eval_always, fingerprint_style, @@ -506,7 +509,7 @@ where }; } - DepKindStruct { + DepKindVTable { is_anon, is_eval_always, fingerprint_style, @@ -811,15 +814,19 @@ macro_rules! define_queries { for<'tcx> fn(TyCtxt<'tcx>) ] = &[$(query_impl::$name::query_key_hash_verify),*]; - #[allow(nonstandard_style)] - mod query_callbacks { + /// Module containing a named function for each dep kind (including queries) + /// that creates a `DepKindVTable`. + /// + /// Consumed via `make_dep_kind_array!` to create a list of vtables. + #[expect(non_snake_case)] + mod _dep_kind_vtable_ctors { use super::*; use rustc_middle::bug; use rustc_query_system::dep_graph::FingerprintStyle; // We use this for most things when incr. comp. is turned off. - pub(crate) fn Null<'tcx>() -> DepKindStruct<'tcx> { - DepKindStruct { + pub(crate) fn Null<'tcx>() -> DepKindVTable<'tcx> { + DepKindVTable { is_anon: false, is_eval_always: false, fingerprint_style: FingerprintStyle::Unit, @@ -830,8 +837,8 @@ macro_rules! define_queries { } // We use this for the forever-red node. - pub(crate) fn Red<'tcx>() -> DepKindStruct<'tcx> { - DepKindStruct { + pub(crate) fn Red<'tcx>() -> DepKindVTable<'tcx> { + DepKindVTable { is_anon: false, is_eval_always: false, fingerprint_style: FingerprintStyle::Unit, @@ -841,8 +848,8 @@ macro_rules! define_queries { } } - pub(crate) fn SideEffect<'tcx>() -> DepKindStruct<'tcx> { - DepKindStruct { + pub(crate) fn SideEffect<'tcx>() -> DepKindVTable<'tcx> { + DepKindVTable { is_anon: false, is_eval_always: false, fingerprint_style: FingerprintStyle::Unit, @@ -855,8 +862,8 @@ macro_rules! define_queries { } } - pub(crate) fn AnonZeroDeps<'tcx>() -> DepKindStruct<'tcx> { - DepKindStruct { + pub(crate) fn AnonZeroDeps<'tcx>() -> DepKindVTable<'tcx> { + DepKindVTable { is_anon: true, is_eval_always: false, fingerprint_style: FingerprintStyle::Opaque, @@ -866,8 +873,8 @@ macro_rules! define_queries { } } - pub(crate) fn TraitSelect<'tcx>() -> DepKindStruct<'tcx> { - DepKindStruct { + pub(crate) fn TraitSelect<'tcx>() -> DepKindVTable<'tcx> { + DepKindVTable { is_anon: true, is_eval_always: false, fingerprint_style: FingerprintStyle::Unit, @@ -877,8 +884,8 @@ macro_rules! define_queries { } } - pub(crate) fn CompileCodegenUnit<'tcx>() -> DepKindStruct<'tcx> { - DepKindStruct { + pub(crate) fn CompileCodegenUnit<'tcx>() -> DepKindVTable<'tcx> { + DepKindVTable { is_anon: false, is_eval_always: false, fingerprint_style: FingerprintStyle::Opaque, @@ -888,8 +895,8 @@ macro_rules! define_queries { } } - pub(crate) fn CompileMonoItem<'tcx>() -> DepKindStruct<'tcx> { - DepKindStruct { + pub(crate) fn CompileMonoItem<'tcx>() -> DepKindVTable<'tcx> { + DepKindVTable { is_anon: false, is_eval_always: false, fingerprint_style: FingerprintStyle::Opaque, @@ -899,8 +906,8 @@ macro_rules! define_queries { } } - pub(crate) fn Metadata<'tcx>() -> DepKindStruct<'tcx> { - DepKindStruct { + pub(crate) fn Metadata<'tcx>() -> DepKindVTable<'tcx> { + DepKindVTable { is_anon: false, is_eval_always: false, fingerprint_style: FingerprintStyle::Unit, @@ -910,16 +917,17 @@ macro_rules! define_queries { } } - $(pub(crate) fn $name<'tcx>()-> DepKindStruct<'tcx> { - $crate::plumbing::query_callback::>( + $(pub(crate) fn $name<'tcx>() -> DepKindVTable<'tcx> { + use $crate::query_impl::$name::QueryType; + $crate::plumbing::make_dep_kind_vtable_for_query::>( is_anon!([$($modifiers)*]), is_eval_always!([$($modifiers)*]), ) })* } - pub fn query_callbacks<'tcx>(arena: &'tcx Arena<'tcx>) -> &'tcx [DepKindStruct<'tcx>] { - arena.alloc_from_iter(rustc_middle::make_dep_kind_array!(query_callbacks)) + pub fn make_dep_kind_vtables<'tcx>(arena: &'tcx Arena<'tcx>) -> &'tcx [DepKindVTable<'tcx>] { + arena.alloc_from_iter(rustc_middle::make_dep_kind_array!(_dep_kind_vtable_ctors)) } } } diff --git a/compiler/rustc_query_system/src/dep_graph/dep_node.rs b/compiler/rustc_query_system/src/dep_graph/dep_node.rs index bdd1d5f3e88a..72bdcd2d534d 100644 --- a/compiler/rustc_query_system/src/dep_graph/dep_node.rs +++ b/compiler/rustc_query_system/src/dep_graph/dep_node.rs @@ -221,12 +221,12 @@ where } } -/// This struct stores metadata about each DepKind. +/// This struct stores function pointers and other metadata for a particular DepKind. /// /// Information is retrieved by indexing the `DEP_KINDS` array using the integer value /// of the `DepKind`. Overall, this allows to implement `DepContext` using this manual /// jump table instead of large matches. -pub struct DepKindStruct { +pub struct DepKindVTable { /// Anonymous queries cannot be replayed from one compiler invocation to the next. /// When their result is needed, it is recomputed. They are useful for fine-grained /// dependency tracking, and caching within one compiler invocation. diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs index 8b9e4fe1bf29..874b41cbf3b1 100644 --- a/compiler/rustc_query_system/src/dep_graph/mod.rs +++ b/compiler/rustc_query_system/src/dep_graph/mod.rs @@ -7,7 +7,7 @@ mod serialized; use std::panic; -pub use dep_node::{DepKind, DepKindStruct, DepNode, DepNodeParams, WorkProductId}; +pub use dep_node::{DepKind, DepKindVTable, DepNode, DepNodeParams, WorkProductId}; pub(crate) use graph::DepGraphData; pub use graph::{DepGraph, DepNodeIndex, TaskDepsRef, WorkProduct, WorkProductMap, hash_result}; pub use query::DepGraphQuery; @@ -35,21 +35,21 @@ pub trait DepContext: Copy { /// Access the compiler session. fn sess(&self) -> &Session; - fn dep_kind_info(&self, dep_node: DepKind) -> &DepKindStruct; + fn dep_kind_vtable(&self, dep_node: DepKind) -> &DepKindVTable; #[inline(always)] fn fingerprint_style(self, kind: DepKind) -> FingerprintStyle { - let data = self.dep_kind_info(kind); - if data.is_anon { + let vtable = self.dep_kind_vtable(kind); + if vtable.is_anon { return FingerprintStyle::Opaque; } - data.fingerprint_style + vtable.fingerprint_style } #[inline(always)] /// Return whether this kind always require evaluation. fn is_eval_always(self, kind: DepKind) -> bool { - self.dep_kind_info(kind).is_eval_always + self.dep_kind_vtable(kind).is_eval_always } /// Try to force a dep node to execute and see if it's green. @@ -65,9 +65,10 @@ pub trait DepContext: Copy { prev_index: SerializedDepNodeIndex, frame: &MarkFrame<'_>, ) -> bool { - let cb = self.dep_kind_info(dep_node.kind); - if let Some(f) = cb.force_from_dep_node { - match panic::catch_unwind(panic::AssertUnwindSafe(|| f(self, dep_node, prev_index))) { + if let Some(force_fn) = self.dep_kind_vtable(dep_node.kind).force_from_dep_node { + match panic::catch_unwind(panic::AssertUnwindSafe(|| { + force_fn(self, dep_node, prev_index) + })) { Err(value) => { if !value.is::() { print_markframe_trace(self.dep_graph(), frame); @@ -83,9 +84,8 @@ pub trait DepContext: Copy { /// Load data from the on-disk cache. fn try_load_from_on_disk_cache(self, dep_node: DepNode) { - let cb = self.dep_kind_info(dep_node.kind); - if let Some(f) = cb.try_load_from_on_disk_cache { - f(self, dep_node) + if let Some(try_load_fn) = self.dep_kind_vtable(dep_node.kind).try_load_from_on_disk_cache { + try_load_fn(self, dep_node) } } diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 79d08d33c0b1..0431151c74c9 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -626,7 +626,7 @@ pub fn print_query_stack( file, "#{} [{}] {}", count_total, - qcx.dep_context().dep_kind_info(query_info.query.dep_kind).name, + qcx.dep_context().dep_kind_vtable(query_info.query.dep_kind).name, query_info.query.description ); } From 23e5b692d98664f62c7af447a0836def3fb3556d Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 22 Jan 2026 19:57:13 +0000 Subject: [PATCH 16/28] Use rustc_proc_macro in rust-analyzer-proc-macro-srv This fixes stage1 builds when the proc-macro bridge api changed. The rustc_proc_macro crate is identical to the proc_macro that would end up in the sysroot of the rustc compiler rustc_proc_macro is linked into. --- .../crates/proc-macro-srv/src/bridge.rs | 2 +- .../crates/proc-macro-srv/src/dylib.rs | 2 +- .../proc-macro-srv/src/dylib/proc_macros.rs | 2 +- .../crates/proc-macro-srv/src/lib.rs | 8 ++-- .../src/server_impl/rust_analyzer_span.rs | 2 +- .../src/server_impl/token_id.rs | 2 +- .../crates/proc-macro-srv/src/token_stream.rs | 42 +++++++++---------- 7 files changed, 31 insertions(+), 29 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/bridge.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/bridge.rs index fc063a07b5f8..fc62f9413a34 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/bridge.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/bridge.rs @@ -1,6 +1,6 @@ //! `proc_macro::bridge` newtypes. -use proc_macro::bridge as pm_bridge; +use rustc_proc_macro::bridge as pm_bridge; pub use pm_bridge::{DelimSpan, Diagnostic, ExpnGlobals, LitKind}; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs index 02bdcc50d387..8680e9180e3a 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs @@ -3,7 +3,7 @@ mod proc_macros; mod version; -use proc_macro::bridge; +use rustc_proc_macro::bridge; use std::{fmt, fs, io, time::SystemTime}; use temp_dir::TempDir; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib/proc_macros.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib/proc_macros.rs index c763301135ee..76c5097101c7 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib/proc_macros.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib/proc_macros.rs @@ -1,6 +1,6 @@ //! Proc macro ABI use crate::{ProcMacroClientHandle, ProcMacroKind, ProcMacroSrvSpan, token_stream::TokenStream}; -use proc_macro::bridge; +use rustc_proc_macro::bridge; #[repr(transparent)] pub(crate) struct ProcMacros([bridge::client::ProcMacro]); diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index f2d1dfbba4cc..920d58b4e981 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -22,9 +22,9 @@ )] #![deny(deprecated_safe, clippy::undocumented_unsafe_blocks)] -extern crate proc_macro; #[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; +extern crate rustc_proc_macro; #[cfg(not(feature = "in-rust-tree"))] extern crate ra_ap_rustc_lexer as rustc_lexer; @@ -52,7 +52,7 @@ use temp_dir::TempDir; pub use crate::server_impl::token_id::SpanId; -pub use proc_macro::Delimiter; +pub use rustc_proc_macro::Delimiter; pub use span; pub use crate::bridge::*; @@ -181,7 +181,9 @@ impl ProcMacroSrv<'_> { } pub trait ProcMacroSrvSpan: Copy + Send + Sync { - type Server<'a>: proc_macro::bridge::server::Server>; + type Server<'a>: rustc_proc_macro::bridge::server::Server< + TokenStream = crate::token_stream::TokenStream, + >; fn make_server<'a>( call_site: Self, def_site: Self, diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index 32725afc5527..1845de75dd8b 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -10,7 +10,7 @@ use std::{ }; use intern::Symbol; -use proc_macro::bridge::server; +use rustc_proc_macro::bridge::server; use span::{FIXUP_ERASED_FILE_AST_ID_MARKER, Span, TextRange, TextSize}; use crate::{ diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs index a968ea4cd225..8b1e3c0d8d6d 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs @@ -6,7 +6,7 @@ use std::{ }; use intern::Symbol; -use proc_macro::bridge::server; +use rustc_proc_macro::bridge::server; use crate::{ ProcMacroClientHandle, diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/token_stream.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/token_stream.rs index 36827d2561f9..2358f6963c79 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/token_stream.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/token_stream.rs @@ -4,8 +4,8 @@ use core::fmt; use std::{mem, sync::Arc}; use intern::Symbol; -use proc_macro::Delimiter; use rustc_lexer::{DocStyle, LiteralKind}; +use rustc_proc_macro::Delimiter; use crate::bridge::{DelimSpan, Group, Ident, LitKind, Literal, Punct, TokenTree}; @@ -52,7 +52,7 @@ impl TokenStream { S: SpanLike + Copy, { let mut groups = Vec::new(); - groups.push((proc_macro::Delimiter::None, 0..0, vec![])); + groups.push((rustc_proc_macro::Delimiter::None, 0..0, vec![])); let mut offset = 0; let mut tokens = rustc_lexer::tokenize(s, rustc_lexer::FrontmatterAllowed::No).peekable(); while let Some(token) = tokens.next() { @@ -102,7 +102,7 @@ impl TokenStream { }; match token.kind { rustc_lexer::TokenKind::OpenParen => { - groups.push((proc_macro::Delimiter::Parenthesis, range, vec![])) + groups.push((rustc_proc_macro::Delimiter::Parenthesis, range, vec![])) } rustc_lexer::TokenKind::CloseParen if *open_delim != Delimiter::Parenthesis => { return if *open_delim == Delimiter::None { @@ -130,7 +130,7 @@ impl TokenStream { ); } rustc_lexer::TokenKind::OpenBrace => { - groups.push((proc_macro::Delimiter::Brace, range, vec![])) + groups.push((rustc_proc_macro::Delimiter::Brace, range, vec![])) } rustc_lexer::TokenKind::CloseBrace if *open_delim != Delimiter::Brace => { return if *open_delim == Delimiter::None { @@ -158,7 +158,7 @@ impl TokenStream { ); } rustc_lexer::TokenKind::OpenBracket => { - groups.push((proc_macro::Delimiter::Bracket, range, vec![])) + groups.push((rustc_proc_macro::Delimiter::Bracket, range, vec![])) } rustc_lexer::TokenKind::CloseBracket if *open_delim != Delimiter::Bracket => { return if *open_delim == Delimiter::None { @@ -460,10 +460,10 @@ fn display_token_tree( f, "{}", match delimiter { - proc_macro::Delimiter::Parenthesis => "(", - proc_macro::Delimiter::Brace => "{", - proc_macro::Delimiter::Bracket => "[", - proc_macro::Delimiter::None => "", + rustc_proc_macro::Delimiter::Parenthesis => "(", + rustc_proc_macro::Delimiter::Brace => "{", + rustc_proc_macro::Delimiter::Bracket => "[", + rustc_proc_macro::Delimiter::None => "", } )?; if let Some(stream) = stream { @@ -473,10 +473,10 @@ fn display_token_tree( f, "{}", match delimiter { - proc_macro::Delimiter::Parenthesis => ")", - proc_macro::Delimiter::Brace => "}", - proc_macro::Delimiter::Bracket => "]", - proc_macro::Delimiter::None => "", + rustc_proc_macro::Delimiter::Parenthesis => ")", + rustc_proc_macro::Delimiter::Brace => "}", + rustc_proc_macro::Delimiter::Bracket => "]", + rustc_proc_macro::Delimiter::None => "", } )?; } @@ -587,16 +587,16 @@ fn debug_token_tree( f, "GROUP {}{} {:#?} {:#?} {:#?}", match delimiter { - proc_macro::Delimiter::Parenthesis => "(", - proc_macro::Delimiter::Brace => "{", - proc_macro::Delimiter::Bracket => "[", - proc_macro::Delimiter::None => "$", + rustc_proc_macro::Delimiter::Parenthesis => "(", + rustc_proc_macro::Delimiter::Brace => "{", + rustc_proc_macro::Delimiter::Bracket => "[", + rustc_proc_macro::Delimiter::None => "$", }, match delimiter { - proc_macro::Delimiter::Parenthesis => ")", - proc_macro::Delimiter::Brace => "}", - proc_macro::Delimiter::Bracket => "]", - proc_macro::Delimiter::None => "$", + rustc_proc_macro::Delimiter::Parenthesis => ")", + rustc_proc_macro::Delimiter::Brace => "}", + rustc_proc_macro::Delimiter::Bracket => "]", + rustc_proc_macro::Delimiter::None => "$", }, span.open, span.close, From eec532020ed77db56f9c400e4b8a3303bc12cb13 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 24 Jan 2026 14:42:49 +0000 Subject: [PATCH 17/28] Disable proc-macro-srv tests on stage 0 They break whenever the proc macro ABI changes due to it building a proc macro against the bootstrap sysroot. --- src/bootstrap/src/core/build_steps/test.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 52b38421eec2..844251f2c64d 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -509,6 +509,12 @@ impl Step for RustAnalyzer { cargo.arg("--workspace"); cargo.arg("--exclude=xtask"); + if build_compiler.stage == 0 { + // This builds a proc macro against the bootstrap libproc_macro, which is not ABI + // compatible with the ABI proc-macro-srv expects to load. + cargo.arg("--exclude=proc-macro-srv"); + } + let mut skip_tests = vec![]; // NOTE: the following test skips is a bit cheeky in that it assumes there are no From ef819e49a80b4220c457dfbbddde58db6a8c72e2 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 3 Oct 2025 21:35:29 +0000 Subject: [PATCH 18/28] Remove a couple of unnecessary impls --- library/proc_macro/src/bridge/client.rs | 6 ------ library/proc_macro/src/bridge/handle.rs | 8 +------- library/proc_macro/src/bridge/server.rs | 11 ----------- 3 files changed, 1 insertion(+), 24 deletions(-) diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index bdaa865a998d..48a43a7bac34 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -52,12 +52,6 @@ macro_rules! define_client_handles { } } - impl Encode for &mut $oty { - fn encode(self, w: &mut Writer, s: &mut S) { - self.handle.encode(w, s); - } - } - impl Decode<'_, '_, S> for $oty { fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { $oty { diff --git a/library/proc_macro/src/bridge/handle.rs b/library/proc_macro/src/bridge/handle.rs index 8c53bb609f60..f0c01e39de32 100644 --- a/library/proc_macro/src/bridge/handle.rs +++ b/library/proc_macro/src/bridge/handle.rs @@ -3,7 +3,7 @@ use std::collections::BTreeMap; use std::hash::Hash; use std::num::NonZero; -use std::ops::{Index, IndexMut}; +use std::ops::Index; use std::sync::atomic::{AtomicU32, Ordering}; use super::fxhash::FxHashMap; @@ -47,12 +47,6 @@ impl Index for OwnedStore { } } -impl IndexMut for OwnedStore { - fn index_mut(&mut self, h: Handle) -> &mut T { - self.data.get_mut(&h).expect("use-after-free in `proc_macro` handle") - } -} - /// Like `OwnedStore`, but avoids storing any value more than once. pub(super) struct InternedStore { owned: OwnedStore, diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs index e9ef26c07f24..27cc5e5d57e3 100644 --- a/library/proc_macro/src/bridge/server.rs +++ b/library/proc_macro/src/bridge/server.rs @@ -47,17 +47,6 @@ macro_rules! define_server_handles { &s.$oty[handle::Handle::decode(r, &mut ())] } } - - impl<'s, S: Types> Decode<'_, 's, HandleStore>> - for &'s mut Marked - { - fn decode( - r: &mut Reader<'_>, - s: &'s mut HandleStore> - ) -> Self { - &mut s.$oty[handle::Handle::decode(r, &mut ())] - } - } )* $( From dabae7eea4b2036477c39c89dfb77b21f03a0ac7 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 3 Oct 2025 21:27:26 +0000 Subject: [PATCH 19/28] Handle FreeFunctions outside with_api_handle_types It is a singleton which doesn't actually need to be passed through over the bridge. --- library/proc_macro/src/bridge/client.rs | 1 + library/proc_macro/src/bridge/mod.rs | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index 48a43a7bac34..e75902eca6bb 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -120,6 +120,7 @@ impl fmt::Debug for Span { } } +pub(crate) use super::FreeFunctions; pub(crate) use super::symbol::Symbol; macro_rules! define_client_side { diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index b0ee9c0cc302..15a81bd5f96f 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -49,7 +49,6 @@ macro_rules! with_api { ($S:ident, $self:ident, $m:ident) => { $m! { FreeFunctions { - fn drop($self: $S::FreeFunctions); fn injected_env_var(var: &str) -> Option; fn track_env_var(var: &str, value: Option<&str>); fn track_path(path: &str); @@ -109,7 +108,7 @@ macro_rules! with_api_handle_types { ($m:ident) => { $m! { 'owned: - FreeFunctions, + // FreeFunctions is handled manually TokenStream, 'interned: @@ -119,6 +118,8 @@ macro_rules! with_api_handle_types { }; } +pub(crate) struct FreeFunctions; + #[allow(unsafe_code)] mod arena; #[allow(unsafe_code)] From 4dc28c59ab31f1f2454c3cd466b6c99c625f426b Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 3 Oct 2025 21:29:58 +0000 Subject: [PATCH 20/28] Expand with_api_handle_types --- library/proc_macro/src/bridge/client.rs | 129 ++++++++++-------------- library/proc_macro/src/bridge/mod.rs | 16 --- library/proc_macro/src/bridge/server.rs | 103 ++++++++----------- 3 files changed, 98 insertions(+), 150 deletions(-) diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index e75902eca6bb..10302cf24f99 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -6,87 +6,66 @@ use std::sync::atomic::AtomicU32; use super::*; -macro_rules! define_client_handles { - ( - 'owned: $($oty:ident,)* - 'interned: $($ity:ident,)* - ) => { - #[repr(C)] - #[allow(non_snake_case)] - pub(super) struct HandleCounters { - $(pub(super) $oty: AtomicU32,)* - $(pub(super) $ity: AtomicU32,)* - } +#[repr(C)] +pub(super) struct HandleCounters { + pub(super) token_stream: AtomicU32, + pub(super) span: AtomicU32, +} - static COUNTERS: HandleCounters = HandleCounters { - $($oty: AtomicU32::new(1),)* - $($ity: AtomicU32::new(1),)* - }; +static COUNTERS: HandleCounters = + HandleCounters { token_stream: AtomicU32::new(1), span: AtomicU32::new(1) }; - $( - pub(crate) struct $oty { - handle: handle::Handle, - } +pub(crate) struct TokenStream { + handle: handle::Handle, +} - impl !Send for $oty {} - impl !Sync for $oty {} +impl !Send for TokenStream {} +impl !Sync for TokenStream {} - // Forward `Drop::drop` to the inherent `drop` method. - impl Drop for $oty { - fn drop(&mut self) { - $oty { - handle: self.handle, - }.drop(); - } - } - - impl Encode for $oty { - fn encode(self, w: &mut Writer, s: &mut S) { - mem::ManuallyDrop::new(self).handle.encode(w, s); - } - } - - impl Encode for &$oty { - fn encode(self, w: &mut Writer, s: &mut S) { - self.handle.encode(w, s); - } - } - - impl Decode<'_, '_, S> for $oty { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { - $oty { - handle: handle::Handle::decode(r, s), - } - } - } - )* - - $( - #[derive(Copy, Clone, PartialEq, Eq, Hash)] - pub(crate) struct $ity { - handle: handle::Handle, - } - - impl !Send for $ity {} - impl !Sync for $ity {} - - impl Encode for $ity { - fn encode(self, w: &mut Writer, s: &mut S) { - self.handle.encode(w, s); - } - } - - impl Decode<'_, '_, S> for $ity { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { - $ity { - handle: handle::Handle::decode(r, s), - } - } - } - )* +// Forward `Drop::drop` to the inherent `drop` method. +impl Drop for TokenStream { + fn drop(&mut self) { + TokenStream { handle: self.handle }.drop(); + } +} + +impl Encode for TokenStream { + fn encode(self, w: &mut Writer, s: &mut S) { + mem::ManuallyDrop::new(self).handle.encode(w, s); + } +} + +impl Encode for &TokenStream { + fn encode(self, w: &mut Writer, s: &mut S) { + self.handle.encode(w, s); + } +} + +impl Decode<'_, '_, S> for TokenStream { + fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { + TokenStream { handle: handle::Handle::decode(r, s) } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub(crate) struct Span { + handle: handle::Handle, +} + +impl !Send for Span {} +impl !Sync for Span {} + +impl Encode for Span { + fn encode(self, w: &mut Writer, s: &mut S) { + self.handle.encode(w, s); + } +} + +impl Decode<'_, '_, S> for Span { + fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { + Span { handle: handle::Handle::decode(r, s) } } } -with_api_handle_types!(define_client_handles); // FIXME(eddyb) generate these impls by pattern-matching on the // names of methods - also could use the presence of `fn drop` diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index 15a81bd5f96f..04a75ed797c8 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -102,22 +102,6 @@ macro_rules! with_api { }; } -// Similar to `with_api`, but only lists the types requiring handles, and they -// are divided into the two storage categories. -macro_rules! with_api_handle_types { - ($m:ident) => { - $m! { - 'owned: - // FreeFunctions is handled manually - TokenStream, - - 'interned: - Span, - // Symbol is handled manually - } - }; -} - pub(crate) struct FreeFunctions; #[allow(unsafe_code)] diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs index 27cc5e5d57e3..c9e42bd1dd15 100644 --- a/library/proc_macro/src/bridge/server.rs +++ b/library/proc_macro/src/bridge/server.rs @@ -5,68 +5,53 @@ use std::marker::PhantomData; use super::*; -macro_rules! define_server_handles { - ( - 'owned: $($oty:ident,)* - 'interned: $($ity:ident,)* - ) => { - #[allow(non_snake_case)] - pub(super) struct HandleStore { - $($oty: handle::OwnedStore,)* - $($ity: handle::InternedStore,)* +pub(super) struct HandleStore { + token_stream: handle::OwnedStore, + span: handle::InternedStore, +} + +impl HandleStore { + fn new(handle_counters: &'static client::HandleCounters) -> Self { + HandleStore { + token_stream: handle::OwnedStore::new(&handle_counters.token_stream), + span: handle::InternedStore::new(&handle_counters.span), } - - impl HandleStore { - fn new(handle_counters: &'static client::HandleCounters) -> Self { - HandleStore { - $($oty: handle::OwnedStore::new(&handle_counters.$oty),)* - $($ity: handle::InternedStore::new(&handle_counters.$ity),)* - } - } - } - - $( - impl Encode>> for Marked { - fn encode(self, w: &mut Writer, s: &mut HandleStore>) { - s.$oty.alloc(self).encode(w, s); - } - } - - impl Decode<'_, '_, HandleStore>> - for Marked - { - fn decode(r: &mut Reader<'_>, s: &mut HandleStore>) -> Self { - s.$oty.take(handle::Handle::decode(r, &mut ())) - } - } - - impl<'s, S: Types> Decode<'_, 's, HandleStore>> - for &'s Marked - { - fn decode(r: &mut Reader<'_>, s: &'s mut HandleStore>) -> Self { - &s.$oty[handle::Handle::decode(r, &mut ())] - } - } - )* - - $( - impl Encode>> for Marked { - fn encode(self, w: &mut Writer, s: &mut HandleStore>) { - s.$ity.alloc(self).encode(w, s); - } - } - - impl Decode<'_, '_, HandleStore>> - for Marked - { - fn decode(r: &mut Reader<'_>, s: &mut HandleStore>) -> Self { - s.$ity.copy(handle::Handle::decode(r, &mut ())) - } - } - )* } } -with_api_handle_types!(define_server_handles); + +impl Encode>> for Marked { + fn encode(self, w: &mut Writer, s: &mut HandleStore>) { + s.token_stream.alloc(self).encode(w, s); + } +} + +impl Decode<'_, '_, HandleStore>> + for Marked +{ + fn decode(r: &mut Reader<'_>, s: &mut HandleStore>) -> Self { + s.token_stream.take(handle::Handle::decode(r, &mut ())) + } +} + +impl<'s, S: Types> Decode<'_, 's, HandleStore>> + for &'s Marked +{ + fn decode(r: &mut Reader<'_>, s: &'s mut HandleStore>) -> Self { + &s.token_stream[handle::Handle::decode(r, &mut ())] + } +} + +impl Encode>> for Marked { + fn encode(self, w: &mut Writer, s: &mut HandleStore>) { + s.span.alloc(self).encode(w, s); + } +} + +impl Decode<'_, '_, HandleStore>> for Marked { + fn decode(r: &mut Reader<'_>, s: &mut HandleStore>) -> Self { + s.span.copy(handle::Handle::decode(r, &mut ())) + } +} pub trait Types { type FreeFunctions: 'static; From 2f4401947065b33de24db7f12498d3f52226f52e Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 22 Jan 2026 18:10:03 +0000 Subject: [PATCH 21/28] Move all bridge methods into a single type --- .../rustc_expand/src/proc_macro_server.rs | 70 +++++++------- library/proc_macro/src/bridge/client.rs | 6 +- library/proc_macro/src/bridge/mod.rs | 92 ++++++++++--------- library/proc_macro/src/bridge/server.rs | 33 +++---- library/proc_macro/src/bridge/symbol.rs | 2 +- library/proc_macro/src/lib.rs | 62 ++++++++----- .../src/server_impl/rust_analyzer_span.rs | 70 +++++++------- .../src/server_impl/token_id.rs | 70 +++++++------- 8 files changed, 218 insertions(+), 187 deletions(-) diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 7688df2d3a55..4f8ff5588581 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -552,14 +552,20 @@ impl server::FreeFunctions for Rustc<'_, '_> { } diag.emit(); } -} -impl server::TokenStream for Rustc<'_, '_> { - fn is_empty(&mut self, stream: &Self::TokenStream) -> bool { + fn tt_drop(&mut self, stream: Self::TokenStream) { + drop(stream); + } + + fn tt_clone(&mut self, stream: &Self::TokenStream) -> Self::TokenStream { + stream.clone() + } + + fn tt_is_empty(&mut self, stream: &Self::TokenStream) -> bool { stream.is_empty() } - fn from_str(&mut self, src: &str) -> Self::TokenStream { + fn tt_from_str(&mut self, src: &str) -> Self::TokenStream { unwrap_or_emit_fatal(source_str_to_stream( self.psess(), FileName::proc_macro_source_code(src), @@ -568,11 +574,11 @@ impl server::TokenStream for Rustc<'_, '_> { )) } - fn to_string(&mut self, stream: &Self::TokenStream) -> String { + fn tt_to_string(&mut self, stream: &Self::TokenStream) -> String { pprust::tts_to_string(stream) } - fn expand_expr(&mut self, stream: &Self::TokenStream) -> Result { + fn tt_expand_expr(&mut self, stream: &Self::TokenStream) -> Result { // Parse the expression from our tokenstream. let expr: PResult<'_, _> = try { let mut p = Parser::new(self.psess(), stream.clone(), Some("proc_macro expand expr")); @@ -633,14 +639,14 @@ impl server::TokenStream for Rustc<'_, '_> { } } - fn from_token_tree( + fn tt_from_token_tree( &mut self, tree: TokenTree, ) -> Self::TokenStream { Self::TokenStream::new((tree, &mut *self).to_internal().into_iter().collect::>()) } - fn concat_trees( + fn tt_concat_trees( &mut self, base: Option, trees: Vec>, @@ -654,7 +660,7 @@ impl server::TokenStream for Rustc<'_, '_> { stream } - fn concat_streams( + fn tt_concat_streams( &mut self, base: Option, streams: Vec, @@ -666,16 +672,14 @@ impl server::TokenStream for Rustc<'_, '_> { stream } - fn into_trees( + fn tt_into_trees( &mut self, stream: Self::TokenStream, ) -> Vec> { FromInternal::from_internal((stream, self)) } -} -impl server::Span for Rustc<'_, '_> { - fn debug(&mut self, span: Self::Span) -> String { + fn span_debug(&mut self, span: Self::Span) -> String { if self.ecx.ecfg.span_debug { format!("{span:?}") } else { @@ -683,7 +687,7 @@ impl server::Span for Rustc<'_, '_> { } } - fn file(&mut self, span: Self::Span) -> String { + fn span_file(&mut self, span: Self::Span) -> String { self.psess() .source_map() .lookup_char_pos(span.lo()) @@ -693,7 +697,7 @@ impl server::Span for Rustc<'_, '_> { .to_string() } - fn local_file(&mut self, span: Self::Span) -> Option { + fn span_local_file(&mut self, span: Self::Span) -> Option { self.psess() .source_map() .lookup_char_pos(span.lo()) @@ -708,15 +712,15 @@ impl server::Span for Rustc<'_, '_> { }) } - fn parent(&mut self, span: Self::Span) -> Option { + fn span_parent(&mut self, span: Self::Span) -> Option { span.parent_callsite() } - fn source(&mut self, span: Self::Span) -> Self::Span { + fn span_source(&mut self, span: Self::Span) -> Self::Span { span.source_callsite() } - fn byte_range(&mut self, span: Self::Span) -> Range { + fn span_byte_range(&mut self, span: Self::Span) -> Range { let source_map = self.psess().source_map(); let relative_start_pos = source_map.lookup_byte_offset(span.lo()).pos; @@ -724,25 +728,25 @@ impl server::Span for Rustc<'_, '_> { Range { start: relative_start_pos.0 as usize, end: relative_end_pos.0 as usize } } - fn start(&mut self, span: Self::Span) -> Self::Span { + fn span_start(&mut self, span: Self::Span) -> Self::Span { span.shrink_to_lo() } - fn end(&mut self, span: Self::Span) -> Self::Span { + fn span_end(&mut self, span: Self::Span) -> Self::Span { span.shrink_to_hi() } - fn line(&mut self, span: Self::Span) -> usize { + fn span_line(&mut self, span: Self::Span) -> usize { let loc = self.psess().source_map().lookup_char_pos(span.lo()); loc.line } - fn column(&mut self, span: Self::Span) -> usize { + fn span_column(&mut self, span: Self::Span) -> usize { let loc = self.psess().source_map().lookup_char_pos(span.lo()); loc.col.to_usize() + 1 } - fn join(&mut self, first: Self::Span, second: Self::Span) -> Option { + fn span_join(&mut self, first: Self::Span, second: Self::Span) -> Option { let self_loc = self.psess().source_map().lookup_char_pos(first.lo()); let other_loc = self.psess().source_map().lookup_char_pos(second.lo()); @@ -753,7 +757,7 @@ impl server::Span for Rustc<'_, '_> { Some(first.to(second)) } - fn subspan( + fn span_subspan( &mut self, span: Self::Span, start: Bound, @@ -789,11 +793,11 @@ impl server::Span for Rustc<'_, '_> { Some(span.with_lo(new_lo).with_hi(new_hi)) } - fn resolved_at(&mut self, span: Self::Span, at: Self::Span) -> Self::Span { + fn span_resolved_at(&mut self, span: Self::Span, at: Self::Span) -> Self::Span { span.with_ctxt(at.ctxt()) } - fn source_text(&mut self, span: Self::Span) -> Option { + fn span_source_text(&mut self, span: Self::Span) -> Option { self.psess().source_map().span_to_snippet(span).ok() } @@ -821,11 +825,11 @@ impl server::Span for Rustc<'_, '_> { /// span from the metadata of `my_proc_macro` (which we have access to, /// since we've loaded `my_proc_macro` from disk in order to execute it). /// In this way, we have obtained a span pointing into `my_proc_macro` - fn save_span(&mut self, span: Self::Span) -> usize { + fn span_save_span(&mut self, span: Self::Span) -> usize { self.psess().save_proc_macro_span(span) } - fn recover_proc_macro_span(&mut self, id: usize) -> Self::Span { + fn span_recover_proc_macro_span(&mut self, id: usize) -> Self::Span { let (resolver, krate, def_site) = (&*self.ecx.resolver, self.krate, self.def_site); *self.rebased_spans.entry(id).or_insert_with(|| { // FIXME: `SyntaxContext` for spans from proc macro crates is lost during encoding, @@ -833,15 +837,19 @@ impl server::Span for Rustc<'_, '_> { resolver.get_proc_macro_quoted_span(krate, id).with_ctxt(def_site.ctxt()) }) } -} -impl server::Symbol for Rustc<'_, '_> { - fn normalize_and_validate_ident(&mut self, string: &str) -> Result { + fn symbol_normalize_and_validate_ident(&mut self, string: &str) -> Result { let sym = nfc_normalize(string); if rustc_lexer::is_ident(sym.as_str()) { Ok(sym) } else { Err(()) } } } +impl server::TokenStream for Rustc<'_, '_> {} + +impl server::Span for Rustc<'_, '_> {} + +impl server::Symbol for Rustc<'_, '_> {} + impl server::Server for Rustc<'_, '_> { fn globals(&mut self) -> ExpnGlobals { ExpnGlobals { diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index 10302cf24f99..d954177e7df5 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -25,7 +25,7 @@ impl !Sync for TokenStream {} // Forward `Drop::drop` to the inherent `drop` method. impl Drop for TokenStream { fn drop(&mut self) { - TokenStream { handle: self.handle }.drop(); + FreeFunctions::tt_drop(TokenStream { handle: self.handle }); } } @@ -75,7 +75,7 @@ impl Decode<'_, '_, S> for Span { impl Clone for TokenStream { fn clone(&self) -> Self { - self.clone() + FreeFunctions::tt_clone(self) } } @@ -95,7 +95,7 @@ impl Span { impl fmt::Debug for Span { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&self.debug()) + f.write_str(&FreeFunctions::span_debug(*self)) } } diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index 04a75ed797c8..027f94c36ff2 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -54,50 +54,50 @@ macro_rules! with_api { fn track_path(path: &str); fn literal_from_str(s: &str) -> Result, ()>; fn emit_diagnostic(diagnostic: Diagnostic<$S::Span>); - }, - TokenStream { - fn drop($self: $S::TokenStream); - fn clone($self: &$S::TokenStream) -> $S::TokenStream; - fn is_empty($self: &$S::TokenStream) -> bool; - fn expand_expr($self: &$S::TokenStream) -> Result<$S::TokenStream, ()>; - fn from_str(src: &str) -> $S::TokenStream; - fn to_string($self: &$S::TokenStream) -> String; - fn from_token_tree( + + fn tt_drop(stream: $S::TokenStream); + fn tt_clone(stream: &$S::TokenStream) -> $S::TokenStream; + fn tt_is_empty(stream: &$S::TokenStream) -> bool; + fn tt_expand_expr(stream: &$S::TokenStream) -> Result<$S::TokenStream, ()>; + fn tt_from_str(src: &str) -> $S::TokenStream; + fn tt_to_string(stream: &$S::TokenStream) -> String; + fn tt_from_token_tree( tree: TokenTree<$S::TokenStream, $S::Span, $S::Symbol>, ) -> $S::TokenStream; - fn concat_trees( + fn tt_concat_trees( base: Option<$S::TokenStream>, trees: Vec>, ) -> $S::TokenStream; - fn concat_streams( + fn tt_concat_streams( base: Option<$S::TokenStream>, streams: Vec<$S::TokenStream>, ) -> $S::TokenStream; - fn into_trees( - $self: $S::TokenStream + fn tt_into_trees( + stream: $S::TokenStream ) -> Vec>; + + fn span_debug(span: $S::Span) -> String; + fn span_parent(span: $S::Span) -> Option<$S::Span>; + fn span_source(span: $S::Span) -> $S::Span; + fn span_byte_range(span: $S::Span) -> Range; + fn span_start(span: $S::Span) -> $S::Span; + fn span_end(span: $S::Span) -> $S::Span; + fn span_line(span: $S::Span) -> usize; + fn span_column(span: $S::Span) -> usize; + fn span_file(span: $S::Span) -> String; + fn span_local_file(span: $S::Span) -> Option; + fn span_join(span: $S::Span, other: $S::Span) -> Option<$S::Span>; + fn span_subspan(span: $S::Span, start: Bound, end: Bound) -> Option<$S::Span>; + fn span_resolved_at(span: $S::Span, at: $S::Span) -> $S::Span; + fn span_source_text(span: $S::Span) -> Option; + fn span_save_span(span: $S::Span) -> usize; + fn span_recover_proc_macro_span(id: usize) -> $S::Span; + + fn symbol_normalize_and_validate_ident(string: &str) -> Result<$S::Symbol, ()>; }, - Span { - fn debug($self: $S::Span) -> String; - fn parent($self: $S::Span) -> Option<$S::Span>; - fn source($self: $S::Span) -> $S::Span; - fn byte_range($self: $S::Span) -> Range; - fn start($self: $S::Span) -> $S::Span; - fn end($self: $S::Span) -> $S::Span; - fn line($self: $S::Span) -> usize; - fn column($self: $S::Span) -> usize; - fn file($self: $S::Span) -> String; - fn local_file($self: $S::Span) -> Option; - fn join($self: $S::Span, other: $S::Span) -> Option<$S::Span>; - fn subspan($self: $S::Span, start: Bound, end: Bound) -> Option<$S::Span>; - fn resolved_at($self: $S::Span, at: $S::Span) -> $S::Span; - fn source_text($self: $S::Span) -> Option; - fn save_span($self: $S::Span) -> usize; - fn recover_proc_macro_span(id: usize) -> $S::Span; - }, - Symbol { - fn normalize_and_validate_ident(string: &str) -> Result<$S::Symbol, ()>; - }, + TokenStream {}, + Span {}, + Symbol {}, } }; } @@ -156,20 +156,22 @@ mod api_tags { use super::rpc::{Decode, Encode, Reader, Writer}; macro_rules! declare_tags { - ($($name:ident { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* - }),* $(,)?) => { - $( - pub(super) enum $name { - $($method),* - } - rpc_encode_decode!(enum $name { $($method),* }); - )* + ( + FreeFunctions { + $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* + }, + $($name:ident { $($x:tt)* }),* $(,)? + ) => { + pub(super) enum FreeFunctions { + $($method),* + } + rpc_encode_decode!(enum FreeFunctions { $($method),* }); + pub(super) enum Method { - $($name($name)),* + FreeFunctions(FreeFunctions), } - rpc_encode_decode!(enum Method { $($name(m)),* }); + rpc_encode_decode!(enum Method { FreeFunctions(m) }); } } with_api!(self, self, declare_tags); diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs index c9e42bd1dd15..16037ed46f06 100644 --- a/library/proc_macro/src/bridge/server.rs +++ b/library/proc_macro/src/bridge/server.rs @@ -60,24 +60,12 @@ pub trait Types { type Symbol: 'static; } -/// Declare an associated fn of one of the traits below, adding necessary -/// default bodies. -macro_rules! associated_fn { - (fn drop(&mut self, $arg:ident: $arg_ty:ty)) => - (fn drop(&mut self, $arg: $arg_ty) { mem::drop($arg) }); - - (fn clone(&mut self, $arg:ident: $arg_ty:ty) -> $ret_ty:ty) => - (fn clone(&mut self, $arg: $arg_ty) -> $ret_ty { $arg.clone() }); - - ($($item:tt)*) => ($($item)*;) -} - macro_rules! declare_server_traits { ($($name:ident { $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* }),* $(,)?) => { $(pub trait $name: Types { - $(associated_fn!(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)?);)* + $(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)?;)* })* pub trait Server: Types $(+ $name)* { @@ -130,18 +118,23 @@ struct Dispatcher { } macro_rules! define_dispatcher_impl { - ($($name:ident { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* - }),* $(,)?) => { + ( + FreeFunctions { + $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* + }, + $($name:ident { $($x:tt)* }),* $(,)? + ) => { // FIXME(eddyb) `pub` only for `ExecutionStrategy` below. pub trait DispatcherTrait { // HACK(eddyb) these are here to allow `Self::$name` to work below. + type FreeFunctions; $(type $name;)* fn dispatch(&mut self, buf: Buffer) -> Buffer; } impl DispatcherTrait for Dispatcher> { + type FreeFunctions = as Types>::FreeFunctions; $(type $name = as Types>::$name;)* fn dispatch(&mut self, mut buf: Buffer) -> Buffer { @@ -149,11 +142,11 @@ macro_rules! define_dispatcher_impl { let mut reader = &buf[..]; match api_tags::Method::decode(&mut reader, &mut ()) { - $(api_tags::Method::$name(m) => match m { - $(api_tags::$name::$method => { + api_tags::Method::FreeFunctions(m) => match m { + $(api_tags::FreeFunctions::$method => { let mut call_method = || { $(let $arg = <$arg_ty>::decode(&mut reader, handle_store);)* - $name::$method(server, $($arg),*) + FreeFunctions::$method(server, $($arg),*) }; // HACK(eddyb) don't use `panic::catch_unwind` in a panic. // If client and server happen to use the same `std`, @@ -169,7 +162,7 @@ macro_rules! define_dispatcher_impl { buf.clear(); r.encode(&mut buf, handle_store); })* - }),* + } } buf } diff --git a/library/proc_macro/src/bridge/symbol.rs b/library/proc_macro/src/bridge/symbol.rs index e070ec07681d..6366ac2e6a93 100644 --- a/library/proc_macro/src/bridge/symbol.rs +++ b/library/proc_macro/src/bridge/symbol.rs @@ -46,7 +46,7 @@ impl Symbol { if string.is_ascii() { Err(()) } else { - client::Symbol::normalize_and_validate_ident(string) + client::FreeFunctions::symbol_normalize_and_validate_ident(string) } .unwrap_or_else(|_| panic!("`{:?}` is not a valid identifier", string)) } diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index a005f743ddfa..8d5491e91851 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -158,7 +158,7 @@ impl TokenStream { /// Checks if this `TokenStream` is empty. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] pub fn is_empty(&self) -> bool { - self.0.as_ref().map(|h| h.is_empty()).unwrap_or(true) + self.0.as_ref().map(|h| bridge::client::FreeFunctions::tt_is_empty(h)).unwrap_or(true) } /// Parses this `TokenStream` as an expression and attempts to expand any @@ -174,7 +174,7 @@ impl TokenStream { #[unstable(feature = "proc_macro_expand", issue = "90765")] pub fn expand_expr(&self) -> Result { let stream = self.0.as_ref().ok_or(ExpandError)?; - match bridge::client::TokenStream::expand_expr(stream) { + match bridge::client::FreeFunctions::tt_expand_expr(stream) { Ok(stream) => Ok(TokenStream(Some(stream))), Err(_) => Err(ExpandError), } @@ -193,7 +193,7 @@ impl FromStr for TokenStream { type Err = LexError; fn from_str(src: &str) -> Result { - Ok(TokenStream(Some(bridge::client::TokenStream::from_str(src)))) + Ok(TokenStream(Some(bridge::client::FreeFunctions::tt_from_str(src)))) } } @@ -212,7 +212,7 @@ impl FromStr for TokenStream { impl fmt::Display for TokenStream { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.0 { - Some(ts) => write!(f, "{}", ts.to_string()), + Some(ts) => write!(f, "{}", bridge::client::FreeFunctions::tt_to_string(ts)), None => Ok(()), } } @@ -252,7 +252,9 @@ fn tree_to_bridge_tree( #[stable(feature = "proc_macro_lib2", since = "1.29.0")] impl From for TokenStream { fn from(tree: TokenTree) -> TokenStream { - TokenStream(Some(bridge::client::TokenStream::from_token_tree(tree_to_bridge_tree(tree)))) + TokenStream(Some(bridge::client::FreeFunctions::tt_from_token_tree(tree_to_bridge_tree( + tree, + )))) } } @@ -281,7 +283,7 @@ impl ConcatTreesHelper { if self.trees.is_empty() { TokenStream(None) } else { - TokenStream(Some(bridge::client::TokenStream::concat_trees(None, self.trees))) + TokenStream(Some(bridge::client::FreeFunctions::tt_concat_trees(None, self.trees))) } } @@ -289,7 +291,7 @@ impl ConcatTreesHelper { if self.trees.is_empty() { return; } - stream.0 = Some(bridge::client::TokenStream::concat_trees(stream.0.take(), self.trees)) + stream.0 = Some(bridge::client::FreeFunctions::tt_concat_trees(stream.0.take(), self.trees)) } } @@ -314,7 +316,7 @@ impl ConcatStreamsHelper { if self.streams.len() <= 1 { TokenStream(self.streams.pop()) } else { - TokenStream(Some(bridge::client::TokenStream::concat_streams(None, self.streams))) + TokenStream(Some(bridge::client::FreeFunctions::tt_concat_streams(None, self.streams))) } } @@ -326,7 +328,7 @@ impl ConcatStreamsHelper { if base.is_none() && self.streams.len() == 1 { stream.0 = self.streams.pop(); } else { - stream.0 = Some(bridge::client::TokenStream::concat_streams(base, self.streams)); + stream.0 = Some(bridge::client::FreeFunctions::tt_concat_streams(base, self.streams)); } } } @@ -437,7 +439,12 @@ pub mod token_stream { type IntoIter = IntoIter; fn into_iter(self) -> IntoIter { - IntoIter(self.0.map(|v| v.into_trees()).unwrap_or_default().into_iter()) + IntoIter( + self.0 + .map(|v| bridge::client::FreeFunctions::tt_into_trees(v)) + .unwrap_or_default() + .into_iter(), + ) } } } @@ -509,7 +516,7 @@ impl Span { /// `self` was generated from, if any. #[unstable(feature = "proc_macro_span", issue = "54725")] pub fn parent(&self) -> Option { - self.0.parent().map(Span) + bridge::client::FreeFunctions::span_parent(self.0).map(Span) } /// The span for the origin source code that `self` was generated from. If @@ -517,25 +524,25 @@ impl Span { /// value is the same as `*self`. #[unstable(feature = "proc_macro_span", issue = "54725")] pub fn source(&self) -> Span { - Span(self.0.source()) + Span(bridge::client::FreeFunctions::span_source(self.0)) } /// Returns the span's byte position range in the source file. #[unstable(feature = "proc_macro_span", issue = "54725")] pub fn byte_range(&self) -> Range { - self.0.byte_range() + bridge::client::FreeFunctions::span_byte_range(self.0) } /// Creates an empty span pointing to directly before this span. #[stable(feature = "proc_macro_span_location", since = "1.88.0")] pub fn start(&self) -> Span { - Span(self.0.start()) + Span(bridge::client::FreeFunctions::span_start(self.0)) } /// Creates an empty span pointing to directly after this span. #[stable(feature = "proc_macro_span_location", since = "1.88.0")] pub fn end(&self) -> Span { - Span(self.0.end()) + Span(bridge::client::FreeFunctions::span_end(self.0)) } /// The one-indexed line of the source file where the span starts. @@ -543,7 +550,7 @@ impl Span { /// To obtain the line of the span's end, use `span.end().line()`. #[stable(feature = "proc_macro_span_location", since = "1.88.0")] pub fn line(&self) -> usize { - self.0.line() + bridge::client::FreeFunctions::span_line(self.0) } /// The one-indexed column of the source file where the span starts. @@ -551,7 +558,7 @@ impl Span { /// To obtain the column of the span's end, use `span.end().column()`. #[stable(feature = "proc_macro_span_location", since = "1.88.0")] pub fn column(&self) -> usize { - self.0.column() + bridge::client::FreeFunctions::span_column(self.0) } /// The path to the source file in which this span occurs, for display purposes. @@ -560,7 +567,7 @@ impl Span { /// It might be remapped (e.g. `"/src/lib.rs"`) or an artificial path (e.g. `""`). #[stable(feature = "proc_macro_span_file", since = "1.88.0")] pub fn file(&self) -> String { - self.0.file() + bridge::client::FreeFunctions::span_file(self.0) } /// The path to the source file in which this span occurs on the local file system. @@ -570,7 +577,7 @@ impl Span { /// This path should not be embedded in the output of the macro; prefer `file()` instead. #[stable(feature = "proc_macro_span_file", since = "1.88.0")] pub fn local_file(&self) -> Option { - self.0.local_file().map(PathBuf::from) + bridge::client::FreeFunctions::span_local_file(self.0).map(PathBuf::from) } /// Creates a new span encompassing `self` and `other`. @@ -578,14 +585,14 @@ impl Span { /// Returns `None` if `self` and `other` are from different files. #[unstable(feature = "proc_macro_span", issue = "54725")] pub fn join(&self, other: Span) -> Option { - self.0.join(other.0).map(Span) + bridge::client::FreeFunctions::span_join(self.0, other.0).map(Span) } /// Creates a new span with the same line/column information as `self` but /// that resolves symbols as though it were at `other`. #[stable(feature = "proc_macro_span_resolved_at", since = "1.45.0")] pub fn resolved_at(&self, other: Span) -> Span { - Span(self.0.resolved_at(other.0)) + Span(bridge::client::FreeFunctions::span_resolved_at(self.0, other.0)) } /// Creates a new span with the same name resolution behavior as `self` but @@ -610,21 +617,21 @@ impl Span { /// be used for diagnostics only. #[stable(feature = "proc_macro_source_text", since = "1.66.0")] pub fn source_text(&self) -> Option { - self.0.source_text() + bridge::client::FreeFunctions::span_source_text(self.0) } // Used by the implementation of `Span::quote` #[doc(hidden)] #[unstable(feature = "proc_macro_internals", issue = "27812")] pub fn save_span(&self) -> usize { - self.0.save_span() + bridge::client::FreeFunctions::span_save_span(self.0) } // Used by the implementation of `Span::quote` #[doc(hidden)] #[unstable(feature = "proc_macro_internals", issue = "27812")] pub fn recover_proc_macro_span(id: usize) -> Span { - Span(bridge::client::Span::recover_proc_macro_span(id)) + Span(bridge::client::FreeFunctions::span_recover_proc_macro_span(id)) } diagnostic_method!(error, Level::Error); @@ -1389,7 +1396,12 @@ impl Literal { // was 'c' or whether it was '\u{63}'. #[unstable(feature = "proc_macro_span", issue = "54725")] pub fn subspan>(&self, range: R) -> Option { - self.0.span.subspan(range.start_bound().cloned(), range.end_bound().cloned()).map(Span) + bridge::client::FreeFunctions::span_subspan( + self.0.span, + range.start_bound().cloned(), + range.end_bound().cloned(), + ) + .map(Span) } fn with_symbol_and_suffix(&self, f: impl FnOnce(&str, &str) -> R) -> R { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index 1845de75dd8b..46891639744d 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -58,13 +58,19 @@ impl server::FreeFunctions for RaSpanServer<'_> { fn emit_diagnostic(&mut self, _: Diagnostic) { // FIXME handle diagnostic } -} -impl server::TokenStream for RaSpanServer<'_> { - fn is_empty(&mut self, stream: &Self::TokenStream) -> bool { + fn tt_drop(&mut self, stream: Self::TokenStream) { + drop(stream); + } + + fn tt_clone(&mut self, stream: &Self::TokenStream) -> Self::TokenStream { + stream.clone() + } + + fn tt_is_empty(&mut self, stream: &Self::TokenStream) -> bool { stream.is_empty() } - fn from_str(&mut self, src: &str) -> Self::TokenStream { + fn tt_from_str(&mut self, src: &str) -> Self::TokenStream { Self::TokenStream::from_str(src, self.call_site).unwrap_or_else(|e| { Self::TokenStream::from_str( &format!("compile_error!(\"failed to parse str to token stream: {e}\")"), @@ -73,15 +79,15 @@ impl server::TokenStream for RaSpanServer<'_> { .unwrap() }) } - fn to_string(&mut self, stream: &Self::TokenStream) -> String { + fn tt_to_string(&mut self, stream: &Self::TokenStream) -> String { stream.to_string() } - fn from_token_tree(&mut self, tree: TokenTree) -> Self::TokenStream { + fn tt_from_token_tree(&mut self, tree: TokenTree) -> Self::TokenStream { Self::TokenStream::new(vec![tree]) } - fn expand_expr(&mut self, self_: &Self::TokenStream) -> Result { + fn tt_expand_expr(&mut self, self_: &Self::TokenStream) -> Result { // FIXME: requires db, more importantly this requires name resolution so we would need to // eagerly expand this proc-macro, but we can't know that this proc-macro is eager until we // expand it ... @@ -90,7 +96,7 @@ impl server::TokenStream for RaSpanServer<'_> { Ok(self_.clone()) } - fn concat_trees( + fn tt_concat_trees( &mut self, base: Option, trees: Vec>, @@ -106,7 +112,7 @@ impl server::TokenStream for RaSpanServer<'_> { } } - fn concat_streams( + fn tt_concat_streams( &mut self, base: Option, streams: Vec, @@ -118,28 +124,26 @@ impl server::TokenStream for RaSpanServer<'_> { stream } - fn into_trees(&mut self, stream: Self::TokenStream) -> Vec> { + fn tt_into_trees(&mut self, stream: Self::TokenStream) -> Vec> { (*stream.0).clone() } -} -impl server::Span for RaSpanServer<'_> { - fn debug(&mut self, span: Self::Span) -> String { + fn span_debug(&mut self, span: Self::Span) -> String { format!("{:?}", span) } - fn file(&mut self, span: Self::Span) -> String { + fn span_file(&mut self, span: Self::Span) -> String { self.callback.as_mut().map(|cb| cb.file(span.anchor.file_id.file_id())).unwrap_or_default() } - fn local_file(&mut self, span: Self::Span) -> Option { + fn span_local_file(&mut self, span: Self::Span) -> Option { self.callback.as_mut().and_then(|cb| cb.local_file(span.anchor.file_id.file_id())) } - fn save_span(&mut self, _span: Self::Span) -> usize { + fn span_save_span(&mut self, _span: Self::Span) -> usize { // FIXME, quote is incompatible with third-party tools // This is called by the quote proc-macro which is expanded when the proc-macro is compiled // As such, r-a will never observe this 0 } - fn recover_proc_macro_span(&mut self, _id: usize) -> Self::Span { + fn span_recover_proc_macro_span(&mut self, _id: usize) -> Self::Span { // FIXME, quote is incompatible with third-party tools // This is called by the expansion of quote!, r-a will observe this, but we don't have // access to the spans that were encoded @@ -149,23 +153,23 @@ impl server::Span for RaSpanServer<'_> { /// /// See PR: /// https://github.com/rust-lang/rust/pull/55780 - fn source_text(&mut self, span: Self::Span) -> Option { + fn span_source_text(&mut self, span: Self::Span) -> Option { self.callback.as_mut()?.source_text(span) } - fn parent(&mut self, _span: Self::Span) -> Option { + fn span_parent(&mut self, _span: Self::Span) -> Option { // FIXME requires db, looks up the parent call site None } - fn source(&mut self, span: Self::Span) -> Self::Span { + fn span_source(&mut self, span: Self::Span) -> Self::Span { // FIXME requires db, returns the top level call site span } - fn byte_range(&mut self, span: Self::Span) -> Range { + fn span_byte_range(&mut self, span: Self::Span) -> Range { // FIXME requires db to resolve the ast id, THIS IS NOT INCREMENTAL Range { start: span.range.start().into(), end: span.range.end().into() } } - fn join(&mut self, first: Self::Span, second: Self::Span) -> Option { + fn span_join(&mut self, first: Self::Span, second: Self::Span) -> Option { // We can't modify the span range for fixup spans, those are meaningful to fixup, so just // prefer the non-fixup span. if first.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { @@ -193,7 +197,7 @@ impl server::Span for RaSpanServer<'_> { ctx: second.ctx, }) } - fn subspan( + fn span_subspan( &mut self, span: Self::Span, start: Bound, @@ -237,11 +241,11 @@ impl server::Span for RaSpanServer<'_> { }) } - fn resolved_at(&mut self, span: Self::Span, at: Self::Span) -> Self::Span { + fn span_resolved_at(&mut self, span: Self::Span, at: Self::Span) -> Self::Span { Span { ctx: at.ctx, ..span } } - fn end(&mut self, span: Self::Span) -> Self::Span { + fn span_end(&mut self, span: Self::Span) -> Self::Span { // We can't modify the span range for fixup spans, those are meaningful to fixup. if span.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { return span; @@ -249,7 +253,7 @@ impl server::Span for RaSpanServer<'_> { Span { range: TextRange::empty(span.range.end()), ..span } } - fn start(&mut self, span: Self::Span) -> Self::Span { + fn span_start(&mut self, span: Self::Span) -> Self::Span { // We can't modify the span range for fixup spans, those are meaningful to fixup. if span.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { return span; @@ -257,24 +261,28 @@ impl server::Span for RaSpanServer<'_> { Span { range: TextRange::empty(span.range.start()), ..span } } - fn line(&mut self, _span: Self::Span) -> usize { + fn span_line(&mut self, _span: Self::Span) -> usize { // FIXME requires db to resolve line index, THIS IS NOT INCREMENTAL 1 } - fn column(&mut self, _span: Self::Span) -> usize { + fn span_column(&mut self, _span: Self::Span) -> usize { // FIXME requires db to resolve line index, THIS IS NOT INCREMENTAL 1 } -} -impl server::Symbol for RaSpanServer<'_> { - fn normalize_and_validate_ident(&mut self, string: &str) -> Result { + fn symbol_normalize_and_validate_ident(&mut self, string: &str) -> Result { // FIXME: nfc-normalize and validate idents Ok(::intern_symbol(string)) } } +impl server::TokenStream for RaSpanServer<'_> {} + +impl server::Span for RaSpanServer<'_> {} + +impl server::Symbol for RaSpanServer<'_> {} + impl server::Server for RaSpanServer<'_> { fn globals(&mut self) -> ExpnGlobals { ExpnGlobals { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs index 8b1e3c0d8d6d..804f6ce614c6 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs @@ -61,13 +61,19 @@ impl server::FreeFunctions for SpanIdServer<'_> { } fn emit_diagnostic(&mut self, _: Diagnostic) {} -} -impl server::TokenStream for SpanIdServer<'_> { - fn is_empty(&mut self, stream: &Self::TokenStream) -> bool { + fn tt_drop(&mut self, stream: Self::TokenStream) { + drop(stream); + } + + fn tt_clone(&mut self, stream: &Self::TokenStream) -> Self::TokenStream { + stream.clone() + } + + fn tt_is_empty(&mut self, stream: &Self::TokenStream) -> bool { stream.is_empty() } - fn from_str(&mut self, src: &str) -> Self::TokenStream { + fn tt_from_str(&mut self, src: &str) -> Self::TokenStream { Self::TokenStream::from_str(src, self.call_site).unwrap_or_else(|e| { Self::TokenStream::from_str( &format!("compile_error!(\"failed to parse str to token stream: {e}\")"), @@ -76,18 +82,18 @@ impl server::TokenStream for SpanIdServer<'_> { .unwrap() }) } - fn to_string(&mut self, stream: &Self::TokenStream) -> String { + fn tt_to_string(&mut self, stream: &Self::TokenStream) -> String { stream.to_string() } - fn from_token_tree(&mut self, tree: TokenTree) -> Self::TokenStream { + fn tt_from_token_tree(&mut self, tree: TokenTree) -> Self::TokenStream { Self::TokenStream::new(vec![tree]) } - fn expand_expr(&mut self, self_: &Self::TokenStream) -> Result { + fn tt_expand_expr(&mut self, self_: &Self::TokenStream) -> Result { Ok(self_.clone()) } - fn concat_trees( + fn tt_concat_trees( &mut self, base: Option, trees: Vec>, @@ -103,7 +109,7 @@ impl server::TokenStream for SpanIdServer<'_> { } } - fn concat_streams( + fn tt_concat_streams( &mut self, base: Option, streams: Vec, @@ -115,49 +121,47 @@ impl server::TokenStream for SpanIdServer<'_> { stream } - fn into_trees(&mut self, stream: Self::TokenStream) -> Vec> { + fn tt_into_trees(&mut self, stream: Self::TokenStream) -> Vec> { (*stream.0).clone() } -} -impl server::Span for SpanIdServer<'_> { - fn debug(&mut self, span: Self::Span) -> String { + fn span_debug(&mut self, span: Self::Span) -> String { format!("{:?}", span.0) } - fn file(&mut self, _span: Self::Span) -> String { + fn span_file(&mut self, _span: Self::Span) -> String { String::new() } - fn local_file(&mut self, _span: Self::Span) -> Option { + fn span_local_file(&mut self, _span: Self::Span) -> Option { None } - fn save_span(&mut self, _span: Self::Span) -> usize { + fn span_save_span(&mut self, _span: Self::Span) -> usize { 0 } - fn recover_proc_macro_span(&mut self, _id: usize) -> Self::Span { + fn span_recover_proc_macro_span(&mut self, _id: usize) -> Self::Span { self.call_site } /// Recent feature, not yet in the proc_macro /// /// See PR: /// https://github.com/rust-lang/rust/pull/55780 - fn source_text(&mut self, _span: Self::Span) -> Option { + fn span_source_text(&mut self, _span: Self::Span) -> Option { None } - fn parent(&mut self, _span: Self::Span) -> Option { + fn span_parent(&mut self, _span: Self::Span) -> Option { None } - fn source(&mut self, span: Self::Span) -> Self::Span { + fn span_source(&mut self, span: Self::Span) -> Self::Span { span } - fn byte_range(&mut self, _span: Self::Span) -> Range { + fn span_byte_range(&mut self, _span: Self::Span) -> Range { Range { start: 0, end: 0 } } - fn join(&mut self, first: Self::Span, _second: Self::Span) -> Option { + fn span_join(&mut self, first: Self::Span, _second: Self::Span) -> Option { // Just return the first span again, because some macros will unwrap the result. Some(first) } - fn subspan( + fn span_subspan( &mut self, span: Self::Span, _start: Bound, @@ -166,34 +170,38 @@ impl server::Span for SpanIdServer<'_> { // Just return the span again, because some macros will unwrap the result. Some(span) } - fn resolved_at(&mut self, _span: Self::Span, _at: Self::Span) -> Self::Span { + fn span_resolved_at(&mut self, _span: Self::Span, _at: Self::Span) -> Self::Span { self.call_site } - fn end(&mut self, _self_: Self::Span) -> Self::Span { + fn span_end(&mut self, _self_: Self::Span) -> Self::Span { self.call_site } - fn start(&mut self, _self_: Self::Span) -> Self::Span { + fn span_start(&mut self, _self_: Self::Span) -> Self::Span { self.call_site } - fn line(&mut self, _span: Self::Span) -> usize { + fn span_line(&mut self, _span: Self::Span) -> usize { 1 } - fn column(&mut self, _span: Self::Span) -> usize { + fn span_column(&mut self, _span: Self::Span) -> usize { 1 } -} -impl server::Symbol for SpanIdServer<'_> { - fn normalize_and_validate_ident(&mut self, string: &str) -> Result { + fn symbol_normalize_and_validate_ident(&mut self, string: &str) -> Result { // FIXME: nfc-normalize and validate idents Ok(::intern_symbol(string)) } } +impl server::TokenStream for SpanIdServer<'_> {} + +impl server::Span for SpanIdServer<'_> {} + +impl server::Symbol for SpanIdServer<'_> {} + impl server::Server for SpanIdServer<'_> { fn globals(&mut self) -> ExpnGlobals { ExpnGlobals { From 948189014306eb2517032e3177e1e30e4e28bfc3 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 22 Jan 2026 18:28:51 +0000 Subject: [PATCH 22/28] Various simplifications after moving all bridge methods to a single type --- .../rustc_expand/src/proc_macro_server.rs | 6 -- library/proc_macro/src/bridge/client.rs | 15 ++-- library/proc_macro/src/bridge/mod.rs | 29 ++++---- library/proc_macro/src/bridge/server.rs | 71 ++++++++++--------- .../src/server_impl/rust_analyzer_span.rs | 6 -- .../src/server_impl/token_id.rs | 6 -- 6 files changed, 59 insertions(+), 74 deletions(-) diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 4f8ff5588581..1bf2edafd7fd 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -844,12 +844,6 @@ impl server::FreeFunctions for Rustc<'_, '_> { } } -impl server::TokenStream for Rustc<'_, '_> {} - -impl server::Span for Rustc<'_, '_> {} - -impl server::Symbol for Rustc<'_, '_> {} - impl server::Server for Rustc<'_, '_> { fn globals(&mut self) -> ExpnGlobals { ExpnGlobals { diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index d954177e7df5..ffbf6767833f 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -103,16 +103,19 @@ pub(crate) use super::FreeFunctions; pub(crate) use super::symbol::Symbol; macro_rules! define_client_side { - ($($name:ident { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* - }),* $(,)?) => { - $(impl $name { + ( + FreeFunctions { + $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* + }, + $($name:ident),* $(,)? + ) => { + impl FreeFunctions { $(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)? { Bridge::with(|bridge| { let mut buf = bridge.cached_buffer.take(); buf.clear(); - api_tags::Method::$name(api_tags::$name::$method).encode(&mut buf, &mut ()); + api_tags::Method::$method.encode(&mut buf, &mut ()); $($arg.encode(&mut buf, &mut ());)* buf = bridge.dispatch.call(buf); @@ -124,7 +127,7 @@ macro_rules! define_client_side { r.unwrap_or_else(|e| panic::resume_unwind(e.into())) }) })* - })* + } } } with_api!(self, self, define_client_side); diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index 027f94c36ff2..ae0f5974de52 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -21,14 +21,15 @@ use crate::{Delimiter, Level, Spacing}; /// `with_api!(MySelf, my_self, my_macro)` expands to: /// ```rust,ignore (pseudo-code) /// my_macro! { -/// // ... -/// Literal { +/// FreeFunctions { /// // ... -/// fn character(ch: char) -> MySelf::Literal; +/// fn lit_character(ch: char) -> MySelf::Literal; /// // ... -/// fn span(my_self: &MySelf::Literal) -> MySelf::Span; -/// fn set_span(my_self: &mut MySelf::Literal, span: MySelf::Span); +/// fn lit_span(my_self: &MySelf::Literal) -> MySelf::Span; +/// fn lit_set_span(my_self: &mut MySelf::Literal, span: MySelf::Span); /// }, +/// Literal, +/// Span, /// // ... /// } /// ``` @@ -95,9 +96,9 @@ macro_rules! with_api { fn symbol_normalize_and_validate_ident(string: &str) -> Result<$S::Symbol, ()>; }, - TokenStream {}, - Span {}, - Symbol {}, + TokenStream, + Span, + Symbol, } }; } @@ -160,18 +161,12 @@ mod api_tags { FreeFunctions { $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* }, - $($name:ident { $($x:tt)* }),* $(,)? + $($name:ident),* $(,)? ) => { - pub(super) enum FreeFunctions { + pub(super) enum Method { $($method),* } - rpc_encode_decode!(enum FreeFunctions { $($method),* }); - - - pub(super) enum Method { - FreeFunctions(FreeFunctions), - } - rpc_encode_decode!(enum Method { FreeFunctions(m) }); + rpc_encode_decode!(enum Method { $($method),* }); } } with_api!(self, self, declare_tags); diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs index 16037ed46f06..7701ba6899f6 100644 --- a/library/proc_macro/src/bridge/server.rs +++ b/library/proc_macro/src/bridge/server.rs @@ -61,14 +61,17 @@ pub trait Types { } macro_rules! declare_server_traits { - ($($name:ident { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* - }),* $(,)?) => { - $(pub trait $name: Types { + ( + FreeFunctions { + $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* + }, + $($name:ident),* $(,)? + ) => { + pub trait FreeFunctions: Types { $(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)?;)* - })* + } - pub trait Server: Types $(+ $name)* { + pub trait Server: Types + FreeFunctions { fn globals(&mut self) -> ExpnGlobals; /// Intern a symbol received from RPC @@ -96,18 +99,22 @@ impl Server for MarkedTypes { } macro_rules! define_mark_types_impls { - ($($name:ident { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* - }),* $(,)?) => { + ( + FreeFunctions { + $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* + }, + $($name:ident),* $(,)? + ) => { impl Types for MarkedTypes { + type FreeFunctions = Marked; $(type $name = Marked;)* } - $(impl $name for MarkedTypes { + impl FreeFunctions for MarkedTypes { $(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)? { - <_>::mark($name::$method(&mut self.0, $($arg.unmark()),*)) + <_>::mark(FreeFunctions::$method(&mut self.0, $($arg.unmark()),*)) })* - })* + } } } with_api!(Self, self_, define_mark_types_impls); @@ -122,7 +129,7 @@ macro_rules! define_dispatcher_impl { FreeFunctions { $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* }, - $($name:ident { $($x:tt)* }),* $(,)? + $($name:ident),* $(,)? ) => { // FIXME(eddyb) `pub` only for `ExecutionStrategy` below. pub trait DispatcherTrait { @@ -142,27 +149,25 @@ macro_rules! define_dispatcher_impl { let mut reader = &buf[..]; match api_tags::Method::decode(&mut reader, &mut ()) { - api_tags::Method::FreeFunctions(m) => match m { - $(api_tags::FreeFunctions::$method => { - let mut call_method = || { - $(let $arg = <$arg_ty>::decode(&mut reader, handle_store);)* - FreeFunctions::$method(server, $($arg),*) - }; - // HACK(eddyb) don't use `panic::catch_unwind` in a panic. - // If client and server happen to use the same `std`, - // `catch_unwind` asserts that the panic counter was 0, - // even when the closure passed to it didn't panic. - let r = if thread::panicking() { - Ok(call_method()) - } else { - panic::catch_unwind(panic::AssertUnwindSafe(call_method)) - .map_err(PanicMessage::from) - }; + $(api_tags::Method::$method => { + let mut call_method = || { + $(let $arg = <$arg_ty>::decode(&mut reader, handle_store);)* + FreeFunctions::$method(server, $($arg),*) + }; + // HACK(eddyb) don't use `panic::catch_unwind` in a panic. + // If client and server happen to use the same `std`, + // `catch_unwind` asserts that the panic counter was 0, + // even when the closure passed to it didn't panic. + let r = if thread::panicking() { + Ok(call_method()) + } else { + panic::catch_unwind(panic::AssertUnwindSafe(call_method)) + .map_err(PanicMessage::from) + }; - buf.clear(); - r.encode(&mut buf, handle_store); - })* - } + buf.clear(); + r.encode(&mut buf, handle_store); + })* } buf } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index 46891639744d..f76a07bc5d6a 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -277,12 +277,6 @@ impl server::FreeFunctions for RaSpanServer<'_> { } } -impl server::TokenStream for RaSpanServer<'_> {} - -impl server::Span for RaSpanServer<'_> {} - -impl server::Symbol for RaSpanServer<'_> {} - impl server::Server for RaSpanServer<'_> { fn globals(&mut self) -> ExpnGlobals { ExpnGlobals { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs index 804f6ce614c6..b3f765026273 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs @@ -196,12 +196,6 @@ impl server::FreeFunctions for SpanIdServer<'_> { } } -impl server::TokenStream for SpanIdServer<'_> {} - -impl server::Span for SpanIdServer<'_> {} - -impl server::Symbol for SpanIdServer<'_> {} - impl server::Server for SpanIdServer<'_> { fn globals(&mut self) -> ExpnGlobals { ExpnGlobals { From 8a119c3145f349f2ff7b2205cb37560e46b60720 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 22 Jan 2026 19:00:17 +0000 Subject: [PATCH 23/28] Merge FreeFunctions trait into Server trait And rename FreeFunctions struct to Methods. --- .../rustc_expand/src/proc_macro_server.rs | 39 +++++------- library/proc_macro/src/bridge/client.rs | 12 ++-- library/proc_macro/src/bridge/mod.rs | 8 +-- library/proc_macro/src/bridge/server.rs | 46 ++++++-------- library/proc_macro/src/bridge/symbol.rs | 2 +- library/proc_macro/src/diagnostic.rs | 2 +- library/proc_macro/src/lib.rs | 62 +++++++++---------- .../src/server_impl/rust_analyzer_span.rs | 39 +++++------- .../src/server_impl/token_id.rs | 39 +++++------- 9 files changed, 112 insertions(+), 137 deletions(-) diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 1bf2edafd7fd..4719829d6d3a 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -431,8 +431,6 @@ impl ToInternal for Level { } } -pub(crate) struct FreeFunctions; - pub(crate) struct Rustc<'a, 'b> { ecx: &'a mut ExtCtxt<'b>, def_site: Span, @@ -461,13 +459,28 @@ impl<'a, 'b> Rustc<'a, 'b> { } impl server::Types for Rustc<'_, '_> { - type FreeFunctions = FreeFunctions; type TokenStream = TokenStream; type Span = Span; type Symbol = Symbol; } -impl server::FreeFunctions for Rustc<'_, '_> { +impl server::Server for Rustc<'_, '_> { + fn globals(&mut self) -> ExpnGlobals { + ExpnGlobals { + def_site: self.def_site, + call_site: self.call_site, + mixed_site: self.mixed_site, + } + } + + fn intern_symbol(string: &str) -> Self::Symbol { + Symbol::intern(string) + } + + fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) { + f(symbol.as_str()) + } + fn injected_env_var(&mut self, var: &str) -> Option { self.ecx.sess.opts.logical_env.get(var).cloned() } @@ -843,21 +856,3 @@ impl server::FreeFunctions for Rustc<'_, '_> { if rustc_lexer::is_ident(sym.as_str()) { Ok(sym) } else { Err(()) } } } - -impl server::Server for Rustc<'_, '_> { - fn globals(&mut self) -> ExpnGlobals { - ExpnGlobals { - def_site: self.def_site, - call_site: self.call_site, - mixed_site: self.mixed_site, - } - } - - fn intern_symbol(string: &str) -> Self::Symbol { - Symbol::intern(string) - } - - fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) { - f(symbol.as_str()) - } -} diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index ffbf6767833f..058a9420f6e0 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -25,7 +25,7 @@ impl !Sync for TokenStream {} // Forward `Drop::drop` to the inherent `drop` method. impl Drop for TokenStream { fn drop(&mut self) { - FreeFunctions::tt_drop(TokenStream { handle: self.handle }); + Methods::tt_drop(TokenStream { handle: self.handle }); } } @@ -75,7 +75,7 @@ impl Decode<'_, '_, S> for Span { impl Clone for TokenStream { fn clone(&self) -> Self { - FreeFunctions::tt_clone(self) + Methods::tt_clone(self) } } @@ -95,21 +95,21 @@ impl Span { impl fmt::Debug for Span { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&FreeFunctions::span_debug(*self)) + f.write_str(&Methods::span_debug(*self)) } } -pub(crate) use super::FreeFunctions; +pub(crate) use super::Methods; pub(crate) use super::symbol::Symbol; macro_rules! define_client_side { ( - FreeFunctions { + Methods { $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* }, $($name:ident),* $(,)? ) => { - impl FreeFunctions { + impl Methods { $(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)? { Bridge::with(|bridge| { let mut buf = bridge.cached_buffer.take(); diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index ae0f5974de52..429335456316 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -21,7 +21,7 @@ use crate::{Delimiter, Level, Spacing}; /// `with_api!(MySelf, my_self, my_macro)` expands to: /// ```rust,ignore (pseudo-code) /// my_macro! { -/// FreeFunctions { +/// Methods { /// // ... /// fn lit_character(ch: char) -> MySelf::Literal; /// // ... @@ -49,7 +49,7 @@ use crate::{Delimiter, Level, Spacing}; macro_rules! with_api { ($S:ident, $self:ident, $m:ident) => { $m! { - FreeFunctions { + Methods { fn injected_env_var(var: &str) -> Option; fn track_env_var(var: &str, value: Option<&str>); fn track_path(path: &str); @@ -103,7 +103,7 @@ macro_rules! with_api { }; } -pub(crate) struct FreeFunctions; +pub(crate) struct Methods; #[allow(unsafe_code)] mod arena; @@ -158,7 +158,7 @@ mod api_tags { macro_rules! declare_tags { ( - FreeFunctions { + Methods { $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* }, $($name:ident),* $(,)? diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs index 7701ba6899f6..501c10511109 100644 --- a/library/proc_macro/src/bridge/server.rs +++ b/library/proc_macro/src/bridge/server.rs @@ -54,7 +54,6 @@ impl Decode<'_, '_, HandleStore>> for Marked $ret_ty:ty)*;)* }, $($name:ident),* $(,)? ) => { - pub trait FreeFunctions: Types { - $(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)?;)* - } - - pub trait Server: Types + FreeFunctions { + pub trait Server: Types { fn globals(&mut self) -> ExpnGlobals; /// Intern a symbol received from RPC @@ -79,6 +74,8 @@ macro_rules! declare_server_traits { /// Recover the string value of a symbol, and invoke a callback with it. fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)); + + $(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)?;)* } } } @@ -86,33 +83,30 @@ with_api!(Self, self_, declare_server_traits); pub(super) struct MarkedTypes(S); -impl Server for MarkedTypes { - fn globals(&mut self) -> ExpnGlobals { - <_>::mark(Server::globals(&mut self.0)) - } - fn intern_symbol(ident: &str) -> Self::Symbol { - <_>::mark(S::intern_symbol(ident)) - } - fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) { - S::with_symbol_string(symbol.unmark(), f) - } -} - macro_rules! define_mark_types_impls { ( - FreeFunctions { + Methods { $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* }, $($name:ident),* $(,)? ) => { impl Types for MarkedTypes { - type FreeFunctions = Marked; $(type $name = Marked;)* } - impl FreeFunctions for MarkedTypes { + impl Server for MarkedTypes { + fn globals(&mut self) -> ExpnGlobals { + <_>::mark(Server::globals(&mut self.0)) + } + fn intern_symbol(ident: &str) -> Self::Symbol { + <_>::mark(S::intern_symbol(ident)) + } + fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) { + S::with_symbol_string(symbol.unmark(), f) + } + $(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)? { - <_>::mark(FreeFunctions::$method(&mut self.0, $($arg.unmark()),*)) + <_>::mark(S::$method(&mut self.0, $($arg.unmark()),*)) })* } } @@ -126,7 +120,7 @@ struct Dispatcher { macro_rules! define_dispatcher_impl { ( - FreeFunctions { + Methods { $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* }, $($name:ident),* $(,)? @@ -134,14 +128,12 @@ macro_rules! define_dispatcher_impl { // FIXME(eddyb) `pub` only for `ExecutionStrategy` below. pub trait DispatcherTrait { // HACK(eddyb) these are here to allow `Self::$name` to work below. - type FreeFunctions; $(type $name;)* fn dispatch(&mut self, buf: Buffer) -> Buffer; } impl DispatcherTrait for Dispatcher> { - type FreeFunctions = as Types>::FreeFunctions; $(type $name = as Types>::$name;)* fn dispatch(&mut self, mut buf: Buffer) -> Buffer { @@ -152,7 +144,7 @@ macro_rules! define_dispatcher_impl { $(api_tags::Method::$method => { let mut call_method = || { $(let $arg = <$arg_ty>::decode(&mut reader, handle_store);)* - FreeFunctions::$method(server, $($arg),*) + server.$method($($arg),*) }; // HACK(eddyb) don't use `panic::catch_unwind` in a panic. // If client and server happen to use the same `std`, diff --git a/library/proc_macro/src/bridge/symbol.rs b/library/proc_macro/src/bridge/symbol.rs index 6366ac2e6a93..995527653095 100644 --- a/library/proc_macro/src/bridge/symbol.rs +++ b/library/proc_macro/src/bridge/symbol.rs @@ -46,7 +46,7 @@ impl Symbol { if string.is_ascii() { Err(()) } else { - client::FreeFunctions::symbol_normalize_and_validate_ident(string) + client::Methods::symbol_normalize_and_validate_ident(string) } .unwrap_or_else(|_| panic!("`{:?}` is not a valid identifier", string)) } diff --git a/library/proc_macro/src/diagnostic.rs b/library/proc_macro/src/diagnostic.rs index 5a209f7c7aa1..39a8ecb2ee52 100644 --- a/library/proc_macro/src/diagnostic.rs +++ b/library/proc_macro/src/diagnostic.rs @@ -170,6 +170,6 @@ impl Diagnostic { } } - crate::bridge::client::FreeFunctions::emit_diagnostic(to_internal(self)); + crate::bridge::client::Methods::emit_diagnostic(to_internal(self)); } } diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 8d5491e91851..cc2de8d470c9 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -158,7 +158,7 @@ impl TokenStream { /// Checks if this `TokenStream` is empty. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] pub fn is_empty(&self) -> bool { - self.0.as_ref().map(|h| bridge::client::FreeFunctions::tt_is_empty(h)).unwrap_or(true) + self.0.as_ref().map(|h| bridge::client::Methods::tt_is_empty(h)).unwrap_or(true) } /// Parses this `TokenStream` as an expression and attempts to expand any @@ -174,7 +174,7 @@ impl TokenStream { #[unstable(feature = "proc_macro_expand", issue = "90765")] pub fn expand_expr(&self) -> Result { let stream = self.0.as_ref().ok_or(ExpandError)?; - match bridge::client::FreeFunctions::tt_expand_expr(stream) { + match bridge::client::Methods::tt_expand_expr(stream) { Ok(stream) => Ok(TokenStream(Some(stream))), Err(_) => Err(ExpandError), } @@ -193,7 +193,7 @@ impl FromStr for TokenStream { type Err = LexError; fn from_str(src: &str) -> Result { - Ok(TokenStream(Some(bridge::client::FreeFunctions::tt_from_str(src)))) + Ok(TokenStream(Some(bridge::client::Methods::tt_from_str(src)))) } } @@ -212,7 +212,7 @@ impl FromStr for TokenStream { impl fmt::Display for TokenStream { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.0 { - Some(ts) => write!(f, "{}", bridge::client::FreeFunctions::tt_to_string(ts)), + Some(ts) => write!(f, "{}", bridge::client::Methods::tt_to_string(ts)), None => Ok(()), } } @@ -252,9 +252,7 @@ fn tree_to_bridge_tree( #[stable(feature = "proc_macro_lib2", since = "1.29.0")] impl From for TokenStream { fn from(tree: TokenTree) -> TokenStream { - TokenStream(Some(bridge::client::FreeFunctions::tt_from_token_tree(tree_to_bridge_tree( - tree, - )))) + TokenStream(Some(bridge::client::Methods::tt_from_token_tree(tree_to_bridge_tree(tree)))) } } @@ -283,7 +281,7 @@ impl ConcatTreesHelper { if self.trees.is_empty() { TokenStream(None) } else { - TokenStream(Some(bridge::client::FreeFunctions::tt_concat_trees(None, self.trees))) + TokenStream(Some(bridge::client::Methods::tt_concat_trees(None, self.trees))) } } @@ -291,7 +289,7 @@ impl ConcatTreesHelper { if self.trees.is_empty() { return; } - stream.0 = Some(bridge::client::FreeFunctions::tt_concat_trees(stream.0.take(), self.trees)) + stream.0 = Some(bridge::client::Methods::tt_concat_trees(stream.0.take(), self.trees)) } } @@ -316,7 +314,7 @@ impl ConcatStreamsHelper { if self.streams.len() <= 1 { TokenStream(self.streams.pop()) } else { - TokenStream(Some(bridge::client::FreeFunctions::tt_concat_streams(None, self.streams))) + TokenStream(Some(bridge::client::Methods::tt_concat_streams(None, self.streams))) } } @@ -328,7 +326,7 @@ impl ConcatStreamsHelper { if base.is_none() && self.streams.len() == 1 { stream.0 = self.streams.pop(); } else { - stream.0 = Some(bridge::client::FreeFunctions::tt_concat_streams(base, self.streams)); + stream.0 = Some(bridge::client::Methods::tt_concat_streams(base, self.streams)); } } } @@ -441,7 +439,7 @@ pub mod token_stream { fn into_iter(self) -> IntoIter { IntoIter( self.0 - .map(|v| bridge::client::FreeFunctions::tt_into_trees(v)) + .map(|v| bridge::client::Methods::tt_into_trees(v)) .unwrap_or_default() .into_iter(), ) @@ -516,7 +514,7 @@ impl Span { /// `self` was generated from, if any. #[unstable(feature = "proc_macro_span", issue = "54725")] pub fn parent(&self) -> Option { - bridge::client::FreeFunctions::span_parent(self.0).map(Span) + bridge::client::Methods::span_parent(self.0).map(Span) } /// The span for the origin source code that `self` was generated from. If @@ -524,25 +522,25 @@ impl Span { /// value is the same as `*self`. #[unstable(feature = "proc_macro_span", issue = "54725")] pub fn source(&self) -> Span { - Span(bridge::client::FreeFunctions::span_source(self.0)) + Span(bridge::client::Methods::span_source(self.0)) } /// Returns the span's byte position range in the source file. #[unstable(feature = "proc_macro_span", issue = "54725")] pub fn byte_range(&self) -> Range { - bridge::client::FreeFunctions::span_byte_range(self.0) + bridge::client::Methods::span_byte_range(self.0) } /// Creates an empty span pointing to directly before this span. #[stable(feature = "proc_macro_span_location", since = "1.88.0")] pub fn start(&self) -> Span { - Span(bridge::client::FreeFunctions::span_start(self.0)) + Span(bridge::client::Methods::span_start(self.0)) } /// Creates an empty span pointing to directly after this span. #[stable(feature = "proc_macro_span_location", since = "1.88.0")] pub fn end(&self) -> Span { - Span(bridge::client::FreeFunctions::span_end(self.0)) + Span(bridge::client::Methods::span_end(self.0)) } /// The one-indexed line of the source file where the span starts. @@ -550,7 +548,7 @@ impl Span { /// To obtain the line of the span's end, use `span.end().line()`. #[stable(feature = "proc_macro_span_location", since = "1.88.0")] pub fn line(&self) -> usize { - bridge::client::FreeFunctions::span_line(self.0) + bridge::client::Methods::span_line(self.0) } /// The one-indexed column of the source file where the span starts. @@ -558,7 +556,7 @@ impl Span { /// To obtain the column of the span's end, use `span.end().column()`. #[stable(feature = "proc_macro_span_location", since = "1.88.0")] pub fn column(&self) -> usize { - bridge::client::FreeFunctions::span_column(self.0) + bridge::client::Methods::span_column(self.0) } /// The path to the source file in which this span occurs, for display purposes. @@ -567,7 +565,7 @@ impl Span { /// It might be remapped (e.g. `"/src/lib.rs"`) or an artificial path (e.g. `""`). #[stable(feature = "proc_macro_span_file", since = "1.88.0")] pub fn file(&self) -> String { - bridge::client::FreeFunctions::span_file(self.0) + bridge::client::Methods::span_file(self.0) } /// The path to the source file in which this span occurs on the local file system. @@ -577,7 +575,7 @@ impl Span { /// This path should not be embedded in the output of the macro; prefer `file()` instead. #[stable(feature = "proc_macro_span_file", since = "1.88.0")] pub fn local_file(&self) -> Option { - bridge::client::FreeFunctions::span_local_file(self.0).map(PathBuf::from) + bridge::client::Methods::span_local_file(self.0).map(PathBuf::from) } /// Creates a new span encompassing `self` and `other`. @@ -585,14 +583,14 @@ impl Span { /// Returns `None` if `self` and `other` are from different files. #[unstable(feature = "proc_macro_span", issue = "54725")] pub fn join(&self, other: Span) -> Option { - bridge::client::FreeFunctions::span_join(self.0, other.0).map(Span) + bridge::client::Methods::span_join(self.0, other.0).map(Span) } /// Creates a new span with the same line/column information as `self` but /// that resolves symbols as though it were at `other`. #[stable(feature = "proc_macro_span_resolved_at", since = "1.45.0")] pub fn resolved_at(&self, other: Span) -> Span { - Span(bridge::client::FreeFunctions::span_resolved_at(self.0, other.0)) + Span(bridge::client::Methods::span_resolved_at(self.0, other.0)) } /// Creates a new span with the same name resolution behavior as `self` but @@ -617,21 +615,21 @@ impl Span { /// be used for diagnostics only. #[stable(feature = "proc_macro_source_text", since = "1.66.0")] pub fn source_text(&self) -> Option { - bridge::client::FreeFunctions::span_source_text(self.0) + bridge::client::Methods::span_source_text(self.0) } // Used by the implementation of `Span::quote` #[doc(hidden)] #[unstable(feature = "proc_macro_internals", issue = "27812")] pub fn save_span(&self) -> usize { - bridge::client::FreeFunctions::span_save_span(self.0) + bridge::client::Methods::span_save_span(self.0) } // Used by the implementation of `Span::quote` #[doc(hidden)] #[unstable(feature = "proc_macro_internals", issue = "27812")] pub fn recover_proc_macro_span(id: usize) -> Span { - Span(bridge::client::FreeFunctions::span_recover_proc_macro_span(id)) + Span(bridge::client::Methods::span_recover_proc_macro_span(id)) } diagnostic_method!(error, Level::Error); @@ -1396,7 +1394,7 @@ impl Literal { // was 'c' or whether it was '\u{63}'. #[unstable(feature = "proc_macro_span", issue = "54725")] pub fn subspan>(&self, range: R) -> Option { - bridge::client::FreeFunctions::span_subspan( + bridge::client::Methods::span_subspan( self.0.span, range.start_bound().cloned(), range.end_bound().cloned(), @@ -1571,7 +1569,7 @@ impl FromStr for Literal { type Err = LexError; fn from_str(src: &str) -> Result { - match bridge::client::FreeFunctions::literal_from_str(src) { + match bridge::client::Methods::literal_from_str(src) { Ok(literal) => Ok(Literal(literal)), Err(()) => Err(LexError), } @@ -1626,9 +1624,9 @@ pub mod tracked { #[unstable(feature = "proc_macro_tracked_env", issue = "99515")] pub fn env_var + AsRef>(key: K) -> Result { let key: &str = key.as_ref(); - let value = crate::bridge::client::FreeFunctions::injected_env_var(key) - .map_or_else(|| env::var(key), Ok); - crate::bridge::client::FreeFunctions::track_env_var(key, value.as_deref().ok()); + let value = + crate::bridge::client::Methods::injected_env_var(key).map_or_else(|| env::var(key), Ok); + crate::bridge::client::Methods::track_env_var(key, value.as_deref().ok()); value } @@ -1638,6 +1636,6 @@ pub mod tracked { #[unstable(feature = "proc_macro_tracked_path", issue = "99515")] pub fn path>(path: P) { let path: &str = path.as_ref().to_str().unwrap(); - crate::bridge::client::FreeFunctions::track_path(path); + crate::bridge::client::Methods::track_path(path); } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index f76a07bc5d6a..4f50c04b2799 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -19,8 +19,6 @@ use crate::{ server_impl::literal_from_str, }; -pub struct FreeFunctions; - pub struct RaSpanServer<'a> { // FIXME: Report this back to the caller to track as dependencies pub tracked_env_vars: HashMap, Option>>, @@ -33,13 +31,28 @@ pub struct RaSpanServer<'a> { } impl server::Types for RaSpanServer<'_> { - type FreeFunctions = FreeFunctions; type TokenStream = crate::token_stream::TokenStream; type Span = Span; type Symbol = Symbol; } -impl server::FreeFunctions for RaSpanServer<'_> { +impl server::Server for RaSpanServer<'_> { + fn globals(&mut self) -> ExpnGlobals { + ExpnGlobals { + def_site: self.def_site, + call_site: self.call_site, + mixed_site: self.mixed_site, + } + } + + fn intern_symbol(ident: &str) -> Self::Symbol { + Symbol::intern(ident) + } + + fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) { + f(symbol.as_str()) + } + fn injected_env_var(&mut self, _: &str) -> Option { None } @@ -276,21 +289,3 @@ impl server::FreeFunctions for RaSpanServer<'_> { Ok(::intern_symbol(string)) } } - -impl server::Server for RaSpanServer<'_> { - fn globals(&mut self) -> ExpnGlobals { - ExpnGlobals { - def_site: self.def_site, - call_site: self.call_site, - mixed_site: self.mixed_site, - } - } - - fn intern_symbol(ident: &str) -> Self::Symbol { - Symbol::intern(ident) - } - - fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) { - f(symbol.as_str()) - } -} diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs index b3f765026273..cd5ba5909561 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs @@ -25,8 +25,6 @@ impl std::fmt::Debug for SpanId { type Span = SpanId; -pub struct FreeFunctions; - pub struct SpanIdServer<'a> { // FIXME: Report this back to the caller to track as dependencies pub tracked_env_vars: HashMap, Option>>, @@ -39,13 +37,28 @@ pub struct SpanIdServer<'a> { } impl server::Types for SpanIdServer<'_> { - type FreeFunctions = FreeFunctions; type TokenStream = crate::token_stream::TokenStream; type Span = Span; type Symbol = Symbol; } -impl server::FreeFunctions for SpanIdServer<'_> { +impl server::Server for SpanIdServer<'_> { + fn globals(&mut self) -> ExpnGlobals { + ExpnGlobals { + def_site: self.def_site, + call_site: self.call_site, + mixed_site: self.mixed_site, + } + } + + fn intern_symbol(ident: &str) -> Self::Symbol { + Symbol::intern(ident) + } + + fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) { + f(symbol.as_str()) + } + fn injected_env_var(&mut self, _: &str) -> Option { None } @@ -195,21 +208,3 @@ impl server::FreeFunctions for SpanIdServer<'_> { Ok(::intern_symbol(string)) } } - -impl server::Server for SpanIdServer<'_> { - fn globals(&mut self) -> ExpnGlobals { - ExpnGlobals { - def_site: self.def_site, - call_site: self.call_site, - mixed_site: self.mixed_site, - } - } - - fn intern_symbol(ident: &str) -> Self::Symbol { - Symbol::intern(ident) - } - - fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) { - f(symbol.as_str()) - } -} From d9ec1aef8d6bcfc08b99df6666c623be7c3ed62a Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 3 Oct 2025 21:49:48 +0000 Subject: [PATCH 24/28] Get rid of MarkedTypes --- library/proc_macro/src/bridge/server.rs | 94 +++++++++---------------- library/proc_macro/src/bridge/symbol.rs | 12 ++-- 2 files changed, 38 insertions(+), 68 deletions(-) diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs index 501c10511109..b79de9984453 100644 --- a/library/proc_macro/src/bridge/server.rs +++ b/library/proc_macro/src/bridge/server.rs @@ -6,8 +6,8 @@ use std::marker::PhantomData; use super::*; pub(super) struct HandleStore { - token_stream: handle::OwnedStore, - span: handle::InternedStore, + token_stream: handle::OwnedStore>, + span: handle::InternedStore>, } impl HandleStore { @@ -19,36 +19,34 @@ impl HandleStore { } } -impl Encode>> for Marked { - fn encode(self, w: &mut Writer, s: &mut HandleStore>) { +impl Encode> for Marked { + fn encode(self, w: &mut Writer, s: &mut HandleStore) { s.token_stream.alloc(self).encode(w, s); } } -impl Decode<'_, '_, HandleStore>> - for Marked -{ - fn decode(r: &mut Reader<'_>, s: &mut HandleStore>) -> Self { +impl Decode<'_, '_, HandleStore> for Marked { + fn decode(r: &mut Reader<'_>, s: &mut HandleStore) -> Self { s.token_stream.take(handle::Handle::decode(r, &mut ())) } } -impl<'s, S: Types> Decode<'_, 's, HandleStore>> +impl<'s, S: Types> Decode<'_, 's, HandleStore> for &'s Marked { - fn decode(r: &mut Reader<'_>, s: &'s mut HandleStore>) -> Self { + fn decode(r: &mut Reader<'_>, s: &'s mut HandleStore) -> Self { &s.token_stream[handle::Handle::decode(r, &mut ())] } } -impl Encode>> for Marked { - fn encode(self, w: &mut Writer, s: &mut HandleStore>) { +impl Encode> for Marked { + fn encode(self, w: &mut Writer, s: &mut HandleStore) { s.span.alloc(self).encode(w, s); } } -impl Decode<'_, '_, HandleStore>> for Marked { - fn decode(r: &mut Reader<'_>, s: &mut HandleStore>) -> Self { +impl Decode<'_, '_, HandleStore> for Marked { + fn decode(r: &mut Reader<'_>, s: &mut HandleStore) -> Self { s.span.copy(handle::Handle::decode(r, &mut ())) } } @@ -81,38 +79,6 @@ macro_rules! declare_server_traits { } with_api!(Self, self_, declare_server_traits); -pub(super) struct MarkedTypes(S); - -macro_rules! define_mark_types_impls { - ( - Methods { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* - }, - $($name:ident),* $(,)? - ) => { - impl Types for MarkedTypes { - $(type $name = Marked;)* - } - - impl Server for MarkedTypes { - fn globals(&mut self) -> ExpnGlobals { - <_>::mark(Server::globals(&mut self.0)) - } - fn intern_symbol(ident: &str) -> Self::Symbol { - <_>::mark(S::intern_symbol(ident)) - } - fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) { - S::with_symbol_string(symbol.unmark(), f) - } - - $(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)? { - <_>::mark(S::$method(&mut self.0, $($arg.unmark()),*)) - })* - } - } -} -with_api!(Self, self_, define_mark_types_impls); - struct Dispatcher { handle_store: HandleStore, server: S, @@ -133,8 +99,8 @@ macro_rules! define_dispatcher_impl { fn dispatch(&mut self, buf: Buffer) -> Buffer; } - impl DispatcherTrait for Dispatcher> { - $(type $name = as Types>::$name;)* + impl DispatcherTrait for Dispatcher { + $(type $name = Marked;)* fn dispatch(&mut self, mut buf: Buffer) -> Buffer { let Dispatcher { handle_store, server } = self; @@ -143,8 +109,12 @@ macro_rules! define_dispatcher_impl { match api_tags::Method::decode(&mut reader, &mut ()) { $(api_tags::Method::$method => { let mut call_method = || { - $(let $arg = <$arg_ty>::decode(&mut reader, handle_store);)* - server.$method($($arg),*) + $(let $arg = <$arg_ty>::decode(&mut reader, handle_store).unmark();)* + let r = server.$method($($arg),*); + $( + let r: $ret_ty = Mark::mark(r); + )* + r }; // HACK(eddyb) don't use `panic::catch_unwind` in a panic. // If client and server happen to use the same `std`, @@ -318,8 +288,8 @@ pub trait MessagePipe: Sized { fn run_server< S: Server, - I: Encode>>, - O: for<'a, 's> Decode<'a, 's, HandleStore>>, + I: Encode>, + O: for<'a, 's> Decode<'a, 's, HandleStore>, >( strategy: &impl ExecutionStrategy, handle_counters: &'static client::HandleCounters, @@ -328,13 +298,13 @@ fn run_server< run_client: extern "C" fn(BridgeConfig<'_>) -> Buffer, force_show_panics: bool, ) -> Result { - let mut dispatcher = - Dispatcher { handle_store: HandleStore::new(handle_counters), server: MarkedTypes(server) }; + let mut dispatcher = Dispatcher { handle_store: HandleStore::new(handle_counters), server }; let globals = dispatcher.server.globals(); let mut buf = Buffer::new(); - (globals, input).encode(&mut buf, &mut dispatcher.handle_store); + (> as Mark>::mark(globals), input) + .encode(&mut buf, &mut dispatcher.handle_store); buf = strategy.run_bridge_and_client(&mut dispatcher, buf, run_client, force_show_panics); @@ -358,11 +328,13 @@ impl client::Client { strategy, handle_counters, server, - as Types>::TokenStream::mark(input), + >::mark(input), run, force_show_panics, ) - .map(|s| as Types>::TokenStream>>::unmark(s).unwrap_or_default()) + .map(|s| { + >>::unmark(s).unwrap_or_default() + }) } } @@ -385,12 +357,14 @@ impl client::Client<(crate::TokenStream, crate::TokenStream), crate::TokenStream handle_counters, server, ( - as Types>::TokenStream::mark(input), - as Types>::TokenStream::mark(input2), + >::mark(input), + >::mark(input2), ), run, force_show_panics, ) - .map(|s| as Types>::TokenStream>>::unmark(s).unwrap_or_default()) + .map(|s| { + >>::unmark(s).unwrap_or_default() + }) } } diff --git a/library/proc_macro/src/bridge/symbol.rs b/library/proc_macro/src/bridge/symbol.rs index 995527653095..edba142bad72 100644 --- a/library/proc_macro/src/bridge/symbol.rs +++ b/library/proc_macro/src/bridge/symbol.rs @@ -99,18 +99,14 @@ impl Encode for Symbol { } } -impl Decode<'_, '_, server::HandleStore>> - for Marked -{ - fn decode(r: &mut Reader<'_>, s: &mut server::HandleStore>) -> Self { +impl Decode<'_, '_, server::HandleStore> for Marked { + fn decode(r: &mut Reader<'_>, s: &mut server::HandleStore) -> Self { Mark::mark(S::intern_symbol(<&str>::decode(r, s))) } } -impl Encode>> - for Marked -{ - fn encode(self, w: &mut Writer, s: &mut server::HandleStore>) { +impl Encode> for Marked { + fn encode(self, w: &mut Writer, s: &mut server::HandleStore) { S::with_symbol_string(&self.unmark(), |sym| sym.encode(w, s)) } } From e8c48c689575c5469cf41f693d80786dfa9df537 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 23 Jan 2026 11:19:21 +0000 Subject: [PATCH 25/28] Fix review comments --- .../rustc_expand/src/proc_macro_server.rs | 20 +++--- library/proc_macro/src/bridge/client.rs | 4 +- library/proc_macro/src/bridge/mod.rs | 20 +++--- library/proc_macro/src/lib.rs | 70 +++++++++---------- .../src/server_impl/rust_analyzer_span.rs | 20 +++--- .../src/server_impl/token_id.rs | 20 +++--- 6 files changed, 76 insertions(+), 78 deletions(-) diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 4719829d6d3a..a51aa90355bc 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -566,19 +566,19 @@ impl server::Server for Rustc<'_, '_> { diag.emit(); } - fn tt_drop(&mut self, stream: Self::TokenStream) { + fn ts_drop(&mut self, stream: Self::TokenStream) { drop(stream); } - fn tt_clone(&mut self, stream: &Self::TokenStream) -> Self::TokenStream { + fn ts_clone(&mut self, stream: &Self::TokenStream) -> Self::TokenStream { stream.clone() } - fn tt_is_empty(&mut self, stream: &Self::TokenStream) -> bool { + fn ts_is_empty(&mut self, stream: &Self::TokenStream) -> bool { stream.is_empty() } - fn tt_from_str(&mut self, src: &str) -> Self::TokenStream { + fn ts_from_str(&mut self, src: &str) -> Self::TokenStream { unwrap_or_emit_fatal(source_str_to_stream( self.psess(), FileName::proc_macro_source_code(src), @@ -587,11 +587,11 @@ impl server::Server for Rustc<'_, '_> { )) } - fn tt_to_string(&mut self, stream: &Self::TokenStream) -> String { + fn ts_to_string(&mut self, stream: &Self::TokenStream) -> String { pprust::tts_to_string(stream) } - fn tt_expand_expr(&mut self, stream: &Self::TokenStream) -> Result { + fn ts_expand_expr(&mut self, stream: &Self::TokenStream) -> Result { // Parse the expression from our tokenstream. let expr: PResult<'_, _> = try { let mut p = Parser::new(self.psess(), stream.clone(), Some("proc_macro expand expr")); @@ -652,14 +652,14 @@ impl server::Server for Rustc<'_, '_> { } } - fn tt_from_token_tree( + fn ts_from_token_tree( &mut self, tree: TokenTree, ) -> Self::TokenStream { Self::TokenStream::new((tree, &mut *self).to_internal().into_iter().collect::>()) } - fn tt_concat_trees( + fn ts_concat_trees( &mut self, base: Option, trees: Vec>, @@ -673,7 +673,7 @@ impl server::Server for Rustc<'_, '_> { stream } - fn tt_concat_streams( + fn ts_concat_streams( &mut self, base: Option, streams: Vec, @@ -685,7 +685,7 @@ impl server::Server for Rustc<'_, '_> { stream } - fn tt_into_trees( + fn ts_into_trees( &mut self, stream: Self::TokenStream, ) -> Vec> { diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index 058a9420f6e0..0d87a727ae40 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -25,7 +25,7 @@ impl !Sync for TokenStream {} // Forward `Drop::drop` to the inherent `drop` method. impl Drop for TokenStream { fn drop(&mut self) { - Methods::tt_drop(TokenStream { handle: self.handle }); + Methods::ts_drop(TokenStream { handle: self.handle }); } } @@ -75,7 +75,7 @@ impl Decode<'_, '_, S> for Span { impl Clone for TokenStream { fn clone(&self) -> Self { - Methods::tt_clone(self) + Methods::ts_clone(self) } } diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index 429335456316..6f7c8726f925 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -56,24 +56,24 @@ macro_rules! with_api { fn literal_from_str(s: &str) -> Result, ()>; fn emit_diagnostic(diagnostic: Diagnostic<$S::Span>); - fn tt_drop(stream: $S::TokenStream); - fn tt_clone(stream: &$S::TokenStream) -> $S::TokenStream; - fn tt_is_empty(stream: &$S::TokenStream) -> bool; - fn tt_expand_expr(stream: &$S::TokenStream) -> Result<$S::TokenStream, ()>; - fn tt_from_str(src: &str) -> $S::TokenStream; - fn tt_to_string(stream: &$S::TokenStream) -> String; - fn tt_from_token_tree( + fn ts_drop(stream: $S::TokenStream); + fn ts_clone(stream: &$S::TokenStream) -> $S::TokenStream; + fn ts_is_empty(stream: &$S::TokenStream) -> bool; + fn ts_expand_expr(stream: &$S::TokenStream) -> Result<$S::TokenStream, ()>; + fn ts_from_str(src: &str) -> $S::TokenStream; + fn ts_to_string(stream: &$S::TokenStream) -> String; + fn ts_from_token_tree( tree: TokenTree<$S::TokenStream, $S::Span, $S::Symbol>, ) -> $S::TokenStream; - fn tt_concat_trees( + fn ts_concat_trees( base: Option<$S::TokenStream>, trees: Vec>, ) -> $S::TokenStream; - fn tt_concat_streams( + fn ts_concat_streams( base: Option<$S::TokenStream>, streams: Vec<$S::TokenStream>, ) -> $S::TokenStream; - fn tt_into_trees( + fn ts_into_trees( stream: $S::TokenStream ) -> Vec>; diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index cc2de8d470c9..95a7ea7d7b3b 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -58,6 +58,7 @@ use rustc_literal_escaper::{MixedUnit, unescape_byte_str, unescape_c_str, unesca #[unstable(feature = "proc_macro_totokens", issue = "130977")] pub use to_tokens::ToTokens; +use crate::bridge::client::Methods as BridgeMethods; use crate::escape::{EscapeOptions, escape_bytes}; /// Errors returned when trying to retrieve a literal unescaped value. @@ -158,7 +159,7 @@ impl TokenStream { /// Checks if this `TokenStream` is empty. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] pub fn is_empty(&self) -> bool { - self.0.as_ref().map(|h| bridge::client::Methods::tt_is_empty(h)).unwrap_or(true) + self.0.as_ref().map(|h| BridgeMethods::ts_is_empty(h)).unwrap_or(true) } /// Parses this `TokenStream` as an expression and attempts to expand any @@ -174,7 +175,7 @@ impl TokenStream { #[unstable(feature = "proc_macro_expand", issue = "90765")] pub fn expand_expr(&self) -> Result { let stream = self.0.as_ref().ok_or(ExpandError)?; - match bridge::client::Methods::tt_expand_expr(stream) { + match BridgeMethods::ts_expand_expr(stream) { Ok(stream) => Ok(TokenStream(Some(stream))), Err(_) => Err(ExpandError), } @@ -193,7 +194,7 @@ impl FromStr for TokenStream { type Err = LexError; fn from_str(src: &str) -> Result { - Ok(TokenStream(Some(bridge::client::Methods::tt_from_str(src)))) + Ok(TokenStream(Some(BridgeMethods::ts_from_str(src)))) } } @@ -212,7 +213,7 @@ impl FromStr for TokenStream { impl fmt::Display for TokenStream { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.0 { - Some(ts) => write!(f, "{}", bridge::client::Methods::tt_to_string(ts)), + Some(ts) => write!(f, "{}", BridgeMethods::ts_to_string(ts)), None => Ok(()), } } @@ -252,7 +253,7 @@ fn tree_to_bridge_tree( #[stable(feature = "proc_macro_lib2", since = "1.29.0")] impl From for TokenStream { fn from(tree: TokenTree) -> TokenStream { - TokenStream(Some(bridge::client::Methods::tt_from_token_tree(tree_to_bridge_tree(tree)))) + TokenStream(Some(BridgeMethods::ts_from_token_tree(tree_to_bridge_tree(tree)))) } } @@ -281,7 +282,7 @@ impl ConcatTreesHelper { if self.trees.is_empty() { TokenStream(None) } else { - TokenStream(Some(bridge::client::Methods::tt_concat_trees(None, self.trees))) + TokenStream(Some(BridgeMethods::ts_concat_trees(None, self.trees))) } } @@ -289,7 +290,7 @@ impl ConcatTreesHelper { if self.trees.is_empty() { return; } - stream.0 = Some(bridge::client::Methods::tt_concat_trees(stream.0.take(), self.trees)) + stream.0 = Some(BridgeMethods::ts_concat_trees(stream.0.take(), self.trees)) } } @@ -314,7 +315,7 @@ impl ConcatStreamsHelper { if self.streams.len() <= 1 { TokenStream(self.streams.pop()) } else { - TokenStream(Some(bridge::client::Methods::tt_concat_streams(None, self.streams))) + TokenStream(Some(BridgeMethods::ts_concat_streams(None, self.streams))) } } @@ -326,7 +327,7 @@ impl ConcatStreamsHelper { if base.is_none() && self.streams.len() == 1 { stream.0 = self.streams.pop(); } else { - stream.0 = Some(bridge::client::Methods::tt_concat_streams(base, self.streams)); + stream.0 = Some(BridgeMethods::ts_concat_streams(base, self.streams)); } } } @@ -377,7 +378,7 @@ impl Extend for TokenStream { macro_rules! extend_items { ($($item:ident)*) => { $( - #[stable(feature = "token_stream_extend_tt_items", since = "1.92.0")] + #[stable(feature = "token_stream_extend_ts_items", since = "1.92.0")] impl Extend<$item> for TokenStream { fn extend>(&mut self, iter: T) { self.extend(iter.into_iter().map(TokenTree::$item)); @@ -392,7 +393,7 @@ extend_items!(Group Literal Punct Ident); /// Public implementation details for the `TokenStream` type, such as iterators. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] pub mod token_stream { - use crate::{Group, Ident, Literal, Punct, TokenStream, TokenTree, bridge}; + use crate::{BridgeMethods, Group, Ident, Literal, Punct, TokenStream, TokenTree, bridge}; /// An iterator over `TokenStream`'s `TokenTree`s. /// The iteration is "shallow", e.g., the iterator doesn't recurse into delimited groups, @@ -438,10 +439,7 @@ pub mod token_stream { fn into_iter(self) -> IntoIter { IntoIter( - self.0 - .map(|v| bridge::client::Methods::tt_into_trees(v)) - .unwrap_or_default() - .into_iter(), + self.0.map(|v| BridgeMethods::ts_into_trees(v)).unwrap_or_default().into_iter(), ) } } @@ -514,7 +512,7 @@ impl Span { /// `self` was generated from, if any. #[unstable(feature = "proc_macro_span", issue = "54725")] pub fn parent(&self) -> Option { - bridge::client::Methods::span_parent(self.0).map(Span) + BridgeMethods::span_parent(self.0).map(Span) } /// The span for the origin source code that `self` was generated from. If @@ -522,25 +520,25 @@ impl Span { /// value is the same as `*self`. #[unstable(feature = "proc_macro_span", issue = "54725")] pub fn source(&self) -> Span { - Span(bridge::client::Methods::span_source(self.0)) + Span(BridgeMethods::span_source(self.0)) } /// Returns the span's byte position range in the source file. #[unstable(feature = "proc_macro_span", issue = "54725")] pub fn byte_range(&self) -> Range { - bridge::client::Methods::span_byte_range(self.0) + BridgeMethods::span_byte_range(self.0) } /// Creates an empty span pointing to directly before this span. #[stable(feature = "proc_macro_span_location", since = "1.88.0")] pub fn start(&self) -> Span { - Span(bridge::client::Methods::span_start(self.0)) + Span(BridgeMethods::span_start(self.0)) } /// Creates an empty span pointing to directly after this span. #[stable(feature = "proc_macro_span_location", since = "1.88.0")] pub fn end(&self) -> Span { - Span(bridge::client::Methods::span_end(self.0)) + Span(BridgeMethods::span_end(self.0)) } /// The one-indexed line of the source file where the span starts. @@ -548,7 +546,7 @@ impl Span { /// To obtain the line of the span's end, use `span.end().line()`. #[stable(feature = "proc_macro_span_location", since = "1.88.0")] pub fn line(&self) -> usize { - bridge::client::Methods::span_line(self.0) + BridgeMethods::span_line(self.0) } /// The one-indexed column of the source file where the span starts. @@ -556,7 +554,7 @@ impl Span { /// To obtain the column of the span's end, use `span.end().column()`. #[stable(feature = "proc_macro_span_location", since = "1.88.0")] pub fn column(&self) -> usize { - bridge::client::Methods::span_column(self.0) + BridgeMethods::span_column(self.0) } /// The path to the source file in which this span occurs, for display purposes. @@ -565,7 +563,7 @@ impl Span { /// It might be remapped (e.g. `"/src/lib.rs"`) or an artificial path (e.g. `""`). #[stable(feature = "proc_macro_span_file", since = "1.88.0")] pub fn file(&self) -> String { - bridge::client::Methods::span_file(self.0) + BridgeMethods::span_file(self.0) } /// The path to the source file in which this span occurs on the local file system. @@ -575,7 +573,7 @@ impl Span { /// This path should not be embedded in the output of the macro; prefer `file()` instead. #[stable(feature = "proc_macro_span_file", since = "1.88.0")] pub fn local_file(&self) -> Option { - bridge::client::Methods::span_local_file(self.0).map(PathBuf::from) + BridgeMethods::span_local_file(self.0).map(PathBuf::from) } /// Creates a new span encompassing `self` and `other`. @@ -583,14 +581,14 @@ impl Span { /// Returns `None` if `self` and `other` are from different files. #[unstable(feature = "proc_macro_span", issue = "54725")] pub fn join(&self, other: Span) -> Option { - bridge::client::Methods::span_join(self.0, other.0).map(Span) + BridgeMethods::span_join(self.0, other.0).map(Span) } /// Creates a new span with the same line/column information as `self` but /// that resolves symbols as though it were at `other`. #[stable(feature = "proc_macro_span_resolved_at", since = "1.45.0")] pub fn resolved_at(&self, other: Span) -> Span { - Span(bridge::client::Methods::span_resolved_at(self.0, other.0)) + Span(BridgeMethods::span_resolved_at(self.0, other.0)) } /// Creates a new span with the same name resolution behavior as `self` but @@ -615,21 +613,21 @@ impl Span { /// be used for diagnostics only. #[stable(feature = "proc_macro_source_text", since = "1.66.0")] pub fn source_text(&self) -> Option { - bridge::client::Methods::span_source_text(self.0) + BridgeMethods::span_source_text(self.0) } // Used by the implementation of `Span::quote` #[doc(hidden)] #[unstable(feature = "proc_macro_internals", issue = "27812")] pub fn save_span(&self) -> usize { - bridge::client::Methods::span_save_span(self.0) + BridgeMethods::span_save_span(self.0) } // Used by the implementation of `Span::quote` #[doc(hidden)] #[unstable(feature = "proc_macro_internals", issue = "27812")] pub fn recover_proc_macro_span(id: usize) -> Span { - Span(bridge::client::Methods::span_recover_proc_macro_span(id)) + Span(BridgeMethods::span_recover_proc_macro_span(id)) } diagnostic_method!(error, Level::Error); @@ -1394,7 +1392,7 @@ impl Literal { // was 'c' or whether it was '\u{63}'. #[unstable(feature = "proc_macro_span", issue = "54725")] pub fn subspan>(&self, range: R) -> Option { - bridge::client::Methods::span_subspan( + BridgeMethods::span_subspan( self.0.span, range.start_bound().cloned(), range.end_bound().cloned(), @@ -1569,7 +1567,7 @@ impl FromStr for Literal { type Err = LexError; fn from_str(src: &str) -> Result { - match bridge::client::Methods::literal_from_str(src) { + match BridgeMethods::literal_from_str(src) { Ok(literal) => Ok(Literal(literal)), Err(()) => Err(LexError), } @@ -1611,11 +1609,12 @@ impl fmt::Debug for Literal { )] /// Functionality for adding environment state to the build dependency info. pub mod tracked { - use std::env::{self, VarError}; use std::ffi::OsStr; use std::path::Path; + use crate::BridgeMethods; + /// Retrieve an environment variable and add it to build dependency info. /// The build system executing the compiler will know that the variable was accessed during /// compilation, and will be able to rerun the build when the value of that variable changes. @@ -1624,9 +1623,8 @@ pub mod tracked { #[unstable(feature = "proc_macro_tracked_env", issue = "99515")] pub fn env_var + AsRef>(key: K) -> Result { let key: &str = key.as_ref(); - let value = - crate::bridge::client::Methods::injected_env_var(key).map_or_else(|| env::var(key), Ok); - crate::bridge::client::Methods::track_env_var(key, value.as_deref().ok()); + let value = BridgeMethods::injected_env_var(key).map_or_else(|| env::var(key), Ok); + BridgeMethods::track_env_var(key, value.as_deref().ok()); value } @@ -1636,6 +1634,6 @@ pub mod tracked { #[unstable(feature = "proc_macro_tracked_path", issue = "99515")] pub fn path>(path: P) { let path: &str = path.as_ref().to_str().unwrap(); - crate::bridge::client::Methods::track_path(path); + BridgeMethods::track_path(path); } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index 4f50c04b2799..ec30630c10bb 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -72,18 +72,18 @@ impl server::Server for RaSpanServer<'_> { // FIXME handle diagnostic } - fn tt_drop(&mut self, stream: Self::TokenStream) { + fn ts_drop(&mut self, stream: Self::TokenStream) { drop(stream); } - fn tt_clone(&mut self, stream: &Self::TokenStream) -> Self::TokenStream { + fn ts_clone(&mut self, stream: &Self::TokenStream) -> Self::TokenStream { stream.clone() } - fn tt_is_empty(&mut self, stream: &Self::TokenStream) -> bool { + fn ts_is_empty(&mut self, stream: &Self::TokenStream) -> bool { stream.is_empty() } - fn tt_from_str(&mut self, src: &str) -> Self::TokenStream { + fn ts_from_str(&mut self, src: &str) -> Self::TokenStream { Self::TokenStream::from_str(src, self.call_site).unwrap_or_else(|e| { Self::TokenStream::from_str( &format!("compile_error!(\"failed to parse str to token stream: {e}\")"), @@ -92,15 +92,15 @@ impl server::Server for RaSpanServer<'_> { .unwrap() }) } - fn tt_to_string(&mut self, stream: &Self::TokenStream) -> String { + fn ts_to_string(&mut self, stream: &Self::TokenStream) -> String { stream.to_string() } - fn tt_from_token_tree(&mut self, tree: TokenTree) -> Self::TokenStream { + fn ts_from_token_tree(&mut self, tree: TokenTree) -> Self::TokenStream { Self::TokenStream::new(vec![tree]) } - fn tt_expand_expr(&mut self, self_: &Self::TokenStream) -> Result { + fn ts_expand_expr(&mut self, self_: &Self::TokenStream) -> Result { // FIXME: requires db, more importantly this requires name resolution so we would need to // eagerly expand this proc-macro, but we can't know that this proc-macro is eager until we // expand it ... @@ -109,7 +109,7 @@ impl server::Server for RaSpanServer<'_> { Ok(self_.clone()) } - fn tt_concat_trees( + fn ts_concat_trees( &mut self, base: Option, trees: Vec>, @@ -125,7 +125,7 @@ impl server::Server for RaSpanServer<'_> { } } - fn tt_concat_streams( + fn ts_concat_streams( &mut self, base: Option, streams: Vec, @@ -137,7 +137,7 @@ impl server::Server for RaSpanServer<'_> { stream } - fn tt_into_trees(&mut self, stream: Self::TokenStream) -> Vec> { + fn ts_into_trees(&mut self, stream: Self::TokenStream) -> Vec> { (*stream.0).clone() } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs index cd5ba5909561..3bf07290c8c0 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs @@ -75,18 +75,18 @@ impl server::Server for SpanIdServer<'_> { fn emit_diagnostic(&mut self, _: Diagnostic) {} - fn tt_drop(&mut self, stream: Self::TokenStream) { + fn ts_drop(&mut self, stream: Self::TokenStream) { drop(stream); } - fn tt_clone(&mut self, stream: &Self::TokenStream) -> Self::TokenStream { + fn ts_clone(&mut self, stream: &Self::TokenStream) -> Self::TokenStream { stream.clone() } - fn tt_is_empty(&mut self, stream: &Self::TokenStream) -> bool { + fn ts_is_empty(&mut self, stream: &Self::TokenStream) -> bool { stream.is_empty() } - fn tt_from_str(&mut self, src: &str) -> Self::TokenStream { + fn ts_from_str(&mut self, src: &str) -> Self::TokenStream { Self::TokenStream::from_str(src, self.call_site).unwrap_or_else(|e| { Self::TokenStream::from_str( &format!("compile_error!(\"failed to parse str to token stream: {e}\")"), @@ -95,18 +95,18 @@ impl server::Server for SpanIdServer<'_> { .unwrap() }) } - fn tt_to_string(&mut self, stream: &Self::TokenStream) -> String { + fn ts_to_string(&mut self, stream: &Self::TokenStream) -> String { stream.to_string() } - fn tt_from_token_tree(&mut self, tree: TokenTree) -> Self::TokenStream { + fn ts_from_token_tree(&mut self, tree: TokenTree) -> Self::TokenStream { Self::TokenStream::new(vec![tree]) } - fn tt_expand_expr(&mut self, self_: &Self::TokenStream) -> Result { + fn ts_expand_expr(&mut self, self_: &Self::TokenStream) -> Result { Ok(self_.clone()) } - fn tt_concat_trees( + fn ts_concat_trees( &mut self, base: Option, trees: Vec>, @@ -122,7 +122,7 @@ impl server::Server for SpanIdServer<'_> { } } - fn tt_concat_streams( + fn ts_concat_streams( &mut self, base: Option, streams: Vec, @@ -134,7 +134,7 @@ impl server::Server for SpanIdServer<'_> { stream } - fn tt_into_trees(&mut self, stream: Self::TokenStream) -> Vec> { + fn ts_into_trees(&mut self, stream: Self::TokenStream) -> Vec> { (*stream.0).clone() } From 6db94dbc25b778f2782663ad20b27eb1380569d5 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Tue, 13 Jan 2026 16:52:02 +0200 Subject: [PATCH 26/28] abi: add a rust-preserve-none calling convention This is the conceptual opposite of the rust-cold calling convention and is particularly useful in combination with the new `explicit_tail_calls` feature. For relatively tight loops implemented with tail calling (`become`) each of the function with the regular calling convention is still responsible for restoring the initial value of the preserved registers. So it is not unusual to end up with a situation where each step in the tail call loop is spilling and reloading registers, along the lines of: foo: push r12 ; do things pop r12 jmp next_step This adds up quickly, especially when most of the clobberable registers are already used to pass arguments or other uses. I was thinking of making the name of this ABI a little less LLVM-derived and more like a conceptual inverse of `rust-cold`, but could not come with a great name (`rust-cold` is itself not a great name: cold in what context? from which perspective? is it supposed to mean that the function is rarely called?) --- compiler/rustc_abi/src/canon_abi.rs | 4 +- compiler/rustc_abi/src/extern_abi.rs | 13 +++- compiler/rustc_ast_lowering/src/stability.rs | 5 ++ .../rustc_ast_passes/src/ast_validation.rs | 1 + .../rustc_codegen_cranelift/src/abi/mod.rs | 3 + compiler/rustc_codegen_gcc/src/abi.rs | 2 + compiler/rustc_codegen_llvm/src/abi.rs | 4 + compiler/rustc_codegen_llvm/src/attributes.rs | 2 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 1 + compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_hir_typeck/src/callee.rs | 1 + compiler/rustc_middle/src/ty/layout.rs | 2 +- compiler/rustc_public/src/abi.rs | 1 + compiler/rustc_public/src/ty.rs | 1 + .../src/unstable/convert/internal.rs | 1 + .../src/unstable/convert/stable/abi.rs | 1 + .../src/unstable/convert/stable/ty.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_target/src/spec/abi_map.rs | 1 + .../rust-analyzer/crates/hir-ty/src/lib.rs | 3 + .../crates/intern/src/symbol/symbols.rs | 1 + tests/codegen-llvm/preserve-none.rs | 33 +++++++++ tests/ui/abi/rust-preserve-none-cc.rs | 67 +++++++++++++++++ .../feature-gate-rust-preserve-none-cc.rs | 21 ++++++ .../feature-gate-rust-preserve-none-cc.stderr | 73 +++++++++++++++++++ .../print-calling-conventions.stdout | 1 + 26 files changed, 241 insertions(+), 5 deletions(-) create mode 100644 tests/codegen-llvm/preserve-none.rs create mode 100644 tests/ui/abi/rust-preserve-none-cc.rs create mode 100644 tests/ui/feature-gates/feature-gate-rust-preserve-none-cc.rs create mode 100644 tests/ui/feature-gates/feature-gate-rust-preserve-none-cc.stderr diff --git a/compiler/rustc_abi/src/canon_abi.rs b/compiler/rustc_abi/src/canon_abi.rs index a5294bbf7171..fd45f0ea0e9e 100644 --- a/compiler/rustc_abi/src/canon_abi.rs +++ b/compiler/rustc_abi/src/canon_abi.rs @@ -27,6 +27,7 @@ pub enum CanonAbi { C, Rust, RustCold, + RustPreserveNone, /// An ABI that rustc does not know how to call or define. Custom, @@ -54,7 +55,7 @@ pub enum CanonAbi { impl CanonAbi { pub fn is_rustic_abi(self) -> bool { match self { - CanonAbi::Rust | CanonAbi::RustCold => true, + CanonAbi::Rust | CanonAbi::RustCold | CanonAbi::RustPreserveNone => true, CanonAbi::C | CanonAbi::Custom | CanonAbi::Arm(_) @@ -74,6 +75,7 @@ impl fmt::Display for CanonAbi { CanonAbi::C => ExternAbi::C { unwind: false }, CanonAbi::Rust => ExternAbi::Rust, CanonAbi::RustCold => ExternAbi::RustCold, + CanonAbi::RustPreserveNone => ExternAbi::RustPreserveNone, CanonAbi::Custom => ExternAbi::Custom, CanonAbi::Arm(arm_call) => match arm_call { ArmCall::Aapcs => ExternAbi::Aapcs { unwind: false }, diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs index e44dea1ce593..9173245d8aa4 100644 --- a/compiler/rustc_abi/src/extern_abi.rs +++ b/compiler/rustc_abi/src/extern_abi.rs @@ -42,6 +42,13 @@ pub enum ExternAbi { /// in a platform-agnostic way. RustInvalid, + /// Preserves no registers. + /// + /// Note, that this ABI is not stable in the registers it uses, is intended as an optimization + /// and may fall-back to a more conservative calling convention if the backend does not support + /// forcing callers to save all registers. + RustPreserveNone, + /// Unstable impl detail that directly uses Rust types to describe the ABI to LLVM. /// Even normally-compatible Rust types can become ABI-incompatible with this ABI! Unadjusted, @@ -163,6 +170,7 @@ abi_impls! { RustCall =><= "rust-call", RustCold =><= "rust-cold", RustInvalid =><= "rust-invalid", + RustPreserveNone =><= "rust-preserve-none", Stdcall { unwind: false } =><= "stdcall", Stdcall { unwind: true } =><= "stdcall-unwind", System { unwind: false } =><= "system", @@ -243,7 +251,7 @@ impl ExternAbi { /// - are subject to change between compiler versions pub fn is_rustic_abi(self) -> bool { use ExternAbi::*; - matches!(self, Rust | RustCall | RustCold) + matches!(self, Rust | RustCall | RustCold | RustPreserveNone) } /// Returns whether the ABI supports C variadics. This only controls whether we allow *imports* @@ -315,7 +323,8 @@ impl ExternAbi { | Self::Thiscall { .. } | Self::Vectorcall { .. } | Self::SysV64 { .. } - | Self::Win64 { .. } => true, + | Self::Win64 { .. } + | Self::RustPreserveNone => true, } } } diff --git a/compiler/rustc_ast_lowering/src/stability.rs b/compiler/rustc_ast_lowering/src/stability.rs index 6752218fa0d4..7674c7df4227 100644 --- a/compiler/rustc_ast_lowering/src/stability.rs +++ b/compiler/rustc_ast_lowering/src/stability.rs @@ -96,6 +96,11 @@ pub fn extern_abi_stability(abi: ExternAbi) -> Result<(), UnstableAbi> { ExternAbi::RustCold => { Err(UnstableAbi { abi, feature: sym::rust_cold_cc, explain: GateReason::Experimental }) } + ExternAbi::RustPreserveNone => Err(UnstableAbi { + abi, + feature: sym::rust_preserve_none_cc, + explain: GateReason::Experimental, + }), ExternAbi::RustInvalid => { Err(UnstableAbi { abi, feature: sym::rustc_attrs, explain: GateReason::ImplDetail }) } diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index eddcf12fca29..2457e0a777e4 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -400,6 +400,7 @@ impl<'a> AstValidator<'a> { CanonAbi::C | CanonAbi::Rust | CanonAbi::RustCold + | CanonAbi::RustPreserveNone | CanonAbi::Arm(_) | CanonAbi::X86(_) => { /* nothing to check */ } diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 09d71f5dd557..5a46f79e2ba0 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -56,6 +56,9 @@ pub(crate) fn conv_to_call_conv( CanonAbi::Rust | CanonAbi::C => default_call_conv, CanonAbi::RustCold => CallConv::Cold, + // Cranelift doesn't currently have anything for this. + CanonAbi::RustPreserveNone => default_call_conv, + // Functions with this calling convention can only be called from assembly, but it is // possible to declare an `extern "custom"` block, so the backend still needs a calling // convention for declaring foreign functions. diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index cc2c9fca94df..56ed7c01ed20 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/src/abi.rs @@ -243,6 +243,8 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { pub fn conv_to_fn_attribute<'gcc>(conv: CanonAbi, arch: &Arch) -> Option> { let attribute = match conv { CanonAbi::C | CanonAbi::Rust => return None, + // gcc/gccjit does not have anything for this. + CanonAbi::RustPreserveNone => return None, CanonAbi::RustCold => FnAttribute::Cold, // Functions with this calling convention can only be called from assembly, but it is // possible to declare an `extern "custom"` block, so the backend still needs a calling diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index c3c1caf086f0..994077251acc 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -694,6 +694,10 @@ pub(crate) fn to_llvm_calling_convention(sess: &Session, abi: CanonAbi) -> llvm: match abi { CanonAbi::C | CanonAbi::Rust => llvm::CCallConv, CanonAbi::RustCold => llvm::PreserveMost, + CanonAbi::RustPreserveNone => match &sess.target.arch { + Arch::X86_64 | Arch::AArch64 => llvm::PreserveNone, + _ => llvm::CCallConv, + }, // Functions with this calling convention can only be called from assembly, but it is // possible to declare an `extern "custom"` block, so the backend still needs a calling // convention for declaring foreign functions. diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index a25ce9e5a90a..d02838e00955 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -369,7 +369,7 @@ fn create_alloc_family_attr(llcx: &llvm::Context) -> &llvm::Attribute { llvm::CreateAttrStringValue(llcx, "alloc-family", "__rust_alloc") } -/// Helper for `FnAbi::apply_attrs_llfn`: +/// Helper for `FnAbiLlvmExt::apply_attrs_llfn`: /// Composite function which sets LLVM attributes for function depending on its AST (`#[attribute]`) /// attributes. pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index a90013801c8c..4b391a489c13 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -167,6 +167,7 @@ pub(crate) enum CallConv { PreserveMost = 14, PreserveAll = 15, Tail = 18, + PreserveNone = 21, X86StdcallCallConv = 64, X86FastcallCallConv = 65, ArmAapcsCallConv = 67, diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 8959bc586af7..e4db420a9d76 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -632,6 +632,8 @@ declare_features! ( (unstable, rtm_target_feature, "1.35.0", Some(150258)), /// Allows `extern "rust-cold"`. (unstable, rust_cold_cc, "1.63.0", Some(97544)), + /// Allows `extern "rust-preserve-none"`. + (unstable, rust_preserve_none_cc, "CURRENT_RUSTC_VERSION", Some(151401)), /// Target features on s390x. (unstable, s390x_target_feature, "1.82.0", Some(150259)), /// Allows the use of the `sanitize` attribute. diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index b60d053957a9..457ae1289792 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -188,6 +188,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { CanonAbi::C | CanonAbi::Rust | CanonAbi::RustCold + | CanonAbi::RustPreserveNone | CanonAbi::Arm(_) | CanonAbi::X86(_) => {} } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 81b8142d03f3..14ebcc968f7a 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -1288,7 +1288,7 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option, abi: ExternAbi) | RiscvInterruptS | RustInvalid | Unadjusted => false, - Rust | RustCall | RustCold => tcx.sess.panic_strategy().unwinds(), + Rust | RustCall | RustCold | RustPreserveNone => tcx.sess.panic_strategy().unwinds(), } } diff --git a/compiler/rustc_public/src/abi.rs b/compiler/rustc_public/src/abi.rs index aced39059f6b..bc18068cfa05 100644 --- a/compiler/rustc_public/src/abi.rs +++ b/compiler/rustc_public/src/abi.rs @@ -432,6 +432,7 @@ pub enum CallConvention { Cold, PreserveMost, PreserveAll, + PreserveNone, Custom, diff --git a/compiler/rustc_public/src/ty.rs b/compiler/rustc_public/src/ty.rs index 14656a2e594a..f4a7873c10f9 100644 --- a/compiler/rustc_public/src/ty.rs +++ b/compiler/rustc_public/src/ty.rs @@ -1139,6 +1139,7 @@ pub enum Abi { RustCold, RiscvInterruptM, RiscvInterruptS, + RustPreserveNone, RustInvalid, Custom, } diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index d9f314a8e29c..73a3fed111b3 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -615,6 +615,7 @@ impl RustcInternal for Abi { Abi::RustInvalid => rustc_abi::ExternAbi::RustInvalid, Abi::RiscvInterruptM => rustc_abi::ExternAbi::RiscvInterruptM, Abi::RiscvInterruptS => rustc_abi::ExternAbi::RiscvInterruptS, + Abi::RustPreserveNone => rustc_abi::ExternAbi::RustPreserveNone, Abi::Custom => rustc_abi::ExternAbi::Custom, } } diff --git a/compiler/rustc_public/src/unstable/convert/stable/abi.rs b/compiler/rustc_public/src/unstable/convert/stable/abi.rs index 03328d084ee9..e4130d7fa4cb 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/abi.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/abi.rs @@ -123,6 +123,7 @@ impl<'tcx> Stable<'tcx> for CanonAbi { CanonAbi::C => CallConvention::C, CanonAbi::Rust => CallConvention::Rust, CanonAbi::RustCold => CallConvention::Cold, + CanonAbi::RustPreserveNone => CallConvention::PreserveNone, CanonAbi::Custom => CallConvention::Custom, CanonAbi::Arm(arm_call) => match arm_call { ArmCall::Aapcs => CallConvention::ArmAapcs, diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index ca8234280be8..9215c41c27eb 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -1020,6 +1020,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::ExternAbi { ExternAbi::RustCall => Abi::RustCall, ExternAbi::Unadjusted => Abi::Unadjusted, ExternAbi::RustCold => Abi::RustCold, + ExternAbi::RustPreserveNone => Abi::RustPreserveNone, ExternAbi::RustInvalid => Abi::RustInvalid, ExternAbi::RiscvInterruptM => Abi::RiscvInterruptM, ExternAbi::RiscvInterruptS => Abi::RiscvInterruptS, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index f56c3421ce0f..dee2c3ca7323 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1907,6 +1907,7 @@ symbols! { rust_future, rust_logo, rust_out, + rust_preserve_none_cc, rustc, rustc_abi, // FIXME(#82232, #143834): temporary name to mitigate `#[align]` nameres ambiguity diff --git a/compiler/rustc_target/src/spec/abi_map.rs b/compiler/rustc_target/src/spec/abi_map.rs index d7fc18cd3761..c0fdd946778b 100644 --- a/compiler/rustc_target/src/spec/abi_map.rs +++ b/compiler/rustc_target/src/spec/abi_map.rs @@ -88,6 +88,7 @@ impl AbiMap { (ExternAbi::RustCold, _) if self.os == OsKind::Windows => CanonAbi::Rust, (ExternAbi::RustCold, _) => CanonAbi::RustCold, + (ExternAbi::RustPreserveNone, _) => CanonAbi::RustPreserveNone, (ExternAbi::Custom, _) => CanonAbi::Custom, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 41c381220cfd..f8920904f06f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -230,6 +230,7 @@ pub enum FnAbi { Win64, Win64Unwind, X86Interrupt, + RustPreserveNone, Unknown, } @@ -271,6 +272,7 @@ impl FnAbi { s if *s == sym::riscv_dash_interrupt_dash_s => FnAbi::RiscvInterruptS, s if *s == sym::rust_dash_call => FnAbi::RustCall, s if *s == sym::rust_dash_cold => FnAbi::RustCold, + s if *s == sym::rust_dash_preserve_dash_none => FnAbi::RustPreserveNone, s if *s == sym::rust_dash_intrinsic => FnAbi::RustIntrinsic, s if *s == sym::Rust => FnAbi::Rust, s if *s == sym::stdcall_dash_unwind => FnAbi::StdcallUnwind, @@ -314,6 +316,7 @@ impl FnAbi { FnAbi::Rust => "Rust", FnAbi::RustCall => "rust-call", FnAbi::RustCold => "rust-cold", + FnAbi::RustPreserveNone => "rust-preserve-none", FnAbi::RustIntrinsic => "rust-intrinsic", FnAbi::Stdcall => "stdcall", FnAbi::StdcallUnwind => "stdcall-unwind", diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 6181413a46db..cbaac64be4bd 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -109,6 +109,7 @@ define_symbols! { vectorcall_dash_unwind = "vectorcall-unwind", win64_dash_unwind = "win64-unwind", x86_dash_interrupt = "x86-interrupt", + rust_dash_preserve_dash_none = "preserve-none", @PLAIN: __ra_fixup, diff --git a/tests/codegen-llvm/preserve-none.rs b/tests/codegen-llvm/preserve-none.rs new file mode 100644 index 000000000000..b45e49a466bf --- /dev/null +++ b/tests/codegen-llvm/preserve-none.rs @@ -0,0 +1,33 @@ +//@ add-minicore +//@ revisions: X86 AARCH64 UNSUPPORTED +//@ [X86] compile-flags: -C no-prepopulate-passes --target=x86_64-unknown-linux-gnu +//@ [X86] needs-llvm-components: x86 +//@ [AARCH64] compile-flags: -C no-prepopulate-passes --target=aarch64-unknown-linux-gnu +//@ [AARCH64] needs-llvm-components: aarch64 +//@ [UNSUPPORTED] compile-flags: -C no-prepopulate-passes --target=i686-unknown-linux-gnu +//@ [UNSUPPORTED] needs-llvm-components: x86 + +#![crate_type = "lib"] +#![feature(rust_preserve_none_cc)] +#![feature(no_core, lang_items)] +#![no_core] + +extern crate minicore; + +// X86: define{{( dso_local)?}} preserve_nonecc void @peach(i16 +// AARCH64: define{{( dso_local)?}} preserve_nonecc void @peach(i16 +// UNSUPPORTED: define{{( dso_local)?}} void @peach(i16 +#[no_mangle] +#[inline(never)] +pub extern "rust-preserve-none" fn peach(x: u16) { + loop {} +} + +// X86: call preserve_nonecc void @peach(i16 +// AARCH64: call preserve_nonecc void @peach(i16 +// UNSUPPORTED: call void @peach(i16 +pub fn quince(x: u16) { + if let 12345u16 = x { + peach(54321); + } +} diff --git a/tests/ui/abi/rust-preserve-none-cc.rs b/tests/ui/abi/rust-preserve-none-cc.rs new file mode 100644 index 000000000000..deacb926971c --- /dev/null +++ b/tests/ui/abi/rust-preserve-none-cc.rs @@ -0,0 +1,67 @@ +//@ run-pass +//@ needs-unwind + +#![feature(rust_preserve_none_cc)] + +struct CrateOf<'a> { + mcintosh: f64, + golden_delicious: u64, + jonagold: Option<&'a u64>, + rome: [u64; 12], +} + +#[inline(never)] +extern "rust-preserve-none" fn oven_explosion() { + panic!("bad time"); +} + +#[inline(never)] +fn bite_into(yummy: u64) -> u64 { + let did_it_actually = std::panic::catch_unwind(move || { + oven_explosion() + }); + assert!(did_it_actually.is_err()); + yummy - 25 +} + +#[inline(never)] +extern "rust-preserve-none" fn lotsa_apples( + honeycrisp: u64, + gala: u32, + fuji: f64, + granny_smith: &[u64], + pink_lady: (), + and_a: CrateOf<'static>, + cosmic_crisp: u64, + ambrosia: f64, + winesap: &[u64], +) -> (u64, f64, u64, u64) { + assert_eq!(honeycrisp, 220); + assert_eq!(gala, 140); + assert_eq!(fuji, 210.54201234); + assert_eq!(granny_smith, &[180, 210]); + assert_eq!(pink_lady, ()); + assert_eq!(and_a.mcintosh, 150.0); + assert_eq!(and_a.golden_delicious, 185); + assert_eq!(and_a.jonagold, None); // my scales can't weight these gargantuans. + assert_eq!(and_a.rome, [180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202]); + assert_eq!(cosmic_crisp, 270); + assert_eq!(ambrosia, 193.1); + assert_eq!(winesap, &[]); + ( + and_a.rome.iter().sum(), + fuji + ambrosia, + cosmic_crisp - honeycrisp, + bite_into(and_a.golden_delicious) + ) +} + +fn main() { + let pie = lotsa_apples(220, 140, 210.54201234, &[180, 210], (), CrateOf { + mcintosh: 150.0, + golden_delicious: 185, + jonagold: None, + rome: [180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202] + }, 270, 193.1, &[]); + assert_eq!(pie, (2292, 403.64201234, 50, 160)); +} diff --git a/tests/ui/feature-gates/feature-gate-rust-preserve-none-cc.rs b/tests/ui/feature-gates/feature-gate-rust-preserve-none-cc.rs new file mode 100644 index 000000000000..e503e353104a --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-rust-preserve-none-cc.rs @@ -0,0 +1,21 @@ +#![crate_type = "lib"] + +extern "rust-preserve-none" fn apple() {} //~ ERROR "rust-preserve-none" ABI is experimental + +trait T { + extern "rust-preserve-none" fn banana(); //~ ERROR "rust-preserve-none" ABI is experimental + extern "rust-preserve-none" fn citrus() {} //~ ERROR "rust-preserve-none" ABI is experimental +} + +struct S; +impl T for S { + extern "rust-preserve-none" fn banana() {} //~ ERROR "rust-preserve-none" ABI is experimental +} + +impl S { + extern "rust-preserve-none" fn durian() {} //~ ERROR "rust-preserve-none" ABI is experimental +} + +type Fig = extern "rust-preserve-none" fn(); //~ ERROR "rust-preserve-none" ABI is experimental + +extern "rust-preserve-none" {} //~ ERROR "rust-preserve-none" ABI is experimental diff --git a/tests/ui/feature-gates/feature-gate-rust-preserve-none-cc.stderr b/tests/ui/feature-gates/feature-gate-rust-preserve-none-cc.stderr new file mode 100644 index 000000000000..b9d00e317b1f --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-rust-preserve-none-cc.stderr @@ -0,0 +1,73 @@ +error[E0658]: the extern "rust-preserve-none" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-preserve-none-cc.rs:3:8 + | +LL | extern "rust-preserve-none" fn apple() {} + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #151401 for more information + = help: add `#![feature(rust_preserve_none_cc)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the extern "rust-preserve-none" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-preserve-none-cc.rs:6:12 + | +LL | extern "rust-preserve-none" fn banana(); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #151401 for more information + = help: add `#![feature(rust_preserve_none_cc)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the extern "rust-preserve-none" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-preserve-none-cc.rs:7:12 + | +LL | extern "rust-preserve-none" fn citrus() {} + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #151401 for more information + = help: add `#![feature(rust_preserve_none_cc)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the extern "rust-preserve-none" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-preserve-none-cc.rs:12:12 + | +LL | extern "rust-preserve-none" fn banana() {} + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #151401 for more information + = help: add `#![feature(rust_preserve_none_cc)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the extern "rust-preserve-none" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-preserve-none-cc.rs:16:12 + | +LL | extern "rust-preserve-none" fn durian() {} + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #151401 for more information + = help: add `#![feature(rust_preserve_none_cc)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the extern "rust-preserve-none" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-preserve-none-cc.rs:19:19 + | +LL | type Fig = extern "rust-preserve-none" fn(); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #151401 for more information + = help: add `#![feature(rust_preserve_none_cc)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the extern "rust-preserve-none" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-preserve-none-cc.rs:21:8 + | +LL | extern "rust-preserve-none" {} + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #151401 for more information + = help: add `#![feature(rust_preserve_none_cc)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/print-request/print-calling-conventions.stdout b/tests/ui/print-request/print-calling-conventions.stdout index b8b939e1c04e..8366697d0fb0 100644 --- a/tests/ui/print-request/print-calling-conventions.stdout +++ b/tests/ui/print-request/print-calling-conventions.stdout @@ -21,6 +21,7 @@ riscv-interrupt-s rust-call rust-cold rust-invalid +rust-preserve-none stdcall stdcall-unwind system From a06fdc18062bb7d9e51384eb92da393ee1aa28ad Mon Sep 17 00:00:00 2001 From: vinDelphini Date: Sun, 25 Jan 2026 03:37:57 +0530 Subject: [PATCH 27/28] Fix 'the the' typo in library/core/src/array/iter.rs --- library/core/src/array/iter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index 1c1f4d78c03f..cd2a9e00b5a6 100644 --- a/library/core/src/array/iter.rs +++ b/library/core/src/array/iter.rs @@ -66,7 +66,7 @@ impl IntoIterator for [T; N] { // FIXME: If normal `transmute` ever gets smart enough to allow this // directly, use it instead of `transmute_unchecked`. let data: [MaybeUninit; N] = unsafe { transmute_unchecked(self) }; - // SAFETY: The original array was entirely initialized and the the alive + // SAFETY: The original array was entirely initialized and the alive // range we're passing here represents that fact. let inner = unsafe { InnerSized::new_unchecked(IndexRange::zero_to(N), data) }; IntoIter { inner: ManuallyDrop::new(inner) } From b235cc22acdde9be7a25c3b5828e9c5863a0b54f Mon Sep 17 00:00:00 2001 From: Adwin White Date: Thu, 7 Aug 2025 19:41:57 +0800 Subject: [PATCH 28/28] fix accidental type variable resolution in array coercion --- compiler/rustc_hir_typeck/src/coercion.rs | 23 +++++++++++++ compiler/rustc_hir_typeck/src/expr.rs | 13 ++++++- tests/ui/coercion/closure-in-array.rs | 7 ++++ tests/ui/coercion/closure-in-array.stderr | 14 ++++++++ tests/ui/coercion/coerce-many-with-ty-var.rs | 36 ++++++++++++++++++++ 5 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 tests/ui/coercion/closure-in-array.rs create mode 100644 tests/ui/coercion/closure-in-array.stderr create mode 100644 tests/ui/coercion/coerce-many-with-ty-var.rs diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 52ea6cdeaa0e..0e87b90dc6c1 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1365,6 +1365,7 @@ pub fn can_coerce<'tcx>( /// - WARNING: I don't believe this final type is guaranteed to be /// related to your initial `expected_ty` in any particular way, /// although it will typically be a subtype, so you should check it. +/// Check the note below for more details. /// - Invoking `complete()` may cause us to go and adjust the "adjustments" on /// previously coerced expressions. /// @@ -1378,6 +1379,28 @@ pub fn can_coerce<'tcx>( /// } /// let final_ty = coerce.complete(fcx); /// ``` +/// +/// NOTE: Why does the `expected_ty` participate in the LUB? +/// When coercing, each branch should use the following expectations for type inference: +/// - The branch can be coerced to the expected type of the match/if/whatever. +/// - The branch can be coercion lub'd with the types of the previous branches. +/// Ideally we'd have some sort of `Expectation::ParticipatesInCoerceLub(ongoing_lub_ty, final_ty)`, +/// but adding and using this feels very challenging. +/// What we instead do is to use the expected type of the match/if/whatever as +/// the initial coercion lub. This allows us to use the lub of "expected type of match" with +/// "types from previous branches" as the coercion target, which can contains both expectations. +/// +/// Two concerns with this approach: +/// - We may have incompatible `final_ty` if that lub is different from the expected +/// type of the match. However, in this case coercing the final type of the +/// `CoerceMany` to its expected type would have error'd anyways, so we don't care. +/// - We may constrain the `expected_ty` too early. For some branches with +/// type `a` and `b`, we end up with `(a lub expected_ty) lub b` instead of +/// `(a lub b) lub expected_ty`. They should be the same type. However, +/// `a lub expected_ty` may constrain inference variables in `expected_ty`. +/// In this case the difference does matter and we get actually incorrect results. +/// FIXME: Ideally we'd compute the final type without unnecessarily constraining +/// the expected type of the match when computing the types of its branches. pub(crate) struct CoerceMany<'tcx> { expected_ty: Ty<'tcx>, final_ty: Option>, diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 885af3b909b8..5b40531f9462 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1670,11 +1670,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let coerce_to = expected .to_option(self) - .and_then(|uty| self.try_structurally_resolve_type(expr.span, uty).builtin_index()) + .and_then(|uty| { + self.try_structurally_resolve_type(expr.span, uty) + .builtin_index() + // Avoid using the original type variable as the coerce_to type, as it may resolve + // during the first coercion instead of being the LUB type. + .filter(|t| !self.try_structurally_resolve_type(expr.span, *t).is_ty_var()) + }) .unwrap_or_else(|| self.next_ty_var(expr.span)); let mut coerce = CoerceMany::with_capacity(coerce_to, args.len()); for e in args { + // FIXME: the element expectation should use + // `try_structurally_resolve_and_adjust_for_branches` just like in `if` and `match`. + // While that fixes nested coercion, it will break [some + // code like this](https://github.com/rust-lang/rust/pull/140283#issuecomment-2958776528). + // If we find a way to support recursive tuple coercion, this break can be avoided. let e_ty = self.check_expr_with_hint(e, coerce_to); let cause = self.misc(e.span); coerce.coerce(self, &cause, e, e_ty); diff --git a/tests/ui/coercion/closure-in-array.rs b/tests/ui/coercion/closure-in-array.rs new file mode 100644 index 000000000000..ca5c021c77a7 --- /dev/null +++ b/tests/ui/coercion/closure-in-array.rs @@ -0,0 +1,7 @@ +// Weakened closure sig inference by #140283. +fn foo usize, const N: usize>(x: [F; N]) {} + +fn main() { + foo([|s| s.len()]) + //~^ ERROR: type annotations needed +} diff --git a/tests/ui/coercion/closure-in-array.stderr b/tests/ui/coercion/closure-in-array.stderr new file mode 100644 index 000000000000..90cf590c09e7 --- /dev/null +++ b/tests/ui/coercion/closure-in-array.stderr @@ -0,0 +1,14 @@ +error[E0282]: type annotations needed + --> $DIR/closure-in-array.rs:5:11 + | +LL | foo([|s| s.len()]) + | ^ - type must be known at this point + | +help: consider giving this closure parameter an explicit type + | +LL | foo([|s: /* Type */| s.len()]) + | ++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/coercion/coerce-many-with-ty-var.rs b/tests/ui/coercion/coerce-many-with-ty-var.rs new file mode 100644 index 000000000000..cbd16f58ea5b --- /dev/null +++ b/tests/ui/coercion/coerce-many-with-ty-var.rs @@ -0,0 +1,36 @@ +//@ run-pass +// Check that least upper bound coercions don't resolve type variable merely based on the first +// coercion. Check issue #136420. + +fn foo() {} +fn bar() {} + +fn infer(_: T) {} + +fn infer_array_element(_: [T; 2]) {} + +fn main() { + // Previously the element type's ty var will be unified with `foo`. + let _: [_; 2] = [foo, bar]; + infer_array_element([foo, bar]); + + let _ = if false { + foo + } else { + bar + }; + infer(if false { + foo + } else { + bar + }); + + let _ = match false { + true => foo, + false => bar, + }; + infer(match false { + true => foo, + false => bar, + }); +}