From 13ec3de67337e0781eaee2d952f746272760a51e Mon Sep 17 00:00:00 2001 From: Flakebi Date: Fri, 24 Apr 2026 10:03:45 +0200 Subject: [PATCH 1/6] 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/6] 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 d173b3068b7d0d771e5c256092d732161bfb471c Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Sat, 25 Apr 2026 09:47:07 +0200 Subject: [PATCH 3/6] Add regression test --- .../ui/consts/const-closure-in-trait-impl.rs | 25 +++++++++++++++++++ .../consts/const-closure-in-trait-impl.stderr | 23 +++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 tests/ui/consts/const-closure-in-trait-impl.rs create mode 100644 tests/ui/consts/const-closure-in-trait-impl.stderr diff --git a/tests/ui/consts/const-closure-in-trait-impl.rs b/tests/ui/consts/const-closure-in-trait-impl.rs new file mode 100644 index 0000000000000..51e2b2dba3771 --- /dev/null +++ b/tests/ui/consts/const-closure-in-trait-impl.rs @@ -0,0 +1,25 @@ +#![feature(const_closures, const_destruct, const_trait_impl)] + +use std::marker::Destruct; +use std::num::NonZero; + +const trait T { + fn a(&mut self, f: impl [const] Fn() + [const] Destruct); + fn b(&mut self); +} + +struct S; + +impl const T for S { + fn a(&mut self, f: impl [const] Fn() + [const] Destruct) { + f() + } + + fn b(&mut self) { + self.a(const || {}); + //~^ ERROR: cannot use `const` closures outside of const contexts + //~| ERROR: [const] Fn()` is not satisfied + } +} + +fn main() {} diff --git a/tests/ui/consts/const-closure-in-trait-impl.stderr b/tests/ui/consts/const-closure-in-trait-impl.stderr new file mode 100644 index 0000000000000..99b90bba092f1 --- /dev/null +++ b/tests/ui/consts/const-closure-in-trait-impl.stderr @@ -0,0 +1,23 @@ +error: cannot use `const` closures outside of const contexts + --> $DIR/const-closure-in-trait-impl.rs:19:16 + | +LL | self.a(const || {}); + | ^^^^^ + +error[E0277]: the trait bound `{closure@$DIR/const-closure-in-trait-impl.rs:19:16: 19:24}: [const] Fn()` is not satisfied + --> $DIR/const-closure-in-trait-impl.rs:19:16 + | +LL | self.a(const || {}); + | - ^^^^^^^^^^^ + | | + | required by a bound introduced by this call + | +note: required by a bound in `T::a` + --> $DIR/const-closure-in-trait-impl.rs:7:29 + | +LL | fn a(&mut self, f: impl [const] Fn() + [const] Destruct); + | ^^^^^^^^^^^^ required by this bound in `T::a` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. From ae91b212bd1515f2acaa707f0ff9852deb4385d1 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Sat, 25 Apr 2026 12:52:15 +0200 Subject: [PATCH 4/6] Check closure's constness validity in the `constness` query instead of during ast lowering, where it's not easily possible to obtain all the right information in time --- compiler/rustc_ast_lowering/src/expr.rs | 11 ++------- compiler/rustc_ast_lowering/src/item.rs | 23 ++++--------------- compiler/rustc_ast_lowering/src/lib.rs | 2 -- .../src/const_eval/fn_queries.rs | 8 ++++++- compiler/rustc_middle/src/hir/map.rs | 12 +--------- .../ui/consts/const-closure-in-trait-impl.rs | 4 ++-- .../consts/const-closure-in-trait-impl.stderr | 23 ------------------- ...-closure-in-non-const-trait-impl-method.rs | 1 + ...sure-in-non-const-trait-impl-method.stderr | 8 ++++++- ...const-closure-in-non-const-trait-method.rs | 1 + ...t-closure-in-non-const-trait-method.stderr | 8 ++++++- ...st-op-const-closure-non-const-outer.stderr | 2 +- 12 files changed, 33 insertions(+), 70 deletions(-) delete mode 100644 tests/ui/consts/const-closure-in-trait-impl.stderr diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index f0b292d3179da..4e1b46d568099 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1052,7 +1052,7 @@ impl<'hir> LoweringContext<'_, 'hir> { binder: &ClosureBinder, capture_clause: CaptureBy, closure_id: NodeId, - mut constness: Const, + constness: Const, movability: Movability, decl: &FnDecl, body: &Expr, @@ -1062,18 +1062,11 @@ impl<'hir> LoweringContext<'_, 'hir> { let closure_def_id = self.local_def_id(closure_id); let (binder_clause, generic_params) = self.lower_closure_binder(binder); - if let Const::Yes(span) = constness { - if !self.is_in_const_context { - self.dcx().span_err(span, "cannot use `const` closures outside of const contexts"); - constness = Const::No; - } - } - let (body_id, closure_kind) = self.with_new_scopes(fn_decl_span, move |this| { let mut coroutine_kind = find_attr!(attrs, Coroutine(_) => hir::CoroutineKind::Coroutine(Movability::Movable)); // FIXME(contracts): Support contracts on closures? - let body_id = this.lower_fn_body(decl, None, constness, |this| { + let body_id = this.lower_fn_body(decl, None, |this| { this.coroutine_kind = coroutine_kind; let e = this.lower_expr_mut(body); coroutine_kind = this.coroutine_kind; diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 2195581eccec3..571da622c9557 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1,4 +1,3 @@ -use std::mem; use rustc_abi::ExternAbi; use rustc_ast::visit::AssocCtxt; @@ -378,7 +377,6 @@ impl<'hir> LoweringContext<'_, 'hir> { body.as_deref(), attrs, contract.as_deref(), - header.constness, ); let itctx = ImplTraitContext::Universal; @@ -1068,7 +1066,6 @@ impl<'hir> LoweringContext<'_, 'hir> { Some(body), attrs, contract.as_deref(), - sig.header.constness, ); let (generics, sig) = self.lower_method_sig( generics, @@ -1262,7 +1259,6 @@ impl<'hir> LoweringContext<'_, 'hir> { body.as_deref(), attrs, contract.as_deref(), - sig.header.constness, ); let (generics, sig) = self.lower_method_sig( generics, @@ -1391,13 +1387,11 @@ impl<'hir> LoweringContext<'_, 'hir> { f: impl FnOnce(&mut Self) -> (&'hir [hir::Param<'hir>], hir::Expr<'hir>), ) -> hir::BodyId { let prev_coroutine_kind = self.coroutine_kind.take(); - let prev_is_in_const_context = mem::take(&mut self.is_in_const_context); let task_context = self.task_context.take(); let (parameters, result) = f(self); let body_id = self.record_body(parameters, result); self.task_context = task_context; self.coroutine_kind = prev_coroutine_kind; - self.is_in_const_context = prev_is_in_const_context; body_id } @@ -1416,13 +1410,9 @@ impl<'hir> LoweringContext<'_, 'hir> { &mut self, decl: &FnDecl, contract: Option<&FnContract>, - constness: Const, body: impl FnOnce(&mut Self) -> hir::Expr<'hir>, ) -> hir::BodyId { self.lower_body(|this| { - if let Const::Yes(_) = constness { - this.is_in_const_context = true; - } let params = this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x))); @@ -1440,9 +1430,8 @@ impl<'hir> LoweringContext<'_, 'hir> { decl: &FnDecl, body: &Block, contract: Option<&FnContract>, - constness: Const, ) -> hir::BodyId { - self.lower_fn_body(decl, contract, constness, |this| this.lower_block_expr(body)) + self.lower_fn_body(decl, contract, |this| this.lower_block_expr(body)) } pub(super) fn lower_const_body(&mut self, span: Span, expr: Option<&Expr>) -> hir::BodyId { @@ -1450,10 +1439,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ( &[], match expr { - Some(expr) => { - this.is_in_const_context = true; - this.lower_expr_mut(expr) - } + Some(expr) => this.lower_expr_mut(expr), None => this.expr_err(span, this.dcx().span_delayed_bug(span, "no block")), }, ) @@ -1472,13 +1458,12 @@ impl<'hir> LoweringContext<'_, 'hir> { body: Option<&Block>, attrs: &'hir [hir::Attribute], contract: Option<&FnContract>, - constness: Const, ) -> hir::BodyId { let Some(body) = body else { // Functions without a body are an error, except if this is an intrinsic. For those we // create a fake body so that the entire rest of the compiler doesn't have to deal with // this as a special case. - return self.lower_fn_body(decl, contract, constness, |this| { + return self.lower_fn_body(decl, contract, |this| { if find_attr!(attrs, RustcIntrinsic) || this.tcx.is_sdylib_interface_build() { let span = this.lower_span(span); let empty_block = hir::Block { @@ -1503,7 +1488,7 @@ impl<'hir> LoweringContext<'_, 'hir> { }; let Some(coroutine_kind) = coroutine_kind else { // Typical case: not a coroutine. - return self.lower_fn_body_block(decl, body, contract, constness); + return self.lower_fn_body_block(decl, body, contract); }; // FIXME(contracts): Support contracts on async fn. self.lower_body(|this| { diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 5188fb8d6aada..5e9674b7a0422 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -124,7 +124,6 @@ struct LoweringContext<'a, 'hir> { loop_scope: Option, is_in_loop_condition: bool, is_in_dyn_type: bool, - is_in_const_context: bool, current_hir_id_owner: hir::OwnerId, item_local_id_counter: hir::ItemLocalId, @@ -193,7 +192,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { loop_scope: None, is_in_loop_condition: false, is_in_dyn_type: false, - is_in_const_context: false, coroutine_kind: None, task_context: None, current_item: None, diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs index 0b5065f12f805..57fd230ec4685 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -16,7 +16,13 @@ fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Constness { // Foreign functions cannot be evaluated at compile-time. Constness::NotConst } - Node::Expr(e) if let ExprKind::Closure(c) = e.kind => c.constness, + Node::Expr(e) if let ExprKind::Closure(c) = e.kind => { + if let Constness::Const = c.constness && tcx.hir_body_const_context(tcx.local_parent(def_id)).is_none() { + tcx.dcx().span_err(tcx.def_span(def_id), "cannot use `const` closures outside of const contexts"); + return Constness::NotConst; + } + c.constness + }, // FIXME(fee1-dead): extract this one out and rename this query to `fn_constness` so we don't need `is_const_fn` anymore. Node::Item(i) if let ItemKind::Impl(impl_) = i.kind => impl_.constness, Node::Item(Item { kind: ItemKind::Fn { sig, .. }, .. }) => sig.header.constness, diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index d63034ed1d2bb..68357212bebe8 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -319,17 +319,7 @@ impl<'tcx> TyCtxt<'tcx> { BodyOwnerKind::Fn if self.is_constructor(def_id) => return None, // Const closures use their parent's const context BodyOwnerKind::Closure if self.is_const_fn(def_id) => { - return Some( - self.hir_body_const_context(self.local_parent(local_def_id)).unwrap_or_else( - || { - assert!( - self.dcx().has_errors().is_some(), - "`const` closure with no enclosing const context", - ); - ConstContext::ConstFn - }, - ), - ); + return self.hir_body_const_context(self.local_parent(local_def_id)); } BodyOwnerKind::Fn if self.is_const_fn(def_id) => ConstContext::ConstFn, BodyOwnerKind::Fn | BodyOwnerKind::Closure | BodyOwnerKind::GlobalAsm => return None, diff --git a/tests/ui/consts/const-closure-in-trait-impl.rs b/tests/ui/consts/const-closure-in-trait-impl.rs index 51e2b2dba3771..29722dd37c1e0 100644 --- a/tests/ui/consts/const-closure-in-trait-impl.rs +++ b/tests/ui/consts/const-closure-in-trait-impl.rs @@ -1,3 +1,5 @@ +//@ check-pass + #![feature(const_closures, const_destruct, const_trait_impl)] use std::marker::Destruct; @@ -17,8 +19,6 @@ impl const T for S { fn b(&mut self) { self.a(const || {}); - //~^ ERROR: cannot use `const` closures outside of const contexts - //~| ERROR: [const] Fn()` is not satisfied } } diff --git a/tests/ui/consts/const-closure-in-trait-impl.stderr b/tests/ui/consts/const-closure-in-trait-impl.stderr deleted file mode 100644 index 99b90bba092f1..0000000000000 --- a/tests/ui/consts/const-closure-in-trait-impl.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error: cannot use `const` closures outside of const contexts - --> $DIR/const-closure-in-trait-impl.rs:19:16 - | -LL | self.a(const || {}); - | ^^^^^ - -error[E0277]: the trait bound `{closure@$DIR/const-closure-in-trait-impl.rs:19:16: 19:24}: [const] Fn()` is not satisfied - --> $DIR/const-closure-in-trait-impl.rs:19:16 - | -LL | self.a(const || {}); - | - ^^^^^^^^^^^ - | | - | required by a bound introduced by this call - | -note: required by a bound in `T::a` - --> $DIR/const-closure-in-trait-impl.rs:7:29 - | -LL | fn a(&mut self, f: impl [const] Fn() + [const] Destruct); - | ^^^^^^^^^^^^ required by this bound in `T::a` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/const-closure-in-non-const-trait-impl-method.rs b/tests/ui/traits/const-traits/const-closure-in-non-const-trait-impl-method.rs index b538c4b6a32fd..7980bd1793f45 100644 --- a/tests/ui/traits/const-traits/const-closure-in-non-const-trait-impl-method.rs +++ b/tests/ui/traits/const-traits/const-closure-in-non-const-trait-impl-method.rs @@ -11,6 +11,7 @@ impl Foo for &mut T { const fn test() -> impl [const] Fn() { //~^ ERROR functions in trait impls cannot be declared const const move || {} + //~^ ERROR: cannot use `const` closures outside of const contexts } } diff --git a/tests/ui/traits/const-traits/const-closure-in-non-const-trait-impl-method.stderr b/tests/ui/traits/const-traits/const-closure-in-non-const-trait-impl-method.stderr index 3ff5578748d29..15bc5b8458d80 100644 --- a/tests/ui/traits/const-traits/const-closure-in-non-const-trait-impl-method.stderr +++ b/tests/ui/traits/const-traits/const-closure-in-non-const-trait-impl-method.stderr @@ -14,6 +14,12 @@ help: ... and declare the impl to be const instead LL | impl const Foo for &mut T { | +++++ -error: aborting due to 1 previous error +error: cannot use `const` closures outside of const contexts + --> $DIR/const-closure-in-non-const-trait-impl-method.rs:13:9 + | +LL | const move || {} + | ^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0379`. diff --git a/tests/ui/traits/const-traits/const-closure-in-non-const-trait-method.rs b/tests/ui/traits/const-traits/const-closure-in-non-const-trait-method.rs index dfd0793180871..9bb6bf8fe704f 100644 --- a/tests/ui/traits/const-traits/const-closure-in-non-const-trait-method.rs +++ b/tests/ui/traits/const-traits/const-closure-in-non-const-trait-method.rs @@ -6,6 +6,7 @@ trait Tr { const fn test() { //~^ ERROR functions in traits cannot be declared const (const || {})() + //~^ ERROR cannot use `const` closures outside of const contexts } } diff --git a/tests/ui/traits/const-traits/const-closure-in-non-const-trait-method.stderr b/tests/ui/traits/const-traits/const-closure-in-non-const-trait-method.stderr index 79462f620da37..dd728840b8252 100644 --- a/tests/ui/traits/const-traits/const-closure-in-non-const-trait-method.stderr +++ b/tests/ui/traits/const-traits/const-closure-in-non-const-trait-method.stderr @@ -7,6 +7,12 @@ LL | const fn test() { | functions in traits cannot be const | help: remove the `const` -error: aborting due to 1 previous error +error: cannot use `const` closures outside of const contexts + --> $DIR/const-closure-in-non-const-trait-method.rs:8:10 + | +LL | (const || {})() + | ^^^^^^^^ + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0379`. diff --git a/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.stderr b/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.stderr index f5521b90cc2d9..7befeec496d80 100644 --- a/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.stderr +++ b/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.stderr @@ -2,7 +2,7 @@ error: cannot use `const` closures outside of const contexts --> $DIR/non-const-op-const-closure-non-const-outer.rs:14:6 | LL | (const || { (()).foo() })(); - | ^^^^^ + | ^^^^^^^^ error: aborting due to 1 previous error From 642ee63c228ad8d8c988cb3c68089776ddc97980 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 22 Apr 2026 16:59:44 +0200 Subject: [PATCH 5/6] 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 6/6] 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) {} }