From 7abc53785aad373ddd9eed830466e9bb97569d37 Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Wed, 8 Apr 2026 21:08:27 +0100 Subject: [PATCH 1/3] Suggest the `[const] Destruct` bound for type parameters in const functions When a const function drops a value whose type is a type parameter, suggest adding a `[const] Destruct` bound so the destructor can be evaluated at compile-time. --- .../rustc_const_eval/src/check_consts/ops.rs | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 1f4b2c7879cc7..06e55c575a35c 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -594,7 +594,7 @@ impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> { } fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { - if self.needs_non_const_drop { + let mut err = if self.needs_non_const_drop { ccx.dcx().create_err(diagnostics::LiveDrop { span, dropped_ty: self.dropped_ty, @@ -611,7 +611,27 @@ impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> { }, sym::const_destruct, ) + }; + + // If the dropped type is a type parameter, suggest adding a `[const] Destruct` bound. + if let Param(param_ty) = self.dropped_ty.kind() { + let tcx = ccx.tcx; + let caller = ccx.def_id(); + if let Some(generics) = tcx.hir_node_by_def_id(caller).generics() { + let destruct_def_id = tcx.lang_items().destruct_trait(); + suggest_constraining_type_param( + tcx, + generics, + &mut err, + param_ty.name.as_str(), + "[const] Destruct", + destruct_def_id, + None, + ); + } } + + err } } From 4fca6bd4ba655a20108027fca6c85b387e79085b Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Wed, 8 Apr 2026 21:38:07 +0100 Subject: [PATCH 2/3] Bless tests affected by the `[const] Destruct` suggestion --- tests/ui/consts/min_const_fn/min_const_fn.stderr | 5 +++++ tests/ui/consts/unstable-const-fn-in-libcore.stderr | 5 +++++ tests/ui/self/arbitrary-self-from-method-substs-ice.stderr | 5 +++++ tests/ui/static/static-drop-scope.stderr | 5 +++++ tests/ui/traits/const-traits/minicore-drop-fail.stderr | 5 +++++ 5 files changed, 25 insertions(+) diff --git a/tests/ui/consts/min_const_fn/min_const_fn.stderr b/tests/ui/consts/min_const_fn/min_const_fn.stderr index 0e939e5121aa9..0904de2af0d46 100644 --- a/tests/ui/consts/min_const_fn/min_const_fn.stderr +++ b/tests/ui/consts/min_const_fn/min_const_fn.stderr @@ -73,6 +73,11 @@ LL | const fn no_apit(_x: impl std::fmt::Debug) {} | ^^ - value is dropped here | | | the destructor for this type cannot be evaluated in constant functions + | +help: consider restricting opaque type `impl std::fmt::Debug` with unstable trait `Destruct` + | +LL | const fn no_apit(_x: impl std::fmt::Debug + [const] Destruct) {} + | ++++++++++++++++++ error: aborting due to 9 previous errors diff --git a/tests/ui/consts/unstable-const-fn-in-libcore.stderr b/tests/ui/consts/unstable-const-fn-in-libcore.stderr index 16db7791cd849..9dcd2b0ac0326 100644 --- a/tests/ui/consts/unstable-const-fn-in-libcore.stderr +++ b/tests/ui/consts/unstable-const-fn-in-libcore.stderr @@ -6,6 +6,11 @@ LL | const fn unwrap_or_else T>(self, f: F) -> T { ... LL | } | - value is dropped here + | +help: consider further restricting type parameter `F` with unstable trait `Destruct` + | +LL | const fn unwrap_or_else T + [const] Destruct>(self, f: F) -> T { + | ++++++++++++++++++ error[E0493]: destructor of `Opt` cannot be evaluated at compile-time --> $DIR/unstable-const-fn-in-libcore.rs:19:55 diff --git a/tests/ui/self/arbitrary-self-from-method-substs-ice.stderr b/tests/ui/self/arbitrary-self-from-method-substs-ice.stderr index f217370b024b5..1b410322aacbe 100644 --- a/tests/ui/self/arbitrary-self-from-method-substs-ice.stderr +++ b/tests/ui/self/arbitrary-self-from-method-substs-ice.stderr @@ -15,6 +15,11 @@ LL | const fn get>(self: R) -> u32 { ... LL | } | - value is dropped here + | +help: consider further restricting type parameter `R` with unstable trait `Destruct` + | +LL | const fn get + [const] Destruct>(self: R) -> u32 { + | ++++++++++++++++++ error[E0801]: invalid generic `self` parameter type: `R` --> $DIR/arbitrary-self-from-method-substs-ice.rs:10:49 diff --git a/tests/ui/static/static-drop-scope.stderr b/tests/ui/static/static-drop-scope.stderr index 0fdf081e23432..84e99a3988121 100644 --- a/tests/ui/static/static-drop-scope.stderr +++ b/tests/ui/static/static-drop-scope.stderr @@ -37,6 +37,11 @@ LL | const fn const_drop(_: T) {} | ^ - value is dropped here | | | the destructor for this type cannot be evaluated in constant functions + | +help: consider restricting type parameter `T` with unstable trait `Destruct` + | +LL | const fn const_drop(_: T) {} + | ++++++++++++++++++ error[E0493]: destructor of `(T, ())` cannot be evaluated at compile-time --> $DIR/static-drop-scope.rs:17:5 diff --git a/tests/ui/traits/const-traits/minicore-drop-fail.stderr b/tests/ui/traits/const-traits/minicore-drop-fail.stderr index 12d1877a18aba..2cb1256e37667 100644 --- a/tests/ui/traits/const-traits/minicore-drop-fail.stderr +++ b/tests/ui/traits/const-traits/minicore-drop-fail.stderr @@ -30,6 +30,11 @@ LL | const fn drop_arbitrary(_: T) { LL | LL | } | - value is dropped here + | +help: consider restricting type parameter `T` with trait `Destruct` + | +LL | const fn drop_arbitrary(_: T) { + | ++++++++++++++++++ error: aborting due to 4 previous errors From 2f961b8f2098b565da95ebca1f1f08487f8bf7ee Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Sun, 12 Jul 2026 16:09:24 +0100 Subject: [PATCH 3/3] Only offer the `[const] Destruct` suggestion on a nightly compiler The suggested bound requires the unstable `const_destruct` feature, so it is not actionable on a stable compiler. Add a run-make test that verifies the suggestion is only emitted on nightly. --- .../rustc_const_eval/src/check_consts/ops.rs | 5 +++- .../const-drop-nightly.stderr | 16 ++++++++++++ .../const-drop-stable.stderr | 11 ++++++++ .../const-drop.rs | 3 +++ .../const-destruct-stable-toolchain/rmake.rs | 26 +++++++++++++++++++ 5 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 tests/run-make/const-destruct-stable-toolchain/const-drop-nightly.stderr create mode 100644 tests/run-make/const-destruct-stable-toolchain/const-drop-stable.stderr create mode 100644 tests/run-make/const-destruct-stable-toolchain/const-drop.rs create mode 100644 tests/run-make/const-destruct-stable-toolchain/rmake.rs diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 06e55c575a35c..e980667c5e468 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -614,7 +614,10 @@ impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> { }; // If the dropped type is a type parameter, suggest adding a `[const] Destruct` bound. - if let Param(param_ty) = self.dropped_ty.kind() { + // The suggestion is only offered on nightly, since `[const]` bounds are unstable. + if let Param(param_ty) = self.dropped_ty.kind() + && ccx.tcx.sess.is_nightly_build() + { let tcx = ccx.tcx; let caller = ccx.def_id(); if let Some(generics) = tcx.hir_node_by_def_id(caller).generics() { diff --git a/tests/run-make/const-destruct-stable-toolchain/const-drop-nightly.stderr b/tests/run-make/const-destruct-stable-toolchain/const-drop-nightly.stderr new file mode 100644 index 0000000000000..52f68743b6275 --- /dev/null +++ b/tests/run-make/const-destruct-stable-toolchain/const-drop-nightly.stderr @@ -0,0 +1,16 @@ +error[E0493]: destructor of `T` cannot be evaluated at compile-time + --> const-drop.rs:1:24 + | +LL | const fn const_drop(_: T) {} + | ^ - value is dropped here + | | + | the destructor for this type cannot be evaluated in constant functions + | +help: consider restricting type parameter `T` with unstable trait `Destruct` + | +LL | const fn const_drop(_: T) {} + | ++++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0493`. diff --git a/tests/run-make/const-destruct-stable-toolchain/const-drop-stable.stderr b/tests/run-make/const-destruct-stable-toolchain/const-drop-stable.stderr new file mode 100644 index 0000000000000..dc37d840658fd --- /dev/null +++ b/tests/run-make/const-destruct-stable-toolchain/const-drop-stable.stderr @@ -0,0 +1,11 @@ +error[E0493]: destructor of `T` cannot be evaluated at compile-time + --> const-drop.rs:1:24 + | +1 | const fn const_drop(_: T) {} + | ^ - value is dropped here + | | + | the destructor for this type cannot be evaluated in constant functions + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0493`. diff --git a/tests/run-make/const-destruct-stable-toolchain/const-drop.rs b/tests/run-make/const-destruct-stable-toolchain/const-drop.rs new file mode 100644 index 0000000000000..6479498281fe5 --- /dev/null +++ b/tests/run-make/const-destruct-stable-toolchain/const-drop.rs @@ -0,0 +1,3 @@ +const fn const_drop(_: T) {} + +fn main() {} diff --git a/tests/run-make/const-destruct-stable-toolchain/rmake.rs b/tests/run-make/const-destruct-stable-toolchain/rmake.rs new file mode 100644 index 0000000000000..c1ff48b3214f0 --- /dev/null +++ b/tests/run-make/const-destruct-stable-toolchain/rmake.rs @@ -0,0 +1,26 @@ +//@ needs-target-std +// +// Test that the suggestion to constrain a type parameter that is dropped in a const +// function with a `[const] Destruct` bound is only offered on nightly, since the bound +// requires an unstable feature. + +use run_make_support::{diff, rustc}; + +fn main() { + let out = rustc() + .input("const-drop.rs") + .env("RUSTC_BOOTSTRAP", "-1") + .run_fail() + .assert_stderr_not_contains("consider restricting type parameter `T`") + .stderr_utf8(); + diff().expected_file("const-drop-stable.stderr").actual_text("(rustc)", &out).run(); + let out = rustc() + .input("const-drop.rs") + .ui_testing() + .run_fail() + .assert_stderr_contains( + "consider restricting type parameter `T` with unstable trait `Destruct`", + ) + .stderr_utf8(); + diff().expected_file("const-drop-nightly.stderr").actual_text("(rustc)", &out).run(); +}