From 13ec3de67337e0781eaee2d952f746272760a51e Mon Sep 17 00:00:00 2001 From: Flakebi Date: Fri, 24 Apr 2026 10:03:45 +0200 Subject: [PATCH 1/5] Add intrinsic for launch-sized workgroup memory on GPUs Workgroup memory is a memory region that is shared between all threads in a workgroup on GPUs. Workgroup memory can be allocated statically or after compilation, when launching a gpu-kernel. The intrinsic added here returns the pointer to the memory that is allocated at launch-time. # Interface With this change, workgroup memory can be accessed in Rust by calling the new `gpu_launch_sized_workgroup_mem() -> *mut T` intrinsic. It returns the pointer to workgroup memory guaranteeing that it is aligned to at least the alignment of `T`. The pointer is dereferencable for the size specified when launching the current gpu-kernel (which may be the size of `T` but can also be larger or smaller or zero). All calls to this intrinsic return a pointer to the same address. See the intrinsic documentation for more details. ## Alternative Interfaces It was also considered to expose dynamic workgroup memory as extern static variables in Rust, like they are represented in LLVM IR. However, due to the pointer not being guaranteed to be dereferencable (that depends on the allocated size at runtime), such a global must be zero-sized, which makes global variables a bad fit. # Implementation Details Workgroup memory in amdgpu and nvptx lives in address space 3. Workgroup memory from a launch is implemented by creating an external global variable in address space 3. The global is declared with size 0, as the actual size is only known at runtime. It is defined behavior in LLVM to access an external global outside the defined size. There is no similar way to get the allocated size of launch-sized workgroup memory on amdgpu an nvptx, so users have to pass this out-of-band or rely on target specific ways for now. --- compiler/rustc_abi/src/lib.rs | 3 ++ compiler/rustc_codegen_llvm/src/declare.rs | 23 +++++++++ compiler/rustc_codegen_llvm/src/intrinsic.rs | 49 +++++++++++++++++-- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 7 +++ .../rustc_codegen_ssa/src/mir/intrinsic.rs | 1 + .../rustc_hir_analysis/src/check/intrinsic.rs | 2 + .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 26 ++++++++-- compiler/rustc_span/src/symbol.rs | 1 + library/core/src/intrinsics/gpu.rs | 45 +++++++++++++++++ src/tools/tidy/src/style.rs | 4 ++ .../gpu-launch-sized-workgroup-memory.rs | 41 ++++++++++++++++ 11 files changed, 193 insertions(+), 9 deletions(-) create mode 100644 tests/codegen-llvm/gpu-launch-sized-workgroup-memory.rs diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 450a93ee8481e..96e4b822b041d 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1753,6 +1753,9 @@ pub struct AddressSpace(pub u32); impl AddressSpace { /// LLVM's `0` address space. pub const ZERO: Self = AddressSpace(0); + /// The address space for workgroup memory on nvptx and amdgpu. + /// See e.g. the `gpu_launch_sized_workgroup_mem` intrinsic for details. + pub const GPU_WORKGROUP: Self = AddressSpace(3); } /// How many scalable vectors are in a `BackendRepr::ScalableVector`? diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index d7b8a304e9591..419d38f95e595 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -14,6 +14,7 @@ use std::borrow::Borrow; use itertools::Itertools; +use rustc_abi::AddressSpace; use rustc_codegen_ssa::traits::{MiscCodegenMethods, TypeMembershipCodegenMethods}; use rustc_data_structures::fx::FxIndexSet; use rustc_middle::ty::{Instance, Ty}; @@ -104,6 +105,28 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { ) } } + + /// Declare a global value in a specific address space. + /// + /// If there’s a value with the same name already declared, the function will + /// return its Value instead. + pub(crate) fn declare_global_in_addrspace( + &self, + name: &str, + ty: &'ll Type, + addr_space: AddressSpace, + ) -> &'ll Value { + debug!("declare_global(name={name:?}, addrspace={addr_space:?})"); + unsafe { + llvm::LLVMRustGetOrInsertGlobalInAddrspace( + (**self).borrow().llmod, + name.as_c_char_ptr(), + name.len(), + ty, + addr_space.0, + ) + } + } } impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index b8ba6db9aa807..a298cf963dccf 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -3,8 +3,8 @@ use std::ffi::c_uint; use std::{assert_matches, iter, ptr}; use rustc_abi::{ - Align, BackendRepr, Float, HasDataLayout, Integer, NumScalableVectors, Primitive, Size, - WrappingRange, + AddressSpace, Align, BackendRepr, Float, HasDataLayout, Integer, NumScalableVectors, Primitive, + Size, WrappingRange, }; use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh, wants_wasm_eh}; use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; @@ -176,6 +176,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { span: Span, ) -> Result<(), ty::Instance<'tcx>> { let tcx = self.tcx; + let llvm_version = crate::llvm_util::get_version(); let name = tcx.item_name(instance.def_id()); let fn_args = instance.args; @@ -192,7 +193,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { | sym::maximum_number_nsz_f64 | sym::maximum_number_nsz_f128 // Need at least LLVM 22 for `min/maximumnum` to not crash LLVM. - if crate::llvm_util::get_version() >= (22, 0, 0) => + if llvm_version >= (22, 0, 0) => { let intrinsic_name = if name.as_str().starts_with("min") { "llvm.minimumnum" @@ -418,7 +419,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } // FIXME move into the branch below when LLVM 22 is the lowest version we support. - sym::carryless_mul if crate::llvm_util::get_version() >= (22, 0, 0) => { + sym::carryless_mul if llvm_version >= (22, 0, 0) => { let ty = args[0].layout.ty; if !ty.is_integral() { tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType { @@ -618,6 +619,46 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { return Ok(()); } + sym::gpu_launch_sized_workgroup_mem => { + // Generate an anonymous global per call, with these properties: + // 1. The global is in the address space for workgroup memory + // 2. It is an `external` global + // 3. It is correctly aligned for the pointee `T` + // All instances of extern addrspace(gpu_workgroup) globals are merged in the LLVM backend. + // The name is irrelevant. + // See https://docs.nvidia.com/cuda/cuda-c-programming-guide/#shared + let name = if llvm_version < (23, 0, 0) && tcx.sess.target.arch == Arch::Nvptx64 { + // The auto-assigned name for extern shared globals in the nvptx backend does + // not compile in ptxas. Workaround this issue by assigning a name. + // Fixed in LLVM 23. + "gpu_launch_sized_workgroup_mem" + } else { + "" + }; + let global = self.declare_global_in_addrspace( + name, + self.type_array(self.type_i8(), 0), + AddressSpace::GPU_WORKGROUP, + ); + let ty::RawPtr(inner_ty, _) = result.layout.ty.kind() else { unreachable!() }; + // The alignment of the global is used to specify the *minimum* alignment that + // must be obeyed by the GPU runtime. + // When multiple of these global variables are used by a kernel, the maximum alignment is taken. + // See https://github.com/llvm/llvm-project/blob/a271d07488a85ce677674bbe8101b10efff58c95/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp#L821 + let alignment = self.align_of(*inner_ty).bytes() as u32; + unsafe { + // FIXME Workaround the above issue by taking maximum alignment if the global existed + if tcx.sess.target.arch == Arch::Nvptx64 { + if alignment > llvm::LLVMGetAlignment(global) { + llvm::LLVMSetAlignment(global, alignment); + } + } else { + llvm::LLVMSetAlignment(global, alignment); + } + } + self.cx().const_pointercast(global, self.type_ptr()) + } + sym::amdgpu_dispatch_ptr => { let val = self.call_intrinsic("llvm.amdgcn.dispatch.ptr", &[], &[]); // Relying on `LLVMBuildPointerCast` to produce an addrspacecast diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 525d1dbe9d0d3..3e373c42eca34 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2003,6 +2003,13 @@ unsafe extern "C" { NameLen: size_t, T: &'a Type, ) -> &'a Value; + pub(crate) fn LLVMRustGetOrInsertGlobalInAddrspace<'a>( + M: &'a Module, + Name: *const c_char, + NameLen: size_t, + T: &'a Type, + AddressSpace: c_uint, + ) -> &'a Value; pub(crate) fn LLVMRustGetNamedValue( M: &Module, Name: *const c_char, diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index fd0c7c656ac21..f4a5e8baa2a5f 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -111,6 +111,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { sym::abort | sym::unreachable | sym::cold_path + | sym::gpu_launch_sized_workgroup_mem | sym::breakpoint | sym::amdgpu_dispatch_ptr | sym::assert_zero_valid diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index d952faa5edb74..9cc66f3c2adf6 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -130,6 +130,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::forget | sym::frem_algebraic | sym::fsub_algebraic + | sym::gpu_launch_sized_workgroup_mem | sym::is_val_statically_known | sym::log2f16 | sym::log2f32 @@ -297,6 +298,7 @@ pub(crate) fn check_intrinsic_type( sym::field_offset => (1, 0, vec![], tcx.types.usize), sym::rustc_peek => (1, 0, vec![param(0)], param(0)), sym::caller_location => (0, 0, vec![], tcx.caller_location_ty()), + sym::gpu_launch_sized_workgroup_mem => (1, 0, vec![], Ty::new_mut_ptr(tcx, param(0))), sym::assert_inhabited | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid => { (1, 0, vec![], tcx.types.unit) } diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index c310e580af559..91bb1c9733630 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -299,10 +299,12 @@ extern "C" LLVMValueRef LLVMRustGetOrInsertFunction(LLVMModuleRef M, .getCallee()); } -extern "C" LLVMValueRef LLVMRustGetOrInsertGlobal(LLVMModuleRef M, - const char *Name, - size_t NameLen, - LLVMTypeRef Ty) { +// Get the global variable with the given name if it exists or create a new +// external global. +extern "C" LLVMValueRef +LLVMRustGetOrInsertGlobalInAddrspace(LLVMModuleRef M, const char *Name, + size_t NameLen, LLVMTypeRef Ty, + unsigned int AddressSpace) { Module *Mod = unwrap(M); auto NameRef = StringRef(Name, NameLen); @@ -313,10 +315,24 @@ extern "C" LLVMValueRef LLVMRustGetOrInsertGlobal(LLVMModuleRef M, GlobalVariable *GV = Mod->getGlobalVariable(NameRef, true); if (!GV) GV = new GlobalVariable(*Mod, unwrap(Ty), false, - GlobalValue::ExternalLinkage, nullptr, NameRef); + GlobalValue::ExternalLinkage, nullptr, NameRef, + nullptr, GlobalValue::NotThreadLocal, AddressSpace); return wrap(GV); } +// Get the global variable with the given name if it exists or create a new +// external global. +extern "C" LLVMValueRef LLVMRustGetOrInsertGlobal(LLVMModuleRef M, + const char *Name, + size_t NameLen, + LLVMTypeRef Ty) { + Module *Mod = unwrap(M); + unsigned int AddressSpace = + Mod->getDataLayout().getDefaultGlobalsAddressSpace(); + return LLVMRustGetOrInsertGlobalInAddrspace(M, Name, NameLen, Ty, + AddressSpace); +} + // Must match the layout of `rustc_codegen_llvm::llvm::ffi::AttributeKind`. enum class LLVMRustAttributeKind { AlwaysInline = 0, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 981bfed363dcc..5e7834e800ff7 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1032,6 +1032,7 @@ symbols! { global_asm, global_registration, globs, + gpu_launch_sized_workgroup_mem, gt, guard, guard_patterns, diff --git a/library/core/src/intrinsics/gpu.rs b/library/core/src/intrinsics/gpu.rs index 9e7624841d0c6..43cb7251c3c88 100644 --- a/library/core/src/intrinsics/gpu.rs +++ b/library/core/src/intrinsics/gpu.rs @@ -5,6 +5,51 @@ #![unstable(feature = "gpu_intrinsics", issue = "none")] +/// Returns the pointer to workgroup memory allocated at launch-time on GPUs. +/// +/// Workgroup memory is a memory region that is shared between all threads in +/// the same workgroup. It is faster to access than other memory but pointers do not +/// work outside the workgroup where they were obtained. +/// Workgroup memory can be allocated statically or after compilation, when +/// launching a gpu-kernel. `gpu_launch_sized_workgroup_mem` returns the pointer to +/// the memory that is allocated at launch-time. +/// The size of this memory can differ between launches of a gpu-kernel, depending on +/// what is specified at launch-time. +/// However, the alignment is fixed by the kernel itself, at compile-time. +/// +/// The returned pointer is the start of the workgroup memory region that is +/// allocated at launch-time. +/// All calls to `gpu_launch_sized_workgroup_mem` in a workgroup, independent of the +/// generic type, return the same address, so alias the same memory. +/// The returned pointer is aligned by at least the alignment of `T`. +/// +/// If `gpu_launch_sized_workgroup_mem` is invoked multiple times with different +/// types that have different alignment, then you may only rely on the resulting +/// pointer having the alignment of `T` after a call to `gpu_launch_sized_workgroup_mem::` +/// has occurred in the current program execution. +/// +/// # Safety +/// +/// The pointer is safe to dereference from the start (the returned pointer) up to the +/// size of workgroup memory that was specified when launching the current gpu-kernel. +/// This allocated size is not related in any way to `T`. +/// +/// The user must take care of synchronizing access to workgroup memory between +/// threads in a workgroup. The usual data race requirements apply. +/// +/// # Other APIs +/// +/// CUDA and HIP call this dynamic shared memory, shared between threads in a block. +/// OpenCL and SYCL call this local memory, shared between threads in a work-group. +/// GLSL calls this shared memory, shared between invocations in a work group. +/// DirectX calls this groupshared memory, shared between threads in a thread-group. +#[must_use = "returns a pointer that does nothing unless used"] +#[rustc_intrinsic] +#[rustc_nounwind] +#[unstable(feature = "gpu_launch_sized_workgroup_mem", issue = "135513")] +#[cfg(any(target_arch = "amdgpu", target_arch = "nvptx64"))] +pub fn gpu_launch_sized_workgroup_mem() -> *mut T; + /// Returns a pointer to the HSA kernel dispatch packet. /// /// A `gpu-kernel` on amdgpu is always launched through a kernel dispatch packet. diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs index d144ffa222097..4e2f71b94ce2d 100644 --- a/src/tools/tidy/src/style.rs +++ b/src/tools/tidy/src/style.rs @@ -222,6 +222,10 @@ fn should_ignore(line: &str) -> bool { || static_regex!( "\\s*//@ \\!?(count|files|has|has-dir|hasraw|matches|matchesraw|snapshot)\\s.*" ).is_match(line) + // Matching for FileCheck checks + || static_regex!( + "\\s*// [a-zA-Z0-9-_]*:\\s.*" + ).is_match(line) } /// Returns `true` if `line` is allowed to be longer than the normal limit. diff --git a/tests/codegen-llvm/gpu-launch-sized-workgroup-memory.rs b/tests/codegen-llvm/gpu-launch-sized-workgroup-memory.rs new file mode 100644 index 0000000000000..4764160fd0b59 --- /dev/null +++ b/tests/codegen-llvm/gpu-launch-sized-workgroup-memory.rs @@ -0,0 +1,41 @@ +// Checks that the GPU intrinsic to get launch-sized workgroup memory works +// and correctly aligns the `external addrspace(...) global`s over multiple calls. + +//@ revisions: amdgpu nvptx-pre-llvm-23 nvptx-post-llvm-23 +//@ compile-flags: --crate-type=rlib -Copt-level=1 +// +//@ [amdgpu] compile-flags: --target amdgcn-amd-amdhsa -Ctarget-cpu=gfx900 +//@ [amdgpu] needs-llvm-components: amdgpu + +//@ [nvptx-pre-llvm-23] compile-flags: --target nvptx64-nvidia-cuda +//@ [nvptx-pre-llvm-23] needs-llvm-components: nvptx +//@ [nvptx-pre-llvm-23] max-llvm-major-version: 22 +//@ [nvptx-post-llvm-23] compile-flags: --target nvptx64-nvidia-cuda +//@ [nvptx-post-llvm-23] needs-llvm-components: nvptx +//@ [nvptx-post-llvm-23] min-llvm-version: 23 +//@ add-minicore +#![feature(intrinsics, no_core, rustc_attrs)] +#![no_core] + +extern crate minicore; + +#[rustc_intrinsic] +#[rustc_nounwind] +fn gpu_launch_sized_workgroup_mem() -> *mut T; + +// amdgpu-DAG: @[[SMALL:[^ ]+]] = external addrspace(3) global [0 x i8], align 4 +// amdgpu-DAG: @[[BIG:[^ ]+]] = external addrspace(3) global [0 x i8], align 8 +// amdgpu: ret { ptr, ptr } { ptr addrspacecast (ptr addrspace(3) @[[SMALL]] to ptr), ptr addrspacecast (ptr addrspace(3) @[[BIG]] to ptr) } + +// nvptx-pre-llvm-23: @[[BIG:[^ ]+]] = external addrspace(3) global [0 x i8], align 8 +// nvptx-pre-llvm-23: ret { ptr, ptr } { ptr addrspacecast (ptr addrspace(3) @[[BIG]] to ptr), ptr addrspacecast (ptr addrspace(3) @[[BIG]] to ptr) } + +// nvptx-post-llvm-23-DAG: @[[SMALL:[^ ]+]] = external addrspace(3) global [0 x i8], align 4 +// nvptx-post-llvm-23-DAG: @[[BIG:[^ ]+]] = external addrspace(3) global [0 x i8], align 8 +// nvptx-post-llvm-23: ret { ptr, ptr } { ptr addrspacecast (ptr addrspace(3) @[[SMALL]] to ptr), ptr addrspacecast (ptr addrspace(3) @[[BIG]] to ptr) } +#[unsafe(no_mangle)] +pub fn fun() -> (*mut i32, *mut f64) { + let small = gpu_launch_sized_workgroup_mem::(); + let big = gpu_launch_sized_workgroup_mem::(); // Increase alignment to 8 + (small, big) +} From b17822575ce06e8d7caa907a7687a2ded0999c05 Mon Sep 17 00:00:00 2001 From: Qai Juang <237468078+qaijuang@users.noreply.github.com> Date: Sat, 25 Apr 2026 11:47:34 -0400 Subject: [PATCH 2/5] Do not suggest internal cfg trace attributes --- compiler/rustc_resolve/src/diagnostics.rs | 5 +++++ tests/ui/suggestions/attribute-typos.rs | 7 +++++++ tests/ui/suggestions/attribute-typos.stderr | 14 +++++++++++++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index a92ed9cd3ae83..cbec647bdc95e 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1281,6 +1281,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { suggestions.extend( BUILTIN_ATTRIBUTES .iter() + // These trace attributes are compiler-generated and have + // deliberately invalid names. + .filter(|attr| { + !matches!(attr.name, sym::cfg_trace | sym::cfg_attr_trace) + }) .map(|attr| TypoSuggestion::typo_from_name(attr.name, res)), ); } diff --git a/tests/ui/suggestions/attribute-typos.rs b/tests/ui/suggestions/attribute-typos.rs index 4c2336e105e2a..6dbfb1df58767 100644 --- a/tests/ui/suggestions/attribute-typos.rs +++ b/tests/ui/suggestions/attribute-typos.rs @@ -8,4 +8,11 @@ fn bar() {} //~^ ERROR cannot find attribute `rustc_dumm` in this scope //~| ERROR attributes starting with `rustc` are reserved for use by the `rustc` compiler +// Regression test for https://github.com/rust-lang/rust/issues/150566. +#[cfg_trace] //~ ERROR cannot find attribute `cfg_trace` in this scope +fn cfg_trace_attr() {} + +#[cfg_attr_trace] //~ ERROR cannot find attribute `cfg_attr_trace` in this scope +fn cfg_attr_trace_attr() {} + fn main() {} diff --git a/tests/ui/suggestions/attribute-typos.stderr b/tests/ui/suggestions/attribute-typos.stderr index 960e0b0f620ff..7ca425fa9bd6f 100644 --- a/tests/ui/suggestions/attribute-typos.stderr +++ b/tests/ui/suggestions/attribute-typos.stderr @@ -4,6 +4,12 @@ error: attributes starting with `rustc` are reserved for use by the `rustc` comp LL | #[rustc_dumm] | ^^^^^^^^^^ +error: cannot find attribute `cfg_attr_trace` in this scope + --> $DIR/attribute-typos.rs:15:3 + | +LL | #[cfg_attr_trace] + | ^^^^^^^^^^^^^^ + error: cannot find attribute `rustc_dumm` in this scope --> $DIR/attribute-typos.rs:7:3 | @@ -15,6 +21,12 @@ help: a built-in attribute with a similar name exists LL | #[rustc_dummy] | + +error: cannot find attribute `cfg_trace` in this scope + --> $DIR/attribute-typos.rs:12:3 + | +LL | #[cfg_trace] + | ^^^^^^^^^ + error: cannot find attribute `tests` in this scope --> $DIR/attribute-typos.rs:4:3 | @@ -41,5 +53,5 @@ help: a built-in attribute with a similar name exists LL | #[deprecated] | + -error: aborting due to 4 previous errors +error: aborting due to 6 previous errors From a4f5c6e9715f4f4198ce358204948f02ba8ee807 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 9 Apr 2026 21:42:34 +0200 Subject: [PATCH 3/5] error on invalid macho section specifier --- .../src/attributes/link_attrs.rs | 40 ++++++++++++- .../src/session_diagnostics.rs | 19 ++++++ .../rustc_hir/src/attrs/data_structures.rs | 2 +- tests/codegen-llvm/link_section.rs | 18 +++--- .../codegen-llvm/naked-fn/naked-functions.rs | 9 ++- tests/rustdoc-html/attributes-2021-edition.rs | 4 +- tests/rustdoc-html/attributes.rs | 1 + tests/rustdoc-html/inline_cross/attributes.rs | 2 +- .../inline_cross/auxiliary/attributes.rs | 2 +- tests/rustdoc-json/attrs/link_section_2021.rs | 4 +- tests/rustdoc-json/attrs/link_section_2024.rs | 4 +- tests/ui/asm/naked-functions.rs | 7 ++- tests/ui/attributes/attr-on-mac-call.rs | 2 +- tests/ui/attributes/attr-on-mac-call.stderr | 4 +- .../codegen_attr_on_required_trait_method.rs | 2 +- ...degen_attr_on_required_trait_method.stderr | 4 +- .../issue-43106-gating-of-builtin-attrs.rs | 26 ++++----- ...issue-43106-gating-of-builtin-attrs.stderr | 32 +++++----- tests/ui/linkage-attr/link-section-macho.rs | 58 +++++++++++++++++++ .../ui/linkage-attr/link-section-macho.stderr | 37 ++++++++++++ .../ui/linkage-attr/link-section-placement.rs | 45 +++++++------- tests/ui/lint/lint-unsafe-code.rs | 4 +- tests/ui/lint/lint-unsafe-code.stderr | 8 +-- tests/ui/lint/unused/unused-attr-duplicate.rs | 11 ++-- .../lint/unused/unused-attr-duplicate.stderr | 48 +++++++-------- .../unsafe-attributes-fix.fixed | 10 ++-- .../unsafe-attributes-fix.rs | 10 ++-- .../unsafe-attributes-fix.stderr | 4 +- 28 files changed, 288 insertions(+), 129 deletions(-) create mode 100644 tests/ui/linkage-attr/link-section-macho.rs create mode 100644 tests/ui/linkage-attr/link-section-macho.stderr diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 9f48f7f8ab559..b563ca5354285 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -16,8 +16,9 @@ use crate::attributes::cfg::parse_cfg_entry; use crate::session_diagnostics::{ AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ExportSymbolsNeedsStatic, ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink, InvalidLinkModifier, - LinkFrameworkApple, LinkOrdinalOutOfRange, LinkRequiresName, MultipleModifiers, - NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows, WholeArchiveNeedsStatic, + InvalidMachoSection, InvalidMachoSectionReason, LinkFrameworkApple, LinkOrdinalOutOfRange, + LinkRequiresName, MultipleModifiers, NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows, + WholeArchiveNeedsStatic, }; pub(crate) struct LinkNameParser; @@ -462,6 +463,29 @@ impl LinkParser { pub(crate) struct LinkSectionParser; +fn check_link_section_macho(name: Symbol) -> Result<(), InvalidMachoSectionReason> { + let mut parts = name.as_str().split(',').map(|s| s.trim()); + + // The segment can be empty. + let _segment = parts.next(); + + // But the section is required. + let section = match parts.next() { + None | Some("") => return Err(InvalidMachoSectionReason::MissingSection), + Some(section) => section, + }; + + if section.len() > 16 { + return Err(InvalidMachoSectionReason::SectionTooLong { section: section.to_string() }); + } + + // LLVM also checks the other components of the section specifier, but that logic is hard to + // keep in sync. We skip it here for now, assuming that if you got that far you'll be able + // to interpret the LLVM errors. + + Ok(()) +} + impl SingleAttributeParser for LinkSectionParser { const PATH: &[Symbol] = &[sym::link_section]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; @@ -495,6 +519,18 @@ impl SingleAttributeParser for LinkSectionParser { return None; } + // We (currently) only validate macho section specifiers. + match cx.sess.target.binary_format { + BinaryFormat::MachO => match check_link_section_macho(name) { + Ok(()) => {} + Err(reason) => { + cx.emit_err(InvalidMachoSection { name_span: nv.value_span, reason }); + return None; + } + }, + BinaryFormat::Coff | BinaryFormat::Elf | BinaryFormat::Wasm | BinaryFormat::Xcoff => {} + } + Some(LinkSection { name, span: cx.attr_span }) } } diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 203c7f8ebff1b..0a9c96033257d 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -1137,3 +1137,22 @@ pub(crate) struct UnstableAttrForAlreadyStableFeature { #[label("the stability attribute annotates this item")] pub item_span: Span, } + +#[derive(Diagnostic)] +#[diag("invalid Mach-O section specifier")] +pub(crate) struct InvalidMachoSection { + #[primary_span] + #[label("not a valid Mach-O section specifier")] + pub name_span: Span, + #[subdiagnostic] + pub reason: InvalidMachoSectionReason, +} + +#[derive(Subdiagnostic)] +pub(crate) enum InvalidMachoSectionReason { + #[note("a Mach-O section specifier requires a segment and a section, separated by a comma")] + #[help("an example of a valid Mach-O section specifier is `__TEXT,__cstring`")] + MissingSection, + #[note("section name `{$section}` is longer than 16 bytes")] + SectionTooLong { section: String }, +} diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 78ce2f019a390..6304b830f6ed7 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1098,8 +1098,8 @@ pub enum AttributeKind { /// Represents [`#[link_section]`](https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute) LinkSection { - name: Symbol, span: Span, + name: Symbol, }, /// Represents `#[linkage]`. diff --git a/tests/codegen-llvm/link_section.rs b/tests/codegen-llvm/link_section.rs index f62f694807933..f196ea86c447d 100644 --- a/tests/codegen-llvm/link_section.rs +++ b/tests/codegen-llvm/link_section.rs @@ -3,14 +3,14 @@ #![crate_type = "lib"] -// CHECK: @VAR1 = {{(dso_local )?}}constant [4 x i8] c"\01\00\00\00", section ".test_one" +// CHECK: @VAR1 = {{(dso_local )?}}constant [4 x i8] c"\01\00\00\00", section "__TEST,one" #[no_mangle] -#[link_section = ".test_one"] +#[link_section = "__TEST,one"] #[cfg(target_endian = "little")] pub static VAR1: u32 = 1; #[no_mangle] -#[link_section = ".test_one"] +#[link_section = "__TEST,one"] #[cfg(target_endian = "big")] pub static VAR1: u32 = 0x01000000; @@ -19,17 +19,17 @@ pub enum E { B(f32), } -// CHECK: @VAR2 = {{(dso_local )?}}constant {{.*}}, section ".test_two" +// CHECK: @VAR2 = {{(dso_local )?}}constant {{.*}}, section "__TEST,two" #[no_mangle] -#[link_section = ".test_two"] +#[link_section = "__TEST,two"] pub static VAR2: E = E::A(666); -// CHECK: @VAR3 = {{(dso_local )?}}constant {{.*}}, section ".test_three" +// CHECK: @VAR3 = {{(dso_local )?}}constant {{.*}}, section "__TEST,three" #[no_mangle] -#[link_section = ".test_three"] +#[link_section = "__TEST,three"] pub static VAR3: E = E::B(1.); -// CHECK: define {{(dso_local )?}}void @fn1() {{.*}} section ".test_four" { +// CHECK: define {{(dso_local )?}}void @fn1() {{.*}} section "__TEST,four" { #[no_mangle] -#[link_section = ".test_four"] +#[link_section = "__TEST,four"] pub fn fn1() {} diff --git a/tests/codegen-llvm/naked-fn/naked-functions.rs b/tests/codegen-llvm/naked-fn/naked-functions.rs index a782ab5310e34..dba77cd0e5365 100644 --- a/tests/codegen-llvm/naked-fn/naked-functions.rs +++ b/tests/codegen-llvm/naked-fn/naked-functions.rs @@ -22,7 +22,7 @@ //@[thumb] needs-llvm-components: arm #![crate_type = "lib"] -#![feature(no_core, lang_items, rustc_attrs)] +#![feature(no_core, lang_items, rustc_attrs, cfg_target_object_format)] #![no_core] extern crate minicore; @@ -170,14 +170,17 @@ pub extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize { } // linux,linux_no_function_sections: .pushsection .text.some_different_name,\22ax\22, @progbits -// macos: .pushsection .text.some_different_name,regular,pure_instructions +// macos: .pushsection __TEXT,different,regular,pure_instructions // win_x86_msvc,win_x86_gnu,win_i686_gnu: .section .text.some_different_name,\22xr\22 // win_x86_gnu_function_sections: .section .text.some_different_name,\22xr\22 // thumb: .pushsection .text.some_different_name,\22ax\22, %progbits // CHECK-LABEL: test_link_section: #[no_mangle] #[unsafe(naked)] -#[link_section = ".text.some_different_name"] +#[link_section = cfg_select!( + target_object_format = "mach-o" => "__TEXT,different", + _ => ".text.some_different_name" +)] pub extern "C" fn test_link_section() { cfg_select! { all(target_arch = "arm", target_feature = "thumb-mode") => { diff --git a/tests/rustdoc-html/attributes-2021-edition.rs b/tests/rustdoc-html/attributes-2021-edition.rs index b5028d8c85254..40d6dda508a59 100644 --- a/tests/rustdoc-html/attributes-2021-edition.rs +++ b/tests/rustdoc-html/attributes-2021-edition.rs @@ -9,6 +9,6 @@ pub extern "C" fn f() {} #[export_name = "bar"] pub extern "C" fn g() {} -//@ has foo/fn.example.html '//pre[@class="rust item-decl"]' '#[unsafe(link_section = ".text")]' -#[link_section = ".text"] +//@ has foo/fn.example.html '//pre[@class="rust item-decl"]' '#[unsafe(link_section = "__TEXT,__text")]' +#[link_section = "__TEXT,__text"] pub extern "C" fn example() {} diff --git a/tests/rustdoc-html/attributes.rs b/tests/rustdoc-html/attributes.rs index 6032c3d388011..8a6dd5ab33d55 100644 --- a/tests/rustdoc-html/attributes.rs +++ b/tests/rustdoc-html/attributes.rs @@ -1,4 +1,5 @@ //@ edition: 2024 +//@ only-linux #![crate_name = "foo"] //@ has foo/fn.f.html '//*[@class="code-attribute"]' '#[unsafe(no_mangle)]' diff --git a/tests/rustdoc-html/inline_cross/attributes.rs b/tests/rustdoc-html/inline_cross/attributes.rs index 1657b7bdc8f77..324686a33612f 100644 --- a/tests/rustdoc-html/inline_cross/attributes.rs +++ b/tests/rustdoc-html/inline_cross/attributes.rs @@ -9,7 +9,7 @@ pub use attributes::no_mangle; //@ has 'user/fn.link_section.html' '//pre[@class="rust item-decl"]' \ -// '#[unsafe(link_section = ".here")]' +// '#[unsafe(link_section = "__TEXT,__here")]' pub use attributes::link_section; //@ has 'user/fn.export_name.html' '//pre[@class="rust item-decl"]' \ diff --git a/tests/rustdoc-html/inline_cross/auxiliary/attributes.rs b/tests/rustdoc-html/inline_cross/auxiliary/attributes.rs index 6068d38558586..efceda87165c5 100644 --- a/tests/rustdoc-html/inline_cross/auxiliary/attributes.rs +++ b/tests/rustdoc-html/inline_cross/auxiliary/attributes.rs @@ -1,7 +1,7 @@ #[unsafe(no_mangle)] pub fn no_mangle() {} -#[unsafe(link_section = ".here")] +#[unsafe(link_section = "__TEXT,__here")] pub fn link_section() {} #[unsafe(export_name = "exonym")] diff --git a/tests/rustdoc-json/attrs/link_section_2021.rs b/tests/rustdoc-json/attrs/link_section_2021.rs index acd8ecd0e30ca..82df8071592c7 100644 --- a/tests/rustdoc-json/attrs/link_section_2021.rs +++ b/tests/rustdoc-json/attrs/link_section_2021.rs @@ -2,6 +2,6 @@ #![no_std] //@ count "$.index[?(@.name=='example')].attrs[*]" 1 -//@ is "$.index[?(@.name=='example')].attrs[*].link_section" '".text"' -#[link_section = ".text"] +//@ is "$.index[?(@.name=='example')].attrs[*].link_section" '"__TEXT,__text"' +#[link_section = "__TEXT,__text"] pub extern "C" fn example() {} diff --git a/tests/rustdoc-json/attrs/link_section_2024.rs b/tests/rustdoc-json/attrs/link_section_2024.rs index 8107493229b52..acf9406ce5284 100644 --- a/tests/rustdoc-json/attrs/link_section_2024.rs +++ b/tests/rustdoc-json/attrs/link_section_2024.rs @@ -5,6 +5,6 @@ // However, the unsafe qualification is not shown by rustdoc. //@ count "$.index[?(@.name=='example')].attrs[*]" 1 -//@ is "$.index[?(@.name=='example')].attrs[*].link_section" '".text"' -#[unsafe(link_section = ".text")] +//@ is "$.index[?(@.name=='example')].attrs[*].link_section" '"__TEXT,__text"' +#[unsafe(link_section = "__TEXT,__text")] pub extern "C" fn example() {} diff --git a/tests/ui/asm/naked-functions.rs b/tests/ui/asm/naked-functions.rs index dadacb8d4683e..55f2f552ad31c 100644 --- a/tests/ui/asm/naked-functions.rs +++ b/tests/ui/asm/naked-functions.rs @@ -3,7 +3,7 @@ //@ ignore-spirv //@ reference: attributes.codegen.naked.body -#![feature(asm_unwind, linkage, rustc_attrs)] +#![feature(asm_unwind, linkage, rustc_attrs, cfg_target_object_format)] #![crate_type = "lib"] use std::arch::{asm, naked_asm}; @@ -200,7 +200,10 @@ pub extern "C" fn compatible_must_use_attributes() -> u64 { } #[export_name = "exported_function_name"] -#[link_section = ".custom_section"] +#[link_section = cfg_select!( + target_object_format = "mach-o" => "__TEXT,__custom", + _ => ".custom", +)] #[unsafe(naked)] pub extern "C" fn compatible_ffi_attributes_1() { naked_asm!("", options(raw)); diff --git a/tests/ui/attributes/attr-on-mac-call.rs b/tests/ui/attributes/attr-on-mac-call.rs index a23ced123efb5..577272a99a0d9 100644 --- a/tests/ui/attributes/attr-on-mac-call.rs +++ b/tests/ui/attributes/attr-on-mac-call.rs @@ -27,7 +27,7 @@ fn main() { #[link_name = "x"] //~^ WARN attribute cannot be used on macro calls //~| WARN previously accepted - #[link_section = "x"] + #[link_section = "__TEXT,__text"] //~^ WARN attribute cannot be used on macro calls //~| WARN previously accepted #[link_ordinal(42)] diff --git a/tests/ui/attributes/attr-on-mac-call.stderr b/tests/ui/attributes/attr-on-mac-call.stderr index 3bb50f2d6f654..9fc4e319117f6 100644 --- a/tests/ui/attributes/attr-on-mac-call.stderr +++ b/tests/ui/attributes/attr-on-mac-call.stderr @@ -78,8 +78,8 @@ LL | #[link_name = "x"] warning: `#[link_section]` attribute cannot be used on macro calls --> $DIR/attr-on-mac-call.rs:30:5 | -LL | #[link_section = "x"] - | ^^^^^^^^^^^^^^^^^^^^^ +LL | #[link_section = "__TEXT,__text"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[link_section]` can be applied to functions and statics diff --git a/tests/ui/attributes/codegen_attr_on_required_trait_method.rs b/tests/ui/attributes/codegen_attr_on_required_trait_method.rs index 6301a5a8ebf70..cd9987f1e6a38 100644 --- a/tests/ui/attributes/codegen_attr_on_required_trait_method.rs +++ b/tests/ui/attributes/codegen_attr_on_required_trait_method.rs @@ -7,7 +7,7 @@ trait Test { //~^ ERROR cannot be used on required trait methods [unused_attributes] //~| WARN previously accepted fn method1(&self); - #[link_section = ".text"] + #[link_section = "__TEXT,__text"] //~^ ERROR cannot be used on required trait methods [unused_attributes] //~| WARN previously accepted fn method2(&self); diff --git a/tests/ui/attributes/codegen_attr_on_required_trait_method.stderr b/tests/ui/attributes/codegen_attr_on_required_trait_method.stderr index 0770ccae41469..7b529440b008c 100644 --- a/tests/ui/attributes/codegen_attr_on_required_trait_method.stderr +++ b/tests/ui/attributes/codegen_attr_on_required_trait_method.stderr @@ -15,8 +15,8 @@ LL | #![deny(unused_attributes)] error: `#[link_section]` attribute cannot be used on required trait methods --> $DIR/codegen_attr_on_required_trait_method.rs:10:5 | -LL | #[link_section = ".text"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[link_section = "__TEXT,__text"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[link_section]` can be applied to functions, inherent methods, provided trait methods, statics, and trait methods in impl blocks diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs index 36568449b82e1..8f3e98cfc2a7c 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs @@ -74,7 +74,7 @@ //~| WARN previously accepted //~| HELP can be applied to //~| HELP remove the attribute -#![link_section = "1800"] +#![link_section = ",1800"] //~^ WARN attribute cannot be used on //~| WARN previously accepted //~| HELP can be applied to @@ -616,66 +616,66 @@ mod link_name { //~| HELP remove the attribute } -#[link_section = "1800"] +#[link_section = ",1800"] //~^ WARN attribute cannot be used on //~| WARN previously accepted //~| HELP can be applied to //~| HELP remove the attribute mod link_section { - mod inner { #![link_section="1800"] } + mod inner { #![link_section=",1800"] } //~^ WARN attribute cannot be used on //~| WARN previously accepted //~| HELP can be applied to //~| HELP remove the attribute - #[link_section = "1800"] fn f() { } + #[link_section = ",1800"] fn f() { } - #[link_section = "1800"] struct S; + #[link_section = ",1800"] struct S; //~^ WARN attribute cannot be used on //~| WARN previously accepted //~| HELP can be applied to //~| HELP remove the attribute - #[link_section = "1800"] type T = S; + #[link_section = ",1800"] type T = S; //~^ WARN attribute cannot be used on //~| WARN previously accepted //~| HELP can be applied to //~| HELP remove the attribute - #[link_section = "1800"] impl S { } + #[link_section = ",1800"] impl S { } //~^ WARN attribute cannot be used on //~| WARN previously accepted //~| HELP can be applied to //~| HELP remove the attribute - #[link_section = "1800"] + #[link_section = ",1800"] //~^ WARN attribute cannot be used on //~| WARN previously accepted //~| HELP can be applied to //~| HELP remove the attribute trait Tr { - #[link_section = "1800"] + #[link_section = ",1800"] //~^ WARN attribute cannot be used on //~| WARN previously accepted //~| HELP can be applied to //~| HELP remove the attribute fn inside_tr_no_default(&self); - #[link_section = "1800"] + #[link_section = ",1800"] fn inside_tr_default(&self) { } } impl S { - #[link_section = "1800"] + #[link_section = ",1800"] fn inside_abc_123(&self) { } } impl Tr for S { - #[link_section = "1800"] + #[link_section = ",1800"] fn inside_tr_no_default(&self) { } } - #[link_section = "1800"] + #[link_section = ",1800"] fn should_always_link() { } } diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr index 724d623e79eee..f6ed182b17ef1 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr @@ -866,8 +866,8 @@ LL | #[link_name = "1900"] impl S { } warning: `#[link_section]` attribute cannot be used on modules --> $DIR/issue-43106-gating-of-builtin-attrs.rs:619:1 | -LL | #[link_section = "1800"] - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[link_section = ",1800"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[link_section]` can be applied to functions and statics @@ -875,8 +875,8 @@ LL | #[link_section = "1800"] warning: `#[link_section]` attribute cannot be used on modules --> $DIR/issue-43106-gating-of-builtin-attrs.rs:625:17 | -LL | mod inner { #![link_section="1800"] } - | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | mod inner { #![link_section=",1800"] } + | ^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[link_section]` can be applied to functions and statics @@ -884,8 +884,8 @@ LL | mod inner { #![link_section="1800"] } warning: `#[link_section]` attribute cannot be used on structs --> $DIR/issue-43106-gating-of-builtin-attrs.rs:633:5 | -LL | #[link_section = "1800"] struct S; - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[link_section = ",1800"] struct S; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[link_section]` can be applied to functions and statics @@ -893,8 +893,8 @@ LL | #[link_section = "1800"] struct S; warning: `#[link_section]` attribute cannot be used on type aliases --> $DIR/issue-43106-gating-of-builtin-attrs.rs:639:5 | -LL | #[link_section = "1800"] type T = S; - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[link_section = ",1800"] type T = S; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[link_section]` can be applied to functions and statics @@ -902,8 +902,8 @@ LL | #[link_section = "1800"] type T = S; warning: `#[link_section]` attribute cannot be used on inherent impl blocks --> $DIR/issue-43106-gating-of-builtin-attrs.rs:645:5 | -LL | #[link_section = "1800"] impl S { } - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[link_section = ",1800"] impl S { } + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[link_section]` can be applied to functions and statics @@ -911,8 +911,8 @@ LL | #[link_section = "1800"] impl S { } warning: `#[link_section]` attribute cannot be used on traits --> $DIR/issue-43106-gating-of-builtin-attrs.rs:651:5 | -LL | #[link_section = "1800"] - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[link_section = ",1800"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[link_section]` can be applied to functions and statics @@ -920,8 +920,8 @@ LL | #[link_section = "1800"] warning: `#[link_section]` attribute cannot be used on required trait methods --> $DIR/issue-43106-gating-of-builtin-attrs.rs:657:9 | -LL | #[link_section = "1800"] - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[link_section = ",1800"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[link_section]` can be applied to functions, inherent methods, provided trait methods, statics, and trait methods in impl blocks @@ -1570,8 +1570,8 @@ LL | #![link_name = "1900"] warning: `#[link_section]` attribute cannot be used on crates --> $DIR/issue-43106-gating-of-builtin-attrs.rs:77:1 | -LL | #![link_section = "1800"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![link_section = ",1800"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[link_section]` can be applied to functions and statics diff --git a/tests/ui/linkage-attr/link-section-macho.rs b/tests/ui/linkage-attr/link-section-macho.rs new file mode 100644 index 0000000000000..b414a8cc18607 --- /dev/null +++ b/tests/ui/linkage-attr/link-section-macho.rs @@ -0,0 +1,58 @@ +//@ add-minicore +//@ compile-flags: --target aarch64-apple-darwin +//@ needs-llvm-components: aarch64 +//@ ignore-backends: gcc +#![feature(no_core, rustc_attrs, lang_items)] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::*; + +#[unsafe(link_section = "foo")] +//~^ ERROR invalid Mach-O section specifier +#[unsafe(no_mangle)] +fn missing_section() {} + +#[unsafe(link_section = "foo,")] +//~^ ERROR invalid Mach-O section specifier +#[unsafe(no_mangle)] +fn empty_section() {} + +#[unsafe(link_section = "foo, ")] +//~^ ERROR invalid Mach-O section specifier +#[unsafe(no_mangle)] +fn whitespace_section() {} + +#[unsafe(link_section = "foo,somelongwindedthing")] +//~^ ERROR invalid Mach-O section specifier +#[unsafe(no_mangle)] +fn section_too_long() {} + +#[unsafe(link_section = "foo,bar")] +#[unsafe(no_mangle)] +fn segment_and_section() {} + +#[unsafe(link_section = "foo,bar,")] +#[unsafe(no_mangle)] +fn segment_and_section_and_comma() {} + +#[unsafe(link_section = ",foo")] +#[unsafe(no_mangle)] +fn missing_segment_is_fine() {} + +#[unsafe(link_section = "__TEXT,__stubs,symbol_stubs,none,16")] +#[unsafe(no_mangle)] +fn stub_size_decimal() {} + +#[unsafe(link_section = "__TEXT,__stubs,symbol_stubs,none,0x10")] +#[unsafe(no_mangle)] +fn stub_size_hex() {} + +#[unsafe(link_section = "__TEXT,__stubs,symbol_stubs,none,020")] +#[unsafe(no_mangle)] +fn stub_size_oct() {} + +#[unsafe(link_section = "__TEXT,__stubs,symbol_stubs,none,020,rest,is,ignored")] +#[unsafe(no_mangle)] +fn rest_is_ignored() {} diff --git a/tests/ui/linkage-attr/link-section-macho.stderr b/tests/ui/linkage-attr/link-section-macho.stderr new file mode 100644 index 0000000000000..42eac39c78069 --- /dev/null +++ b/tests/ui/linkage-attr/link-section-macho.stderr @@ -0,0 +1,37 @@ +error: invalid Mach-O section specifier + --> $DIR/link-section-macho.rs:12:25 + | +LL | #[unsafe(link_section = "foo")] + | ^^^^^ not a valid Mach-O section specifier + | + = note: a Mach-O section specifier requires a segment and a section, separated by a comma + = help: an example of a valid Mach-O section specifier is `__TEXT,__cstring` + +error: invalid Mach-O section specifier + --> $DIR/link-section-macho.rs:17:25 + | +LL | #[unsafe(link_section = "foo,")] + | ^^^^^^ not a valid Mach-O section specifier + | + = note: a Mach-O section specifier requires a segment and a section, separated by a comma + = help: an example of a valid Mach-O section specifier is `__TEXT,__cstring` + +error: invalid Mach-O section specifier + --> $DIR/link-section-macho.rs:22:25 + | +LL | #[unsafe(link_section = "foo, ")] + | ^^^^^^^ not a valid Mach-O section specifier + | + = note: a Mach-O section specifier requires a segment and a section, separated by a comma + = help: an example of a valid Mach-O section specifier is `__TEXT,__cstring` + +error: invalid Mach-O section specifier + --> $DIR/link-section-macho.rs:27:25 + | +LL | #[unsafe(link_section = "foo,somelongwindedthing")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ not a valid Mach-O section specifier + | + = note: section name `somelongwindedthing` is longer than 16 bytes + +error: aborting due to 4 previous errors + diff --git a/tests/ui/linkage-attr/link-section-placement.rs b/tests/ui/linkage-attr/link-section-placement.rs index 6a143bfedb451..299ad31d82d0d 100644 --- a/tests/ui/linkage-attr/link-section-placement.rs +++ b/tests/ui/linkage-attr/link-section-placement.rs @@ -2,36 +2,37 @@ //@ run-pass +#![feature(cfg_target_object_format)] // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] #![allow(non_upper_case_globals)] -#[cfg(not(target_vendor = "apple"))] -#[link_section = ".moretext"] -fn i_live_in_more_text() -> &'static str { - "knock knock" -} -#[cfg(not(target_vendor = "apple"))] -#[link_section = ".imm"] -static magic: usize = 42; +cfg_select! { + target_object_format = "mach-o" => { + #[link_section = "__TEXT,__moretext"] + fn i_live_in_more_text() -> &'static str { + "knock knock" + } -#[cfg(not(target_vendor = "apple"))] -#[link_section = ".mut"] -static mut frobulator: usize = 0xdeadbeef; + #[link_section = "__RODATA,__imm"] + static magic: usize = 42; -#[cfg(target_vendor = "apple")] -#[link_section = "__TEXT,__moretext"] -fn i_live_in_more_text() -> &'static str { - "knock knock" -} + #[link_section = "__DATA,__mut"] + static mut frobulator: usize = 0xdeadbeef; + } + _ => { + #[link_section = ".moretext"] + fn i_live_in_more_text() -> &'static str { + "knock knock" + } -#[cfg(target_vendor = "apple")] -#[link_section = "__RODATA,__imm"] -static magic: usize = 42; + #[link_section = ".imm"] + static magic: usize = 42; -#[cfg(target_vendor = "apple")] -#[link_section = "__DATA,__mut"] -static mut frobulator: usize = 0xdeadbeef; + #[link_section = ".mut"] + static mut frobulator: usize = 0xdeadbeef; + } +} pub fn main() { unsafe { diff --git a/tests/ui/lint/lint-unsafe-code.rs b/tests/ui/lint/lint-unsafe-code.rs index b72e4c3a9e7fb..9b32336b05d49 100644 --- a/tests/ui/lint/lint-unsafe-code.rs +++ b/tests/ui/lint/lint-unsafe-code.rs @@ -48,8 +48,8 @@ impl AssocFnTrait for AssocFnFoo { #[export_name = "bar"] fn bar() {} //~ ERROR: declaration of a function with `export_name` #[export_name = "BAR"] static BAR: u32 = 5; //~ ERROR: declaration of a static with `export_name` -#[link_section = ".example_section"] fn uwu() {} //~ ERROR: declaration of a function with `link_section` -#[link_section = ".example_section"] static UWU: u32 = 5; //~ ERROR: declaration of a static with `link_section` +#[link_section = "__TEXT,__text"] fn uwu() {} //~ ERROR: declaration of a function with `link_section` +#[link_section = "__TEXT,__text"] static UWU: u32 = 5; //~ ERROR: declaration of a static with `link_section` struct AssocFnBar; diff --git a/tests/ui/lint/lint-unsafe-code.stderr b/tests/ui/lint/lint-unsafe-code.stderr index 037f0a8323a75..aade33aef44ee 100644 --- a/tests/ui/lint/lint-unsafe-code.stderr +++ b/tests/ui/lint/lint-unsafe-code.stderr @@ -54,16 +54,16 @@ LL | #[export_name = "BAR"] static BAR: u32 = 5; error: declaration of a function with `link_section` --> $DIR/lint-unsafe-code.rs:51:1 | -LL | #[link_section = ".example_section"] fn uwu() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[link_section = "__TEXT,__text"] fn uwu() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the program's behavior with overridden link sections on items is unpredictable and Rust cannot provide guarantees when you manually override them error: declaration of a static with `link_section` --> $DIR/lint-unsafe-code.rs:52:1 | -LL | #[link_section = ".example_section"] static UWU: u32 = 5; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[link_section = "__TEXT,__text"] static UWU: u32 = 5; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the program's behavior with overridden link sections on items is unpredictable and Rust cannot provide guarantees when you manually override them diff --git a/tests/ui/lint/unused/unused-attr-duplicate.rs b/tests/ui/lint/unused/unused-attr-duplicate.rs index 2b29fde128e4f..54c040f4bcac4 100644 --- a/tests/ui/lint/unused/unused-attr-duplicate.rs +++ b/tests/ui/lint/unused/unused-attr-duplicate.rs @@ -61,8 +61,9 @@ pub mod from_path; fn t1() {} #[must_use] -#[must_use = "some message"] //~ ERROR unused attribute -//~^ WARN this was previously accepted +#[must_use = "some message"] +//~^ ERROR unused attribute +//~| WARN this was previously accepted // No warnings for #[repr], would require more logic. #[repr(C)] #[repr(C)] @@ -96,7 +97,7 @@ extern "C" { } #[export_name = "exported_symbol_name"] -#[export_name = "exported_symbol_name2"] //~ ERROR unused attribute +#[export_name = "exported_symbol_name2"] //~ ERROR unused attribute //~^ WARN this was previously accepted pub fn export_test() {} @@ -108,8 +109,8 @@ pub fn no_mangle_test() {} #[used] //~ ERROR unused attribute static FOO: u32 = 0; -#[link_section = ".text"] -#[link_section = ".bss"] +#[link_section = "__TEXT,__text"] +#[link_section = "__DATA,__mod_init_func"] //~^ ERROR unused attribute //~| WARN this was previously accepted pub extern "C" fn example() {} diff --git a/tests/ui/lint/unused/unused-attr-duplicate.stderr b/tests/ui/lint/unused/unused-attr-duplicate.stderr index 1942249d1f8e1..f25263e9cef90 100644 --- a/tests/ui/lint/unused/unused-attr-duplicate.stderr +++ b/tests/ui/lint/unused/unused-attr-duplicate.stderr @@ -104,127 +104,127 @@ LL | #[must_use] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:70:1 + --> $DIR/unused-attr-duplicate.rs:71:1 | LL | #[non_exhaustive] | ^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:69:1 + --> $DIR/unused-attr-duplicate.rs:70:1 | LL | #[non_exhaustive] | ^^^^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:76:1 + --> $DIR/unused-attr-duplicate.rs:77:1 | LL | #[automatically_derived] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:75:1 + --> $DIR/unused-attr-duplicate.rs:76:1 | LL | #[automatically_derived] | ^^^^^^^^^^^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:80:1 + --> $DIR/unused-attr-duplicate.rs:81:1 | LL | #[inline(never)] | ^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:79:1 + --> $DIR/unused-attr-duplicate.rs:80:1 | LL | #[inline(always)] | ^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:83:1 + --> $DIR/unused-attr-duplicate.rs:84:1 | LL | #[cold] | ^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:82:1 + --> $DIR/unused-attr-duplicate.rs:83:1 | LL | #[cold] | ^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:85:1 + --> $DIR/unused-attr-duplicate.rs:86:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:84:1 + --> $DIR/unused-attr-duplicate.rs:85:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:93:5 + --> $DIR/unused-attr-duplicate.rs:94:5 | LL | #[link_name = "rust_dbg_extern_identity_u32"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:92:5 + --> $DIR/unused-attr-duplicate.rs:93:5 | LL | #[link_name = "this_does_not_exist"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:99:1 + --> $DIR/unused-attr-duplicate.rs:100:1 | LL | #[export_name = "exported_symbol_name2"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:98:1 + --> $DIR/unused-attr-duplicate.rs:99:1 | LL | #[export_name = "exported_symbol_name"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:104:1 + --> $DIR/unused-attr-duplicate.rs:105:1 | LL | #[no_mangle] | ^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:103:1 + --> $DIR/unused-attr-duplicate.rs:104:1 | LL | #[no_mangle] | ^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:108:1 + --> $DIR/unused-attr-duplicate.rs:109:1 | LL | #[used] | ^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:107:1 + --> $DIR/unused-attr-duplicate.rs:108:1 | LL | #[used] | ^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:112:1 + --> $DIR/unused-attr-duplicate.rs:113:1 | -LL | #[link_section = ".bss"] - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute +LL | #[link_section = "__DATA,__mod_init_func"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:111:1 + --> $DIR/unused-attr-duplicate.rs:112:1 | -LL | #[link_section = ".text"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[link_section = "__TEXT,__text"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute diff --git a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.fixed b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.fixed index c1adc90161a46..1865405de8e52 100644 --- a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.fixed +++ b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.fixed @@ -15,7 +15,7 @@ macro_rules! ident { //~^ ERROR: unsafe attribute used without unsafe //~| WARN this is accepted in the current edition extern "C" fn bar() {} - } + }; } macro_rules! ident2 { @@ -24,26 +24,26 @@ macro_rules! ident2 { //~^ ERROR: unsafe attribute used without unsafe //~| WARN this is accepted in the current edition extern "C" fn bars() {} - } + }; } macro_rules! meta { ($m:meta) => { #[$m] extern "C" fn baz() {} - } + }; } macro_rules! meta2 { ($m:meta) => { #[$m] extern "C" fn baw() {} - } + }; } macro_rules! with_cfg_attr { () => { - #[cfg_attr(true, unsafe(link_section = ".custom_section"))] + #[cfg_attr(true, unsafe(link_section = "__TEXT,__custom"))] //~^ ERROR: unsafe attribute used without unsafe //~| WARN this is accepted in the current edition pub extern "C" fn abc() {} diff --git a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.rs b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.rs index 9fdf379046345..ec290957e537e 100644 --- a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.rs +++ b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.rs @@ -15,7 +15,7 @@ macro_rules! ident { //~^ ERROR: unsafe attribute used without unsafe //~| WARN this is accepted in the current edition extern "C" fn bar() {} - } + }; } macro_rules! ident2 { @@ -24,26 +24,26 @@ macro_rules! ident2 { //~^ ERROR: unsafe attribute used without unsafe //~| WARN this is accepted in the current edition extern "C" fn bars() {} - } + }; } macro_rules! meta { ($m:meta) => { #[$m] extern "C" fn baz() {} - } + }; } macro_rules! meta2 { ($m:meta) => { #[$m] extern "C" fn baw() {} - } + }; } macro_rules! with_cfg_attr { () => { - #[cfg_attr(true, link_section = ".custom_section")] + #[cfg_attr(true, link_section = "__TEXT,__custom")] //~^ ERROR: unsafe attribute used without unsafe //~| WARN this is accepted in the current edition pub extern "C" fn abc() {} diff --git a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.stderr b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.stderr index 279e61a9cb673..f03842139f0b8 100644 --- a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.stderr +++ b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.stderr @@ -79,7 +79,7 @@ LL | #[unsafe($e = $l)] error: unsafe attribute used without unsafe --> $DIR/unsafe-attributes-fix.rs:46:26 | -LL | #[cfg_attr(true, link_section = ".custom_section")] +LL | #[cfg_attr(true, link_section = "__TEXT,__custom")] | ^^^^^^^^^^^^ usage of unsafe attribute ... LL | with_cfg_attr!(); @@ -90,7 +90,7 @@ LL | with_cfg_attr!(); = note: this error originates in the macro `with_cfg_attr` (in Nightly builds, run with -Z macro-backtrace for more info) help: wrap the attribute in `unsafe(...)` | -LL | #[cfg_attr(true, unsafe(link_section = ".custom_section"))] +LL | #[cfg_attr(true, unsafe(link_section = "__TEXT,__custom"))] | +++++++ + error: unsafe attribute used without unsafe From 642ee63c228ad8d8c988cb3c68089776ddc97980 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 22 Apr 2026 16:59:44 +0200 Subject: [PATCH 4/5] Add regression test --- .../consts/drop-impl-nonconst-drop-field.rs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 tests/ui/consts/drop-impl-nonconst-drop-field.rs diff --git a/tests/ui/consts/drop-impl-nonconst-drop-field.rs b/tests/ui/consts/drop-impl-nonconst-drop-field.rs new file mode 100644 index 0000000000000..a431c91bf2d9e --- /dev/null +++ b/tests/ui/consts/drop-impl-nonconst-drop-field.rs @@ -0,0 +1,31 @@ +#![feature(const_trait_impl)] +#![feature(const_destruct)] +//@ check-pass + +use std::marker::Destruct; + +struct NotConstDrop; + +impl Drop for NotConstDrop { + fn drop(&mut self) {} +} + +struct ConstDrop(NotConstDrop); + +impl const Drop for ConstDrop { + fn drop(&mut self) {} +} + +struct ConstDrop2(T); + +impl const Drop for ConstDrop2 { + fn drop(&mut self) {} +} + +struct ConstDrop3(T); + +impl const Drop for ConstDrop3 { + fn drop(&mut self) {} +} + +fn main() {} From 7dcedafff23aaddf40d061d9dd5be3b62aa2cfe8 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 22 Apr 2026 17:09:33 +0200 Subject: [PATCH 5/5] Reject implementing `const Drop` for types that are not const `Destruct` already --- .../src/check/always_applicable.rs | 62 ++++++++++++++++++- .../consts/drop-impl-nonconst-drop-field.rs | 3 +- .../drop-impl-nonconst-drop-field.stderr | 27 ++++++++ .../const-drop-fail.new_precise.stderr | 25 ++++++-- .../const-drop-fail.new_stock.stderr | 25 ++++++-- .../const-drop-fail.old_precise.stderr | 25 ++++++-- .../const-drop-fail.old_stock.stderr | 25 ++++++-- .../ui/traits/const-traits/const-drop-fail.rs | 1 + .../traits/const-traits/minicore-drop-fail.rs | 2 +- 9 files changed, 168 insertions(+), 27 deletions(-) create mode 100644 tests/ui/consts/drop-impl-nonconst-drop-field.stderr diff --git a/compiler/rustc_hir_analysis/src/check/always_applicable.rs b/compiler/rustc_hir_analysis/src/check/always_applicable.rs index bdef0b57d6914..dbf5465ee18b3 100644 --- a/compiler/rustc_hir_analysis/src/check/always_applicable.rs +++ b/compiler/rustc_hir_analysis/src/check/always_applicable.rs @@ -11,7 +11,7 @@ use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt}; use rustc_infer::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::span_bug; use rustc_middle::ty::util::CheckRegions; -use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypingMode}; +use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, TypingMode}; use rustc_trait_selection::regions::InferCtxtRegionExt; use rustc_trait_selection::traits::{self, ObligationCtxt}; @@ -65,6 +65,8 @@ pub(crate) fn check_drop_impl( adt_to_impl_args, )?; + ensure_all_fields_are_const_destruct(tcx, drop_impl_did, adt_def.did())?; + ensure_impl_predicates_are_implied_by_item_defn( tcx, drop_impl_did, @@ -173,6 +175,64 @@ fn ensure_impl_params_and_item_params_correspond<'tcx>( Err(err.emit()) } +fn ensure_all_fields_are_const_destruct<'tcx>( + tcx: TyCtxt<'tcx>, + impl_def_id: LocalDefId, + adt_def_id: DefId, +) -> Result<(), ErrorGuaranteed> { + if !tcx.is_conditionally_const(impl_def_id) { + return Ok(()); + } + let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); + + let impl_span = tcx.def_span(impl_def_id.to_def_id()); + let env = + ty::EarlyBinder::bind(tcx.param_env(impl_def_id)).instantiate_identity().skip_norm_wip(); + let args = ty::GenericArgs::identity_for_item(tcx, impl_def_id); + let destruct_trait = tcx.lang_items().destruct_trait().unwrap(); + for field in tcx.adt_def(adt_def_id).all_fields() { + let field_ty = field.ty(tcx, args); + let cause = traits::ObligationCause::new( + tcx.def_span(field.did), + impl_def_id, + ObligationCauseCode::Misc, + ); + ocx.register_obligation(traits::Obligation::new( + tcx, + cause, + env, + ty::ClauseKind::HostEffect(ty::HostEffectPredicate { + trait_ref: ty::TraitRef::new(tcx, destruct_trait, [field_ty]), + constness: ty::BoundConstness::Maybe, + }), + )); + } + ocx.evaluate_obligations_error_on_ambiguity() + .into_iter() + .map(|error| { + let ty::ClauseKind::HostEffect(eff) = + error.root_obligation.predicate.expect_clause().kind().no_bound_vars().unwrap() + else { + unreachable!() + }; + let field_ty = eff.trait_ref.self_ty(); + let diag = struct_span_code_err!( + tcx.dcx(), + error.root_obligation.cause.span, + E0367, + "`{field_ty}` does not implement `[const] Destruct`", + ) + .with_span_note(impl_span, "required for this `Drop` impl"); + if field_ty.has_param() { + // FIXME: suggest adding `[const] Destruct` by teaching + // `suggest_restricting_param_bound` about const traits. + } + Err(diag.emit()) + }) + .collect() +} + /// Confirms that all predicates defined on the `Drop` impl (`drop_impl_def_id`) are able to be /// proven from within `adt_def_id`'s environment. I.e. all the predicates on the impl are /// implied by the ADT being well formed. diff --git a/tests/ui/consts/drop-impl-nonconst-drop-field.rs b/tests/ui/consts/drop-impl-nonconst-drop-field.rs index a431c91bf2d9e..9715724007cc4 100644 --- a/tests/ui/consts/drop-impl-nonconst-drop-field.rs +++ b/tests/ui/consts/drop-impl-nonconst-drop-field.rs @@ -1,6 +1,5 @@ #![feature(const_trait_impl)] #![feature(const_destruct)] -//@ check-pass use std::marker::Destruct; @@ -11,12 +10,14 @@ impl Drop for NotConstDrop { } struct ConstDrop(NotConstDrop); +//~^ ERROR: `NotConstDrop` does not implement `[const] Destruct` impl const Drop for ConstDrop { fn drop(&mut self) {} } struct ConstDrop2(T); +//~^ ERROR: `T` does not implement `[const] Destruct` impl const Drop for ConstDrop2 { fn drop(&mut self) {} diff --git a/tests/ui/consts/drop-impl-nonconst-drop-field.stderr b/tests/ui/consts/drop-impl-nonconst-drop-field.stderr new file mode 100644 index 0000000000000..0e626176579c6 --- /dev/null +++ b/tests/ui/consts/drop-impl-nonconst-drop-field.stderr @@ -0,0 +1,27 @@ +error[E0367]: `NotConstDrop` does not implement `[const] Destruct` + --> $DIR/drop-impl-nonconst-drop-field.rs:12:18 + | +LL | struct ConstDrop(NotConstDrop); + | ^^^^^^^^^^^^ + | +note: required for this `Drop` impl + --> $DIR/drop-impl-nonconst-drop-field.rs:15:1 + | +LL | impl const Drop for ConstDrop { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0367]: `T` does not implement `[const] Destruct` + --> $DIR/drop-impl-nonconst-drop-field.rs:19:22 + | +LL | struct ConstDrop2(T); + | ^ + | +note: required for this `Drop` impl + --> $DIR/drop-impl-nonconst-drop-field.rs:22:1 + | +LL | impl const Drop for ConstDrop2 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0367`. diff --git a/tests/ui/traits/const-traits/const-drop-fail.new_precise.stderr b/tests/ui/traits/const-traits/const-drop-fail.new_precise.stderr index ff803ff889b8f..db4df30800b6e 100644 --- a/tests/ui/traits/const-traits/const-drop-fail.new_precise.stderr +++ b/tests/ui/traits/const-traits/const-drop-fail.new_precise.stderr @@ -1,5 +1,17 @@ +error[E0367]: `NonTrivialDrop` does not implement `[const] Destruct` + --> $DIR/const-drop-fail.rs:19:30 + | +LL | struct ConstImplWithDropGlue(NonTrivialDrop); + | ^^^^^^^^^^^^^^ + | +note: required for this `Drop` impl + --> $DIR/const-drop-fail.rs:22:1 + | +LL | impl const Drop for ConstImplWithDropGlue { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied - --> $DIR/const-drop-fail.rs:34:5 + --> $DIR/const-drop-fail.rs:35:5 | LL | const _: () = check($exp); | ----- required by a bound introduced by this call @@ -8,13 +20,13 @@ LL | NonTrivialDrop, | ^^^^^^^^^^^^^^ | note: required by a bound in `check` - --> $DIR/const-drop-fail.rs:25:19 + --> $DIR/const-drop-fail.rs:26:19 | LL | const fn check(_: T) {} | ^^^^^^^^^^^^^^^^ required by this bound in `check` error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied - --> $DIR/const-drop-fail.rs:36:5 + --> $DIR/const-drop-fail.rs:37:5 | LL | const _: () = check($exp); | ----- required by a bound introduced by this call @@ -23,11 +35,12 @@ LL | ConstImplWithDropGlue(NonTrivialDrop), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: required by a bound in `check` - --> $DIR/const-drop-fail.rs:25:19 + --> $DIR/const-drop-fail.rs:26:19 | LL | const fn check(_: T) {} | ^^^^^^^^^^^^^^^^ required by this bound in `check` -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0277, E0367. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/const-drop-fail.new_stock.stderr b/tests/ui/traits/const-traits/const-drop-fail.new_stock.stderr index ff803ff889b8f..db4df30800b6e 100644 --- a/tests/ui/traits/const-traits/const-drop-fail.new_stock.stderr +++ b/tests/ui/traits/const-traits/const-drop-fail.new_stock.stderr @@ -1,5 +1,17 @@ +error[E0367]: `NonTrivialDrop` does not implement `[const] Destruct` + --> $DIR/const-drop-fail.rs:19:30 + | +LL | struct ConstImplWithDropGlue(NonTrivialDrop); + | ^^^^^^^^^^^^^^ + | +note: required for this `Drop` impl + --> $DIR/const-drop-fail.rs:22:1 + | +LL | impl const Drop for ConstImplWithDropGlue { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied - --> $DIR/const-drop-fail.rs:34:5 + --> $DIR/const-drop-fail.rs:35:5 | LL | const _: () = check($exp); | ----- required by a bound introduced by this call @@ -8,13 +20,13 @@ LL | NonTrivialDrop, | ^^^^^^^^^^^^^^ | note: required by a bound in `check` - --> $DIR/const-drop-fail.rs:25:19 + --> $DIR/const-drop-fail.rs:26:19 | LL | const fn check(_: T) {} | ^^^^^^^^^^^^^^^^ required by this bound in `check` error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied - --> $DIR/const-drop-fail.rs:36:5 + --> $DIR/const-drop-fail.rs:37:5 | LL | const _: () = check($exp); | ----- required by a bound introduced by this call @@ -23,11 +35,12 @@ LL | ConstImplWithDropGlue(NonTrivialDrop), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: required by a bound in `check` - --> $DIR/const-drop-fail.rs:25:19 + --> $DIR/const-drop-fail.rs:26:19 | LL | const fn check(_: T) {} | ^^^^^^^^^^^^^^^^ required by this bound in `check` -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0277, E0367. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/const-drop-fail.old_precise.stderr b/tests/ui/traits/const-traits/const-drop-fail.old_precise.stderr index ff803ff889b8f..db4df30800b6e 100644 --- a/tests/ui/traits/const-traits/const-drop-fail.old_precise.stderr +++ b/tests/ui/traits/const-traits/const-drop-fail.old_precise.stderr @@ -1,5 +1,17 @@ +error[E0367]: `NonTrivialDrop` does not implement `[const] Destruct` + --> $DIR/const-drop-fail.rs:19:30 + | +LL | struct ConstImplWithDropGlue(NonTrivialDrop); + | ^^^^^^^^^^^^^^ + | +note: required for this `Drop` impl + --> $DIR/const-drop-fail.rs:22:1 + | +LL | impl const Drop for ConstImplWithDropGlue { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied - --> $DIR/const-drop-fail.rs:34:5 + --> $DIR/const-drop-fail.rs:35:5 | LL | const _: () = check($exp); | ----- required by a bound introduced by this call @@ -8,13 +20,13 @@ LL | NonTrivialDrop, | ^^^^^^^^^^^^^^ | note: required by a bound in `check` - --> $DIR/const-drop-fail.rs:25:19 + --> $DIR/const-drop-fail.rs:26:19 | LL | const fn check(_: T) {} | ^^^^^^^^^^^^^^^^ required by this bound in `check` error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied - --> $DIR/const-drop-fail.rs:36:5 + --> $DIR/const-drop-fail.rs:37:5 | LL | const _: () = check($exp); | ----- required by a bound introduced by this call @@ -23,11 +35,12 @@ LL | ConstImplWithDropGlue(NonTrivialDrop), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: required by a bound in `check` - --> $DIR/const-drop-fail.rs:25:19 + --> $DIR/const-drop-fail.rs:26:19 | LL | const fn check(_: T) {} | ^^^^^^^^^^^^^^^^ required by this bound in `check` -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0277, E0367. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/const-drop-fail.old_stock.stderr b/tests/ui/traits/const-traits/const-drop-fail.old_stock.stderr index ff803ff889b8f..db4df30800b6e 100644 --- a/tests/ui/traits/const-traits/const-drop-fail.old_stock.stderr +++ b/tests/ui/traits/const-traits/const-drop-fail.old_stock.stderr @@ -1,5 +1,17 @@ +error[E0367]: `NonTrivialDrop` does not implement `[const] Destruct` + --> $DIR/const-drop-fail.rs:19:30 + | +LL | struct ConstImplWithDropGlue(NonTrivialDrop); + | ^^^^^^^^^^^^^^ + | +note: required for this `Drop` impl + --> $DIR/const-drop-fail.rs:22:1 + | +LL | impl const Drop for ConstImplWithDropGlue { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied - --> $DIR/const-drop-fail.rs:34:5 + --> $DIR/const-drop-fail.rs:35:5 | LL | const _: () = check($exp); | ----- required by a bound introduced by this call @@ -8,13 +20,13 @@ LL | NonTrivialDrop, | ^^^^^^^^^^^^^^ | note: required by a bound in `check` - --> $DIR/const-drop-fail.rs:25:19 + --> $DIR/const-drop-fail.rs:26:19 | LL | const fn check(_: T) {} | ^^^^^^^^^^^^^^^^ required by this bound in `check` error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied - --> $DIR/const-drop-fail.rs:36:5 + --> $DIR/const-drop-fail.rs:37:5 | LL | const _: () = check($exp); | ----- required by a bound introduced by this call @@ -23,11 +35,12 @@ LL | ConstImplWithDropGlue(NonTrivialDrop), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: required by a bound in `check` - --> $DIR/const-drop-fail.rs:25:19 + --> $DIR/const-drop-fail.rs:26:19 | LL | const fn check(_: T) {} | ^^^^^^^^^^^^^^^^ required by this bound in `check` -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0277, E0367. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/const-drop-fail.rs b/tests/ui/traits/const-traits/const-drop-fail.rs index b74716f00617f..7e12043ce74a7 100644 --- a/tests/ui/traits/const-traits/const-drop-fail.rs +++ b/tests/ui/traits/const-traits/const-drop-fail.rs @@ -17,6 +17,7 @@ impl Drop for NonTrivialDrop { } struct ConstImplWithDropGlue(NonTrivialDrop); +//~^ ERROR: `NonTrivialDrop` does not implement `[const] Destruct` impl const Drop for ConstImplWithDropGlue { fn drop(&mut self) {} diff --git a/tests/ui/traits/const-traits/minicore-drop-fail.rs b/tests/ui/traits/const-traits/minicore-drop-fail.rs index d01d259040c6e..f17a88dd90211 100644 --- a/tests/ui/traits/const-traits/minicore-drop-fail.rs +++ b/tests/ui/traits/const-traits/minicore-drop-fail.rs @@ -19,7 +19,7 @@ const trait Foo {} impl Foo for () {} struct Conditional(T); -impl const Drop for Conditional where T: [const] Foo { +impl const Drop for Conditional where T: [const] Foo + [const] Destruct { fn drop(&mut self) {} }