From 3ae69015cc07fcd675447ffd656448d6812d1192 Mon Sep 17 00:00:00 2001 From: Aelin Reidel Date: Tue, 13 Jan 2026 15:49:47 +0100 Subject: [PATCH 01/29] rustc_target: callconv: powerpc64: Use the ABI set in target options instead of guessing All PowerPC64 targets except AIX explicitly set the ABI in the target options. We can therefore stop hardcoding the ABI to be used based on the target environment or OS, except for the AIX special case. The fallback based on endianness is kept for the sake of compatibility with custom targets. This makes it so that big endian targets not explicitly accounted for before (powerpc64-unknown-openbsd) and targets that don't use the expected default ABI (ELFv2 Glibc targets) use the correct ABI in the calling convention code. --- compiler/rustc_target/src/callconv/powerpc64.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_target/src/callconv/powerpc64.rs b/compiler/rustc_target/src/callconv/powerpc64.rs index a77724a572dca..1d83799d7dab1 100644 --- a/compiler/rustc_target/src/callconv/powerpc64.rs +++ b/compiler/rustc_target/src/callconv/powerpc64.rs @@ -5,7 +5,7 @@ use rustc_abi::{Endian, HasDataLayout, TyAbiInterface}; use crate::callconv::{Align, ArgAbi, FnAbi, Reg, RegKind, Uniform}; -use crate::spec::{Env, HasTargetSpec, Os}; +use crate::spec::{Abi, HasTargetSpec, Os}; #[derive(Debug, Clone, Copy, PartialEq)] enum ABI { @@ -106,8 +106,10 @@ where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout + HasTargetSpec, { - let abi = if cx.target_spec().env == Env::Musl || cx.target_spec().os == Os::FreeBsd { + let abi = if cx.target_spec().options.abi == Abi::ElfV2 { ELFv2 + } else if cx.target_spec().options.abi == Abi::ElfV1 { + ELFv1 } else if cx.target_spec().os == Os::Aix { AIX } else { From 168f324f78abde44d107a2eb67d6d32f1a6a24f3 Mon Sep 17 00:00:00 2001 From: Aelin Reidel Date: Tue, 13 Jan 2026 15:49:56 +0100 Subject: [PATCH 02/29] rustc_target: spec: Ensure that a valid llvm_abiname value is set for PowerPC64(LE) PowerPC64 ELF targets (effectively anything that isn't AIX) use either the ELFv1 or ELFv2 ABI. The ELFv1 ABI is only specified for big endian targets, while ELFv2 can be used by both little- and big-endian targets. Make sure that, if an LLVM ABI is set, it is set to one of the two. AIX does not set an LLVM ABI name, so ensure that AIX targets don't set anything other than an empty ABI name. --- compiler/rustc_target/src/spec/mod.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index ab52a7d63301a..b3ebc94bb4abb 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -3182,6 +3182,27 @@ impl Target { "ARM targets must set `llvm-floatabi` to `hard` or `soft`", ) } + // PowerPC64 targets that are not AIX must set their ABI to either ELFv1 or ELFv2 + Arch::PowerPC64 => { + if self.os == Os::Aix { + check!( + self.llvm_abiname.is_empty(), + "AIX targets always use the AIX ABI and `llvm_abiname` should be left empty", + ); + } else if self.endian == Endian::Big { + check_matches!( + &*self.llvm_abiname, + "elfv1" | "elfv2", + "invalid PowerPC64 ABI name: {}", + self.llvm_abiname, + ); + } else { + check!( + self.llvm_abiname == "elfv2", + "little-endian PowerPC64 targets only support the `elfv2` ABI", + ); + } + } _ => {} } From c45a51397c09a371b13627f28ab75016f8832b32 Mon Sep 17 00:00:00 2001 From: enthropy7 <221884178+enthropy7@users.noreply.github.com> Date: Sun, 25 Jan 2026 15:40:10 +0300 Subject: [PATCH 03/29] Fix ICE in const eval when using packed SIMD with non-power-of-two length Packed SIMD types with non-power-of-two element counts use BackendRepr::Memory instead of BackendRepr::SimdVector. The check_simd_ptr_alignment function now handles both cases to prevent ICEs when evaluating SIMD intrinsics in const contexts. --- .../src/interpret/intrinsics/simd.rs | 16 ++++++++++++++-- .../const-eval/simd/simd-packed-non-pow2.rs | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 tests/ui/consts/const-eval/simd/simd-packed-non-pow2.rs diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs index d7fe7801fb082..0cfb9bd6d7940 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs @@ -2,7 +2,6 @@ use either::Either; use rustc_abi::{BackendRepr, Endian}; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_apfloat::{Float, Round}; -use rustc_data_structures::assert_matches; use rustc_middle::mir::interpret::{InterpErrorKind, Pointer, UndefinedBehaviorInfo}; use rustc_middle::ty::{FloatTy, ScalarInt, SimdAlign}; use rustc_middle::{bug, err_ub_format, mir, span_bug, throw_unsup_format, ty}; @@ -829,7 +828,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { vector_layout: TyAndLayout<'tcx>, alignment: SimdAlign, ) -> InterpResult<'tcx> { - assert_matches!(vector_layout.backend_repr, BackendRepr::SimdVector { .. }); + // Packed SIMD types with non-power-of-two element counts use BackendRepr::Memory + // instead of BackendRepr::SimdVector. We need to handle both cases. + // FIXME: remove the BackendRepr::Memory case when SIMD vectors are always passed as BackendRepr::SimdVector. + assert!(vector_layout.ty.is_simd(), "check_simd_ptr_alignment called on non-SIMD type"); + match vector_layout.backend_repr { + BackendRepr::SimdVector { .. } | BackendRepr::Memory { .. } => {} + _ => { + span_bug!( + self.cur_span(), + "SIMD type has unexpected backend_repr: {:?}", + vector_layout.backend_repr + ); + } + } let align = match alignment { ty::SimdAlign::Unaligned => { diff --git a/tests/ui/consts/const-eval/simd/simd-packed-non-pow2.rs b/tests/ui/consts/const-eval/simd/simd-packed-non-pow2.rs new file mode 100644 index 0000000000000..f09e52e9298bb --- /dev/null +++ b/tests/ui/consts/const-eval/simd/simd-packed-non-pow2.rs @@ -0,0 +1,18 @@ +//@ check-pass +// Fixes #151537 +#![feature(portable_simd, core_intrinsics)] +use std::intrinsics::simd::SimdAlign; +use std::{ptr::null, simd::prelude::*}; + +const _: () = { + let c = Simd::from_array([0; 3]); + unsafe { + core::intrinsics::simd::simd_masked_store::<_, _, _, { SimdAlign::Element }>( + c, + null::(), + c, + ) + }; +}; + +fn main() {} From ce4c17f61588ab794c966b052dbc0f69cee47e8d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 13 Feb 2026 14:55:54 -0800 Subject: [PATCH 04/29] Simplify internals of `{Rc,Arc}::default` This commit simplifies the internal implementation of `Default` for these two pointer types to have the same performance characteristics as before (a side effect of changes in 131460) while avoid use of internal private APIs of Rc/Arc. To preserve the same codegen as before some non-generic functions needed to be tagged as `#[inline]` as well, but otherwise the same IR is produced before/after this change. The motivation of this commit is I was studying up on the state of initialization of `Arc` and `Rc` and figured it'd be nicer to reduce the use of internal APIs and instead use public stable APIs where possible, even in the implementation itself. --- library/alloc/src/rc.rs | 25 +++++++++++++------ library/alloc/src/sync.rs | 29 ++++++++++++++--------- tests/codegen-llvm/issues/issue-111603.rs | 12 +++++----- 3 files changed, 42 insertions(+), 24 deletions(-) diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index cec41524325e0..f63351ebfd809 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -289,6 +289,7 @@ struct RcInner { } /// Calculate layout for `RcInner` using the inner value's layout +#[inline] fn rc_inner_layout_for_value_layout(layout: Layout) -> Layout { // Calculate layout using the given value layout. // Previously, layout was calculated on the expression @@ -2518,15 +2519,25 @@ impl Default for Rc { /// ``` #[inline] fn default() -> Self { + // First create an uninitialized allocation before creating an instance + // of `T`. This avoids having `T` on the stack and avoids the need to + // codegen a call to the destructor for `T` leading to generally better + // codegen. See #131460 for some more details. + let mut rc = Rc::new_uninit(); + + // SAFETY: this is a freshly allocated `Rc` so it's guaranteed there are + // no other strong or weak pointers other than `rc` itself. unsafe { - Self::from_inner( - Box::leak(Box::write( - Box::new_uninit(), - RcInner { strong: Cell::new(1), weak: Cell::new(1), value: T::default() }, - )) - .into(), - ) + let raw = Rc::get_mut_unchecked(&mut rc); + + // Note that `ptr::write` here is used specifically instead of + // `MaybeUninit::write` to avoid creating an extra stack copy of `T` + // in debug mode. See #136043 for more context. + ptr::write(raw.as_mut_ptr(), T::default()); } + + // SAFETY: this allocation was just initialized above. + unsafe { rc.assume_init() } } } diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index dc82357dd146b..d097588f8e633 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -392,6 +392,7 @@ struct ArcInner { } /// Calculate layout for `ArcInner` using the inner value's layout +#[inline] fn arcinner_layout_for_value_layout(layout: Layout) -> Layout { // Calculate layout using the given value layout. // Previously, layout was calculated on the expression @@ -3724,19 +3725,25 @@ impl Default for Arc { /// assert_eq!(*x, 0); /// ``` fn default() -> Arc { + // First create an uninitialized allocation before creating an instance + // of `T`. This avoids having `T` on the stack and avoids the need to + // codegen a call to the destructor for `T` leading to generally better + // codegen. See #131460 for some more details. + let mut arc = Arc::new_uninit(); + + // SAFETY: this is a freshly allocated `Arc` so it's guaranteed there + // are no other strong or weak pointers other than `arc` itself. unsafe { - Self::from_inner( - Box::leak(Box::write( - Box::new_uninit(), - ArcInner { - strong: atomic::AtomicUsize::new(1), - weak: atomic::AtomicUsize::new(1), - data: T::default(), - }, - )) - .into(), - ) + let raw = Arc::get_mut_unchecked(&mut arc); + + // Note that `ptr::write` here is used specifically instead of + // `MaybeUninit::write` to avoid creating an extra stack copy of `T` + // in debug mode. See #136043 for more context. + ptr::write(raw.as_mut_ptr(), T::default()); } + + // SAFETY: this allocation was just initialized above. + unsafe { arc.assume_init() } } } diff --git a/tests/codegen-llvm/issues/issue-111603.rs b/tests/codegen-llvm/issues/issue-111603.rs index 2ba5a3f876aed..91eb836478eb1 100644 --- a/tests/codegen-llvm/issues/issue-111603.rs +++ b/tests/codegen-llvm/issues/issue-111603.rs @@ -10,9 +10,9 @@ use std::sync::Arc; pub fn new_from_array(x: u64) -> Arc<[u64]> { // Ensure that we only generate one alloca for the array. - // CHECK: alloca + // CHECK: %[[A:.+]] = alloca // CHECK-SAME: [8000 x i8] - // CHECK-NOT: alloca + // CHECK-NOT: %[[B:.+]] = alloca let array = [x; 1000]; Arc::new(array) } @@ -20,8 +20,9 @@ pub fn new_from_array(x: u64) -> Arc<[u64]> { // CHECK-LABEL: @new_uninit #[no_mangle] pub fn new_uninit(x: u64) -> Arc<[u64; 1000]> { - // CHECK: call alloc::sync::arcinner_layout_for_value_layout - // CHECK-NOT: call alloc::sync::arcinner_layout_for_value_layout + // CHECK: %[[A:.+]] = alloca + // CHECK-SAME: [8000 x i8] + // CHECK-NOT: %[[B:.+]] = alloca let mut arc = Arc::new_uninit(); unsafe { Arc::get_mut_unchecked(&mut arc) }.write([x; 1000]); unsafe { arc.assume_init() } @@ -30,8 +31,7 @@ pub fn new_uninit(x: u64) -> Arc<[u64; 1000]> { // CHECK-LABEL: @new_uninit_slice #[no_mangle] pub fn new_uninit_slice(x: u64) -> Arc<[u64]> { - // CHECK: call alloc::sync::arcinner_layout_for_value_layout - // CHECK-NOT: call alloc::sync::arcinner_layout_for_value_layout + // CHECK-NOT: %[[B:.+]] = alloca let mut arc = Arc::new_uninit_slice(1000); for elem in unsafe { Arc::get_mut_unchecked(&mut arc) } { elem.write(x); From a2699f0c5ae8f5c14c131cebec29f0573054821d Mon Sep 17 00:00:00 2001 From: Paul Mabileau Date: Wed, 15 Oct 2025 13:02:34 +0200 Subject: [PATCH 05/29] Test(lib/win/proc): Skip `raw_attributes` doctest under Win7 The current doctest for `ProcThreadAttributeListBuilder::raw_attribute` uses `CreatePseudoConsole`, which is only available on Windows 10 October 2018 Update and above. On older versions of Windows, the test fails due to trying to link against a function that is not present in the kernel32 DLL. This therefore ensures the test is still built, but not run under the Win7 target. Signed-off-by: Paul Mabileau --- library/std/src/os/windows/process.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index b32c6cd442ffa..ff3ae8145e0f6 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -573,7 +573,8 @@ impl<'a> ProcThreadAttributeListBuilder<'a> { /// /// # Example /// - /// ``` + #[cfg_attr(target_vendor = "win7", doc = "```no_run")] + #[cfg_attr(not(target_vendor = "win7"), doc = "```")] /// #![feature(windows_process_extensions_raw_attribute)] /// use std::ffi::c_void; /// use std::os::windows::process::{CommandExt, ProcThreadAttributeList}; From f01b2f938bb51c108fefca069816b72f24582992 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Thu, 6 Nov 2025 23:10:28 +0000 Subject: [PATCH 06/29] Try harder to evaluate constants. --- compiler/rustc_mir_transform/src/gvn.rs | 49 ++++++++++++------- ...onential_common.GVN.32bit.panic-abort.diff | 4 +- ...nential_common.GVN.32bit.panic-unwind.diff | 4 +- ...onential_common.GVN.64bit.panic-abort.diff | 4 +- ...nential_common.GVN.64bit.panic-unwind.diff | 4 +- ...onst_eval_polymorphic.no_optimize.GVN.diff | 3 +- tests/mir-opt/gvn_const_eval_polymorphic.rs | 2 +- ...ecked_shl.PreCodegen.after.panic-abort.mir | 2 +- ...cked_shl.PreCodegen.after.panic-unwind.mir | 2 +- ...p_forward.PreCodegen.after.panic-abort.mir | 2 +- ..._forward.PreCodegen.after.panic-unwind.mir | 2 +- 11 files changed, 45 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 6507a5194adda..cb9b8b5d7f0ce 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1002,21 +1002,19 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { operand: &mut Operand<'tcx>, location: Location, ) -> Option { - match *operand { - Operand::RuntimeChecks(c) => { - Some(self.insert(self.tcx.types.bool, Value::RuntimeChecks(c))) - } - Operand::Constant(ref constant) => Some(self.insert_constant(constant.const_)), + let value = match *operand { + Operand::RuntimeChecks(c) => self.insert(self.tcx.types.bool, Value::RuntimeChecks(c)), + Operand::Constant(ref constant) => self.insert_constant(constant.const_), Operand::Copy(ref mut place) | Operand::Move(ref mut place) => { - let value = self.simplify_place_value(place, location)?; - if let Some(const_) = self.try_as_constant(value) { - *operand = Operand::Constant(Box::new(const_)); - } else if let Value::RuntimeChecks(c) = self.get(value) { - *operand = Operand::RuntimeChecks(c); - } - Some(value) + self.simplify_place_value(place, location)? } + }; + if let Some(const_) = self.try_as_constant(value) { + *operand = Operand::Constant(Box::new(const_)); + } else if let Value::RuntimeChecks(c) = self.get(value) { + *operand = Operand::RuntimeChecks(c); } + Some(value) } #[instrument(level = "trace", skip(self), ret)] @@ -1831,14 +1829,28 @@ impl<'tcx> VnState<'_, '_, 'tcx> { /// If `index` is a `Value::Constant`, return the `Constant` to be put in the MIR. fn try_as_constant(&mut self, index: VnIndex) -> Option> { - // This was already constant in MIR, do not change it. If the constant is not - // deterministic, adding an additional mention of it in MIR will not give the same value as - // the former mention. - if let Value::Constant { value, disambiguator: None } = self.get(index) { - debug_assert!(value.is_deterministic()); + let value = self.get(index); + + // This was already an *evaluated* constant in MIR, do not change it. + if let Value::Constant { value, disambiguator: None } = value + && let Const::Val(..) = value + { + return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value }); + } + + if let Some(value) = self.try_as_evaluated_constant(index) { + return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value }); + } + + // We failed to provide an evaluated form, fallback to using the unevaluated constant. + if let Value::Constant { value, disambiguator: None } = value { return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value }); } + None + } + + fn try_as_evaluated_constant(&mut self, index: VnIndex) -> Option> { let op = self.eval_to_const(index)?; if op.layout.is_unsized() { // Do not attempt to propagate unsized locals. @@ -1852,8 +1864,7 @@ impl<'tcx> VnState<'_, '_, 'tcx> { // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed. assert!(!value.may_have_provenance(self.tcx, op.layout.size)); - let const_ = Const::Val(value, op.layout.ty); - Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_ }) + Some(Const::Val(value, op.layout.ty)) } /// Construct a place which holds the same value as `index` and for which all locals strictly diff --git a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.32bit.panic-abort.diff b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.32bit.panic-abort.diff index 88c77832a4e18..61cc233bcb528 100644 --- a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.32bit.panic-abort.diff @@ -47,7 +47,7 @@ StorageLive(_20); StorageLive(_21); _21 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32); - _20 = BitAnd(move _21, const core::fmt::flags::SIGN_PLUS_FLAG); + _20 = BitAnd(move _21, const 2097152_u32); StorageDead(_21); _4 = Ne(move _20, const 0_u32); StorageDead(_20); @@ -72,7 +72,7 @@ StorageLive(_22); StorageLive(_23); _23 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32); - _22 = BitAnd(move _23, const core::fmt::flags::PRECISION_FLAG); + _22 = BitAnd(move _23, const 268435456_u32); StorageDead(_23); switchInt(copy _22) -> [0: bb10, otherwise: bb11]; } diff --git a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.32bit.panic-unwind.diff b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.32bit.panic-unwind.diff index 8a6e7fd35ccd2..655762567da32 100644 --- a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.32bit.panic-unwind.diff @@ -47,7 +47,7 @@ StorageLive(_20); StorageLive(_21); _21 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32); - _20 = BitAnd(move _21, const core::fmt::flags::SIGN_PLUS_FLAG); + _20 = BitAnd(move _21, const 2097152_u32); StorageDead(_21); _4 = Ne(move _20, const 0_u32); StorageDead(_20); @@ -72,7 +72,7 @@ StorageLive(_22); StorageLive(_23); _23 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32); - _22 = BitAnd(move _23, const core::fmt::flags::PRECISION_FLAG); + _22 = BitAnd(move _23, const 268435456_u32); StorageDead(_23); switchInt(copy _22) -> [0: bb10, otherwise: bb11]; } diff --git a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.64bit.panic-abort.diff b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.64bit.panic-abort.diff index ce10f4bb247a8..ca47d2c580b50 100644 --- a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.64bit.panic-abort.diff @@ -47,7 +47,7 @@ StorageLive(_20); StorageLive(_21); _21 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32); - _20 = BitAnd(move _21, const core::fmt::flags::SIGN_PLUS_FLAG); + _20 = BitAnd(move _21, const 2097152_u32); StorageDead(_21); _4 = Ne(move _20, const 0_u32); StorageDead(_20); @@ -72,7 +72,7 @@ StorageLive(_22); StorageLive(_23); _23 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32); - _22 = BitAnd(move _23, const core::fmt::flags::PRECISION_FLAG); + _22 = BitAnd(move _23, const 268435456_u32); StorageDead(_23); switchInt(copy _22) -> [0: bb10, otherwise: bb11]; } diff --git a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.64bit.panic-unwind.diff b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.64bit.panic-unwind.diff index b19f2438d0225..9239c365537c7 100644 --- a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.64bit.panic-unwind.diff @@ -47,7 +47,7 @@ StorageLive(_20); StorageLive(_21); _21 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32); - _20 = BitAnd(move _21, const core::fmt::flags::SIGN_PLUS_FLAG); + _20 = BitAnd(move _21, const 2097152_u32); StorageDead(_21); _4 = Ne(move _20, const 0_u32); StorageDead(_20); @@ -72,7 +72,7 @@ StorageLive(_22); StorageLive(_23); _23 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32); - _22 = BitAnd(move _23, const core::fmt::flags::PRECISION_FLAG); + _22 = BitAnd(move _23, const 268435456_u32); StorageDead(_23); switchInt(copy _22) -> [0: bb10, otherwise: bb11]; } diff --git a/tests/mir-opt/gvn_const_eval_polymorphic.no_optimize.GVN.diff b/tests/mir-opt/gvn_const_eval_polymorphic.no_optimize.GVN.diff index a91561ba304b8..92158682c9993 100644 --- a/tests/mir-opt/gvn_const_eval_polymorphic.no_optimize.GVN.diff +++ b/tests/mir-opt/gvn_const_eval_polymorphic.no_optimize.GVN.diff @@ -5,7 +5,8 @@ let mut _0: bool; bb0: { - _0 = Eq(const no_optimize::::{constant#0}, const no_optimize::::{constant#1}); +- _0 = Eq(const no_optimize::::{constant#0}, const no_optimize::::{constant#1}); ++ _0 = Eq(const no_optimize::::{constant#0}, const true); return; } } diff --git a/tests/mir-opt/gvn_const_eval_polymorphic.rs b/tests/mir-opt/gvn_const_eval_polymorphic.rs index 7320ad947ff2e..722720b2a6f07 100644 --- a/tests/mir-opt/gvn_const_eval_polymorphic.rs +++ b/tests/mir-opt/gvn_const_eval_polymorphic.rs @@ -51,7 +51,7 @@ fn optimize_false() -> bool { // EMIT_MIR gvn_const_eval_polymorphic.no_optimize.GVN.diff fn no_optimize() -> bool { // CHECK-LABEL: fn no_optimize( - // CHECK: _0 = Eq(const no_optimize::::{constant#0}, const no_optimize::::{constant#1}); + // CHECK: _0 = Eq(const no_optimize::::{constant#0}, const true); // CHECK-NEXT: return; (const { type_name_contains_i32(&generic::) }) == const { true } } diff --git a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-abort.mir index 18eeb8e4d3b61..c97c9b45d6ad5 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-abort.mir @@ -17,7 +17,7 @@ fn checked_shl(_1: u32, _2: u32) -> Option { bb0: { StorageLive(_3); - _3 = Lt(copy _2, const core::num::::BITS); + _3 = Lt(copy _2, const 32_u32); switchInt(move _3) -> [0: bb1, otherwise: bb2]; } diff --git a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-unwind.mir index 18eeb8e4d3b61..c97c9b45d6ad5 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-unwind.mir @@ -17,7 +17,7 @@ fn checked_shl(_1: u32, _2: u32) -> Option { bb0: { StorageLive(_3); - _3 = Lt(copy _2, const core::num::::BITS); + _3 = Lt(copy _2, const 32_u32); switchInt(move _3) -> [0: bb1, otherwise: bb2]; } diff --git a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir index 83478e60b5d4e..7b681946bee1e 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir @@ -72,7 +72,7 @@ fn step_forward(_1: u16, _2: usize) -> u16 { } bb6: { - assert(!const true, "attempt to compute `{} + {}`, which would overflow", const core::num::::MAX, const 1_u16) -> [success: bb7, unwind unreachable]; + assert(!const true, "attempt to compute `{} + {}`, which would overflow", const u16::MAX, const 1_u16) -> [success: bb7, unwind unreachable]; } bb7: { diff --git a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir index ac7a6e0445191..a0137b23ec4f4 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir @@ -72,7 +72,7 @@ fn step_forward(_1: u16, _2: usize) -> u16 { } bb6: { - assert(!const true, "attempt to compute `{} + {}`, which would overflow", const core::num::::MAX, const 1_u16) -> [success: bb7, unwind continue]; + assert(!const true, "attempt to compute `{} + {}`, which would overflow", const u16::MAX, const 1_u16) -> [success: bb7, unwind continue]; } bb7: { From 54b07a31ffe78065171c925580badc75836d85f5 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Thu, 6 Nov 2025 23:00:23 +0000 Subject: [PATCH 07/29] Constants of primitive types are always deterministic. --- compiler/rustc_middle/src/mir/consts.rs | 20 +++++++++---------- ...ace.PreCodegen.after.32bit.panic-abort.mir | 20 ++++++++----------- ...ce.PreCodegen.after.32bit.panic-unwind.mir | 20 ++++++++----------- ...ace.PreCodegen.after.64bit.panic-abort.mir | 20 ++++++++----------- ...ce.PreCodegen.after.64bit.panic-unwind.mir | 20 ++++++++----------- tests/mir-opt/pre-codegen/drop_boxed_slice.rs | 5 ++--- 6 files changed, 44 insertions(+), 61 deletions(-) diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index afe39e4481efc..715e6e2917fc2 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -478,6 +478,11 @@ impl<'tcx> Const<'tcx> { /// Return true if any evaluation of this constant always returns the same value, /// taking into account even pointer identity tests. pub fn is_deterministic(&self) -> bool { + // Primitive types cannot contain provenance and always have the same value. + if self.ty().is_primitive() { + return true; + } + // Some constants may generate fresh allocations for pointers they contain, // so using the same constant twice can yield two different results. // Notably, valtrees purposefully generate new allocations. @@ -487,24 +492,19 @@ impl<'tcx> Const<'tcx> { // A valtree may be a reference. Valtree references correspond to a // different allocation each time they are evaluated. Valtrees for primitive // types are fine though. - ty::ConstKind::Value(cv) => cv.ty.is_primitive(), - ty::ConstKind::Unevaluated(..) | ty::ConstKind::Expr(..) => false, + ty::ConstKind::Value(..) + | ty::ConstKind::Expr(..) + | ty::ConstKind::Unevaluated(..) // This can happen if evaluation of a constant failed. The result does not matter // much since compilation is doomed. - ty::ConstKind::Error(..) => false, + | ty::ConstKind::Error(..) => false, // Should not appear in runtime MIR. ty::ConstKind::Infer(..) | ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(..) => bug!(), }, Const::Unevaluated(..) => false, - Const::Val( - ConstValue::Slice { .. } - | ConstValue::ZeroSized - | ConstValue::Scalar(_) - | ConstValue::Indirect { .. }, - _, - ) => true, + Const::Val(..) => true, } } } 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 f8e575f490b0c..f4794a974bf22 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 @@ -8,7 +8,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { let _2: std::ptr::NonNull<[T]>; let mut _3: *mut [T]; let mut _4: *const [T]; - let _9: (); + let _8: (); scope 3 { scope 4 { scope 17 (inlined Layout::size) { @@ -26,7 +26,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { scope 23 (inlined ::deallocate) { scope 24 (inlined std::alloc::Global::deallocate_impl) { scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) { - let mut _8: *mut u8; + let mut _7: *mut u8; scope 26 (inlined Layout::size) { } scope 27 (inlined NonNull::::as_ptr) { @@ -47,7 +47,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 7 (inlined Layout::for_value_raw::<[T]>) { let mut _5: usize; - let mut _7: std::ptr::Alignment; + let mut _6: std::ptr::Alignment; scope 8 { scope 16 (inlined #[track_caller] Layout::from_size_alignment_unchecked) { } @@ -55,7 +55,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { scope 9 (inlined size_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) { @@ -82,22 +81,19 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } bb1: { - StorageLive(_6); - _6 = const ::ALIGN; - _7 = copy _6 as std::ptr::Alignment (Transmute); - StorageDead(_6); + _6 = const ::ALIGN as std::ptr::Alignment (Transmute); StorageDead(_4); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { - StorageLive(_8); - _8 = copy _3 as *mut u8 (PtrToPtr); - _9 = alloc::alloc::__rust_dealloc(move _8, move _5, move _7) -> [return: bb3, unwind unreachable]; + StorageLive(_7); + _7 = copy _3 as *mut u8 (PtrToPtr); + _8 = alloc::alloc::__rust_dealloc(move _7, move _5, move _6) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_8); + StorageDead(_7); goto -> bb4; } 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 f8e575f490b0c..f4794a974bf22 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 @@ -8,7 +8,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { let _2: std::ptr::NonNull<[T]>; let mut _3: *mut [T]; let mut _4: *const [T]; - let _9: (); + let _8: (); scope 3 { scope 4 { scope 17 (inlined Layout::size) { @@ -26,7 +26,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { scope 23 (inlined ::deallocate) { scope 24 (inlined std::alloc::Global::deallocate_impl) { scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) { - let mut _8: *mut u8; + let mut _7: *mut u8; scope 26 (inlined Layout::size) { } scope 27 (inlined NonNull::::as_ptr) { @@ -47,7 +47,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 7 (inlined Layout::for_value_raw::<[T]>) { let mut _5: usize; - let mut _7: std::ptr::Alignment; + let mut _6: std::ptr::Alignment; scope 8 { scope 16 (inlined #[track_caller] Layout::from_size_alignment_unchecked) { } @@ -55,7 +55,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { scope 9 (inlined size_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) { @@ -82,22 +81,19 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } bb1: { - StorageLive(_6); - _6 = const ::ALIGN; - _7 = copy _6 as std::ptr::Alignment (Transmute); - StorageDead(_6); + _6 = const ::ALIGN as std::ptr::Alignment (Transmute); StorageDead(_4); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { - StorageLive(_8); - _8 = copy _3 as *mut u8 (PtrToPtr); - _9 = alloc::alloc::__rust_dealloc(move _8, move _5, move _7) -> [return: bb3, unwind unreachable]; + StorageLive(_7); + _7 = copy _3 as *mut u8 (PtrToPtr); + _8 = alloc::alloc::__rust_dealloc(move _7, move _5, move _6) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_8); + StorageDead(_7); goto -> bb4; } 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 f8e575f490b0c..f4794a974bf22 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 @@ -8,7 +8,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { let _2: std::ptr::NonNull<[T]>; let mut _3: *mut [T]; let mut _4: *const [T]; - let _9: (); + let _8: (); scope 3 { scope 4 { scope 17 (inlined Layout::size) { @@ -26,7 +26,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { scope 23 (inlined ::deallocate) { scope 24 (inlined std::alloc::Global::deallocate_impl) { scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) { - let mut _8: *mut u8; + let mut _7: *mut u8; scope 26 (inlined Layout::size) { } scope 27 (inlined NonNull::::as_ptr) { @@ -47,7 +47,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 7 (inlined Layout::for_value_raw::<[T]>) { let mut _5: usize; - let mut _7: std::ptr::Alignment; + let mut _6: std::ptr::Alignment; scope 8 { scope 16 (inlined #[track_caller] Layout::from_size_alignment_unchecked) { } @@ -55,7 +55,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { scope 9 (inlined size_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) { @@ -82,22 +81,19 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } bb1: { - StorageLive(_6); - _6 = const ::ALIGN; - _7 = copy _6 as std::ptr::Alignment (Transmute); - StorageDead(_6); + _6 = const ::ALIGN as std::ptr::Alignment (Transmute); StorageDead(_4); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { - StorageLive(_8); - _8 = copy _3 as *mut u8 (PtrToPtr); - _9 = alloc::alloc::__rust_dealloc(move _8, move _5, move _7) -> [return: bb3, unwind unreachable]; + StorageLive(_7); + _7 = copy _3 as *mut u8 (PtrToPtr); + _8 = alloc::alloc::__rust_dealloc(move _7, move _5, move _6) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_8); + StorageDead(_7); goto -> bb4; } 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 f8e575f490b0c..f4794a974bf22 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 @@ -8,7 +8,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { let _2: std::ptr::NonNull<[T]>; let mut _3: *mut [T]; let mut _4: *const [T]; - let _9: (); + let _8: (); scope 3 { scope 4 { scope 17 (inlined Layout::size) { @@ -26,7 +26,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { scope 23 (inlined ::deallocate) { scope 24 (inlined std::alloc::Global::deallocate_impl) { scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) { - let mut _8: *mut u8; + let mut _7: *mut u8; scope 26 (inlined Layout::size) { } scope 27 (inlined NonNull::::as_ptr) { @@ -47,7 +47,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 7 (inlined Layout::for_value_raw::<[T]>) { let mut _5: usize; - let mut _7: std::ptr::Alignment; + let mut _6: std::ptr::Alignment; scope 8 { scope 16 (inlined #[track_caller] Layout::from_size_alignment_unchecked) { } @@ -55,7 +55,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { scope 9 (inlined size_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) { @@ -82,22 +81,19 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } bb1: { - StorageLive(_6); - _6 = const ::ALIGN; - _7 = copy _6 as std::ptr::Alignment (Transmute); - StorageDead(_6); + _6 = const ::ALIGN as std::ptr::Alignment (Transmute); StorageDead(_4); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { - StorageLive(_8); - _8 = copy _3 as *mut u8 (PtrToPtr); - _9 = alloc::alloc::__rust_dealloc(move _8, move _5, move _7) -> [return: bb3, unwind unreachable]; + StorageLive(_7); + _7 = copy _3 as *mut u8 (PtrToPtr); + _8 = alloc::alloc::__rust_dealloc(move _7, move _5, move _6) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_8); + StorageDead(_7); goto -> bb4; } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.rs b/tests/mir-opt/pre-codegen/drop_boxed_slice.rs index ae10cfb0b1713..9e56b310e8188 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.rs +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.rs @@ -9,8 +9,7 @@ pub unsafe fn generic_in_place(ptr: *mut Box<[T]>) { // CHECK-LABEL: fn generic_in_place(_1: *mut Box<[T]>) // CHECK: (inlined as Drop>::drop) // CHECK: [[SIZE:_.+]] = std::intrinsics::size_of_val::<[T]> - // CHECK: [[ALIGN:_.+]] = const ::ALIGN; - // CHECK: [[B:_.+]] = copy [[ALIGN]] as std::ptr::Alignment (Transmute); - // CHECK: = alloc::alloc::__rust_dealloc({{.+}}, move [[SIZE]], move [[B]]) -> + // CHECK: [[ALIGN:_.+]] = const ::ALIGN as std::ptr::Alignment (Transmute); + // CHECK: = alloc::alloc::__rust_dealloc({{.+}}, move [[SIZE]], move [[ALIGN]]) -> std::ptr::drop_in_place(ptr) } From fd1a413a30a7686aa52b07ae2b50091186ee2651 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Fri, 28 Nov 2025 23:27:21 +0000 Subject: [PATCH 08/29] Bless incremental. --- tests/incremental/hashes/enum_constructors.rs | 2 +- tests/incremental/hashes/struct_constructors.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/incremental/hashes/enum_constructors.rs b/tests/incremental/hashes/enum_constructors.rs index dee485681e447..5962727b3af0b 100644 --- a/tests/incremental/hashes/enum_constructors.rs +++ b/tests/incremental/hashes/enum_constructors.rs @@ -65,7 +65,7 @@ pub fn change_field_order_struct_like() -> Enum { #[cfg(not(any(cfail1,cfail4)))] #[rustc_clean(cfg="cfail2", except="opt_hir_owner_nodes,typeck")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(cfg="cfail5", except="opt_hir_owner_nodes,typeck,optimized_mir")] +#[rustc_clean(cfg="cfail5", except="opt_hir_owner_nodes,typeck")] #[rustc_clean(cfg="cfail6")] // FIXME(michaelwoerister):Interesting. I would have thought that that changes the MIR. And it // would if it were not all constants diff --git a/tests/incremental/hashes/struct_constructors.rs b/tests/incremental/hashes/struct_constructors.rs index da7abe049d982..b2f8d116b1e94 100644 --- a/tests/incremental/hashes/struct_constructors.rs +++ b/tests/incremental/hashes/struct_constructors.rs @@ -62,7 +62,7 @@ pub fn change_field_order_regular_struct() -> RegularStruct { #[cfg(not(any(cfail1,cfail4)))] #[rustc_clean(cfg="cfail2", except="opt_hir_owner_nodes,typeck")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(cfg="cfail5", except="opt_hir_owner_nodes,typeck,optimized_mir")] +#[rustc_clean(cfg="cfail5", except="opt_hir_owner_nodes,typeck")] #[rustc_clean(cfg="cfail6")] pub fn change_field_order_regular_struct() -> RegularStruct { RegularStruct { From 1ddf683521d38821864b993a91b3efc7d084cae9 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Tue, 16 Dec 2025 01:14:00 +0000 Subject: [PATCH 09/29] Elaborate comments. --- compiler/rustc_middle/src/mir/consts.rs | 52 +++++++++---------------- compiler/rustc_mir_transform/src/gvn.rs | 2 +- 2 files changed, 20 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 715e6e2917fc2..5949eaf2efcd6 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -183,21 +183,16 @@ impl ConstValue { /// Check if a constant may contain provenance information. This is used by MIR opts. /// Can return `true` even if there is no provenance. - pub fn may_have_provenance(&self, tcx: TyCtxt<'_>, size: Size) -> bool { + pub fn may_have_provenance(&self, tcx: TyCtxt<'_>, size: Option) -> bool { match *self { ConstValue::ZeroSized | ConstValue::Scalar(Scalar::Int(_)) => return false, - ConstValue::Scalar(Scalar::Ptr(..)) => return true, - // It's hard to find out the part of the allocation we point to; - // just conservatively check everything. - ConstValue::Slice { alloc_id, meta: _ } => { - !tcx.global_alloc(alloc_id).unwrap_memory().inner().provenance().ptrs().is_empty() + ConstValue::Scalar(Scalar::Ptr(..)) | ConstValue::Slice { .. } => return true, + ConstValue::Indirect { alloc_id, offset } => { + let allocation = tcx.global_alloc(alloc_id).unwrap_memory().inner(); + let end = if let Some(size) = size { offset + size } else { allocation.size() }; + let provenance_map = allocation.provenance(); + !provenance_map.range_empty(AllocRange::from(offset..end), &tcx) } - ConstValue::Indirect { alloc_id, offset } => !tcx - .global_alloc(alloc_id) - .unwrap_memory() - .inner() - .provenance() - .range_empty(AllocRange::from(offset..offset + size), &tcx), } } @@ -475,35 +470,26 @@ impl<'tcx> Const<'tcx> { Self::Val(val, ty) } - /// Return true if any evaluation of this constant always returns the same value, - /// taking into account even pointer identity tests. + /// Return true if any evaluation of this constant in the same MIR body + /// always returns the same value, taking into account even pointer identity tests. + /// + /// In other words, this answers: is "cloning" the mir::ConstOperand ok? pub fn is_deterministic(&self) -> bool { // Primitive types cannot contain provenance and always have the same value. if self.ty().is_primitive() { return true; } - // Some constants may generate fresh allocations for pointers they contain, - // so using the same constant twice can yield two different results. - // Notably, valtrees purposefully generate new allocations. match self { - Const::Ty(_, c) => match c.kind() { - ty::ConstKind::Param(..) => true, - // A valtree may be a reference. Valtree references correspond to a - // different allocation each time they are evaluated. Valtrees for primitive - // types are fine though. - ty::ConstKind::Value(..) - | ty::ConstKind::Expr(..) - | ty::ConstKind::Unevaluated(..) - // This can happen if evaluation of a constant failed. The result does not matter - // much since compilation is doomed. - | ty::ConstKind::Error(..) => false, - // Should not appear in runtime MIR. - ty::ConstKind::Infer(..) - | ty::ConstKind::Bound(..) - | ty::ConstKind::Placeholder(..) => bug!(), - }, + // Some constants may generate fresh allocations for pointers they contain, + // so using the same constant twice can yield two different results. + // Notably, valtrees purposefully generate new allocations. + Const::Ty(..) => false, + // We do not know the contents, so don't attempt to do anything clever. Const::Unevaluated(..) => false, + // When an evaluated contant contains provenance, it is encoded as an `AllocId`. + // Cloning the constant will reuse the same `AllocId`. If this is in the same MIR + // body, this same `AllocId` will result in the same pointer in codegen. Const::Val(..) => true, } } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index cb9b8b5d7f0ce..3dc827dac47f0 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1862,7 +1862,7 @@ impl<'tcx> VnState<'_, '_, 'tcx> { // Check that we do not leak a pointer. // Those pointers may lose part of their identity in codegen. // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed. - assert!(!value.may_have_provenance(self.tcx, op.layout.size)); + assert!(!value.may_have_provenance(self.tcx, Some(op.layout.size))); Some(Const::Val(value, op.layout.ty)) } From 92e2d078c008f7345dcd8d0c67a92c6c1d20d4c1 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Wed, 17 Dec 2025 00:37:19 +0000 Subject: [PATCH 10/29] Move methods to gvn.rs. --- compiler/rustc_middle/src/mir/consts.rs | 39 ------------- compiler/rustc_mir_transform/src/gvn.rs | 75 +++++++++++++++++++++---- 2 files changed, 63 insertions(+), 51 deletions(-) diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 5949eaf2efcd6..de3ef6deca1fc 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -181,21 +181,6 @@ impl ConstValue { Some(data.inner().inspect_with_uninit_and_ptr_outside_interpreter(start..end)) } - /// Check if a constant may contain provenance information. This is used by MIR opts. - /// Can return `true` even if there is no provenance. - pub fn may_have_provenance(&self, tcx: TyCtxt<'_>, size: Option) -> bool { - match *self { - ConstValue::ZeroSized | ConstValue::Scalar(Scalar::Int(_)) => return false, - ConstValue::Scalar(Scalar::Ptr(..)) | ConstValue::Slice { .. } => return true, - ConstValue::Indirect { alloc_id, offset } => { - let allocation = tcx.global_alloc(alloc_id).unwrap_memory().inner(); - let end = if let Some(size) = size { offset + size } else { allocation.size() }; - let provenance_map = allocation.provenance(); - !provenance_map.range_empty(AllocRange::from(offset..end), &tcx) - } - } - } - /// Check if a constant only contains uninitialized bytes. pub fn all_bytes_uninit(&self, tcx: TyCtxt<'_>) -> bool { let ConstValue::Indirect { alloc_id, .. } = self else { @@ -469,30 +454,6 @@ impl<'tcx> Const<'tcx> { let val = ConstValue::Scalar(s); Self::Val(val, ty) } - - /// Return true if any evaluation of this constant in the same MIR body - /// always returns the same value, taking into account even pointer identity tests. - /// - /// In other words, this answers: is "cloning" the mir::ConstOperand ok? - pub fn is_deterministic(&self) -> bool { - // Primitive types cannot contain provenance and always have the same value. - if self.ty().is_primitive() { - return true; - } - - match self { - // Some constants may generate fresh allocations for pointers they contain, - // so using the same constant twice can yield two different results. - // Notably, valtrees purposefully generate new allocations. - Const::Ty(..) => false, - // We do not know the contents, so don't attempt to do anything clever. - Const::Unevaluated(..) => false, - // When an evaluated contant contains provenance, it is encoded as an `AllocId`. - // Cloning the constant will reuse the same `AllocId`. If this is in the same MIR - // body, this same `AllocId` will result in the same pointer in codegen. - Const::Val(..) => true, - } - } } /// An unevaluated (potentially generic) constant used in MIR. diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 3dc827dac47f0..3caec75d80c5a 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -61,10 +61,10 @@ //! The evaluated form is inserted in `evaluated` as an `OpTy` or `None` if evaluation failed. //! //! The difficulty is non-deterministic evaluation of MIR constants. Some `Const` can have -//! different runtime values each time they are evaluated. This is the case with -//! `Const::Slice` which have a new pointer each time they are evaluated, and constants that -//! contain a fn pointer (`AllocId` pointing to a `GlobalAlloc::Function`) pointing to a different -//! symbol in each codegen unit. +//! different runtime values each time they are evaluated. This used to be the case with +//! `ConstValue::Slice` which have a new pointer each time they are evaluated, and is still the +//! case with valtrees that generate a new allocation each time they are used. This is checked by +//! `is_deterministic`. //! //! Meanwhile, we want to be able to read indirect constants. For instance: //! ``` @@ -81,8 +81,11 @@ //! may be non-deterministic. When that happens, we assign a disambiguator to ensure that we do not //! merge the constants. See `duplicate_slice` test in `gvn.rs`. //! -//! Second, when writing constants in MIR, we do not write `Const::Slice` or `Const` -//! that contain `AllocId`s. +//! Conversely, some constants cannot cross crate boundaries, which could happen because of +//! inlining. For instance, constants that contain a fn pointer (`AllocId` pointing to a +//! `GlobalAlloc::Function`) point to a different symbol in each codegen unit. To avoid this, +//! when writing constants in MIR, we do not write `Const`s that contain `AllocId`s. This is +//! checked by `may_have_provenance`. use std::borrow::Cow; use std::hash::{Hash, Hasher}; @@ -103,7 +106,7 @@ use rustc_hir::def::DefKind; use rustc_index::bit_set::DenseBitSet; use rustc_index::{IndexVec, newtype_index}; use rustc_middle::bug; -use rustc_middle::mir::interpret::GlobalAlloc; +use rustc_middle::mir::interpret::{AllocRange, GlobalAlloc}; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; use rustc_middle::ty::layout::HasTypingEnv; @@ -487,7 +490,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { #[instrument(level = "trace", skip(self), ret)] fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex { - if value.is_deterministic() { + if is_deterministic(value) { // The constant is deterministic, no need to disambiguate. let constant = Value::Constant { value, disambiguator: None }; self.insert(value.ty(), constant) @@ -522,14 +525,14 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { fn insert_bool(&mut self, flag: bool) -> VnIndex { // Booleans are deterministic. let value = Const::from_bool(self.tcx, flag); - debug_assert!(value.is_deterministic()); + debug_assert!(is_deterministic(value)); self.insert(self.tcx.types.bool, Value::Constant { value, disambiguator: None }) } fn insert_scalar(&mut self, ty: Ty<'tcx>, scalar: Scalar) -> VnIndex { // Scalars are deterministic. let value = Const::from_scalar(self.tcx, scalar, ty); - debug_assert!(value.is_deterministic()); + debug_assert!(is_deterministic(value)); self.insert(ty, Value::Constant { value, disambiguator: None }) } @@ -1729,6 +1732,45 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { } } +/// Return true if any evaluation of this constant in the same MIR body +/// always returns the same value, taking into account even pointer identity tests. +/// +/// In other words, this answers: is "cloning" the `Const` ok? +fn is_deterministic(c: Const<'_>) -> bool { + // Primitive types cannot contain provenance and always have the same value. + if c.ty().is_primitive() { + return true; + } + + match c { + // Some constants may generate fresh allocations for pointers they contain, + // so using the same constant twice can yield two different results. + // Notably, valtrees purposefully generate new allocations. + Const::Ty(..) => false, + // We do not know the contents, so don't attempt to do anything clever. + Const::Unevaluated(..) => false, + // When an evaluated constant contains provenance, it is encoded as an `AllocId`. + // Cloning the constant will reuse the same `AllocId`. If this is in the same MIR + // body, this same `AllocId` will result in the same pointer in codegen. + Const::Val(..) => true, + } +} + +/// Check if a constant may contain provenance information. +/// Can return `true` even if there is no provenance. +fn may_have_provenance(tcx: TyCtxt<'_>, value: ConstValue, size: Size) -> bool { + match value { + ConstValue::ZeroSized | ConstValue::Scalar(Scalar::Int(_)) => return false, + ConstValue::Scalar(Scalar::Ptr(..)) | ConstValue::Slice { .. } => return true, + ConstValue::Indirect { alloc_id, offset } => !tcx + .global_alloc(alloc_id) + .unwrap_memory() + .inner() + .provenance() + .range_empty(AllocRange::from(offset..offset + size), &tcx), + } +} + fn op_to_prop_const<'tcx>( ecx: &mut InterpCx<'tcx, DummyMachine>, op: &OpTy<'tcx>, @@ -1804,7 +1846,16 @@ fn op_to_prop_const<'tcx>( // Check that we do not leak a pointer. // Those pointers may lose part of their identity in codegen. // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed. - if ecx.tcx.global_alloc(alloc_id).unwrap_memory().inner().provenance().ptrs().is_empty() { + if ecx + .tcx + .global_alloc(alloc_id) + .unwrap_memory() + .inner() + .provenance() + .provenances() + .next() + .is_none() + { return Some(value); } @@ -1862,7 +1913,7 @@ impl<'tcx> VnState<'_, '_, 'tcx> { // Check that we do not leak a pointer. // Those pointers may lose part of their identity in codegen. // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed. - assert!(!value.may_have_provenance(self.tcx, Some(op.layout.size))); + assert!(!may_have_provenance(self.tcx, value, op.layout.size)); Some(Const::Val(value, op.layout.ty)) } From 6f0004f9f5744ed1325780053ecc7be16880286b Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sat, 10 Jan 2026 14:32:39 +0000 Subject: [PATCH 11/29] Update comment. --- compiler/rustc_mir_transform/src/gvn.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 3caec75d80c5a..8ceb9cc6a44a1 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -61,10 +61,8 @@ //! The evaluated form is inserted in `evaluated` as an `OpTy` or `None` if evaluation failed. //! //! The difficulty is non-deterministic evaluation of MIR constants. Some `Const` can have -//! different runtime values each time they are evaluated. This used to be the case with -//! `ConstValue::Slice` which have a new pointer each time they are evaluated, and is still the -//! case with valtrees that generate a new allocation each time they are used. This is checked by -//! `is_deterministic`. +//! different runtime values each time they are evaluated. This happens with valtrees that +//! generate a new allocation each time they are used. This is checked by `is_deterministic`. //! //! Meanwhile, we want to be able to read indirect constants. For instance: //! ``` @@ -81,7 +79,7 @@ //! may be non-deterministic. When that happens, we assign a disambiguator to ensure that we do not //! merge the constants. See `duplicate_slice` test in `gvn.rs`. //! -//! Conversely, some constants cannot cross crate boundaries, which could happen because of +//! Conversely, some constants cannot cross function boundaries, which could happen because of //! inlining. For instance, constants that contain a fn pointer (`AllocId` pointing to a //! `GlobalAlloc::Function`) point to a different symbol in each codegen unit. To avoid this, //! when writing constants in MIR, we do not write `Const`s that contain `AllocId`s. This is From 918a98ab548ced011bbc231090d65f96b4331f40 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sat, 10 Jan 2026 14:33:15 +0000 Subject: [PATCH 12/29] Correct issue number. --- compiler/rustc_mir_transform/src/gvn.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 8ceb9cc6a44a1..ae3b2507395ec 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -83,7 +83,8 @@ //! inlining. For instance, constants that contain a fn pointer (`AllocId` pointing to a //! `GlobalAlloc::Function`) point to a different symbol in each codegen unit. To avoid this, //! when writing constants in MIR, we do not write `Const`s that contain `AllocId`s. This is -//! checked by `may_have_provenance`. +//! checked by `may_have_provenance`. See https://github.com/rust-lang/rust/issues/128775 for more +//! information. use std::borrow::Cow; use std::hash::{Hash, Hasher}; @@ -1800,7 +1801,7 @@ fn op_to_prop_const<'tcx>( if !scalar.try_to_scalar_int().is_ok() { // Check that we do not leak a pointer. // Those pointers may lose part of their identity in codegen. - // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed. + // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/128775 is fixed. return None; } return Some(ConstValue::Scalar(scalar)); @@ -1812,7 +1813,7 @@ fn op_to_prop_const<'tcx>( let (size, _align) = ecx.size_and_align_of_val(&mplace).discard_err()??; // Do not try interning a value that contains provenance. - // Due to https://github.com/rust-lang/rust/issues/79738, doing so could lead to bugs. + // Due to https://github.com/rust-lang/rust/issues/128775, doing so could lead to bugs. // FIXME: remove this hack once that issue is fixed. let alloc_ref = ecx.get_ptr_alloc(mplace.ptr(), size).discard_err()??; if alloc_ref.has_provenance() { @@ -1843,7 +1844,7 @@ fn op_to_prop_const<'tcx>( // Check that we do not leak a pointer. // Those pointers may lose part of their identity in codegen. - // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed. + // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/128775 is fixed. if ecx .tcx .global_alloc(alloc_id) @@ -1910,7 +1911,7 @@ impl<'tcx> VnState<'_, '_, 'tcx> { // Check that we do not leak a pointer. // Those pointers may lose part of their identity in codegen. - // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed. + // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/128775 is fixed. assert!(!may_have_provenance(self.tcx, value, op.layout.size)); Some(Const::Val(value, op.layout.ty)) From b56e5b34a18872dffb073b9328139992997ecfb9 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sat, 10 Jan 2026 14:41:12 +0000 Subject: [PATCH 13/29] Use may_have_provenance. --- compiler/rustc_mir_transform/src/gvn.rs | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index ae3b2507395ec..4b8c6d9c8b31a 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1840,25 +1840,7 @@ fn op_to_prop_const<'tcx>( // Everything failed: create a new allocation to hold the data. let alloc_id = ecx.intern_with_temp_alloc(op.layout, |ecx, dest| ecx.copy_op(op, dest)).discard_err()?; - let value = ConstValue::Indirect { alloc_id, offset: Size::ZERO }; - - // Check that we do not leak a pointer. - // Those pointers may lose part of their identity in codegen. - // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/128775 is fixed. - if ecx - .tcx - .global_alloc(alloc_id) - .unwrap_memory() - .inner() - .provenance() - .provenances() - .next() - .is_none() - { - return Some(value); - } - - None + Some(ConstValue::Indirect { alloc_id, offset: Size::ZERO }) } impl<'tcx> VnState<'_, '_, 'tcx> { @@ -1912,7 +1894,9 @@ impl<'tcx> VnState<'_, '_, 'tcx> { // Check that we do not leak a pointer. // Those pointers may lose part of their identity in codegen. // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/128775 is fixed. - assert!(!may_have_provenance(self.tcx, value, op.layout.size)); + if may_have_provenance(self.tcx, value, op.layout.size) { + return None; + } Some(Const::Val(value, op.layout.ty)) } From 223be90a998e638614ca46c89cd54e41bcefb696 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sun, 11 Jan 2026 18:18:31 +0000 Subject: [PATCH 14/29] Tidy. --- compiler/rustc_mir_transform/src/gvn.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 4b8c6d9c8b31a..6a858fc93261f 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -83,8 +83,8 @@ //! inlining. For instance, constants that contain a fn pointer (`AllocId` pointing to a //! `GlobalAlloc::Function`) point to a different symbol in each codegen unit. To avoid this, //! when writing constants in MIR, we do not write `Const`s that contain `AllocId`s. This is -//! checked by `may_have_provenance`. See https://github.com/rust-lang/rust/issues/128775 for more -//! information. +//! checked by `may_have_provenance`. See for +//! more information. use std::borrow::Cow; use std::hash::{Hash, Hasher}; From 3f4ce076f225c4be3c94bcb5b5d29c186258e9ee Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Tue, 17 Feb 2026 10:12:42 -0300 Subject: [PATCH 15/29] Update compiler/rustc_mir_transform/src/gvn.rs Co-authored-by: Ralf Jung --- compiler/rustc_mir_transform/src/gvn.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 6a858fc93261f..2ad5090f484bf 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1735,6 +1735,10 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { /// always returns the same value, taking into account even pointer identity tests. /// /// In other words, this answers: is "cloning" the `Const` ok? +/// +/// This returns `false` for constants that synthesize new `AllocId` when they are instantiated. +/// It is `true` for anything else, since a given `AllocId` *does* have a unique runtime value +/// within the scope of a single MIR body. fn is_deterministic(c: Const<'_>) -> bool { // Primitive types cannot contain provenance and always have the same value. if c.ty().is_primitive() { From f067c75ffcd33e428a409dacee08593b9b95e77b Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 17 Feb 2026 20:40:21 +0100 Subject: [PATCH 16/29] fix typo in `carryless_mul` macro invocation --- library/core/src/num/uint_macros.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index cf79635dcd877..c3e9453f523ef 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -17,8 +17,8 @@ macro_rules! uint_impl { fsh_op = $fsh_op:literal, fshl_result = $fshl_result:literal, fshr_result = $fshr_result:literal, - clmul_lhs = $clmul_rhs:literal, - clmul_rhs = $clmul_lhs:literal, + clmul_lhs = $clmul_lhs:literal, + clmul_rhs = $clmul_rhs:literal, clmul_result = $clmul_result:literal, swap_op = $swap_op:literal, swapped = $swapped:literal, From 6314bd72db4badf597b584b33a00967cc7f590f1 Mon Sep 17 00:00:00 2001 From: Tony Kan Date: Wed, 18 Feb 2026 21:52:42 -0800 Subject: [PATCH 17/29] fix(codegen): Use `body_codegen_attrs` for caller in `adjust_target_feature_sig` --- compiler/rustc_middle/src/ty/context.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 94a77ce13c14a..469ad8913a310 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1330,8 +1330,8 @@ impl<'tcx> TyCtxt<'tcx> { caller: DefId, ) -> Option>> { let fun_features = &self.codegen_fn_attrs(fun_def).target_features; - let callee_features = &self.codegen_fn_attrs(caller).target_features; - if self.is_target_feature_call_safe(&fun_features, &callee_features) { + let caller_features = &self.body_codegen_attrs(caller).target_features; + if self.is_target_feature_call_safe(&fun_features, &caller_features) { return Some(fun_sig.map_bound(|sig| ty::FnSig { safety: hir::Safety::Safe, ..sig })); } None From 034f7f058a0ae2a18e7775c7c05982af99fac13b Mon Sep 17 00:00:00 2001 From: Tony Kan Date: Wed, 18 Feb 2026 21:52:55 -0800 Subject: [PATCH 18/29] test(codegen): Add regression test for issue #152340 --- .../const-target-feature-fn-ptr-coercion.rs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 tests/ui/target-feature/const-target-feature-fn-ptr-coercion.rs diff --git a/tests/ui/target-feature/const-target-feature-fn-ptr-coercion.rs b/tests/ui/target-feature/const-target-feature-fn-ptr-coercion.rs new file mode 100644 index 0000000000000..380de2412b728 --- /dev/null +++ b/tests/ui/target-feature/const-target-feature-fn-ptr-coercion.rs @@ -0,0 +1,33 @@ +//@ only-x86_64 +//@ check-pass +// +// Regression test for . + +#![allow(dead_code)] + +#[target_feature(enable = "sse2")] +const fn foo() {} + +// DefKind::Const +const _: () = unsafe { + let _: unsafe fn() = foo; +}; + +// DefKind::AssocConst +struct S; +impl S { + const C: () = unsafe { + let _: unsafe fn() = foo; + }; +} + +// DefKind::InlineConst +fn bar() { + let _ = const { + unsafe { + let _: unsafe fn() = foo; + } + }; +} + +fn main() {} From c316bdf2930f636ef9c2ef06c42ed6f6d74da5bc Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 29 Jan 2026 18:50:11 +0100 Subject: [PATCH 19/29] don't use env with infer vars --- .../rustc_trait_selection/src/traits/coherence.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index fc628e78a3e23..43a351fd14fba 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -505,15 +505,12 @@ fn impl_intersection_has_negative_obligation( assert_eq!(root_universe, ty::UniverseIndex::ROOT); let impl1_header = fresh_impl_header(infcx, impl1_def_id, is_of_trait); - let param_env = - ty::EarlyBinder::bind(tcx.param_env(impl1_def_id)).instantiate(tcx, impl1_header.impl_args); - let impl2_header = fresh_impl_header(infcx, impl2_def_id, is_of_trait); // Equate the headers to find their intersection (the general type, with infer vars, // that may apply both impls). let Some(equate_obligations) = - equate_impl_headers(infcx, param_env, &impl1_header, &impl2_header) + equate_impl_headers(infcx, ty::ParamEnv::empty(), &impl1_header, &impl2_header) else { return false; }; @@ -531,7 +528,11 @@ fn impl_intersection_has_negative_obligation( root_universe, (impl1_header.impl_args, impl2_header.impl_args), ); - let param_env = infcx.resolve_vars_if_possible(param_env); + + assert!(!impl1_header.impl_args.has_non_region_infer()); + let impl1_header_args = infcx.resolve_vars_if_possible(impl1_header.impl_args); + let param_env = + ty::EarlyBinder::bind(tcx.param_env(impl1_def_id)).instantiate(tcx, impl1_header_args); util::elaborate(tcx, tcx.predicates_of(impl2_def_id).instantiate(tcx, impl2_header.impl_args)) .elaborate_sized() From 642d9c430b339a0998a013abf2081108038241c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Thu, 19 Feb 2026 17:21:24 +0100 Subject: [PATCH 20/29] modernize comments about typing modes --- .../rustc_trait_selection/src/traits/coherence.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 43a351fd14fba..484b16a1296d8 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -498,8 +498,8 @@ fn impl_intersection_has_negative_obligation( ) -> bool { debug!("negative_impl(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id); - // N.B. We need to unify impl headers *with* intercrate mode, even if proving negative predicates - // do not need intercrate mode enabled. + // N.B. We need to unify impl headers *with* `TypingMode::Coherence`, + // even if proving negative predicates doesn't need `TypingMode::Coherence`. let ref infcx = tcx.infer_ctxt().with_next_trait_solver(true).build(TypingMode::Coherence); let root_universe = infcx.universe(); assert_eq!(root_universe, ty::UniverseIndex::ROOT); @@ -529,8 +529,13 @@ fn impl_intersection_has_negative_obligation( (impl1_header.impl_args, impl2_header.impl_args), ); - assert!(!impl1_header.impl_args.has_non_region_infer()); + // Right above we plug inference variables with placeholders, + // this gets us new impl1_header_args with the inference variables actually resolved + // to those placeholders. let impl1_header_args = infcx.resolve_vars_if_possible(impl1_header.impl_args); + // So there are no infer variables left now, except regions which aren't resolved by `resolve_vars_if_possible`. + assert!(!impl1_header_args.has_non_region_infer()); + let param_env = ty::EarlyBinder::bind(tcx.param_env(impl1_def_id)).instantiate(tcx, impl1_header_args); From fb811f8d537625f4442e8c00c47eeb2d67e0dda4 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 20 Feb 2026 13:27:06 +0000 Subject: [PATCH 21/29] Enable rust.remap-debuginfo in the dist profile Anyone who distributes rustc almost certainly wants to enable this option. It is necessary for reproducibility and for a distributed rustc local paths are useless anyway. And finally it improves privacy if you distribute a local build. --- src/bootstrap/defaults/bootstrap.dist.toml | 2 ++ src/ci/run.sh | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/defaults/bootstrap.dist.toml b/src/bootstrap/defaults/bootstrap.dist.toml index bb0592ce947ab..cce3f068aabcd 100644 --- a/src/bootstrap/defaults/bootstrap.dist.toml +++ b/src/bootstrap/defaults/bootstrap.dist.toml @@ -24,6 +24,8 @@ channel = "auto-detect" download-rustc = false # Build the llvm-bitcode-linker llvm-bitcode-linker = true +# Required to make builds reproducible. +remap-debuginfo = true [dist] # Use better compression when preparing tarballs. diff --git a/src/ci/run.sh b/src/ci/run.sh index b486f0525f40d..215292965e65d 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -116,7 +116,6 @@ if [ "$DEPLOY$DEPLOY_ALT" = "1" ]; then else RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-llvm-static-stdcpp" fi - RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.remap-debuginfo" if [ "$DEPLOY_ALT" != "" ] && isLinux; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --debuginfo-level=2" @@ -139,6 +138,8 @@ if [ "$DEPLOY$DEPLOY_ALT" = "1" ]; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-debug-assertions" fi else + RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.remap-debuginfo=false" + # We almost always want debug assertions enabled, but sometimes this takes too # long for too little benefit, so we just turn them off. if [ "$NO_DEBUG_ASSERTIONS" = "" ]; then From 7df748542c53e937613ce0f85ab813467ed62eaa Mon Sep 17 00:00:00 2001 From: Brent Kerby Date: Fri, 20 Feb 2026 09:24:06 -0700 Subject: [PATCH 22/29] Add build.rustdoc option to bootstrap config --- bootstrap.example.toml | 5 +++++ src/bootstrap/src/core/config/config.rs | 6 ++++++ src/bootstrap/src/core/config/toml/build.rs | 1 + src/bootstrap/src/lib.rs | 4 +--- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/bootstrap.example.toml b/bootstrap.example.toml index e0cbb0c0e747c..662bcc5d61e75 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -302,6 +302,11 @@ # If you set this, you likely want to set `cargo` as well. #build.rustc = "/path/to/rustc" +# Use this rustdoc binary as the stage0 snapshot rustdoc. +# If unspecified, then the binary "rustdoc" (with platform-specific extension, e.g. ".exe") +# in the same directory as "rustc" will be used. +#build.rustdoc = "/path/to/rustdoc" + # Instead of downloading the src/stage0 version of rustfmt specified, # use this rustfmt binary instead as the stage0 snapshot rustfmt. #build.rustfmt = "/path/to/rustfmt" diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index bc68bfe396425..e975d71c8c70b 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -298,6 +298,7 @@ pub struct Config { // These are either the stage0 downloaded binaries or the locally installed ones. pub initial_cargo: PathBuf, pub initial_rustc: PathBuf, + pub initial_rustdoc: PathBuf, pub initial_cargo_clippy: Option, pub initial_sysroot: PathBuf, pub initial_rustfmt: Option, @@ -456,6 +457,7 @@ impl Config { build_dir: build_build_dir, cargo: mut build_cargo, rustc: mut build_rustc, + rustdoc: build_rustdoc, rustfmt: build_rustfmt, cargo_clippy: build_cargo_clippy, docs: build_docs, @@ -751,6 +753,9 @@ impl Config { default_stage0_rustc_path(&out) }); + let initial_rustdoc = build_rustdoc + .unwrap_or_else(|| initial_rustc.with_file_name(exe("rustdoc", host_target))); + let initial_sysroot = t!(PathBuf::from_str( command(&initial_rustc) .args(["--print", "sysroot"]) @@ -1348,6 +1353,7 @@ impl Config { initial_cargo, initial_cargo_clippy: build_cargo_clippy, initial_rustc, + initial_rustdoc, initial_rustfmt, initial_sysroot, jemalloc: rust_jemalloc.unwrap_or(false), diff --git a/src/bootstrap/src/core/config/toml/build.rs b/src/bootstrap/src/core/config/toml/build.rs index 192f875587209..27bf753f6914d 100644 --- a/src/bootstrap/src/core/config/toml/build.rs +++ b/src/bootstrap/src/core/config/toml/build.rs @@ -25,6 +25,7 @@ define_config! { build_dir: Option = "build-dir", cargo: Option = "cargo", rustc: Option = "rustc", + rustdoc: Option = "rustdoc", rustfmt: Option = "rustfmt", cargo_clippy: Option = "cargo-clippy", docs: Option = "docs", diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index ec5c6fbe1d01d..b9a914f53cec1 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -535,9 +535,7 @@ impl Build { initial_lld, initial_relative_libdir, initial_rustc: config.initial_rustc.clone(), - initial_rustdoc: config - .initial_rustc - .with_file_name(exe("rustdoc", config.host_target)), + initial_rustdoc: config.initial_rustdoc.clone(), initial_cargo: config.initial_cargo.clone(), initial_sysroot: config.initial_sysroot.clone(), local_rebuild: config.local_rebuild, From 6c6f5860f66c390f2477a3f713a9085ad49fe60e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Miku=C5=82a?= Date: Thu, 19 Feb 2026 22:28:52 +0100 Subject: [PATCH 23/29] Fix warnings in rs{begin,end}.rs files As can be seen locally and in CI logs (dist-i686-mingw) that code used to trigger `static_mut_refs` warning. --- library/rtstartup/rsbegin.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/rtstartup/rsbegin.rs b/library/rtstartup/rsbegin.rs index 62d247fafb7ad..6ee06cce5c630 100644 --- a/library/rtstartup/rsbegin.rs +++ b/library/rtstartup/rsbegin.rs @@ -90,12 +90,12 @@ pub mod eh_frames { unsafe extern "C" fn init() { // register unwind info on module startup - __register_frame_info(&__EH_FRAME_BEGIN__ as *const u8, &mut OBJ as *mut _ as *mut u8); + __register_frame_info(&__EH_FRAME_BEGIN__ as *const u8, &raw mut OBJ as *mut u8); } unsafe extern "C" fn uninit() { // unregister on shutdown - __deregister_frame_info(&__EH_FRAME_BEGIN__ as *const u8, &mut OBJ as *mut _ as *mut u8); + __deregister_frame_info(&__EH_FRAME_BEGIN__ as *const u8, &raw mut OBJ as *mut u8); } // MinGW-specific init/uninit routine registration From d6ff921cd940e6500f4e477c164627e129535b72 Mon Sep 17 00:00:00 2001 From: Mahdi Ali-Raihan Date: Thu, 19 Feb 2026 14:53:10 -0500 Subject: [PATCH 24/29] Fixed ByteStr not padding within its Display trait when no specific alignment is not mentioned (e.g. ':10' instead of ':<10', ':>10', or ':^1') --- library/core/src/bstr/mod.rs | 35 +++++++++++++++++------------------ library/std/tests/path.rs | 20 ++++++++++++++++++++ 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/library/core/src/bstr/mod.rs b/library/core/src/bstr/mod.rs index 34e1ea66c99ad..2be7dfc9bfdda 100644 --- a/library/core/src/bstr/mod.rs +++ b/library/core/src/bstr/mod.rs @@ -174,39 +174,38 @@ impl fmt::Debug for ByteStr { #[unstable(feature = "bstr", issue = "134915")] impl fmt::Display for ByteStr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fn fmt_nopad(this: &ByteStr, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for chunk in this.utf8_chunks() { - f.write_str(chunk.valid())?; - if !chunk.invalid().is_empty() { - f.write_str("\u{FFFD}")?; - } - } - Ok(()) - } - - let Some(align) = f.align() else { - return fmt_nopad(self, f); - }; let nchars: usize = self .utf8_chunks() .map(|chunk| { chunk.valid().chars().count() + if chunk.invalid().is_empty() { 0 } else { 1 } }) .sum(); + let padding = f.width().unwrap_or(0).saturating_sub(nchars); let fill = f.fill(); - let (lpad, rpad) = match align { - fmt::Alignment::Left => (0, padding), - fmt::Alignment::Right => (padding, 0), - fmt::Alignment::Center => { + + let (lpad, rpad) = match f.align() { + Some(fmt::Alignment::Right) => (padding, 0), + Some(fmt::Alignment::Center) => { let half = padding / 2; (half, half + padding % 2) } + // Either alignment is not specified or it's left aligned + // which behaves the same with padding + _ => (0, padding), }; + for _ in 0..lpad { write!(f, "{fill}")?; } - fmt_nopad(self, f)?; + + for chunk in self.utf8_chunks() { + f.write_str(chunk.valid())?; + if !chunk.invalid().is_empty() { + f.write_str("\u{FFFD}")?; + } + } + for _ in 0..rpad { write!(f, "{fill}")?; } diff --git a/library/std/tests/path.rs b/library/std/tests/path.rs index 4094b7acd8749..8997b8ad192dc 100644 --- a/library/std/tests/path.rs +++ b/library/std/tests/path.rs @@ -2291,6 +2291,26 @@ fn display_format_flags() { assert_eq!(format!("a{:#<5}b", Path::new("a").display()), "aa####b"); } +#[test] +fn display_path_with_padding_no_align() { + assert_eq!(format!("{:10}", Path::new("/foo/bar").display()), "/foo/bar "); +} + +#[test] +fn display_path_with_padding_align_left() { + assert_eq!(format!("{:<10}", Path::new("/foo/bar").display()), "/foo/bar "); +} + +#[test] +fn display_path_with_padding_align_right() { + assert_eq!(format!("{:>10}", Path::new("/foo/bar").display()), " /foo/bar"); +} + +#[test] +fn display_path_with_padding_align_center() { + assert_eq!(format!("{:^10}", Path::new("/foo/bar").display()), " /foo/bar "); +} + #[test] fn into_rc() { let orig = "hello/world"; From bb2d55e2ec072e71e423037d842101e5dcf6dbb4 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 21 Feb 2026 12:46:53 +0100 Subject: [PATCH 25/29] Add new `MultiSpan` methods: `push_primary_span`, `push_span_diag` and `span_labels_raw` --- compiler/rustc_error_messages/src/lib.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 0b30102eb992b..42d41421355a8 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -316,10 +316,18 @@ impl MultiSpan { MultiSpan { primary_spans: vec, span_labels: vec![] } } + pub fn push_primary_span(&mut self, primary_span: Span) { + self.primary_spans.push(primary_span); + } + pub fn push_span_label(&mut self, span: Span, label: impl Into) { self.span_labels.push((span, label.into())); } + pub fn push_span_diag(&mut self, span: Span, diag: DiagMessage) { + self.span_labels.push((span, diag)); + } + /// Selects the first primary span (if any). pub fn primary_span(&self) -> Option { self.primary_spans.first().cloned() @@ -386,6 +394,11 @@ impl MultiSpan { span_labels } + /// Returns the span labels as contained by `MultiSpan`. + pub fn span_labels_raw(&self) -> &[(Span, DiagMessage)] { + &self.span_labels + } + /// Returns `true` if any of the span labels is displayable. pub fn has_span_labels(&self) -> bool { self.span_labels.iter().any(|(sp, _)| !sp.is_dummy()) From 36116e3c9b98ecca3661ba9455aa55a3d29dd9a9 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 21 Feb 2026 12:49:02 +0100 Subject: [PATCH 26/29] Create new `diag_lint_level` function, aiming to replace `lint_level` once all `LintDiagnostic` items have been removed --- compiler/rustc_middle/src/lint.rs | 204 +++++++++++++++++++++++++++++- 1 file changed, 203 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index e4715f6e2c10b..005531632538f 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -2,7 +2,7 @@ use std::cmp; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sorted_map::SortedMap; -use rustc_errors::{Diag, MultiSpan}; +use rustc_errors::{Diag, Diagnostic, MultiSpan}; use rustc_hir::{HirId, ItemLocalId}; use rustc_lint_defs::EditionFcw; use rustc_macros::{Decodable, Encodable, HashStable}; @@ -472,3 +472,205 @@ pub fn lint_level( } lint_level_impl(sess, lint, level, span, Box::new(decorate)) } + +/// The innermost function for emitting lints implementing the [`trait@Diagnostic`] trait. +/// +/// If you are looking to implement a lint, look for higher level functions, +/// for example: +/// +/// - [`TyCtxt::emit_node_span_lint`] +/// - [`TyCtxt::node_span_lint`] +/// - [`TyCtxt::emit_node_lint`] +/// - [`TyCtxt::node_lint`] +/// - `LintContext::opt_span_lint` +/// +/// This function will replace `lint_level` once all `LintDiagnostic` items have been migrated to +/// `Diagnostic`. +#[track_caller] +pub fn diag_lint_level<'a, D: Diagnostic<'a, ()> + 'a>( + sess: &'a Session, + lint: &'static Lint, + level: LevelAndSource, + span: Option, + decorate: D, +) { + // Avoid codegen bloat from monomorphization by immediately doing dyn dispatch of `decorate` to + // the "real" work. + #[track_caller] + fn diag_lint_level_impl<'a>( + sess: &'a Session, + lint: &'static Lint, + level: LevelAndSource, + span: Option, + decorate: Box< + dyn FnOnce(rustc_errors::DiagCtxtHandle<'a>, rustc_errors::Level) -> Diag<'a, ()> + 'a, + >, + ) { + let LevelAndSource { level, lint_id, src } = level; + + // Check for future incompatibility lints and issue a stronger warning. + let future_incompatible = lint.future_incompatible; + + let has_future_breakage = future_incompatible.map_or( + // Default allow lints trigger too often for testing. + sess.opts.unstable_opts.future_incompat_test && lint.default_level != Level::Allow, + |incompat| incompat.report_in_deps, + ); + + // Convert lint level to error level. + let err_level = match level { + Level::Allow => { + if has_future_breakage { + rustc_errors::Level::Allow + } else { + return; + } + } + Level::Expect => { + // This case is special as we actually allow the lint itself in this context, but + // we can't return early like in the case for `Level::Allow` because we still + // need the lint diagnostic to be emitted to `rustc_error::DiagCtxtInner`. + // + // We can also not mark the lint expectation as fulfilled here right away, as it + // can still be cancelled in the decorate function. All of this means that we simply + // create a `Diag` and continue as we would for warnings. + rustc_errors::Level::Expect + } + Level::ForceWarn => rustc_errors::Level::ForceWarning, + Level::Warn => rustc_errors::Level::Warning, + Level::Deny | Level::Forbid => rustc_errors::Level::Error, + }; + // Finally, run `decorate`. `decorate` can call `trimmed_path_str` (directly or indirectly), + // so we need to make sure when we do call `decorate` that the diagnostic is eventually + // emitted or we'll get a `must_produce_diag` ICE. + // + // When is a diagnostic *eventually* emitted? Well, that is determined by 2 factors: + // 1. If the corresponding `rustc_errors::Level` is beyond warning, i.e. `ForceWarning(_)` + // or `Error`, then the diagnostic will be emitted regardless of CLI options. + // 2. If the corresponding `rustc_errors::Level` is warning, then that can be affected by + // `-A warnings` or `--cap-lints=xxx` on the command line. In which case, the diagnostic + // will be emitted if `can_emit_warnings` is true. + let skip = err_level == rustc_errors::Level::Warning && !sess.dcx().can_emit_warnings(); + + let disable_suggestions = if let Some(ref span) = span + // If this code originates in a foreign macro, aka something that this crate + // did not itself author, then it's likely that there's nothing this crate + // can do about it. We probably want to skip the lint entirely. + && span.primary_spans().iter().any(|s| s.in_external_macro(sess.source_map())) + { + true + } else { + false + }; + + let mut err: Diag<'_, ()> = if !skip { + decorate(sess.dcx(), err_level) + } else { + Diag::new(sess.dcx(), err_level, "") + }; + if let Some(span) = span + && err.span.primary_span().is_none() + { + // We can't use `err.span()` because it overwrites the labels, so we need to do it manually. + for primary in span.primary_spans() { + err.span.push_primary_span(*primary); + } + for (label_span, label) in span.span_labels_raw() { + err.span.push_span_diag(*label_span, label.clone()); + } + } + if let Some(lint_id) = lint_id { + err.lint_id(lint_id); + } + + if disable_suggestions { + // Any suggestions made here are likely to be incorrect, so anything we + // emit shouldn't be automatically fixed by rustfix. + err.disable_suggestions(); + + // If this is a future incompatible that is not an edition fixing lint + // it'll become a hard error, so we have to emit *something*. Also, + // if this lint occurs in the expansion of a macro from an external crate, + // allow individual lints to opt-out from being reported. + let incompatible = future_incompatible.is_some_and(|f| f.reason.edition().is_none()); + + if !incompatible && !lint.report_in_external_macro { + err.cancel(); + + // Don't continue further, since we don't want to have + // `diag_span_note_once` called for a diagnostic that isn't emitted. + return; + } + } + + err.is_lint(lint.name_lower(), has_future_breakage); + // Lint diagnostics that are covered by the expect level will not be emitted outside + // the compiler. It is therefore not necessary to add any information for the user. + // This will therefore directly call the decorate function which will in turn emit + // the diagnostic. + if let Level::Expect = level { + err.emit(); + return; + } + + if let Some(future_incompatible) = future_incompatible { + let explanation = match future_incompatible.reason { + FutureIncompatibilityReason::FutureReleaseError(_) => { + "this was previously accepted by the compiler but is being phased out; \ + it will become a hard error in a future release!" + .to_owned() + } + FutureIncompatibilityReason::FutureReleaseSemanticsChange(_) => { + "this will change its meaning in a future release!".to_owned() + } + FutureIncompatibilityReason::EditionError(EditionFcw { edition, .. }) => { + let current_edition = sess.edition(); + format!( + "this is accepted in the current edition (Rust {current_edition}) but is a hard error in Rust {edition}!" + ) + } + FutureIncompatibilityReason::EditionSemanticsChange(EditionFcw { + edition, .. + }) => { + format!("this changes meaning in Rust {edition}") + } + FutureIncompatibilityReason::EditionAndFutureReleaseError(EditionFcw { + edition, + .. + }) => { + format!( + "this was previously accepted by the compiler but is being phased out; \ + it will become a hard error in Rust {edition} and in a future release in all editions!" + ) + } + FutureIncompatibilityReason::EditionAndFutureReleaseSemanticsChange( + EditionFcw { edition, .. }, + ) => { + format!( + "this changes meaning in Rust {edition} and in a future release in all editions!" + ) + } + FutureIncompatibilityReason::Custom(reason, _) => reason.to_owned(), + FutureIncompatibilityReason::Unreachable => unreachable!(), + }; + + if future_incompatible.explain_reason { + err.warn(explanation); + } + + let citation = + format!("for more information, see {}", future_incompatible.reason.reference()); + err.note(citation); + } + + explain_lint_level_source(sess, lint, level, src, &mut err); + err.emit(); + } + diag_lint_level_impl( + sess, + lint, + level, + span, + Box::new(move |dcx, level| decorate.into_diag(dcx, level)), + ); +} From 4f23c485ac1f11ffa56f647db04f91e64085aba7 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 21 Feb 2026 12:49:24 +0100 Subject: [PATCH 27/29] Migrate `LinkerOutput` lint to `Diagnostic` --- compiler/rustc_codegen_ssa/src/back/link.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index acc1c0b9f0de8..388503c720532 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -18,18 +18,18 @@ use rustc_attr_parsing::eval_config_entry; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::temp_dir::MaybeTempDir; -use rustc_errors::{DiagCtxtHandle, LintDiagnostic}; +use rustc_errors::DiagCtxtHandle; use rustc_fs_util::{TempDirBuilder, fix_windows_verbatim_for_gcc, try_canonicalize}; use rustc_hir::attrs::NativeLibKind; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; -use rustc_macros::LintDiagnostic; +use rustc_macros::Diagnostic; use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file}; use rustc_metadata::{ EncodedMetadata, NativeLibSearchFallback, find_native_static_library, walk_native_lib_search_dirs, }; use rustc_middle::bug; -use rustc_middle::lint::lint_level; +use rustc_middle::lint::diag_lint_level; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::SymbolExportKind; @@ -662,7 +662,7 @@ fn link_dwarf_object(sess: &Session, cg_results: &CodegenResults, executable_out } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("{$inner}")] /// Translating this is kind of useless. We don't pass translation flags to the linker, so we'd just /// end up with inconsistent languages within the same diagnostic. @@ -938,9 +938,7 @@ fn link_natively( let level = codegen_results.crate_info.lint_levels.linker_messages; let lint = |msg| { - lint_level(sess, LINKER_MESSAGES, level, None, |diag| { - LinkerOutput { inner: msg }.decorate_lint(diag) - }) + diag_lint_level(sess, LINKER_MESSAGES, level, None, LinkerOutput { inner: msg }); }; if !prog.stderr.is_empty() { From 223c253d83e40ed7772145e24470dc7cd0a06b20 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Sat, 21 Feb 2026 12:56:50 +0000 Subject: [PATCH 28/29] remove unneeded reboxing --- compiler/rustc_ast/src/ast.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 738435891f16f..026758958f04f 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -4211,9 +4211,7 @@ impl ForeignItemKind { impl From for ItemKind { fn from(foreign_item_kind: ForeignItemKind) -> ItemKind { match foreign_item_kind { - ForeignItemKind::Static(box static_foreign_item) => { - ItemKind::Static(Box::new(static_foreign_item)) - } + ForeignItemKind::Static(static_foreign_item) => ItemKind::Static(static_foreign_item), ForeignItemKind::Fn(fn_kind) => ItemKind::Fn(fn_kind), ForeignItemKind::TyAlias(ty_alias_kind) => ItemKind::TyAlias(ty_alias_kind), ForeignItemKind::MacCall(a) => ItemKind::MacCall(a), @@ -4226,7 +4224,7 @@ impl TryFrom for ForeignItemKind { fn try_from(item_kind: ItemKind) -> Result { Ok(match item_kind { - ItemKind::Static(box static_item) => ForeignItemKind::Static(Box::new(static_item)), + ItemKind::Static(static_item) => ForeignItemKind::Static(static_item), ItemKind::Fn(fn_kind) => ForeignItemKind::Fn(fn_kind), ItemKind::TyAlias(ty_alias_kind) => ForeignItemKind::TyAlias(ty_alias_kind), ItemKind::MacCall(a) => ForeignItemKind::MacCall(a), From 916f40cf109d7620564153840422a0e4881d5d03 Mon Sep 17 00:00:00 2001 From: joboet Date: Sun, 15 Feb 2026 12:55:04 +0100 Subject: [PATCH 29/29] std: move `exit` out of PAL --- library/std/src/process.rs | 2 +- library/std/src/rt.rs | 2 +- .../std/src/sys/{exit_guard.rs => exit.rs} | 81 ++++++++++++++++++- library/std/src/sys/mod.rs | 2 +- library/std/src/sys/pal/hermit/os.rs | 4 - library/std/src/sys/pal/motor/os.rs | 4 - library/std/src/sys/pal/sgx/os.rs | 4 - library/std/src/sys/pal/solid/os.rs | 4 - library/std/src/sys/pal/teeos/os.rs | 4 - library/std/src/sys/pal/uefi/os.rs | 20 ----- library/std/src/sys/pal/unix/os.rs | 5 -- library/std/src/sys/pal/unsupported/os.rs | 4 - library/std/src/sys/pal/vexos/mod.rs | 1 + library/std/src/sys/pal/vexos/os.rs | 19 ----- library/std/src/sys/pal/wasi/os.rs | 4 - library/std/src/sys/pal/windows/os.rs | 4 - library/std/src/sys/pal/xous/os.rs | 4 - library/std/src/sys/pal/zkvm/os.rs | 4 - 18 files changed, 81 insertions(+), 91 deletions(-) rename library/std/src/sys/{exit_guard.rs => exit.rs} (60%) delete mode 100644 library/std/src/sys/pal/vexos/os.rs diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 6838bb422b0e0..d3f47a01c0ff6 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -2464,7 +2464,7 @@ impl Child { #[cfg_attr(not(test), rustc_diagnostic_item = "process_exit")] pub fn exit(code: i32) -> ! { crate::rt::cleanup(); - crate::sys::os::exit(code) + crate::sys::exit::exit(code) } /// Terminates the process in an abnormal fashion. diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 11c0a0b9daf7b..1e7de695ddae7 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -187,7 +187,7 @@ fn lang_start_internal( cleanup(); // Guard against multiple threads calling `libc::exit` concurrently. // See the documentation for `unique_thread_exit` for more information. - crate::sys::exit_guard::unique_thread_exit(); + crate::sys::exit::unique_thread_exit(); ret_code }) diff --git a/library/std/src/sys/exit_guard.rs b/library/std/src/sys/exit.rs similarity index 60% rename from library/std/src/sys/exit_guard.rs rename to library/std/src/sys/exit.rs index e7d7a478a5baa..53fb92ba077e0 100644 --- a/library/std/src/sys/exit_guard.rs +++ b/library/std/src/sys/exit.rs @@ -19,8 +19,7 @@ cfg_select! { /// * If it is called again on the same thread as the first call, it will abort. /// * If it is called again on a different thread, it will wait in a loop /// (waiting for the process to exit). - #[cfg_attr(any(test, doctest), allow(dead_code))] - pub(crate) fn unique_thread_exit() { + pub fn unique_thread_exit() { use crate::ffi::c_int; use crate::ptr; use crate::sync::atomic::AtomicPtr; @@ -62,9 +61,83 @@ cfg_select! { /// /// Mitigation is ***NOT*** implemented on this platform, either because this platform /// is not affected, or because mitigation is not yet implemented for this platform. - #[cfg_attr(any(test, doctest), allow(dead_code))] - pub(crate) fn unique_thread_exit() { + #[cfg_attr(any(test, doctest), expect(dead_code))] + pub fn unique_thread_exit() { // Mitigation not required on platforms where `exit` is thread-safe. } } } + +pub fn exit(code: i32) -> ! { + cfg_select! { + target_os = "hermit" => { + unsafe { hermit_abi::exit(code) } + } + target_os = "linux" => { + unsafe { + unique_thread_exit(); + libc::exit(code) + } + } + target_os = "motor" => { + moto_rt::process::exit(code) + } + all(target_vendor = "fortanix", target_env = "sgx") => { + crate::sys::pal::abi::exit_with_code(code as _) + } + target_os = "solid_asp3" => { + rtabort!("exit({}) called", code) + } + target_os = "teeos" => { + let _ = code; + panic!("TA should not call `exit`") + } + target_os = "uefi" => { + use r_efi::base::Status; + + use crate::os::uefi::env; + + if let (Some(boot_services), Some(handle)) = + (env::boot_services(), env::try_image_handle()) + { + let boot_services = boot_services.cast::(); + let _ = unsafe { + ((*boot_services.as_ptr()).exit)( + handle.as_ptr(), + Status::from_usize(code as usize), + 0, + crate::ptr::null_mut(), + ) + }; + } + crate::intrinsics::abort() + } + any( + target_family = "unix", + target_os = "wasi", + ) => { + unsafe { libc::exit(code as crate::ffi::c_int) } + } + target_os = "vexos" => { + let _ = code; + + unsafe { + vex_sdk::vexSystemExitRequest(); + + loop { + vex_sdk::vexTasksRun(); + } + } + } + target_os = "windows" => { + unsafe { crate::sys::pal::c::ExitProcess(code as u32) } + } + target_os = "xous" => { + crate::os::xous::ffi::exit(code as u32) + } + _ => { + let _ = code; + crate::intrinsics::abort() + } + } +} diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 5436c144d3330..5ad23972860bb 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -11,7 +11,7 @@ pub mod backtrace; pub mod cmath; pub mod env; pub mod env_consts; -pub mod exit_guard; +pub mod exit; pub mod fd; pub mod fs; pub mod io; diff --git a/library/std/src/sys/pal/hermit/os.rs b/library/std/src/sys/pal/hermit/os.rs index 48a7cdcd2f763..05afb41647872 100644 --- a/library/std/src/sys/pal/hermit/os.rs +++ b/library/std/src/sys/pal/hermit/os.rs @@ -57,10 +57,6 @@ pub fn home_dir() -> Option { None } -pub fn exit(code: i32) -> ! { - unsafe { hermit_abi::exit(code) } -} - pub fn getpid() -> u32 { unsafe { hermit_abi::getpid() as u32 } } diff --git a/library/std/src/sys/pal/motor/os.rs b/library/std/src/sys/pal/motor/os.rs index cdf66e3958dbe..202841a0dbfca 100644 --- a/library/std/src/sys/pal/motor/os.rs +++ b/library/std/src/sys/pal/motor/os.rs @@ -63,10 +63,6 @@ pub fn home_dir() -> Option { None } -pub fn exit(code: i32) -> ! { - moto_rt::process::exit(code) -} - pub fn getpid() -> u32 { panic!("Pids on Motor OS are u64.") } diff --git a/library/std/src/sys/pal/sgx/os.rs b/library/std/src/sys/pal/sgx/os.rs index ba47af7ff88d7..dc6352da7c2e6 100644 --- a/library/std/src/sys/pal/sgx/os.rs +++ b/library/std/src/sys/pal/sgx/os.rs @@ -56,10 +56,6 @@ pub fn home_dir() -> Option { None } -pub fn exit(code: i32) -> ! { - super::abi::exit_with_code(code as _) -} - pub fn getpid() -> u32 { panic!("no pids in SGX") } diff --git a/library/std/src/sys/pal/solid/os.rs b/library/std/src/sys/pal/solid/os.rs index c336a1042da40..aeb1c7f46e52a 100644 --- a/library/std/src/sys/pal/solid/os.rs +++ b/library/std/src/sys/pal/solid/os.rs @@ -63,10 +63,6 @@ pub fn home_dir() -> Option { None } -pub fn exit(code: i32) -> ! { - rtabort!("exit({}) called", code); -} - pub fn getpid() -> u32 { panic!("no pids on this platform") } diff --git a/library/std/src/sys/pal/teeos/os.rs b/library/std/src/sys/pal/teeos/os.rs index a4b1d3c6ae670..72d14ec7fc9df 100644 --- a/library/std/src/sys/pal/teeos/os.rs +++ b/library/std/src/sys/pal/teeos/os.rs @@ -67,10 +67,6 @@ pub fn home_dir() -> Option { None } -pub fn exit(_code: i32) -> ! { - panic!("TA should not call `exit`") -} - pub fn getpid() -> u32 { panic!("no pids on this platform") } diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs index 5b9785c8371e3..7d54bc9aff131 100644 --- a/library/std/src/sys/pal/uefi/os.rs +++ b/library/std/src/sys/pal/uefi/os.rs @@ -1,13 +1,10 @@ -use r_efi::efi::Status; use r_efi::efi::protocols::{device_path, loaded_image_device_path}; use super::{helpers, unsupported_err}; use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; -use crate::os::uefi; use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; use crate::path::{self, PathBuf}; -use crate::ptr::NonNull; use crate::{fmt, io}; const PATHS_SEP: u16 = b';' as u16; @@ -105,23 +102,6 @@ pub fn home_dir() -> Option { None } -pub fn exit(code: i32) -> ! { - if let (Some(boot_services), Some(handle)) = - (uefi::env::boot_services(), uefi::env::try_image_handle()) - { - let boot_services: NonNull = boot_services.cast(); - let _ = unsafe { - ((*boot_services.as_ptr()).exit)( - handle.as_ptr(), - Status::from_usize(code as usize), - 0, - crate::ptr::null_mut(), - ) - }; - } - crate::intrinsics::abort() -} - pub fn getpid() -> u32 { panic!("no pids on this platform") } diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index b8280a8f29a02..494d94433db34 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -533,11 +533,6 @@ pub fn home_dir() -> Option { } } -pub fn exit(code: i32) -> ! { - crate::sys::exit_guard::unique_thread_exit(); - unsafe { libc::exit(code as c_int) } -} - pub fn getpid() -> u32 { unsafe { libc::getpid() as u32 } } diff --git a/library/std/src/sys/pal/unsupported/os.rs b/library/std/src/sys/pal/unsupported/os.rs index cb925ef4348db..99568458184b6 100644 --- a/library/std/src/sys/pal/unsupported/os.rs +++ b/library/std/src/sys/pal/unsupported/os.rs @@ -56,10 +56,6 @@ pub fn home_dir() -> Option { None } -pub fn exit(_code: i32) -> ! { - crate::intrinsics::abort() -} - pub fn getpid() -> u32 { panic!("no pids on this platform") } diff --git a/library/std/src/sys/pal/vexos/mod.rs b/library/std/src/sys/pal/vexos/mod.rs index 16aa3f088f04b..d1380ab8dff14 100644 --- a/library/std/src/sys/pal/vexos/mod.rs +++ b/library/std/src/sys/pal/vexos/mod.rs @@ -1,3 +1,4 @@ +#[path = "../unsupported/os.rs"] pub mod os; #[expect(dead_code)] diff --git a/library/std/src/sys/pal/vexos/os.rs b/library/std/src/sys/pal/vexos/os.rs deleted file mode 100644 index 303b452a078ff..0000000000000 --- a/library/std/src/sys/pal/vexos/os.rs +++ /dev/null @@ -1,19 +0,0 @@ -#[expect(dead_code)] -#[path = "../unsupported/os.rs"] -mod unsupported_os; -pub use unsupported_os::{ - JoinPathsError, SplitPaths, chdir, current_exe, getcwd, getpid, home_dir, join_paths, - split_paths, temp_dir, -}; - -pub use super::unsupported; - -pub fn exit(_code: i32) -> ! { - unsafe { - vex_sdk::vexSystemExitRequest(); - - loop { - vex_sdk::vexTasksRun(); - } - } -} diff --git a/library/std/src/sys/pal/wasi/os.rs b/library/std/src/sys/pal/wasi/os.rs index 285be3ca9fda4..4a92e577c6503 100644 --- a/library/std/src/sys/pal/wasi/os.rs +++ b/library/std/src/sys/pal/wasi/os.rs @@ -102,10 +102,6 @@ pub fn home_dir() -> Option { None } -pub fn exit(code: i32) -> ! { - unsafe { libc::exit(code) } -} - pub fn getpid() -> u32 { panic!("unsupported"); } diff --git a/library/std/src/sys/pal/windows/os.rs b/library/std/src/sys/pal/windows/os.rs index 3eb6ec8278401..ebbbb128a7c9b 100644 --- a/library/std/src/sys/pal/windows/os.rs +++ b/library/std/src/sys/pal/windows/os.rs @@ -189,10 +189,6 @@ pub fn home_dir() -> Option { .or_else(home_dir_crt) } -pub fn exit(code: i32) -> ! { - unsafe { c::ExitProcess(code as u32) } -} - pub fn getpid() -> u32 { unsafe { c::GetCurrentProcessId() } } diff --git a/library/std/src/sys/pal/xous/os.rs b/library/std/src/sys/pal/xous/os.rs index cd7b7b59d1127..b915bccc7f7d0 100644 --- a/library/std/src/sys/pal/xous/os.rs +++ b/library/std/src/sys/pal/xous/os.rs @@ -119,10 +119,6 @@ pub fn home_dir() -> Option { None } -pub fn exit(code: i32) -> ! { - crate::os::xous::ffi::exit(code as u32); -} - pub fn getpid() -> u32 { panic!("no pids on this platform") } diff --git a/library/std/src/sys/pal/zkvm/os.rs b/library/std/src/sys/pal/zkvm/os.rs index cb925ef4348db..99568458184b6 100644 --- a/library/std/src/sys/pal/zkvm/os.rs +++ b/library/std/src/sys/pal/zkvm/os.rs @@ -56,10 +56,6 @@ pub fn home_dir() -> Option { None } -pub fn exit(_code: i32) -> ! { - crate::intrinsics::abort() -} - pub fn getpid() -> u32 { panic!("no pids on this platform") }