From ba2c9baccbb22e97aefe46724a36f2f95908250e Mon Sep 17 00:00:00 2001 From: joboet Date: Thu, 25 Jun 2026 15:51:06 +0200 Subject: [PATCH 1/5] std: truncate thread names on NetBSD --- library/std/src/sys/thread/unix.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/library/std/src/sys/thread/unix.rs b/library/std/src/sys/thread/unix.rs index eb3fcff05ea9e..dd7b7940de510 100644 --- a/library/std/src/sys/thread/unix.rs +++ b/library/std/src/sys/thread/unix.rs @@ -392,6 +392,7 @@ pub fn current_os_id() -> Option { target_os = "vxworks", target_os = "cygwin", target_vendor = "apple", + target_os = "netbsd", ))] fn truncate_cstr(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] { let mut result = [0; MAX_WITH_NUL]; @@ -463,7 +464,12 @@ pub fn set_name(name: &CStr) { #[cfg(target_os = "netbsd")] pub fn set_name(name: &CStr) { + // See https://github.com/NetBSD/src/blob/8d40872b4c550a802379f3b9c22a40212d5e149d/lib/libpthread/pthread.h#L281 + // FIXME: move to libc. + const PTHREAD_MAX_NAMELEN_NP: usize = 32; + unsafe { + let name = truncate_cstr::<{ PTHREAD_MAX_NAMELEN_NP }>(name); let res = libc::pthread_setname_np( libc::pthread_self(), c"%s".as_ptr(), From 6b9de05805cc1301e26ca9214fc2e653873f382c Mon Sep 17 00:00:00 2001 From: Emmanuel Ugwu Date: Fri, 26 Jun 2026 03:23:03 +0000 Subject: [PATCH 2/5] Attribute docs `deprecated` , `warn`, `allow`, `cfg`, `deny`, and `forbid` * Added documentation for allow, cfg, deny, forbid, deprecated and warn attribute Signed-off-by: Emmanuel Ugwu * address review feedback Signed-off-by: Emmanuel Ugwu * address review feedback Signed-off-by: Emmanuel Ugwu * address review feedback Signed-off-by: Emmanuel Ugwu * address feedback Signed-off-by: Emmanuel Ugwu * add the right deprecated link Signed-off-by: Emmanuel Ugwu * add the right deprecated link Signed-off-by: Emmanuel Ugwu * fix typo Signed-off-by: Emmanuel Ugwu * fix phrasing Signed-off-by: Emmanuel Ugwu * fix phrasing Signed-off-by: Emmanuel Ugwu * address feedback Signed-off-by: Emmanuel Ugwu * address feedback and add link to rustc book Signed-off-by: Emmanuel Ugwu * address feedback Signed-off-by: Emmanuel Ugwu --- library/std/src/attribute_docs.rs | 250 ++++++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) diff --git a/library/std/src/attribute_docs.rs b/library/std/src/attribute_docs.rs index 1d8dddb7ca54a..e8d36483f3139 100644 --- a/library/std/src/attribute_docs.rs +++ b/library/std/src/attribute_docs.rs @@ -85,3 +85,253 @@ /// [`unused_must_use`]: ../rustc/lints/listing/warn-by-default.html#unused-must-use /// [the `must_use` attribute]: ../reference/attributes/diagnostics.html#the-must_use-attribute mod must_use_attribute {} + +#[doc(attribute = "allow")] +// +/// The `allow` attribute suppresses lint diagnostics that would otherwise produce +/// warnings or errors. It can be used on any lint or lint group (except those +/// set to `forbid`). +/// +/// ```rust +/// #[allow(dead_code)] +/// fn unused_function() { +/// // ... +/// } +/// +/// fn main() { +/// // `unused_function` does not generate a compiler warning. +/// } +/// ``` +/// +/// Without `#[allow(dead_code)]`, the example above would emit: +/// +/// ```text +/// warning: function `unused_function` is never used +/// --> main.rs:1:4 +/// | +/// 1 | fn unused_function() { +/// | ^^^^^^^^^^^^^^^ +/// | +/// = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default +/// +/// warning: 1 warning emitted +/// ``` +/// +/// Multiple lints can be set to `allow` at once with commas: +/// +/// ```rust +/// #[allow(unused_variables, unused_mut)] +/// fn main() { +/// let mut x: u32 = 42; +/// } +/// ``` +/// +/// This is mostly used to prevent lint warnings or errors while still under development. +/// +/// It cannot override a lint that has been set to `forbid`. +/// +/// It's also important to consider that overusing `allow` could make code harder to maintain +/// and possibly hide issues. To mitigate this issue, using the `expect` attribute is preferred. +/// +/// `allow` can be overridden by `warn`, `deny`, and `forbid`. +/// +/// The lint checks supported by rustc can be found via `rustc -W help`, +/// along with their default settings and are documented in [the `rustc` book]. +/// +/// [the `rustc` book]: ../rustc/lints/listing/index.html +/// +/// For more information, see the Reference on [the `allow` attribute]. +/// +/// [the `allow` attribute]: ../reference/attributes/diagnostics.html#lint-check-attributes +mod allow_attribute {} + +#[doc(attribute = "cfg")] +// +/// Used for conditional compilation. +/// +/// The `cfg` attribute allows compiling an item under specific conditions, otherwise it +/// will be ignored. +/// +/// ```rust +/// // Only compiles this function for Linux. +/// #[cfg(target_os = "linux")] +/// fn platform_specific() { +/// println!("Running on Linux"); +/// } +/// +/// // Only compiles this function if not for Linux. +/// #[cfg(not(target_os = "linux"))] +/// fn platform_specific() { +/// println!("Running on something else"); +/// } +/// ``` +/// +/// Depending on the platform you're targeting, only one of these two functions will be considered +/// during the compilation. +/// +/// Conditions can also be combined with `all(...)`, `any(...)`, and `not(...)`. +/// +/// * `all`: True if all given predicates are true. +/// * `any`: True if at least one of the given predicates is true. +/// * `not`: True if the predicate is false and false if the predicate is true. +/// +/// ```rust +/// #[cfg(all(unix, target_pointer_width = "64"))] +/// fn unix_64bit() { +/// } +/// ``` +/// +/// If you want to use this mechanism in an `if` condition in your code, you +/// can use the [`cfg!`] macro. To conditionally apply an attribute, +/// see [`cfg_attr`]. +/// +/// For more information, see the Reference on [the `cfg` attribute]. +/// +/// [`cfg_attr`]: ../reference/conditional-compilation.html#the-cfg_attr-attribute +/// [the `cfg` attribute]: ../reference/conditional-compilation.html#the-cfg-attribute +mod cfg_attribute {} + +#[doc(attribute = "deny")] +// +/// Emits an error, preventing the compilation from finishing, when a lint check has failed. +/// This is useful for enforcing rules or preventing certain patterns: +/// +/// ```rust,compile_fail +/// #[deny(unused)] +/// fn foo() { +/// let x = 42; // Emits an error because x is unused. +/// } +/// ``` +/// +/// `deny` can be overridden by `allow`, `warn`, and `forbid`: +/// +/// ```rust +/// #![deny(unused)] +/// +/// #[allow(unused)] // We override the `deny` for this function. +/// fn foo() { +/// let x = 42; // No lint emitted even though `x` is unused. +/// } +/// ``` +/// +/// Multiple lints can also be set to `deny` at once: +/// +/// ```rust,compile_fail +/// #![deny(unused_imports, unused_variables)] +/// use std::collections::*; +/// +/// fn main() { +/// let mut x = 10; +/// } +/// ``` +/// +/// The lint checks supported by rustc can be found via `rustc -W help`, +/// along with their default settings and are documented in [the `rustc` book]. +/// +/// [the `rustc` book]: ../rustc/lints/listing/index.html +/// +/// For more information, see the Reference on [the `deny` attribute]. +/// +/// [the `deny` attribute]: ../reference/attributes/diagnostics.html#lint-check-attributes +mod deny_attribute {} + +#[doc(attribute = "forbid")] +// +/// Emits an error, preventing the compilation from finishing, when a lint check has failed. +/// +/// A lint set to `forbid` cannot be overridden by `allow` or `warn`. +/// Attempting either will result in a compilation error. Writing `#[deny(...)]` on the same lint inside a +/// `forbid` scope is permitted, but has no effect; the lint remains at the `forbid` level. +/// +/// This is useful for enforcing strict policies that should not be relaxed +/// anywhere in the codebase. Example: +/// +/// ```rust +/// #![forbid(unsafe_code)] +/// +/// // This would cause a compilation error if uncommented: +/// // #[allow(unsafe_code)] // error: cannot override `forbid` +/// ``` +/// +/// Multiple lints can be set to `forbid` at once: +/// +/// ```rust +/// #![forbid(unsafe_code, unused)] +/// ``` +/// +/// The lint checks supported by rustc can be found via `rustc -W help`, +/// along with their default settings and are documented in [the `rustc` book]. +/// +/// [the `rustc` book]: ../rustc/lints/listing/index.html +/// +/// For more information, see the Reference on [the `forbid` attribute]. +/// +/// [the `forbid` attribute]: ../reference/attributes/diagnostics.html#lint-check-attributes +mod forbid_attribute {} + +#[doc(attribute = "deprecated")] +// +/// Emits a warning during compilation when an item with this attribute is used. +/// `since` and `note` are optional fields giving more detail about why the item is deprecated. +/// +/// * `since`: the version since when the item is deprecated. +/// * `note`: the reason why an item is deprecated. +/// +/// Example: +/// +/// ```rust +/// #[deprecated(since = "1.0.0", note = "Use bar instead")] +/// struct Foo; +/// struct Bar; +/// ``` +/// +/// `deprecated` attribute helps developers transition away from old code by providing warnings when +/// deprecated items are used. Note that during `Cargo` builds, warnings on dependencies get silenced +/// by default, so you may not see a deprecation warning unless you build that dependency directly. +/// +/// For more information, see the Reference on [the `deprecated` attribute]. +/// +/// [the `deprecated` attribute]: ../reference/attributes/diagnostics.html#the-deprecated-attribute +mod deprecated_attribute {} + +#[doc(attribute = "warn")] +// +/// Emits a warning during compilation when a lint check failed. +/// +/// Unlike `deny` or `forbid`, `warn` does not produce a hard error: the compilation continues, but +/// the compiler emits a warning message. `warn` can be overridden by `allow`, `deny`, and `forbid`. +/// +/// Example: +/// +/// ```rust,compile_fail +/// #![allow(unused)] +/// +/// #[warn(unused)] // We override the allowed `unused` lint. +/// fn foo() { +/// // This lint warns by default even without #[warn(unused)] being explicitly set +/// let x = 42; // warning: unused variable `x` +/// } +/// ``` +/// +/// +/// Many lints, including `unused`, are already set to `warn` by default so this attribute is +/// mainly useful for lints that are normally `allow` by default. +/// +/// Multiple lints can be set to `warn` at once: +/// +/// ```rust,compile_fail +/// #[warn(unused_mut, unused_variables)] +/// fn main() { +/// let mut x = 42; +/// } +/// ``` +/// +/// The lint checks supported by rustc can be found via `rustc -W help`, +/// along with their default settings and are documented in [the `rustc` book]. +/// +/// [the `rustc` book]: ../rustc/lints/listing/index.html +/// +/// For more information, see the Reference on [the `warn` attribute]. +/// +/// [the `warn` attribute]: ../reference/attributes/diagnostics.html#lint-check-attributes +mod warn_attribute {} From 9b188dd6c50ed504ed0eaf80dfe8f4ef1b56cf5c Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sat, 23 May 2026 15:27:28 -0700 Subject: [PATCH 3/5] cg_LLVM: Stop needing an alloca for volatile loads And while I'm here, improve the tests to check that the unaligned ones are actually unaligned, since `unaligned_volatile_load::` doesn't actually test anything. --- compiler/rustc_codegen_gcc/src/builder.rs | 3 +- .../rustc_codegen_gcc/src/intrinsic/mod.rs | 7 +- compiler/rustc_codegen_llvm/src/builder.rs | 4 +- compiler/rustc_codegen_llvm/src/context.rs | 2 +- .../rustc_codegen_llvm/src/debuginfo/gdb.rs | 6 +- compiler/rustc_codegen_llvm/src/intrinsic.rs | 46 ++++--- .../rustc_codegen_ssa/src/traits/builder.rs | 2 +- tests/codegen-llvm/i128-x86-align.rs | 6 +- tests/codegen-llvm/intrinsics/volatile.rs | 121 +++++++++++++++++- 9 files changed, 161 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 8ae4dedff8f28..6cbc0054cc015 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -993,7 +993,8 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { loaded_value.to_rvalue() } - fn volatile_load(&mut self, ty: Type<'gcc>, ptr: RValue<'gcc>) -> RValue<'gcc> { + fn volatile_load(&mut self, ty: Type<'gcc>, ptr: RValue<'gcc>, _: Align) -> RValue<'gcc> { + // FIXME(antoyo): set alignment. let ptr = self.context.new_cast(self.location, ptr, ty.make_volatile().make_pointer()); // (FractalFir): We insert a local here, to ensure this volatile load can't move across // blocks. diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index a12116d5b9d39..78a4c7e88c895 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -5,7 +5,7 @@ mod simd; use std::iter; use gccjit::{ComparisonOp, Function, FunctionType, RValue, ToRValue, Type, UnaryOp}; -use rustc_abi::{BackendRepr, HasDataLayout, WrappingRange}; +use rustc_abi::{Align, BackendRepr, HasDataLayout, WrappingRange}; use rustc_codegen_ssa::base::wants_msvc_seh; use rustc_codegen_ssa::common::IntPredicate; use rustc_codegen_ssa::errors::InvalidMonomorphization; @@ -368,8 +368,9 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc sym::volatile_load | sym::unaligned_volatile_load => { let ptr = args[0].immediate(); - let load = self.volatile_load(result.layout.gcc_type(self), ptr); - // FIXME(antoyo): set alignment. + let abi_align = result_layout.align.abi; + let ptr_align = if name == sym::volatile_load { abi_align } else { Align::ONE }; + let load = self.volatile_load(result.layout.gcc_type(self), ptr, ptr_align); if let BackendRepr::Scalar(scalar) = result.layout.backend_repr { self.to_immediate_scalar(load, scalar) } else { diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index afb6985d21a95..7535c41cd1ed3 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -682,9 +682,9 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } } - fn volatile_load(&mut self, ty: &'ll Type, ptr: &'ll Value) -> &'ll Value { + fn volatile_load(&mut self, ty: &'ll Type, ptr: &'ll Value, align: Align) -> &'ll Value { unsafe { - let load = llvm::LLVMBuildLoad2(self.llbuilder, ty, ptr, UNNAMED); + let load = self.load(ty, ptr, align); llvm::LLVMSetVolatile(load, llvm::TRUE); load } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 84661f5160b14..6198a98e5f7ae 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -978,7 +978,7 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } fn intrinsic_call_expects_place_always(&self, name: Symbol) -> bool { - matches!(name, sym::volatile_load | sym::unaligned_volatile_load | sym::black_box) + matches!(name, sym::black_box) } } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs index f88f0c2fb0994..f5b843b4e3e74 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs @@ -1,5 +1,6 @@ // .debug_gdb_scripts binary section. +use rustc_abi::Align; use rustc_codegen_ssa::base::collect_debugger_visualizers_transitive; use rustc_codegen_ssa::traits::*; use rustc_hir::attrs::DebuggerVisualizerType; @@ -18,10 +19,7 @@ pub(crate) fn insert_reference_to_gdb_debug_scripts_section_global(bx: &mut Buil let gdb_debug_scripts_section = get_or_insert_gdb_debug_scripts_section_global(bx); // Load just the first byte as that's all that's necessary to force // LLVM to keep around the reference to the global. - let volatile_load_instruction = bx.volatile_load(bx.type_i8(), gdb_debug_scripts_section); - unsafe { - llvm::LLVMSetAlignment(volatile_load_instruction, 1); - } + bx.volatile_load(bx.type_i8(), gdb_debug_scripts_section, Align::ONE); } } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 1caa95f369360..98e2cbac03dfe 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -354,25 +354,39 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } sym::volatile_load | sym::unaligned_volatile_load => { - let result = PlaceRef { - val: result_place.unwrap(), - layout: result_layout, - }; - + // Note that we cannot just load the `llvm_type` because we should never load non-scalars. + // Trying to do so blows up horribly in some cases -- for example loading a + // `MaybeUninint<&dyn Trait>` would load as `{ [i64x2] }` which gives assertions later + // (if we're lucky) from things not being pointers that ought to be. let ptr = args[0].immediate(); - let load = self.volatile_load(result_layout.llvm_type(self), ptr); - let align = if name == sym::unaligned_volatile_load { - 1 + let abi_align = result_layout.align.abi; + let ptr_align = if name == sym::volatile_load { abi_align } else { Align::ONE }; + if result_layout.is_zst() { + return IntrinsicResult::Operand(OperandValue::ZeroSized); + } else if let BackendRepr::Scalar(scalar) = result_layout.backend_repr { + let load = self.volatile_load(self.type_from_scalar(scalar), ptr, ptr_align); + self.to_immediate_scalar(load, scalar) } else { - result_layout.align.bytes() as u32 - }; - unsafe { - llvm::LLVMSetAlignment(load, align); - } - if !result_layout.is_zst() { - self.store_to_place(load, result.val); + // One day Rust will probably want to define how we split up a volatile load + // of something that's *not* just an ordinary scalar, but for now we can just + // use an LLVM integer type of the correct width and let it split it however. + let llty = self.type_ix(result_layout.size.bits()); + let temp = if let Some(result_place) = result_place { + PlaceRef { + val: result_place, + layout: result_layout, + } + } else { + PlaceRef::alloca(self, result_layout) + }; + let llval = self.volatile_load(llty, ptr, ptr_align); + self.store(llval, temp.val.llval, abi_align); + return if result_place.is_none() { + IntrinsicResult::Operand(self.load_operand(temp).val) + } else { + IntrinsicResult::WroteIntoPlace + }; } - return IntrinsicResult::WroteIntoPlace; } sym::volatile_store => { let dst = args[0].deref(self.cx()); diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index d68549c6871f4..39c2529152566 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -242,7 +242,7 @@ pub trait BuilderMethods<'a, 'tcx>: fn alloca_with_ty(&mut self, layout: TyAndLayout<'tcx>) -> Self::Value; fn load(&mut self, ty: Self::Type, ptr: Self::Value, align: Align) -> Self::Value; - fn volatile_load(&mut self, ty: Self::Type, ptr: Self::Value) -> Self::Value; + fn volatile_load(&mut self, ty: Self::Type, ptr: Self::Value, align: Align) -> Self::Value; fn atomic_load( &mut self, ty: Self::Type, diff --git a/tests/codegen-llvm/i128-x86-align.rs b/tests/codegen-llvm/i128-x86-align.rs index 75802b0c5056a..998821d17e80c 100644 --- a/tests/codegen-llvm/i128-x86-align.rs +++ b/tests/codegen-llvm/i128-x86-align.rs @@ -5,8 +5,6 @@ // while rustc wants it to have 16 byte alignment. This test checks that we handle this // correctly. -// CHECK: %ScalarPair = type { i32, [3 x i32], i128 } - #![feature(core_intrinsics)] #[repr(C)] @@ -62,8 +60,8 @@ pub fn load_volatile(x: &ScalarPair) -> ScalarPair { // CHECK-SAME: dereferenceable(32) %_0, // CHECK-SAME: align 16 // CHECK-SAME: dereferenceable(32) %x - // CHECK: [[LOAD:%.*]] = load volatile %ScalarPair, ptr %x, align 16 - // CHECK-NEXT: store %ScalarPair [[LOAD]], ptr %_0, align 16 + // CHECK: [[LOAD:%.*]] = load volatile i256, ptr %x, align 16 + // CHECK-NEXT: store i256 [[LOAD]], ptr %_0, align 16 // CHECK-NEXT: ret void unsafe { std::intrinsics::volatile_load(x) } } diff --git a/tests/codegen-llvm/intrinsics/volatile.rs b/tests/codegen-llvm/intrinsics/volatile.rs index 2dea5ecb2ca92..4ba4c5edd2df6 100644 --- a/tests/codegen-llvm/intrinsics/volatile.rs +++ b/tests/codegen-llvm/intrinsics/volatile.rs @@ -5,6 +5,11 @@ use std::intrinsics; +#[repr(align(32))] +pub struct CustomZst; + +type UninitFatPointer = std::mem::MaybeUninit<&'static dyn std::fmt::Debug>; + // CHECK-LABEL: @volatile_copy_memory #[no_mangle] pub unsafe fn volatile_copy_memory(a: *mut u8, b: *const u8) { @@ -28,8 +33,62 @@ pub unsafe fn volatile_set_memory(a: *mut u8, b: u8) { // CHECK-LABEL: @volatile_load #[no_mangle] -pub unsafe fn volatile_load(a: *const u8) -> u8 { - // CHECK: load volatile +pub unsafe fn volatile_load(a: *const u16) -> u16 { + // CHECK: [[TEMP:%.+]] = load volatile i16, ptr %a + // CHECK-SAME: align 2{{,|$}} + // CHECK-NEXT: ret i16 [[TEMP]] + intrinsics::volatile_load(a) +} + +// CHECK-LABEL: @volatile_load_bool +#[no_mangle] +pub unsafe fn volatile_load_bool(a: *const bool) -> bool { + // CHECK: [[TEMP:%.+]] = load volatile i8, ptr %a + // CHECK-SAME: align 1{{,|$}} + // CHECK: [[TRUNC:%.+]] = trunc nuw i8 [[TEMP]] to i1 + // CHECK: ret i1 [[TRUNC]] + intrinsics::volatile_load(a) +} + +// CHECK-LABEL: @volatile_load_zst +#[no_mangle] +pub unsafe fn volatile_load_zst(a: *const CustomZst) -> CustomZst { + // CHECK: start: + // CHECK-NEXT: ret void + intrinsics::volatile_load(a) +} + +// CHECK-LABEL: @volatile_load_array +// CHECK-SAME: ptr{{.+}}sret([16 x i8]){{.+}}%_0 +#[no_mangle] +pub unsafe fn volatile_load_array(a: *const [u16; 8]) -> [u16; 8] { + // CHECK-NOT: alloca + // CHECK: [[TEMP:%.+]] = load volatile i128, ptr %a, + // CHECK-SAME: align 2{{,|$}} + // CHECK: store i128 [[TEMP]], ptr %_0, + // CHECK-SAME: align 2{{,|$}} + // CHECK-NEXT: ret void + intrinsics::volatile_load(a) +} + +// CHECK-LABEL: @volatile_load_fat +#[no_mangle] +pub unsafe fn volatile_load_fat(a: *const UninitFatPointer) -> UninitFatPointer { + // CHECK: [[ALLOCA:%.+]] = alloca + // CHECK-SAME: [[SIZE:4|8|16]] x i8 + // CHECK-SAME: align [[ALIGN:2|4|8]] + + // CHECK: [[TEMP:%.+]] = load volatile [[INT:i32|i64|i128]], ptr %a, + // CHECK-SAME: align [[ALIGN]]{{,|$}} + // CHECK: store [[INT]] [[TEMP]], ptr [[ALLOCA]], + // CHECK-SAME: align [[ALIGN]]{{,|$}} + + // CHECK: [[T0:%.+]] = load ptr, ptr [[ALLOCA]], align [[ALIGN]] + // CHECK: [[T1:%.+]] = getelementptr inbounds i8, ptr [[ALLOCA]] + // CHECK: [[T2:%.+]] = load ptr, ptr [[T1]], align [[ALIGN]] + // CHECK: [[P1:%.+]] = insertvalue { ptr, ptr } poison, ptr [[T0]], 0 + // CHECK: [[P2:%.+]] = insertvalue { ptr, ptr } [[P1]], ptr [[T2]], 1 + // CHECK: ret { ptr, ptr } [[P2]] intrinsics::volatile_load(a) } @@ -42,8 +101,62 @@ pub unsafe fn volatile_store(a: *mut u8, b: u8) { // CHECK-LABEL: @unaligned_volatile_load #[no_mangle] -pub unsafe fn unaligned_volatile_load(a: *const u8) -> u8 { - // CHECK: load volatile +pub unsafe fn unaligned_volatile_load(a: *const u16) -> u16 { + // CHECK: [[TEMP:%.+]] = load volatile i16, ptr %a + // CHECK-SAME: align 1{{,|$}} + // CHECK-NEXT: ret i16 [[TEMP]] + intrinsics::unaligned_volatile_load(a) +} + +// CHECK-LABEL: @unaligned_volatile_load_bool +#[no_mangle] +pub unsafe fn unaligned_volatile_load_bool(a: *const bool) -> bool { + // CHECK: [[TEMP:%.+]] = load volatile i8, ptr %a + // CHECK-SAME: align 1{{,|$}} + // CHECK: [[TRUNC:%.+]] = trunc nuw i8 [[TEMP]] to i1 + // CHECK: ret i1 [[TRUNC]] + intrinsics::unaligned_volatile_load(a) +} + +// CHECK-LABEL: @unaligned_volatile_load_zst +#[no_mangle] +pub unsafe fn unaligned_volatile_load_zst(a: *const CustomZst) -> CustomZst { + // CHECK: start: + // CHECK-NEXT: ret void + intrinsics::unaligned_volatile_load(a) +} + +// CHECK-LABEL: @unaligned_volatile_load_array +// CHECK-SAME: ptr{{.+}}sret([16 x i8]){{.+}}%_0, +#[no_mangle] +pub unsafe fn unaligned_volatile_load_array(a: *const [u16; 8]) -> [u16; 8] { + // CHECK-NOT: alloca + // CHECK: [[TEMP:%.+]] = load volatile i128, ptr %a, + // CHECK-SAME: align 1{{,|$}} + // CHECK: store i128 [[TEMP]], ptr %_0, + // CHECK-SAME: align 2{{,|$}} + // CHECK-NEXT: ret void + intrinsics::unaligned_volatile_load(a) +} + +// CHECK-LABEL: @unaligned_volatile_load_fat +#[no_mangle] +pub unsafe fn unaligned_volatile_load_fat(a: *const UninitFatPointer) -> UninitFatPointer { + // CHECK: [[ALLOCA:%.+]] = alloca + // CHECK-SAME: [[SIZE]] x i8 + // CHECK-SAME: align [[ALIGN]] + + // CHECK: [[TEMP:%.+]] = load volatile [[INT]], ptr %a, + // CHECK-SAME: align 1{{,|$}} + // CHECK: store [[INT]] [[TEMP]], ptr [[ALLOCA]], + // CHECK-SAME: align [[ALIGN]]{{,|$}} + + // CHECK: [[T0:%.+]] = load ptr, ptr [[ALLOCA]], align [[ALIGN]] + // CHECK: [[T1:%.+]] = getelementptr inbounds i8, ptr [[ALLOCA]] + // CHECK: [[T2:%.+]] = load ptr, ptr [[T1]], align [[ALIGN]] + // CHECK: [[P1:%.+]] = insertvalue { ptr, ptr } poison, ptr [[T0]], 0 + // CHECK: [[P2:%.+]] = insertvalue { ptr, ptr } [[P1]], ptr [[T2]], 1 + // CHECK: ret { ptr, ptr } [[P2]] intrinsics::unaligned_volatile_load(a) } From 6d6fcc9f77e4473047c3763c155a9cf5995c3142 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Fri, 26 Jun 2026 14:36:44 +0900 Subject: [PATCH 4/5] add regression test for clone suggestion empty obligations --- ...constrained-impl-param-clone-suggestion.rs | 20 +++++++++++++++++++ ...trained-impl-param-clone-suggestion.stderr | 19 ++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 tests/ui/moves/unconstrained-impl-param-clone-suggestion.rs create mode 100644 tests/ui/moves/unconstrained-impl-param-clone-suggestion.stderr diff --git a/tests/ui/moves/unconstrained-impl-param-clone-suggestion.rs b/tests/ui/moves/unconstrained-impl-param-clone-suggestion.rs new file mode 100644 index 0000000000000..694c95da7b482 --- /dev/null +++ b/tests/ui/moves/unconstrained-impl-param-clone-suggestion.rs @@ -0,0 +1,20 @@ +// Regression test for https://github.com/rust-lang/rust/issues/148631 + +struct C; + +struct S(T); + +trait Tr {} + +impl Clone for S +//~^ ERROR the type parameter `T` is not constrained +where + S: Tr, +{ + fn clone(&self) -> Self { + *self + //~^ ERROR cannot move out of `*self` + } +} + +fn main() {} diff --git a/tests/ui/moves/unconstrained-impl-param-clone-suggestion.stderr b/tests/ui/moves/unconstrained-impl-param-clone-suggestion.stderr new file mode 100644 index 0000000000000..9cf17045b0296 --- /dev/null +++ b/tests/ui/moves/unconstrained-impl-param-clone-suggestion.stderr @@ -0,0 +1,19 @@ +error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates + --> $DIR/unconstrained-impl-param-clone-suggestion.rs:9:6 + | +LL | impl Clone for S + | -^- + | || + | |unconstrained type parameter + | help: remove the unused type parameter `T` + +error[E0507]: cannot move out of `*self` which is behind a shared reference + --> $DIR/unconstrained-impl-param-clone-suggestion.rs:15:9 + | +LL | *self + | ^^^^^ move occurs because `*self` has type `S`, which does not implement the `Copy` trait + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0207, E0507. +For more information about an error, try `rustc --explain E0207`. From f08762cf489011e9d91fb5dbe47f6d31bcb2e83e Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Fri, 26 Jun 2026 14:38:51 +0900 Subject: [PATCH 5/5] guard clone suggestion against empty obligation errors --- .../src/diagnostics/conflict_errors.rs | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index caa566e79e29b..6acf685459cf3 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1461,15 +1461,19 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let cause = ObligationCause::misc(expr.span, self.mir_def_id()); ocx.register_bound(cause, self.infcx.param_env, ty, clone_trait); let errors = ocx.evaluate_obligations_error_on_ambiguity(); - if errors.iter().all(|error| { - match error.obligation.predicate.as_clause().and_then(|c| c.as_trait_clause()) { - Some(clause) => match clause.self_ty().skip_binder().kind() { - ty::Adt(def, _) => def.did().is_local() && clause.def_id() == clone_trait, - _ => false, - }, - None => false, - } - }) { + if !errors.is_empty() + && errors.iter().all(|error| { + match error.obligation.predicate.as_clause().and_then(|c| c.as_trait_clause()) { + Some(clause) => match clause.self_ty().skip_binder().kind() { + ty::Adt(def, _) => { + def.did().is_local() && clause.def_id() == clone_trait + } + _ => false, + }, + None => false, + } + }) + { let mut type_spans = vec![]; let mut types = FxIndexSet::default(); for clause in errors