From 7dcedafff23aaddf40d061d9dd5be3b62aa2cfe8 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 22 Apr 2026 17:09:33 +0200 Subject: [PATCH] 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 bdef0b57d691..dbf5465ee18b 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::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 a431c91bf2d9..9715724007cc 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 @@ fn drop(&mut self) {} } 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 000000000000..0e626176579c --- /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 ff803ff889b8..db4df30800b6 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 ff803ff889b8..db4df30800b6 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 ff803ff889b8..db4df30800b6 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 ff803ff889b8..db4df30800b6 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 b74716f00617..7e12043ce74a 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 @@ fn drop(&mut self) { } 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 d01d259040c6..f17a88dd9021 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) {} }