From 59408add4d67e7e04fa1dcf378e4e1cc71c6ff48 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Tue, 29 Oct 2024 20:08:55 +0000
Subject: [PATCH 1/5] Implement ~const Destruct in new solver

---
 .../src/check_consts/qualifs.rs               | 81 ++++++++++---------
 .../rustc_hir_typeck/src/fn_ctxt/_impl.rs     |  6 +-
 compiler/rustc_middle/src/ty/adt.rs           | 12 ++-
 compiler/rustc_middle/src/ty/context.rs       |  5 ++
 .../src/solve/assembly/structural_traits.rs   | 74 ++++++++++++++++-
 .../src/solve/effect_goals.rs                 | 27 ++++++-
 compiler/rustc_type_ir/src/inherent.rs        |  4 +-
 compiler/rustc_type_ir/src/interner.rs        |  1 +
 compiler/rustc_type_ir/src/lang_items.rs      |  1 +
 compiler/rustc_type_ir/src/solve/mod.rs       |  7 ++
 tests/ui/consts/promoted_const_call.stderr    | 25 ++----
 .../const-traits/const-drop.precise.stderr    |  8 +-
 .../const-traits/const-drop.stock.stderr      | 10 ++-
 .../effects/auxiliary/minicore.rs             |  6 +-
 .../effects/minicore-drop-fail.rs             | 37 +++++++++
 .../effects/minicore-drop-fail.stderr         | 36 +++++++++
 16 files changed, 266 insertions(+), 74 deletions(-)
 create mode 100644 tests/ui/traits/const-traits/effects/minicore-drop-fail.rs
 create mode 100644 tests/ui/traits/const-traits/effects/minicore-drop-fail.stderr

diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs
index bc416acc58d7b..b965ad7bd8715 100644
--- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs
+++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs
@@ -2,11 +2,14 @@
 //!
 //! See the `Qualif` trait for more info.
 
+// FIXME(const_trait_impl): This API should be really reworked. It's dangerously general for
+// having basically only two use-cases that act in different ways.
+
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir::LangItem;
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::mir::*;
-use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty};
+use rustc_middle::ty::{self, AdtDef, Ty};
 use rustc_middle::{bug, mir};
 use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt};
 use tracing::instrument;
@@ -59,19 +62,9 @@ pub trait Qualif {
     /// It also determines the `Qualif`s for primitive types.
     fn in_any_value_of_ty<'tcx>(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool;
 
-    /// Returns `true` if this `Qualif` is inherent to the given struct or enum.
-    ///
-    /// By default, `Qualif`s propagate into ADTs in a structural way: An ADT only becomes
-    /// qualified if part of it is assigned a value with that `Qualif`. However, some ADTs *always*
-    /// have a certain `Qualif`, regardless of whether their fields have it. For example, a type
-    /// with a custom `Drop` impl is inherently `NeedsDrop`.
-    ///
-    /// Returning `true` for `in_adt_inherently` but `false` for `in_any_value_of_ty` is unsound.
-    fn in_adt_inherently<'tcx>(
-        cx: &ConstCx<'_, 'tcx>,
-        adt: AdtDef<'tcx>,
-        args: GenericArgsRef<'tcx>,
-    ) -> bool;
+    /// Returns `true` if the `Qualif` is not structural, i.e. that we should not recurse
+    /// into the operand.
+    fn is_non_structural<'tcx>(cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>) -> bool;
 
     /// Returns `true` if this `Qualif` behaves sructurally for pointers and references:
     /// the pointer/reference qualifies if and only if the pointee qualifies.
@@ -101,6 +94,11 @@ impl Qualif for HasMutInterior {
             return false;
         }
 
+        // Avoid selecting for `UnsafeCell` either.
+        if ty.ty_adt_def().is_some_and(|adt| adt.is_unsafe_cell()) {
+            return true;
+        }
+
         // We do not use `ty.is_freeze` here, because that requires revealing opaque types, which
         // requires borrowck, which in turn will invoke mir_const_qualifs again, causing a cycle error.
         // Instead we invoke an obligation context manually, and provide the opaque type inference settings
@@ -129,11 +127,7 @@ impl Qualif for HasMutInterior {
         !errors.is_empty()
     }
 
-    fn in_adt_inherently<'tcx>(
-        _cx: &ConstCx<'_, 'tcx>,
-        adt: AdtDef<'tcx>,
-        _: GenericArgsRef<'tcx>,
-    ) -> bool {
+    fn is_non_structural<'tcx>(_cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>) -> bool {
         // Exactly one type, `UnsafeCell`, has the `HasMutInterior` qualif inherently.
         // It arises structurally for all other types.
         adt.is_unsafe_cell()
@@ -144,6 +138,7 @@ impl Qualif for HasMutInterior {
     }
 }
 
+// FIXME(const_trait_impl): Get rid of this!
 /// Constant containing an ADT that implements `Drop`.
 /// This must be ruled out because implicit promotion would remove side-effects
 /// that occur as part of dropping that value. N.B., the implicit promotion has
@@ -163,11 +158,7 @@ impl Qualif for NeedsDrop {
         ty.needs_drop(cx.tcx, cx.typing_env)
     }
 
-    fn in_adt_inherently<'tcx>(
-        cx: &ConstCx<'_, 'tcx>,
-        adt: AdtDef<'tcx>,
-        _: GenericArgsRef<'tcx>,
-    ) -> bool {
+    fn is_non_structural<'tcx>(cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>) -> bool {
         adt.has_dtor(cx.tcx)
     }
 
@@ -196,16 +187,32 @@ impl Qualif for NeedsNonConstDrop {
             return false;
         }
 
-        // FIXME(const_trait_impl): Reimplement const drop checking.
-        NeedsDrop::in_any_value_of_ty(cx, ty)
+        if cx.tcx.features().const_trait_impl() {
+            let destruct_def_id = cx.tcx.require_lang_item(LangItem::Destruct, Some(cx.body.span));
+            let infcx = cx.tcx.infer_ctxt().build(TypingMode::from_param_env(cx.param_env));
+            let ocx = ObligationCtxt::new(&infcx);
+            ocx.register_obligation(Obligation::new(
+                cx.tcx,
+                ObligationCause::misc(cx.body.span, cx.def_id()),
+                cx.param_env,
+                ty::Binder::dummy(ty::TraitRef::new(cx.tcx, destruct_def_id, [ty]))
+                    .to_host_effect_clause(cx.tcx, match cx.const_kind() {
+                        rustc_hir::ConstContext::ConstFn => ty::BoundConstness::Maybe,
+                        rustc_hir::ConstContext::Static(_)
+                        | rustc_hir::ConstContext::Const { .. } => ty::BoundConstness::Const,
+                    }),
+            ));
+            !ocx.select_all_or_error().is_empty()
+        } else {
+            NeedsDrop::in_any_value_of_ty(cx, ty)
+        }
     }
 
-    fn in_adt_inherently<'tcx>(
-        cx: &ConstCx<'_, 'tcx>,
-        adt: AdtDef<'tcx>,
-        _: GenericArgsRef<'tcx>,
-    ) -> bool {
-        adt.has_non_const_dtor(cx.tcx)
+    fn is_non_structural<'tcx>(cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>) -> bool {
+        // Even a `const` dtor may have `~const` bounds that may need to
+        // be satisfied, so this becomes non-structural as soon as the
+        // ADT gets a destructor at all.
+        adt.has_dtor(cx.tcx)
     }
 
     fn deref_structural<'tcx>(_cx: &ConstCx<'_, 'tcx>) -> bool {
@@ -261,14 +268,10 @@ where
         Rvalue::Aggregate(kind, operands) => {
             // Return early if we know that the struct or enum being constructed is always
             // qualified.
-            if let AggregateKind::Adt(adt_did, _, args, ..) = **kind {
+            if let AggregateKind::Adt(adt_did, ..) = **kind {
                 let def = cx.tcx.adt_def(adt_did);
-                if Q::in_adt_inherently(cx, def, args) {
-                    return true;
-                }
-                // Don't do any value-based reasoning for unions.
-                if def.is_union() && Q::in_any_value_of_ty(cx, rvalue.ty(cx.body, cx.tcx)) {
-                    return true;
+                if def.is_union() || Q::is_non_structural(cx, def) {
+                    return Q::in_any_value_of_ty(cx, rvalue.ty(cx.body, cx.tcx));
                 }
             }
 
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index ce6ce0381a91b..47af8c681da00 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -269,13 +269,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     );
                 }
                 Adjust::Deref(None) => {
-                    // FIXME(effects): We *could* enforce `&T: ~const Deref` here.
+                    // FIXME(const_trait_impl): We *could* enforce `&T: ~const Deref` here.
                 }
                 Adjust::Pointer(_pointer_coercion) => {
-                    // FIXME(effects): We should probably enforce these.
+                    // FIXME(const_trait_impl): We should probably enforce these.
                 }
                 Adjust::ReborrowPin(_mutability) => {
-                    // FIXME(effects): We could enforce these; they correspond to
+                    // FIXME(const_trait_impl): We could enforce these; they correspond to
                     // `&mut T: DerefMut` tho, so it's kinda moot.
                 }
                 Adjust::Borrow(_) => {
diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs
index 79d56702be256..447cbc8932ee5 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -18,6 +18,7 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable};
 use rustc_query_system::ich::StableHashingContext;
 use rustc_session::DataTypeKind;
 use rustc_span::symbol::sym;
+use rustc_type_ir::solve::AdtDestructorKind;
 use tracing::{debug, info, trace};
 
 use super::{
@@ -232,6 +233,13 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef<TyCtxt<'tcx>> for AdtDef<'tcx> {
     fn is_fundamental(self) -> bool {
         self.is_fundamental()
     }
+
+    fn destructor(self, tcx: TyCtxt<'tcx>) -> Option<AdtDestructorKind> {
+        Some(match self.destructor(tcx)?.constness {
+            hir::Constness::Const => AdtDestructorKind::Const,
+            hir::Constness::NotConst => AdtDestructorKind::NotConst,
+        })
+    }
 }
 
 #[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable, TyEncodable, TyDecodable)]
@@ -402,10 +410,6 @@ impl<'tcx> AdtDef<'tcx> {
         self.destructor(tcx).is_some()
     }
 
-    pub fn has_non_const_dtor(self, tcx: TyCtxt<'tcx>) -> bool {
-        matches!(self.destructor(tcx), Some(Destructor { constness: hir::Constness::NotConst, .. }))
-    }
-
     /// Asserts this is a struct or union and returns its unique variant.
     pub fn non_enum_variant(self) -> &'tcx VariantDef {
         assert!(self.is_struct() || self.is_union());
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 55c29825fbcb7..4a0d50c153c8a 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -384,6 +384,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.is_conditionally_const(def_id)
     }
 
+    fn alias_has_const_conditions(self, def_id: DefId) -> bool {
+        self.is_conditionally_const(def_id)
+    }
+
     fn const_conditions(
         self,
         def_id: DefId,
@@ -663,6 +667,7 @@ bidirectional_lang_item_map! {
     CoroutineYield,
     Destruct,
     DiscriminantKind,
+    Drop,
     DynMetadata,
     Fn,
     FnMut,
diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs
index a56febec48c45..05ce61bc0678f 100644
--- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs
@@ -12,7 +12,7 @@ use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic};
 use tracing::instrument;
 
 use crate::delegate::SolverDelegate;
-use crate::solve::{EvalCtxt, Goal, NoSolution};
+use crate::solve::{AdtDestructorKind, EvalCtxt, Goal, NoSolution};
 
 // Calculates the constituent types of a type for `auto trait` purposes.
 #[instrument(level = "trace", skip(ecx), ret)]
@@ -703,6 +703,78 @@ pub(in crate::solve) fn extract_fn_def_from_const_callable<I: Interner>(
     }
 }
 
+pub(in crate::solve) fn const_conditions_for_destruct<I: Interner>(
+    cx: I,
+    self_ty: I::Ty,
+) -> Result<Vec<ty::TraitRef<I>>, NoSolution> {
+    let destruct_def_id = cx.require_lang_item(TraitSolverLangItem::Destruct);
+
+    match self_ty.kind() {
+        // An ADT is `~const Destruct` only if all of the fields are,
+        // *and* if there is a `Drop` impl, that `Drop` impl is also `~const`.
+        ty::Adt(adt_def, args) => {
+            let mut const_conditions: Vec<_> = adt_def
+                .all_field_tys(cx)
+                .iter_instantiated(cx, args)
+                .map(|field_ty| ty::TraitRef::new(cx, destruct_def_id, [field_ty]))
+                .collect();
+            match adt_def.destructor(cx) {
+                // `Drop` impl exists, but it's not const. Type cannot be `~const Destruct`.
+                Some(AdtDestructorKind::NotConst) => return Err(NoSolution),
+                // `Drop` impl exists, and it's const. Require `Ty: ~const Drop` to hold.
+                Some(AdtDestructorKind::Const) => {
+                    let drop_def_id = cx.require_lang_item(TraitSolverLangItem::Drop);
+                    let drop_trait_ref = ty::TraitRef::new(cx, drop_def_id, [self_ty]);
+                    const_conditions.push(drop_trait_ref);
+                }
+                // No `Drop` impl, no need to require anything else.
+                None => {}
+            }
+            Ok(const_conditions)
+        }
+
+        ty::Array(ty, _) | ty::Pat(ty, _) | ty::Slice(ty) => {
+            Ok(vec![ty::TraitRef::new(cx, destruct_def_id, [ty])])
+        }
+
+        ty::Tuple(tys) => Ok(tys
+            .iter()
+            .map(|field_ty| ty::TraitRef::new(cx, destruct_def_id, [field_ty]))
+            .collect()),
+
+        // Trivially implement `~const Destruct`
+        ty::Bool
+        | ty::Char
+        | ty::Int(..)
+        | ty::Uint(..)
+        | ty::Float(..)
+        | ty::Str
+        | ty::RawPtr(..)
+        | ty::Ref(..)
+        | ty::FnDef(..)
+        | ty::FnPtr(..)
+        | ty::Never
+        | ty::Infer(ty::InferTy::FloatVar(_) | ty::InferTy::IntVar(_))
+        | ty::Error(_) => Ok(vec![]),
+
+        // Coroutines and closures could implement `~const Drop`,
+        // but they don't really need to right now.
+        ty::Closure(_, _)
+        | ty::CoroutineClosure(_, _)
+        | ty::Coroutine(_, _)
+        | ty::CoroutineWitness(_, _) => Err(NoSolution),
+
+        ty::Dynamic(..) | ty::Param(_) | ty::Alias(..) | ty::Placeholder(_) | ty::Foreign(_) => {
+            Err(NoSolution)
+        }
+
+        ty::Bound(..)
+        | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
+            panic!("unexpected type `{self_ty:?}`")
+        }
+    }
+}
+
 /// Assemble a list of predicates that would be present on a theoretical
 /// user impl for an object type. These predicates must be checked any time
 /// we assemble a built-in object candidate for an object type, since they
diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
index 603a68eb890cb..81b5199002b23 100644
--- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
@@ -84,6 +84,10 @@ where
         let cx = ecx.cx();
         let mut candidates = vec![];
 
+        if !ecx.cx().alias_has_const_conditions(alias_ty.def_id) {
+            return vec![];
+        }
+
         for clause in elaborate::elaborate(
             cx,
             cx.explicit_implied_const_bounds(alias_ty.def_id)
@@ -338,10 +342,27 @@ where
     }
 
     fn consider_builtin_destruct_candidate(
-        _ecx: &mut EvalCtxt<'_, D>,
-        _goal: Goal<I, Self>,
+        ecx: &mut EvalCtxt<'_, D>,
+        goal: Goal<I, Self>,
     ) -> Result<Candidate<I>, NoSolution> {
-        Err(NoSolution)
+        let cx = ecx.cx();
+
+        let self_ty = goal.predicate.self_ty();
+        let const_conditions = structural_traits::const_conditions_for_destruct(cx, self_ty)?;
+
+        ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
+            ecx.add_goals(
+                GoalSource::Misc,
+                const_conditions.into_iter().map(|trait_ref| {
+                    goal.with(
+                        cx,
+                        ty::Binder::dummy(trait_ref)
+                            .to_host_effect_clause(cx, goal.predicate.constness),
+                    )
+                }),
+            );
+            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+        })
     }
 
     fn consider_builtin_transmute_candidate(
diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs
index 5af1aa2f8faa9..f7e0570bdd555 100644
--- a/compiler/rustc_type_ir/src/inherent.rs
+++ b/compiler/rustc_type_ir/src/inherent.rs
@@ -11,7 +11,7 @@ use rustc_ast_ir::Mutability;
 use crate::elaborate::Elaboratable;
 use crate::fold::{TypeFoldable, TypeSuperFoldable};
 use crate::relate::Relate;
-use crate::solve::Reveal;
+use crate::solve::{AdtDestructorKind, Reveal};
 use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable};
 use crate::{self as ty, CollectAndApply, Interner, UpcastFrom};
 
@@ -537,6 +537,8 @@ pub trait AdtDef<I: Interner>: Copy + Debug + Hash + Eq {
     fn sized_constraint(self, interner: I) -> Option<ty::EarlyBinder<I, I::Ty>>;
 
     fn is_fundamental(self) -> bool;
+
+    fn destructor(self, interner: I) -> Option<AdtDestructorKind>;
 }
 
 pub trait ParamEnv<I: Interner>: Copy + Debug + Hash + Eq + TypeFoldable<I> {
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index 93b9c2e289249..0ae688848ebfc 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -225,6 +225,7 @@ pub trait Interner:
 
     fn impl_is_const(self, def_id: Self::DefId) -> bool;
     fn fn_is_const(self, def_id: Self::DefId) -> bool;
+    fn alias_has_const_conditions(self, def_id: Self::DefId) -> bool;
     fn const_conditions(
         self,
         def_id: Self::DefId,
diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs
index df43346065d40..eeb80bc3ab420 100644
--- a/compiler/rustc_type_ir/src/lang_items.rs
+++ b/compiler/rustc_type_ir/src/lang_items.rs
@@ -19,6 +19,7 @@ pub enum TraitSolverLangItem {
     CoroutineYield,
     Destruct,
     DiscriminantKind,
+    Drop,
     DynMetadata,
     Fn,
     FnMut,
diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs
index fe4558730513a..13081f3154be9 100644
--- a/compiler/rustc_type_ir/src/solve/mod.rs
+++ b/compiler/rustc_type_ir/src/solve/mod.rs
@@ -326,3 +326,10 @@ impl MaybeCause {
         }
     }
 }
+
+/// Indicates that a `impl Drop for Adt` is `const` or not.
+#[derive(Debug)]
+pub enum AdtDestructorKind {
+    NotConst,
+    Const,
+}
diff --git a/tests/ui/consts/promoted_const_call.stderr b/tests/ui/consts/promoted_const_call.stderr
index bcb9dfb704b1d..7a96b6e770582 100644
--- a/tests/ui/consts/promoted_const_call.stderr
+++ b/tests/ui/consts/promoted_const_call.stderr
@@ -7,25 +7,13 @@ LL | impl const Drop for Panic { fn drop(&mut self) { panic!(); } }
    = note: marking a trait with `#[const_trait]` ensures all default method bodies are `const`
    = note: adding a non-const method body in the future would be a breaking change
 
-error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promoted_const_call.rs:10:26
-   |
-LL |     let _: &'static _ = &id(&Panic);
-   |            ----------    ^^^^^^^^^^ creates a temporary value which is freed while still in use
-   |            |
-   |            type annotation requires that borrow lasts for `'static`
-...
-LL | };
-   | - temporary value is freed at the end of this statement
-
-error[E0716]: temporary value dropped while borrowed
+error[E0493]: destructor of `Panic` cannot be evaluated at compile-time
   --> $DIR/promoted_const_call.rs:10:30
    |
 LL |     let _: &'static _ = &id(&Panic);
-   |            ----------        ^^^^^ - temporary value is freed at the end of this statement
-   |            |                 |
-   |            |                 creates a temporary value which is freed while still in use
-   |            type annotation requires that borrow lasts for `'static`
+   |                              ^^^^^ - value is dropped here
+   |                              |
+   |                              the destructor for this type cannot be evaluated in constants
 
 error[E0716]: temporary value dropped while borrowed
   --> $DIR/promoted_const_call.rs:16:26
@@ -69,6 +57,7 @@ LL |     let _: &'static _ = &&(Panic, 0).1;
 LL | }
    | - temporary value is freed at the end of this statement
 
-error: aborting due to 7 previous errors
+error: aborting due to 6 previous errors
 
-For more information about this error, try `rustc --explain E0716`.
+Some errors have detailed explanations: E0493, E0716.
+For more information about an error, try `rustc --explain E0493`.
diff --git a/tests/ui/traits/const-traits/const-drop.precise.stderr b/tests/ui/traits/const-traits/const-drop.precise.stderr
index ed90b234761ad..4f673187d499f 100644
--- a/tests/ui/traits/const-traits/const-drop.precise.stderr
+++ b/tests/ui/traits/const-traits/const-drop.precise.stderr
@@ -72,6 +72,12 @@ note: required by a bound in `t::ConstDropWithBound`
 LL |     pub struct ConstDropWithBound<T: const SomeTrait>(pub core::marker::PhantomData<T>);
    |                                      ^^^^^ required by this bound in `ConstDropWithBound`
 
+error[E0493]: destructor of `S<'_>` cannot be evaluated at compile-time
+  --> $DIR/const-drop.rs:23:13
+   |
+LL |     let _ = S(&mut c);
+   |             ^^^^^^^^^ the destructor for this type cannot be evaluated in constant functions
+
 error[E0493]: destructor of `T` cannot be evaluated at compile-time
   --> $DIR/const-drop.rs:18:32
    |
@@ -84,7 +90,7 @@ error[E0277]: the trait bound `T: ~const SomeTrait` is not satisfied
 LL |             T::foo();
    |             ^^^^^^^^
 
-error: aborting due to 10 previous errors
+error: aborting due to 11 previous errors
 
 Some errors have detailed explanations: E0277, E0493.
 For more information about an error, try `rustc --explain E0277`.
diff --git a/tests/ui/traits/const-traits/const-drop.stock.stderr b/tests/ui/traits/const-traits/const-drop.stock.stderr
index 2b46b048e9095..51a66396c4b42 100644
--- a/tests/ui/traits/const-traits/const-drop.stock.stderr
+++ b/tests/ui/traits/const-traits/const-drop.stock.stderr
@@ -72,6 +72,14 @@ note: required by a bound in `t::ConstDropWithBound`
 LL |     pub struct ConstDropWithBound<T: const SomeTrait>(pub core::marker::PhantomData<T>);
    |                                      ^^^^^ required by this bound in `ConstDropWithBound`
 
+error[E0493]: destructor of `S<'_>` cannot be evaluated at compile-time
+  --> $DIR/const-drop.rs:23:13
+   |
+LL |     let _ = S(&mut c);
+   |             ^^^^^^^^^- value is dropped here
+   |             |
+   |             the destructor for this type cannot be evaluated in constant functions
+
 error[E0493]: destructor of `T` cannot be evaluated at compile-time
   --> $DIR/const-drop.rs:18:32
    |
@@ -86,7 +94,7 @@ error[E0277]: the trait bound `T: ~const SomeTrait` is not satisfied
 LL |             T::foo();
    |             ^^^^^^^^
 
-error: aborting due to 10 previous errors
+error: aborting due to 11 previous errors
 
 Some errors have detailed explanations: E0277, E0493.
 For more information about an error, try `rustc --explain E0277`.
diff --git a/tests/ui/traits/const-traits/effects/auxiliary/minicore.rs b/tests/ui/traits/const-traits/effects/auxiliary/minicore.rs
index 209a111c24366..498e698ef1c1f 100644
--- a/tests/ui/traits/const-traits/effects/auxiliary/minicore.rs
+++ b/tests/ui/traits/const-traits/effects/auxiliary/minicore.rs
@@ -444,12 +444,12 @@ impl<T: ?Sized> Deref for Ref<'_, T> {
 
 #[lang = "clone"]
 #[rustc_trivial_field_reads]
-// FIXME: #[const_trait]
+#[const_trait]
 pub trait Clone: Sized {
     fn clone(&self) -> Self;
     fn clone_from(&mut self, source: &Self)
     where
-    // FIXME: Self: ~const Destruct,
+    Self: ~const Destruct,
     {
         *self = source.clone()
     }
@@ -458,7 +458,7 @@ pub trait Clone: Sized {
 #[lang = "structural_peq"]
 pub trait StructuralPartialEq {}
 
-// FIXME: const fn drop<T: ~const Destruct>(_: T) {}
+pub const fn drop<T: ~const Destruct>(_: T) {}
 
 #[rustc_intrinsic_must_be_overridden]
 #[rustc_intrinsic]
diff --git a/tests/ui/traits/const-traits/effects/minicore-drop-fail.rs b/tests/ui/traits/const-traits/effects/minicore-drop-fail.rs
new file mode 100644
index 0000000000000..57c39d2c1a157
--- /dev/null
+++ b/tests/ui/traits/const-traits/effects/minicore-drop-fail.rs
@@ -0,0 +1,37 @@
+//@ aux-build:minicore.rs
+//@ compile-flags: --crate-type=lib -Znext-solver
+
+#![feature(no_core, const_trait_impl)]
+#![no_std]
+#![no_core]
+
+extern crate minicore;
+use minicore::*;
+
+struct Contains<T>(T);
+
+struct NotDropImpl;
+impl Drop for NotDropImpl {
+    fn drop(&mut self) {}
+}
+
+#[const_trait] trait Foo {}
+impl Foo for () {}
+
+struct Conditional<T: Foo>(T);
+impl<T> const Drop for Conditional<T> where T: ~const Foo {
+    fn drop(&mut self) {}
+}
+
+const fn test() {
+    let _ = NotDropImpl;
+    //~^ ERROR destructor of `NotDropImpl` cannot be evaluated at compile-time
+    let _ = Contains(NotDropImpl);
+    //~^ ERROR destructor of `Contains<NotDropImpl>` cannot be evaluated at compile-time
+    let _ = Conditional(());
+    //~^ ERROR destructor of `Conditional<()>` cannot be evaluated at compile-time
+}
+
+const fn drop_arbitrary<T>(_: T) {
+    //~^ ERROR destructor of `T` cannot be evaluated at compile-time
+}
diff --git a/tests/ui/traits/const-traits/effects/minicore-drop-fail.stderr b/tests/ui/traits/const-traits/effects/minicore-drop-fail.stderr
new file mode 100644
index 0000000000000..12d1877a18aba
--- /dev/null
+++ b/tests/ui/traits/const-traits/effects/minicore-drop-fail.stderr
@@ -0,0 +1,36 @@
+error[E0493]: destructor of `NotDropImpl` cannot be evaluated at compile-time
+  --> $DIR/minicore-drop-fail.rs:27:13
+   |
+LL |     let _ = NotDropImpl;
+   |             ^^^^^^^^^^^- value is dropped here
+   |             |
+   |             the destructor for this type cannot be evaluated in constant functions
+
+error[E0493]: destructor of `Contains<NotDropImpl>` cannot be evaluated at compile-time
+  --> $DIR/minicore-drop-fail.rs:29:13
+   |
+LL |     let _ = Contains(NotDropImpl);
+   |             ^^^^^^^^^^^^^^^^^^^^^- value is dropped here
+   |             |
+   |             the destructor for this type cannot be evaluated in constant functions
+
+error[E0493]: destructor of `Conditional<()>` cannot be evaluated at compile-time
+  --> $DIR/minicore-drop-fail.rs:31:13
+   |
+LL |     let _ = Conditional(());
+   |             ^^^^^^^^^^^^^^^- value is dropped here
+   |             |
+   |             the destructor for this type cannot be evaluated in constant functions
+
+error[E0493]: destructor of `T` cannot be evaluated at compile-time
+  --> $DIR/minicore-drop-fail.rs:35:28
+   |
+LL | const fn drop_arbitrary<T>(_: T) {
+   |                            ^ the destructor for this type cannot be evaluated in constant functions
+LL |
+LL | }
+   | - value is dropped here
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0493`.

From b75c1c3dd64d6846d670adca10ebd3735b48dc6a Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Tue, 12 Nov 2024 17:44:31 +0000
Subject: [PATCH 2/5] More comments, reverse polarity of structural check

---
 .../src/check_consts/qualifs.rs               | 76 ++++++++++---------
 1 file changed, 41 insertions(+), 35 deletions(-)

diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs
index b965ad7bd8715..df19ff2c16a76 100644
--- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs
+++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs
@@ -62,16 +62,12 @@ pub trait Qualif {
     /// It also determines the `Qualif`s for primitive types.
     fn in_any_value_of_ty<'tcx>(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool;
 
-    /// Returns `true` if the `Qualif` is not structural, i.e. that we should not recurse
-    /// into the operand.
-    fn is_non_structural<'tcx>(cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>) -> bool;
-
-    /// Returns `true` if this `Qualif` behaves sructurally for pointers and references:
-    /// the pointer/reference qualifies if and only if the pointee qualifies.
+    /// Returns `true` if the `Qualif` is structural in an ADT's fields, i.e. that we may
+    /// recurse into an operand if we know what it is.
     ///
-    /// (This is currently `false` for all our instances, but that may change in the future. Also,
-    /// by keeping it abstract, the handling of `Deref` in `in_place` becomes more clear.)
-    fn deref_structural<'tcx>(cx: &ConstCx<'_, 'tcx>) -> bool;
+    /// If this returns false, `in_any_value_of_ty` will be invoked to determine the
+    /// final qualif for this ADT.
+    fn is_structural_in_adt<'tcx>(cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>) -> bool;
 }
 
 /// Constant containing interior mutability (`UnsafeCell<T>`).
@@ -127,18 +123,13 @@ impl Qualif for HasMutInterior {
         !errors.is_empty()
     }
 
-    fn is_non_structural<'tcx>(_cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>) -> bool {
+    fn is_structural_in_adt<'tcx>(_cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>) -> bool {
         // Exactly one type, `UnsafeCell`, has the `HasMutInterior` qualif inherently.
         // It arises structurally for all other types.
-        adt.is_unsafe_cell()
-    }
-
-    fn deref_structural<'tcx>(_cx: &ConstCx<'_, 'tcx>) -> bool {
-        false
+        !adt.is_unsafe_cell()
     }
 }
 
-// FIXME(const_trait_impl): Get rid of this!
 /// Constant containing an ADT that implements `Drop`.
 /// This must be ruled out because implicit promotion would remove side-effects
 /// that occur as part of dropping that value. N.B., the implicit promotion has
@@ -158,12 +149,8 @@ impl Qualif for NeedsDrop {
         ty.needs_drop(cx.tcx, cx.typing_env)
     }
 
-    fn is_non_structural<'tcx>(cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>) -> bool {
-        adt.has_dtor(cx.tcx)
-    }
-
-    fn deref_structural<'tcx>(_cx: &ConstCx<'_, 'tcx>) -> bool {
-        false
+    fn is_structural_in_adt<'tcx>(cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>) -> bool {
+        !adt.has_dtor(cx.tcx)
     }
 }
 
@@ -187,14 +174,20 @@ impl Qualif for NeedsNonConstDrop {
             return false;
         }
 
+        // We check that the type is `~const Destruct` since that will verify that
+        // the type is both `~const Drop` (if a drop impl exists for the adt), *and*
+        // that the components of this type are also `~const Destruct`. This
+        // amounts to verifying that there are no values in this ADT that may have
+        // a non-const drop.
         if cx.tcx.features().const_trait_impl() {
             let destruct_def_id = cx.tcx.require_lang_item(LangItem::Destruct, Some(cx.body.span));
-            let infcx = cx.tcx.infer_ctxt().build(TypingMode::from_param_env(cx.param_env));
+            let infcx =
+                cx.tcx.infer_ctxt().build(TypingMode::from_param_env(cx.typing_env.param_env));
             let ocx = ObligationCtxt::new(&infcx);
             ocx.register_obligation(Obligation::new(
                 cx.tcx,
                 ObligationCause::misc(cx.body.span, cx.def_id()),
-                cx.param_env,
+                cx.typing_env.param_env,
                 ty::Binder::dummy(ty::TraitRef::new(cx.tcx, destruct_def_id, [ty]))
                     .to_host_effect_clause(cx.tcx, match cx.const_kind() {
                         rustc_hir::ConstContext::ConstFn => ty::BoundConstness::Maybe,
@@ -208,15 +201,18 @@ impl Qualif for NeedsNonConstDrop {
         }
     }
 
-    fn is_non_structural<'tcx>(cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>) -> bool {
-        // Even a `const` dtor may have `~const` bounds that may need to
-        // be satisfied, so this becomes non-structural as soon as the
-        // ADT gets a destructor at all.
-        adt.has_dtor(cx.tcx)
-    }
-
-    fn deref_structural<'tcx>(_cx: &ConstCx<'_, 'tcx>) -> bool {
-        false
+    fn is_structural_in_adt<'tcx>(cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>) -> bool {
+        // As soon as an ADT has a destructor, then the drop becomes non-structural
+        // in its value since:
+        // 1. The destructor may have `~const` bounds that need to be satisfied on
+        //   top of checking that the components of a specific operand are const-drop.
+        //   While this could be instead satisfied by checking that the `~const Drop`
+        //   impl holds (i.e. replicating part of the `in_any_value_of_ty` logic above),
+        //   even in this case, we have another problem, which is,
+        // 2. The destructor may *modify* the operand being dropped, so even if we
+        //   did recurse on the components of the operand, we may not be even dropping
+        //   the same values that were present before the custom destructor was invoked.
+        !adt.has_dtor(cx.tcx)
     }
 }
 
@@ -270,7 +266,12 @@ where
             // qualified.
             if let AggregateKind::Adt(adt_did, ..) = **kind {
                 let def = cx.tcx.adt_def(adt_did);
-                if def.is_union() || Q::is_non_structural(cx, def) {
+                // Don't do any value-based reasoning for unions.
+                // Also, if the ADT is not structural in its fields,
+                // then we cannot recurse on its fields. Instead,
+                // we fall back to checking the qualif for *any* value
+                // of the ADT.
+                if def.is_union() || !Q::is_structural_in_adt(cx, def) {
                     return Q::in_any_value_of_ty(cx, rvalue.ty(cx.body, cx.tcx));
                 }
             }
@@ -308,7 +309,12 @@ where
             return false;
         }
 
-        if matches!(elem, ProjectionElem::Deref) && !Q::deref_structural(cx) {
+        // This is currently unconditionally true for all qualifs, since we do
+        // not recurse into the pointer of a deref projection, but that may change
+        // in the future. If that changes, each qualif should be required to
+        // specify whether it operates structurally for deref projections, just like
+        // we do for `Qualif::is_structural_in_adt`.
+        if matches!(elem, ProjectionElem::Deref) {
             // We have to assume that this qualifies.
             return true;
         }

From 20882608529a969bd878ad787cf0038716c021df Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Tue, 19 Nov 2024 17:08:52 +0000
Subject: [PATCH 3/5] Gate const drop behind const_destruct feature, and fix
 const_precise_live_drops post-drop-elaboration check

---
 .../src/check_consts/check.rs                 | 44 +++++++------
 .../rustc_const_eval/src/check_consts/ops.rs  | 38 ++++++++++--
 .../src/check_consts/post_drop_elaboration.rs | 62 ++++++++++---------
 .../src/check_consts/qualifs.rs               | 57 ++++++++---------
 compiler/rustc_feature/src/unstable.rs        |  2 +
 compiler/rustc_span/src/symbol.rs             |  1 +
 library/core/src/marker.rs                    |  2 +-
 .../ui/consts/const-block-const-bound.stderr  | 25 +++++++-
 .../control-flow/drop-fail.precise.stderr     | 10 ++-
 tests/ui/consts/control-flow/drop-fail.rs     |  1 +
 .../control-flow/drop-fail.stock.stderr       |  8 +--
 tests/ui/consts/drop_zst.stderr               |  2 +
 tests/ui/consts/fn_trait_refs.stderr          | 54 +++++++++++++++-
 tests/ui/consts/promoted_const_call2.stderr   |  4 +-
 .../qualif-indirect-mutation-fail.stderr      | 25 ++++++++
 .../ui/impl-trait/normalize-tait-in-const.rs  |  2 +-
 .../const-traits/const-drop-bound.stderr      | 49 ++++++++++++++-
 .../const-traits/const-drop-fail-2.stderr     | 24 ++++++-
 .../const-drop-fail.precise.stderr            |  4 +-
 .../ui/traits/const-traits/const-drop-fail.rs |  2 +-
 .../const-traits/const-drop.precise.stderr    |  8 ++-
 tests/ui/traits/const-traits/const-drop.rs    |  2 +-
 .../effects/auxiliary/minicore.rs             |  3 +-
 .../effects/minicore-drop-fail.rs             |  2 +-
 ...nicore-drop-without-feature-gate.no.stderr | 15 +++++
 .../minicore-drop-without-feature-gate.rs     | 25 ++++++++
 tests/ui/traits/const-traits/issue-92111.rs   |  2 +-
 tests/ui/traits/next-solver/destruct.rs       |  2 +-
 28 files changed, 366 insertions(+), 109 deletions(-)
 create mode 100644 tests/ui/traits/const-traits/effects/minicore-drop-without-feature-gate.no.stderr
 create mode 100644 tests/ui/traits/const-traits/effects/minicore-drop-without-feature-gate.rs

diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs
index 1129b5caec54e..a088a210e2640 100644
--- a/compiler/rustc_const_eval/src/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/check_consts/check.rs
@@ -24,7 +24,7 @@ use rustc_span::{Span, Symbol, sym};
 use rustc_trait_selection::traits::{
     Obligation, ObligationCause, ObligationCauseCode, ObligationCtxt,
 };
-use tracing::{debug, instrument, trace};
+use tracing::{instrument, trace};
 
 use super::ops::{self, NonConstOp, Status};
 use super::qualifs::{self, HasMutInterior, NeedsDrop, NeedsNonConstDrop};
@@ -47,7 +47,7 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
     /// Returns `true` if `local` is `NeedsDrop` at the given `Location`.
     ///
     /// Only updates the cursor if absolutely necessary
-    fn needs_drop(
+    pub(crate) fn needs_drop(
         &mut self,
         ccx: &'mir ConstCx<'mir, 'tcx>,
         local: Local,
@@ -324,6 +324,14 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
         }
     }
 
+    /// Emits an error at the given `span` if an expression cannot be evaluated in the current
+    /// context. This is meant for use in a post-const-checker pass such as the const precise
+    /// live drops lint.
+    pub fn check_op_spanned_post<O: NonConstOp<'tcx>>(mut self, op: O, span: Span) {
+        self.check_op_spanned(op, span);
+        assert!(self.secondary_errors.is_empty());
+    }
+
     fn check_static(&mut self, def_id: DefId, span: Span) {
         if self.tcx.is_thread_local_static(def_id) {
             self.tcx.dcx().span_bug(span, "tls access is checked in `Rvalue::ThreadLocalRef`");
@@ -869,12 +877,13 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                 let mut err_span = self.span;
                 let ty_of_dropped_place = dropped_place.ty(self.body, self.tcx).ty;
 
-                let ty_needs_non_const_drop =
-                    qualifs::NeedsNonConstDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place);
-
-                debug!(?ty_of_dropped_place, ?ty_needs_non_const_drop);
-
-                if !ty_needs_non_const_drop {
+                let needs_drop = if let Some(local) = dropped_place.as_local() {
+                    self.qualifs.needs_drop(self.ccx, local, location)
+                } else {
+                    qualifs::NeedsDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place)
+                };
+                // If this type doesn't need a drop at all, then there's nothing to enforce.
+                if !needs_drop {
                     return;
                 }
 
@@ -883,18 +892,17 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                     err_span = self.body.local_decls[local].source_info.span;
                     self.qualifs.needs_non_const_drop(self.ccx, local, location)
                 } else {
-                    true
+                    qualifs::NeedsNonConstDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place)
                 };
 
-                if needs_non_const_drop {
-                    self.check_op_spanned(
-                        ops::LiveDrop {
-                            dropped_at: Some(terminator.source_info.span),
-                            dropped_ty: ty_of_dropped_place,
-                        },
-                        err_span,
-                    );
-                }
+                self.check_op_spanned(
+                    ops::LiveDrop {
+                        dropped_at: Some(terminator.source_info.span),
+                        dropped_ty: ty_of_dropped_place,
+                        needs_non_const_drop,
+                    },
+                    err_span,
+                );
             }
 
             TerminatorKind::InlineAsm { .. } => self.check_op(ops::InlineAsm),
diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs
index 8ba6b89aad4d5..c8d6358161fca 100644
--- a/compiler/rustc_const_eval/src/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/check_consts/ops.rs
@@ -461,15 +461,41 @@ impl<'tcx> NonConstOp<'tcx> for InlineAsm {
 pub(crate) struct LiveDrop<'tcx> {
     pub dropped_at: Option<Span>,
     pub dropped_ty: Ty<'tcx>,
+    pub needs_non_const_drop: bool,
 }
 impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> {
+    fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
+        if self.needs_non_const_drop {
+            Status::Forbidden
+        } else {
+            Status::Unstable {
+                gate: sym::const_destruct,
+                gate_already_checked: false,
+                safe_to_expose_on_stable: false,
+                is_function_call: false,
+            }
+        }
+    }
+
     fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
-        ccx.dcx().create_err(errors::LiveDrop {
-            span,
-            dropped_ty: self.dropped_ty,
-            kind: ccx.const_kind(),
-            dropped_at: self.dropped_at,
-        })
+        if self.needs_non_const_drop {
+            ccx.dcx().create_err(errors::LiveDrop {
+                span,
+                dropped_ty: self.dropped_ty,
+                kind: ccx.const_kind(),
+                dropped_at: self.dropped_at,
+            })
+        } else {
+            ccx.tcx.sess.create_feature_err(
+                errors::LiveDrop {
+                    span,
+                    dropped_ty: self.dropped_ty,
+                    kind: ccx.const_kind(),
+                    dropped_at: self.dropped_at,
+                },
+                sym::const_destruct,
+            )
+        }
     }
 }
 
diff --git a/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs
index f6eb130fbd385..c3a9aad642129 100644
--- a/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs
+++ b/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs
@@ -1,14 +1,15 @@
 use rustc_middle::mir::visit::Visitor;
 use rustc_middle::mir::{self, BasicBlock, Location};
-use rustc_middle::ty::{Ty, TyCtxt};
-use rustc_span::Span;
+use rustc_middle::ty::TyCtxt;
 use rustc_span::symbol::sym;
 use tracing::trace;
 
 use super::ConstCx;
 use super::check::Qualifs;
-use super::ops::{self, NonConstOp};
+use super::ops::{self};
 use super::qualifs::{NeedsNonConstDrop, Qualif};
+use crate::check_consts::check::Checker;
+use crate::check_consts::qualifs::NeedsDrop;
 use crate::check_consts::rustc_allow_const_fn_unstable;
 
 /// Returns `true` if we should use the more precise live drop checker that runs after drop
@@ -64,12 +65,6 @@ impl<'mir, 'tcx> std::ops::Deref for CheckLiveDrops<'mir, 'tcx> {
     }
 }
 
-impl<'tcx> CheckLiveDrops<'_, 'tcx> {
-    fn check_live_drop(&self, span: Span, dropped_ty: Ty<'tcx>) {
-        ops::LiveDrop { dropped_at: None, dropped_ty }.build_error(self.ccx, span).emit();
-    }
-}
-
 impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> {
     fn visit_basic_block_data(&mut self, bb: BasicBlock, block: &mir::BasicBlockData<'tcx>) {
         trace!("visit_basic_block_data: bb={:?} is_cleanup={:?}", bb, block.is_cleanup);
@@ -87,28 +82,39 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> {
 
         match &terminator.kind {
             mir::TerminatorKind::Drop { place: dropped_place, .. } => {
-                let dropped_ty = dropped_place.ty(self.body, self.tcx).ty;
-
-                if !NeedsNonConstDrop::in_any_value_of_ty(self.ccx, dropped_ty) {
-                    // Instead of throwing a bug, we just return here. This is because we have to
-                    // run custom `const Drop` impls.
+                let ty_of_dropped_place = dropped_place.ty(self.body, self.tcx).ty;
+
+                let needs_drop = if let Some(local) = dropped_place.as_local() {
+                    self.qualifs.needs_drop(self.ccx, local, location)
+                } else {
+                    NeedsDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place)
+                };
+                // If this type doesn't need a drop at all, then there's nothing to enforce.
+                if !needs_drop {
                     return;
                 }
 
-                if dropped_place.is_indirect() {
-                    self.check_live_drop(terminator.source_info.span, dropped_ty);
-                    return;
-                }
-
-                // Drop elaboration is not precise enough to accept code like
-                // `tests/ui/consts/control-flow/drop-pass.rs`; e.g., when an `Option<Vec<T>>` is
-                // initialized with `None` and never changed, it still emits drop glue.
-                // Hence we additionally check the qualifs here to allow more code to pass.
-                if self.qualifs.needs_non_const_drop(self.ccx, dropped_place.local, location) {
-                    // Use the span where the dropped local was declared for the error.
-                    let span = self.body.local_decls[dropped_place.local].source_info.span;
-                    self.check_live_drop(span, dropped_ty);
-                }
+                let mut err_span = terminator.source_info.span;
+
+                let needs_non_const_drop = if let Some(local) = dropped_place.as_local() {
+                    // Use the span where the local was declared as the span of the drop error.
+                    err_span = self.body.local_decls[local].source_info.span;
+                    self.qualifs.needs_non_const_drop(self.ccx, local, location)
+                } else {
+                    NeedsNonConstDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place)
+                };
+
+                // I know it's not great to be creating a new const checker, but I'd
+                // rather use it so we can deduplicate the error emitting logic that
+                // it contains.
+                Checker::new(self.ccx).check_op_spanned_post(
+                    ops::LiveDrop {
+                        dropped_at: Some(terminator.source_info.span),
+                        dropped_ty: ty_of_dropped_place,
+                        needs_non_const_drop,
+                    },
+                    err_span,
+                );
             }
 
             mir::TerminatorKind::UnwindTerminate(_)
diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs
index df19ff2c16a76..29fd3aa7d4de2 100644
--- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs
+++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs
@@ -62,12 +62,12 @@ pub trait Qualif {
     /// It also determines the `Qualif`s for primitive types.
     fn in_any_value_of_ty<'tcx>(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool;
 
-    /// Returns `true` if the `Qualif` is structural in an ADT's fields, i.e. that we may
-    /// recurse into an operand if we know what it is.
+    /// Returns `true` if the `Qualif` is structural in an ADT's fields, i.e. if we may
+    /// recurse into an operand *value* to determine whether it has this `Qualif`.
     ///
     /// If this returns false, `in_any_value_of_ty` will be invoked to determine the
     /// final qualif for this ADT.
-    fn is_structural_in_adt<'tcx>(cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>) -> bool;
+    fn is_structural_in_adt_value<'tcx>(cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>) -> bool;
 }
 
 /// Constant containing interior mutability (`UnsafeCell<T>`).
@@ -123,7 +123,7 @@ impl Qualif for HasMutInterior {
         !errors.is_empty()
     }
 
-    fn is_structural_in_adt<'tcx>(_cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>) -> bool {
+    fn is_structural_in_adt_value<'tcx>(_cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>) -> bool {
         // Exactly one type, `UnsafeCell`, has the `HasMutInterior` qualif inherently.
         // It arises structurally for all other types.
         !adt.is_unsafe_cell()
@@ -140,6 +140,7 @@ pub struct NeedsDrop;
 impl Qualif for NeedsDrop {
     const ANALYSIS_NAME: &'static str = "flow_needs_drop";
     const IS_CLEARED_ON_MOVE: bool = true;
+    const ALLOW_PROMOTED: bool = true;
 
     fn in_qualifs(qualifs: &ConstQualifs) -> bool {
         qualifs.needs_drop
@@ -149,7 +150,7 @@ impl Qualif for NeedsDrop {
         ty.needs_drop(cx.tcx, cx.typing_env)
     }
 
-    fn is_structural_in_adt<'tcx>(cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>) -> bool {
+    fn is_structural_in_adt_value<'tcx>(cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>) -> bool {
         !adt.has_dtor(cx.tcx)
     }
 }
@@ -179,34 +180,30 @@ impl Qualif for NeedsNonConstDrop {
         // that the components of this type are also `~const Destruct`. This
         // amounts to verifying that there are no values in this ADT that may have
         // a non-const drop.
-        if cx.tcx.features().const_trait_impl() {
-            let destruct_def_id = cx.tcx.require_lang_item(LangItem::Destruct, Some(cx.body.span));
-            let infcx =
-                cx.tcx.infer_ctxt().build(TypingMode::from_param_env(cx.typing_env.param_env));
-            let ocx = ObligationCtxt::new(&infcx);
-            ocx.register_obligation(Obligation::new(
-                cx.tcx,
-                ObligationCause::misc(cx.body.span, cx.def_id()),
-                cx.typing_env.param_env,
-                ty::Binder::dummy(ty::TraitRef::new(cx.tcx, destruct_def_id, [ty]))
-                    .to_host_effect_clause(cx.tcx, match cx.const_kind() {
-                        rustc_hir::ConstContext::ConstFn => ty::BoundConstness::Maybe,
-                        rustc_hir::ConstContext::Static(_)
-                        | rustc_hir::ConstContext::Const { .. } => ty::BoundConstness::Const,
-                    }),
-            ));
-            !ocx.select_all_or_error().is_empty()
-        } else {
-            NeedsDrop::in_any_value_of_ty(cx, ty)
-        }
+        let destruct_def_id = cx.tcx.require_lang_item(LangItem::Destruct, Some(cx.body.span));
+        let (infcx, param_env) = cx.tcx.infer_ctxt().build_with_typing_env(cx.typing_env);
+        let ocx = ObligationCtxt::new(&infcx);
+        ocx.register_obligation(Obligation::new(
+            cx.tcx,
+            ObligationCause::misc(cx.body.span, cx.def_id()),
+            param_env,
+            ty::Binder::dummy(ty::TraitRef::new(cx.tcx, destruct_def_id, [ty]))
+                .to_host_effect_clause(cx.tcx, match cx.const_kind() {
+                    rustc_hir::ConstContext::ConstFn => ty::BoundConstness::Maybe,
+                    rustc_hir::ConstContext::Static(_) | rustc_hir::ConstContext::Const { .. } => {
+                        ty::BoundConstness::Const
+                    }
+                }),
+        ));
+        !ocx.select_all_or_error().is_empty()
     }
 
-    fn is_structural_in_adt<'tcx>(cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>) -> bool {
+    fn is_structural_in_adt_value<'tcx>(cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>) -> bool {
         // As soon as an ADT has a destructor, then the drop becomes non-structural
         // in its value since:
-        // 1. The destructor may have `~const` bounds that need to be satisfied on
-        //   top of checking that the components of a specific operand are const-drop.
-        //   While this could be instead satisfied by checking that the `~const Drop`
+        // 1. The destructor may have `~const` bounds which are not present on the type.
+        //   Someone needs to check that those are satisfied.
+        //   While this could be done instead satisfied by checking that the `~const Drop`
         //   impl holds (i.e. replicating part of the `in_any_value_of_ty` logic above),
         //   even in this case, we have another problem, which is,
         // 2. The destructor may *modify* the operand being dropped, so even if we
@@ -271,7 +268,7 @@ where
                 // then we cannot recurse on its fields. Instead,
                 // we fall back to checking the qualif for *any* value
                 // of the ADT.
-                if def.is_union() || !Q::is_structural_in_adt(cx, def) {
+                if def.is_union() || !Q::is_structural_in_adt_value(cx, def) {
                     return Q::in_any_value_of_ty(cx, rvalue.ty(cx.body, cx.tcx));
                 }
             }
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index e3dc73c14014f..b9a75fedcbefd 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -426,6 +426,8 @@ declare_features! (
     (unstable, const_async_blocks, "1.53.0", Some(85368)),
     /// Allows `const || {}` closures in const contexts.
     (incomplete, const_closures, "1.68.0", Some(106003)),
+    /// Uwu
+    (unstable, const_destruct, "CURRENT_RUSTC_VERSION", Some(133214)),
     /// Allows `for _ in _` loops in const contexts.
     (unstable, const_for, "1.56.0", Some(87575)),
     /// Be more precise when looking for live drops in a const context.
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index a2d9859645ff6..c7c29abc3cbbe 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -610,6 +610,7 @@ symbols! {
         const_compare_raw_pointers,
         const_constructor,
         const_deallocate,
+        const_destruct,
         const_eval_limit,
         const_eval_select,
         const_evaluatable_checked,
diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs
index c8ea52a1fb0b4..bd1a8a02dbf47 100644
--- a/library/core/src/marker.rs
+++ b/library/core/src/marker.rs
@@ -953,7 +953,7 @@ marker_impls! {
 ///
 /// This should be used for `~const` bounds,
 /// as non-const bounds will always hold for every type.
-#[unstable(feature = "const_trait_impl", issue = "67792")]
+#[unstable(feature = "const_destruct", issue = "10")]
 #[lang = "destruct"]
 #[rustc_on_unimplemented(message = "can't drop `{Self}`", append_const_msg)]
 #[rustc_deny_explicit_impl(implement_via_object = false)]
diff --git a/tests/ui/consts/const-block-const-bound.stderr b/tests/ui/consts/const-block-const-bound.stderr
index 5e24959146b83..3b8f6b6359123 100644
--- a/tests/ui/consts/const-block-const-bound.stderr
+++ b/tests/ui/consts/const-block-const-bound.stderr
@@ -1,3 +1,23 @@
+error[E0658]: use of unstable library feature `const_destruct`
+  --> $DIR/const-block-const-bound.rs:6:5
+   |
+LL | use std::marker::Destruct;
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
+   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: use of unstable library feature `const_destruct`
+  --> $DIR/const-block-const-bound.rs:8:22
+   |
+LL | const fn f<T: ~const Destruct>(x: T) {}
+   |                      ^^^^^^^^
+   |
+   = note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
+   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
 error: `~const` can only be applied to `#[const_trait]` traits
   --> $DIR/const-block-const-bound.rs:8:15
    |
@@ -20,6 +40,7 @@ LL | const fn f<T: ~const Destruct>(x: T) {}
    |                                |
    |                                the destructor for this type cannot be evaluated in constant functions
 
-error: aborting due to 3 previous errors
+error: aborting due to 5 previous errors
 
-For more information about this error, try `rustc --explain E0493`.
+Some errors have detailed explanations: E0493, E0658.
+For more information about an error, try `rustc --explain E0493`.
diff --git a/tests/ui/consts/control-flow/drop-fail.precise.stderr b/tests/ui/consts/control-flow/drop-fail.precise.stderr
index 93b5f257efb77..32afc51c3ee34 100644
--- a/tests/ui/consts/control-flow/drop-fail.precise.stderr
+++ b/tests/ui/consts/control-flow/drop-fail.precise.stderr
@@ -1,14 +1,20 @@
 error[E0493]: destructor of `Option<Vec<i32>>` cannot be evaluated at compile-time
-  --> $DIR/drop-fail.rs:8:9
+  --> $DIR/drop-fail.rs:9:9
    |
 LL |     let x = Some(Vec::new());
    |         ^ the destructor for this type cannot be evaluated in constants
+...
+LL | };
+   | - value is dropped here
 
 error[E0493]: destructor of `Option<Vec<i32>>` cannot be evaluated at compile-time
-  --> $DIR/drop-fail.rs:39:9
+  --> $DIR/drop-fail.rs:40:9
    |
 LL |     let mut tmp = None;
    |         ^^^^^^^ the destructor for this type cannot be evaluated in constants
+...
+LL | };
+   | - value is dropped here
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/consts/control-flow/drop-fail.rs b/tests/ui/consts/control-flow/drop-fail.rs
index 25afe5d08d970..2b73e37b23d03 100644
--- a/tests/ui/consts/control-flow/drop-fail.rs
+++ b/tests/ui/consts/control-flow/drop-fail.rs
@@ -1,5 +1,6 @@
 //@ revisions: stock precise
 
+#![feature(const_destruct)]
 #![cfg_attr(precise, feature(const_precise_live_drops))]
 
 // `x` is *not* always moved into the final value and may be dropped inside the initializer.
diff --git a/tests/ui/consts/control-flow/drop-fail.stock.stderr b/tests/ui/consts/control-flow/drop-fail.stock.stderr
index 2cc8568026eb5..8fe60fd736765 100644
--- a/tests/ui/consts/control-flow/drop-fail.stock.stderr
+++ b/tests/ui/consts/control-flow/drop-fail.stock.stderr
@@ -1,5 +1,5 @@
 error[E0493]: destructor of `Option<Vec<i32>>` cannot be evaluated at compile-time
-  --> $DIR/drop-fail.rs:8:9
+  --> $DIR/drop-fail.rs:9:9
    |
 LL |     let x = Some(Vec::new());
    |         ^ the destructor for this type cannot be evaluated in constants
@@ -8,7 +8,7 @@ LL | };
    | - value is dropped here
 
 error[E0493]: destructor of `(Vec<i32>,)` cannot be evaluated at compile-time
-  --> $DIR/drop-fail.rs:21:9
+  --> $DIR/drop-fail.rs:22:9
    |
 LL |     let vec_tuple = (Vec::new(),);
    |         ^^^^^^^^^ the destructor for this type cannot be evaluated in constants
@@ -17,7 +17,7 @@ LL | };
    | - value is dropped here
 
 error[E0493]: destructor of `Result<Vec<i32>, Vec<i32>>` cannot be evaluated at compile-time
-  --> $DIR/drop-fail.rs:29:9
+  --> $DIR/drop-fail.rs:30:9
    |
 LL |     let x: Result<_, Vec<i32>> = Ok(Vec::new());
    |         ^ the destructor for this type cannot be evaluated in constants
@@ -26,7 +26,7 @@ LL | };
    | - value is dropped here
 
 error[E0493]: destructor of `Option<Vec<i32>>` cannot be evaluated at compile-time
-  --> $DIR/drop-fail.rs:39:9
+  --> $DIR/drop-fail.rs:40:9
    |
 LL |     let mut tmp = None;
    |         ^^^^^^^ the destructor for this type cannot be evaluated in constants
diff --git a/tests/ui/consts/drop_zst.stderr b/tests/ui/consts/drop_zst.stderr
index e3c6785290d76..4d1af7ef93540 100644
--- a/tests/ui/consts/drop_zst.stderr
+++ b/tests/ui/consts/drop_zst.stderr
@@ -3,6 +3,8 @@ error[E0493]: destructor of `S` cannot be evaluated at compile-time
    |
 LL |     let s = S;
    |         ^ the destructor for this type cannot be evaluated in constant functions
+LL | }
+   | - value is dropped here
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/consts/fn_trait_refs.stderr b/tests/ui/consts/fn_trait_refs.stderr
index a686bc23c0f41..15d3ae98c7e2b 100644
--- a/tests/ui/consts/fn_trait_refs.stderr
+++ b/tests/ui/consts/fn_trait_refs.stderr
@@ -1,3 +1,53 @@
+error[E0658]: use of unstable library feature `const_destruct`
+  --> $DIR/fn_trait_refs.rs:9:5
+   |
+LL | use std::marker::Destruct;
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
+   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: use of unstable library feature `const_destruct`
+  --> $DIR/fn_trait_refs.rs:13:31
+   |
+LL |     T: ~const Fn<()> + ~const Destruct,
+   |                               ^^^^^^^^
+   |
+   = note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
+   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: use of unstable library feature `const_destruct`
+  --> $DIR/fn_trait_refs.rs:20:34
+   |
+LL |     T: ~const FnMut<()> + ~const Destruct,
+   |                                  ^^^^^^^^
+   |
+   = note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
+   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: use of unstable library feature `const_destruct`
+  --> $DIR/fn_trait_refs.rs:34:31
+   |
+LL |     T: ~const Fn<()> + ~const Destruct,
+   |                               ^^^^^^^^
+   |
+   = note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
+   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: use of unstable library feature `const_destruct`
+  --> $DIR/fn_trait_refs.rs:48:34
+   |
+LL |     T: ~const FnMut<()> + ~const Destruct,
+   |                                  ^^^^^^^^
+   |
+   = note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
+   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
 error[E0635]: unknown feature `const_fn_trait_ref_impls`
   --> $DIR/fn_trait_refs.rs:3:12
    |
@@ -264,7 +314,7 @@ LL | const fn test_fn_mut<T>(mut f: T) -> (T::Output, T::Output)
 LL | }
    | - value is dropped here
 
-error: aborting due to 34 previous errors
+error: aborting due to 39 previous errors
 
-Some errors have detailed explanations: E0015, E0493, E0635.
+Some errors have detailed explanations: E0015, E0493, E0635, E0658.
 For more information about an error, try `rustc --explain E0015`.
diff --git a/tests/ui/consts/promoted_const_call2.stderr b/tests/ui/consts/promoted_const_call2.stderr
index 177f7aed17df2..bdb43385d2032 100644
--- a/tests/ui/consts/promoted_const_call2.stderr
+++ b/tests/ui/consts/promoted_const_call2.stderr
@@ -22,7 +22,9 @@ error[E0493]: destructor of `String` cannot be evaluated at compile-time
   --> $DIR/promoted_const_call2.rs:4:30
    |
 LL |     let _: &'static _ = &id(&String::new());
-   |                              ^^^^^^^^^^^^^ the destructor for this type cannot be evaluated in constants
+   |                              ^^^^^^^^^^^^^ - value is dropped here
+   |                              |
+   |                              the destructor for this type cannot be evaluated in constants
 
 error[E0716]: temporary value dropped while borrowed
   --> $DIR/promoted_const_call2.rs:11:26
diff --git a/tests/ui/consts/qualif-indirect-mutation-fail.stderr b/tests/ui/consts/qualif-indirect-mutation-fail.stderr
index 433dfba2257d0..f706b7cf699d0 100644
--- a/tests/ui/consts/qualif-indirect-mutation-fail.stderr
+++ b/tests/ui/consts/qualif-indirect-mutation-fail.stderr
@@ -3,6 +3,9 @@ error[E0493]: destructor of `Option<String>` cannot be evaluated at compile-time
    |
 LL |     let mut x = None;
    |         ^^^^^ the destructor for this type cannot be evaluated in constants
+...
+LL | };
+   | - value is dropped here
 
 error[E0080]: evaluation of constant value failed
   --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
@@ -26,6 +29,8 @@ error[E0493]: destructor of `Option<String>` cannot be evaluated at compile-time
    |
 LL |     let _z = x;
    |         ^^ the destructor for this type cannot be evaluated in constants
+LL | };
+   | - value is dropped here
 
 error[E0080]: evaluation of constant value failed
   --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
@@ -49,42 +54,62 @@ error[E0493]: destructor of `(u32, Option<String>)` cannot be evaluated at compi
    |
 LL |     let mut a: (u32, Option<String>) = (0, None);
    |         ^^^^^ the destructor for this type cannot be evaluated in constant functions
+LL |     let _ = &mut a.1;
+LL | }
+   | - value is dropped here
 
 error[E0493]: destructor of `Option<T>` cannot be evaluated at compile-time
   --> $DIR/qualif-indirect-mutation-fail.rs:34:9
    |
 LL |     let x: Option<T> = None;
    |         ^ the destructor for this type cannot be evaluated in constant functions
+LL |     let _ = x.is_some();
+LL | }
+   | - value is dropped here
 
 error[E0493]: destructor of `Option<T>` cannot be evaluated at compile-time
   --> $DIR/qualif-indirect-mutation-fail.rs:42:9
    |
 LL |     let _y = x;
    |         ^^ the destructor for this type cannot be evaluated in constant functions
+LL | }
+   | - value is dropped here
 
 error[E0493]: destructor of `Option<String>` cannot be evaluated at compile-time
   --> $DIR/qualif-indirect-mutation-fail.rs:50:9
    |
 LL |     let mut y: Option<String> = None;
    |         ^^^^^ the destructor for this type cannot be evaluated in constant functions
+LL |     std::ptr::addr_of_mut!(y);
+LL | }
+   | - value is dropped here
 
 error[E0493]: destructor of `Option<String>` cannot be evaluated at compile-time
   --> $DIR/qualif-indirect-mutation-fail.rs:47:9
    |
 LL |     let mut x: Option<String> = None;
    |         ^^^^^ the destructor for this type cannot be evaluated in constant functions
+...
+LL | }
+   | - value is dropped here
 
 error[E0493]: destructor of `Option<String>` cannot be evaluated at compile-time
   --> $DIR/qualif-indirect-mutation-fail.rs:60:9
    |
 LL |     let y: Option<String> = None;
    |         ^ the destructor for this type cannot be evaluated in constant functions
+LL |     std::ptr::addr_of!(y);
+LL | }
+   | - value is dropped here
 
 error[E0493]: destructor of `Option<String>` cannot be evaluated at compile-time
   --> $DIR/qualif-indirect-mutation-fail.rs:57:9
    |
 LL |     let x: Option<String> = None;
    |         ^ the destructor for this type cannot be evaluated in constant functions
+...
+LL | }
+   | - value is dropped here
 
 error: aborting due to 11 previous errors
 
diff --git a/tests/ui/impl-trait/normalize-tait-in-const.rs b/tests/ui/impl-trait/normalize-tait-in-const.rs
index 134b202d65582..1fd543b72e7cc 100644
--- a/tests/ui/impl-trait/normalize-tait-in-const.rs
+++ b/tests/ui/impl-trait/normalize-tait-in-const.rs
@@ -1,7 +1,7 @@
 //@ known-bug: #103507
 
 #![feature(type_alias_impl_trait)]
-#![feature(const_trait_impl)]
+#![feature(const_trait_impl, const_destruct)]
 
 use std::marker::Destruct;
 
diff --git a/tests/ui/traits/const-traits/const-drop-bound.stderr b/tests/ui/traits/const-traits/const-drop-bound.stderr
index 3f71864543337..530b23975ad00 100644
--- a/tests/ui/traits/const-traits/const-drop-bound.stderr
+++ b/tests/ui/traits/const-traits/const-drop-bound.stderr
@@ -1,3 +1,43 @@
+error[E0658]: use of unstable library feature `const_destruct`
+  --> $DIR/const-drop-bound.rs:7:5
+   |
+LL | use std::marker::Destruct;
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
+   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: use of unstable library feature `const_destruct`
+  --> $DIR/const-drop-bound.rs:9:68
+   |
+LL | const fn foo<T, E>(res: Result<T, E>) -> Option<T> where E: ~const Destruct {
+   |                                                                    ^^^^^^^^
+   |
+   = note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
+   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: use of unstable library feature `const_destruct`
+  --> $DIR/const-drop-bound.rs:20:15
+   |
+LL |     T: ~const Destruct,
+   |               ^^^^^^^^
+   |
+   = note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
+   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: use of unstable library feature `const_destruct`
+  --> $DIR/const-drop-bound.rs:21:15
+   |
+LL |     E: ~const Destruct,
+   |               ^^^^^^^^
+   |
+   = note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
+   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
 error: `~const` can only be applied to `#[const_trait]` traits
   --> $DIR/const-drop-bound.rs:9:61
    |
@@ -44,8 +84,11 @@ error[E0493]: destructor of `E` cannot be evaluated at compile-time
   --> $DIR/const-drop-bound.rs:12:13
    |
 LL |         Err(_e) => None,
-   |             ^^ the destructor for this type cannot be evaluated in constant functions
+   |             ^^        - value is dropped here
+   |             |
+   |             the destructor for this type cannot be evaluated in constant functions
 
-error: aborting due to 7 previous errors
+error: aborting due to 11 previous errors
 
-For more information about this error, try `rustc --explain E0493`.
+Some errors have detailed explanations: E0493, E0658.
+For more information about an error, try `rustc --explain E0493`.
diff --git a/tests/ui/traits/const-traits/const-drop-fail-2.stderr b/tests/ui/traits/const-traits/const-drop-fail-2.stderr
index fde106599c236..83c72efb7eb4d 100644
--- a/tests/ui/traits/const-traits/const-drop-fail-2.stderr
+++ b/tests/ui/traits/const-traits/const-drop-fail-2.stderr
@@ -1,3 +1,23 @@
+error[E0658]: use of unstable library feature `const_destruct`
+  --> $DIR/const-drop-fail-2.rs:5:19
+   |
+LL | use std::marker::{Destruct, PhantomData};
+   |                   ^^^^^^^^
+   |
+   = note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
+   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: use of unstable library feature `const_destruct`
+  --> $DIR/const-drop-fail-2.rs:20:26
+   |
+LL | const fn check<T: ~const Destruct>(_: T) {}
+   |                          ^^^^^^^^
+   |
+   = note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
+   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
 error: const `impl` for trait `Drop` which is not marked with `#[const_trait]`
   --> $DIR/const-drop-fail-2.rs:39:25
    |
@@ -35,7 +55,7 @@ LL | const fn check<T: ~const Destruct>(_: T) {}
    |                                    |
    |                                    the destructor for this type cannot be evaluated in constant functions
 
-error: aborting due to 5 previous errors
+error: aborting due to 7 previous errors
 
-Some errors have detailed explanations: E0277, E0493.
+Some errors have detailed explanations: E0277, E0493, E0658.
 For more information about an error, try `rustc --explain E0277`.
diff --git a/tests/ui/traits/const-traits/const-drop-fail.precise.stderr b/tests/ui/traits/const-traits/const-drop-fail.precise.stderr
index 859fdfae81aef..91ca60b750d81 100644
--- a/tests/ui/traits/const-traits/const-drop-fail.precise.stderr
+++ b/tests/ui/traits/const-traits/const-drop-fail.precise.stderr
@@ -25,7 +25,9 @@ error[E0493]: destructor of `T` cannot be evaluated at compile-time
   --> $DIR/const-drop-fail.rs:23:36
    |
 LL | const fn check<T: ~const Destruct>(_: T) {}
-   |                                    ^ the destructor for this type cannot be evaluated in constant functions
+   |                                    ^      - value is dropped here
+   |                                    |
+   |                                    the destructor for this type cannot be evaluated in constant functions
 
 error[E0080]: evaluation of constant value failed
   --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
diff --git a/tests/ui/traits/const-traits/const-drop-fail.rs b/tests/ui/traits/const-traits/const-drop-fail.rs
index 5a98c32e83887..c61afc0e17a3b 100644
--- a/tests/ui/traits/const-traits/const-drop-fail.rs
+++ b/tests/ui/traits/const-traits/const-drop-fail.rs
@@ -1,7 +1,7 @@
 //@ known-bug: #110395
 
 //@ revisions: stock precise
-#![feature(const_trait_impl)]
+#![feature(const_trait_impl, const_destruct)]
 #![cfg_attr(precise, feature(const_precise_live_drops))]
 
 use std::marker::{Destruct, PhantomData};
diff --git a/tests/ui/traits/const-traits/const-drop.precise.stderr b/tests/ui/traits/const-traits/const-drop.precise.stderr
index 4f673187d499f..51a66396c4b42 100644
--- a/tests/ui/traits/const-traits/const-drop.precise.stderr
+++ b/tests/ui/traits/const-traits/const-drop.precise.stderr
@@ -76,13 +76,17 @@ error[E0493]: destructor of `S<'_>` cannot be evaluated at compile-time
   --> $DIR/const-drop.rs:23:13
    |
 LL |     let _ = S(&mut c);
-   |             ^^^^^^^^^ the destructor for this type cannot be evaluated in constant functions
+   |             ^^^^^^^^^- value is dropped here
+   |             |
+   |             the destructor for this type cannot be evaluated in constant functions
 
 error[E0493]: destructor of `T` cannot be evaluated at compile-time
   --> $DIR/const-drop.rs:18:32
    |
 LL | const fn a<T: ~const Destruct>(_: T) {}
-   |                                ^ the destructor for this type cannot be evaluated in constant functions
+   |                                ^      - value is dropped here
+   |                                |
+   |                                the destructor for this type cannot be evaluated in constant functions
 
 error[E0277]: the trait bound `T: ~const SomeTrait` is not satisfied
   --> $DIR/const-drop.rs:69:13
diff --git a/tests/ui/traits/const-traits/const-drop.rs b/tests/ui/traits/const-traits/const-drop.rs
index 5bd81fb3ab631..8bf1dd68b5c0a 100644
--- a/tests/ui/traits/const-traits/const-drop.rs
+++ b/tests/ui/traits/const-traits/const-drop.rs
@@ -1,7 +1,7 @@
 // FIXME run-pass
 //@ known-bug: #110395
 //@ revisions: stock precise
-#![feature(const_trait_impl)]
+#![feature(const_trait_impl, const_destruct)]
 #![feature(never_type)]
 #![cfg_attr(precise, feature(const_precise_live_drops))]
 
diff --git a/tests/ui/traits/const-traits/effects/auxiliary/minicore.rs b/tests/ui/traits/const-traits/effects/auxiliary/minicore.rs
index 498e698ef1c1f..660fb43259408 100644
--- a/tests/ui/traits/const-traits/effects/auxiliary/minicore.rs
+++ b/tests/ui/traits/const-traits/effects/auxiliary/minicore.rs
@@ -11,7 +11,8 @@
     rustc_attrs,
     fundamental,
     marker_trait_attr,
-    const_trait_impl
+    const_trait_impl,
+    const_destruct,
 )]
 #![allow(internal_features, incomplete_features)]
 #![no_std]
diff --git a/tests/ui/traits/const-traits/effects/minicore-drop-fail.rs b/tests/ui/traits/const-traits/effects/minicore-drop-fail.rs
index 57c39d2c1a157..274e5db21c4f6 100644
--- a/tests/ui/traits/const-traits/effects/minicore-drop-fail.rs
+++ b/tests/ui/traits/const-traits/effects/minicore-drop-fail.rs
@@ -1,7 +1,7 @@
 //@ aux-build:minicore.rs
 //@ compile-flags: --crate-type=lib -Znext-solver
 
-#![feature(no_core, const_trait_impl)]
+#![feature(no_core, const_trait_impl, const_destruct)]
 #![no_std]
 #![no_core]
 
diff --git a/tests/ui/traits/const-traits/effects/minicore-drop-without-feature-gate.no.stderr b/tests/ui/traits/const-traits/effects/minicore-drop-without-feature-gate.no.stderr
new file mode 100644
index 0000000000000..a6dcc38816a12
--- /dev/null
+++ b/tests/ui/traits/const-traits/effects/minicore-drop-without-feature-gate.no.stderr
@@ -0,0 +1,15 @@
+error[E0493]: destructor of `ConstDrop` cannot be evaluated at compile-time
+  --> $DIR/minicore-drop-without-feature-gate.rs:23:13
+   |
+LL |     let _ = ConstDrop;
+   |             ^^^^^^^^^- value is dropped here
+   |             |
+   |             the destructor for this type cannot be evaluated in constant functions
+   |
+   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
+   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0493`.
diff --git a/tests/ui/traits/const-traits/effects/minicore-drop-without-feature-gate.rs b/tests/ui/traits/const-traits/effects/minicore-drop-without-feature-gate.rs
new file mode 100644
index 0000000000000..06014ad1291aa
--- /dev/null
+++ b/tests/ui/traits/const-traits/effects/minicore-drop-without-feature-gate.rs
@@ -0,0 +1,25 @@
+//@ aux-build:minicore.rs
+//@ compile-flags: --crate-type=lib -Znext-solver
+//@ revisions: yes no
+//@[yes] check-pass
+
+#![feature(no_core, const_trait_impl)]
+#![cfg_attr(yes, feature(const_destruct))]
+#![no_std]
+#![no_core]
+
+extern crate minicore;
+use minicore::*;
+
+struct ConstDrop;
+impl const Drop for ConstDrop {
+    fn drop(&mut self) {}
+}
+
+// Make sure that `ConstDrop` can only be dropped when the `const_drop`
+// feature gate is enabled. Otherwise, we should error if there is a drop
+// impl at all.
+const fn test() {
+    let _ = ConstDrop;
+    //[no]~^ ERROR destructor of `ConstDrop` cannot be evaluated at compile-time
+}
diff --git a/tests/ui/traits/const-traits/issue-92111.rs b/tests/ui/traits/const-traits/issue-92111.rs
index 64fa32156c39a..64bf0f20e91d6 100644
--- a/tests/ui/traits/const-traits/issue-92111.rs
+++ b/tests/ui/traits/const-traits/issue-92111.rs
@@ -3,7 +3,7 @@
 //@ known-bug: #110395
 // FIXME check-pass
 
-#![feature(const_trait_impl)]
+#![feature(const_trait_impl, const_destruct)]
 
 use std::marker::Destruct;
 
diff --git a/tests/ui/traits/next-solver/destruct.rs b/tests/ui/traits/next-solver/destruct.rs
index f595cb30db846..fd14132124c4b 100644
--- a/tests/ui/traits/next-solver/destruct.rs
+++ b/tests/ui/traits/next-solver/destruct.rs
@@ -1,7 +1,7 @@
 //@ compile-flags: -Znext-solver
 //@ check-pass
 
-#![feature(const_trait_impl)]
+#![feature(const_trait_impl, const_destruct)]
 
 fn foo(_: impl std::marker::Destruct) {}
 

From af0d566e76a709caa2a89123202f583cbcc877e2 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Tue, 19 Nov 2024 17:18:54 +0000
Subject: [PATCH 4/5] Deduplicate checking drop terminator

---
 .../src/check_consts/check.rs                 | 75 ++++++++++---------
 .../rustc_const_eval/src/check_consts/ops.rs  |  2 +-
 .../src/check_consts/post_drop_elaboration.rs | 57 +++-----------
 .../src/check_consts/qualifs.rs               |  9 +--
 compiler/rustc_const_eval/src/errors.rs       |  2 +-
 library/core/src/marker.rs                    |  2 +-
 .../ui/consts/const-block-const-bound.stderr  |  4 +-
 tests/ui/consts/fn_trait_refs.stderr          | 10 +--
 .../const-traits/const-drop-bound.stderr      |  8 +-
 .../const-traits/const-drop-fail-2.stderr     |  4 +-
 ...nicore-drop-without-feature-gate.no.stderr |  2 +-
 .../minicore-drop-without-feature-gate.rs     |  1 +
 12 files changed, 69 insertions(+), 107 deletions(-)

diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs
index a088a210e2640..9f21c8644876f 100644
--- a/compiler/rustc_const_eval/src/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/check_consts/check.rs
@@ -324,14 +324,6 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
         }
     }
 
-    /// Emits an error at the given `span` if an expression cannot be evaluated in the current
-    /// context. This is meant for use in a post-const-checker pass such as the const precise
-    /// live drops lint.
-    pub fn check_op_spanned_post<O: NonConstOp<'tcx>>(mut self, op: O, span: Span) {
-        self.check_op_spanned(op, span);
-        assert!(self.secondary_errors.is_empty());
-    }
-
     fn check_static(&mut self, def_id: DefId, span: Span) {
         if self.tcx.is_thread_local_static(def_id) {
             self.tcx.dcx().span_bug(span, "tls access is checked in `Rvalue::ThreadLocalRef`");
@@ -429,6 +421,43 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
 
         true
     }
+
+    pub fn check_drop_terminator(
+        &mut self,
+        dropped_place: Place<'tcx>,
+        location: Location,
+        terminator_span: Span,
+    ) {
+        let ty_of_dropped_place = dropped_place.ty(self.body, self.tcx).ty;
+
+        let needs_drop = if let Some(local) = dropped_place.as_local() {
+            self.qualifs.needs_drop(self.ccx, local, location)
+        } else {
+            qualifs::NeedsDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place)
+        };
+        // If this type doesn't need a drop at all, then there's nothing to enforce.
+        if !needs_drop {
+            return;
+        }
+
+        let mut err_span = self.span;
+        let needs_non_const_drop = if let Some(local) = dropped_place.as_local() {
+            // Use the span where the local was declared as the span of the drop error.
+            err_span = self.body.local_decls[local].source_info.span;
+            self.qualifs.needs_non_const_drop(self.ccx, local, location)
+        } else {
+            qualifs::NeedsNonConstDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place)
+        };
+
+        self.check_op_spanned(
+            ops::LiveDrop {
+                dropped_at: terminator_span,
+                dropped_ty: ty_of_dropped_place,
+                needs_non_const_drop,
+            },
+            err_span,
+        );
+    }
 }
 
 impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
@@ -874,35 +903,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                     return;
                 }
 
-                let mut err_span = self.span;
-                let ty_of_dropped_place = dropped_place.ty(self.body, self.tcx).ty;
-
-                let needs_drop = if let Some(local) = dropped_place.as_local() {
-                    self.qualifs.needs_drop(self.ccx, local, location)
-                } else {
-                    qualifs::NeedsDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place)
-                };
-                // If this type doesn't need a drop at all, then there's nothing to enforce.
-                if !needs_drop {
-                    return;
-                }
-
-                let needs_non_const_drop = if let Some(local) = dropped_place.as_local() {
-                    // Use the span where the local was declared as the span of the drop error.
-                    err_span = self.body.local_decls[local].source_info.span;
-                    self.qualifs.needs_non_const_drop(self.ccx, local, location)
-                } else {
-                    qualifs::NeedsNonConstDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place)
-                };
-
-                self.check_op_spanned(
-                    ops::LiveDrop {
-                        dropped_at: Some(terminator.source_info.span),
-                        dropped_ty: ty_of_dropped_place,
-                        needs_non_const_drop,
-                    },
-                    err_span,
-                );
+                self.check_drop_terminator(*dropped_place, location, terminator.source_info.span);
             }
 
             TerminatorKind::InlineAsm { .. } => self.check_op(ops::InlineAsm),
diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs
index c8d6358161fca..ab81e60a33f23 100644
--- a/compiler/rustc_const_eval/src/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/check_consts/ops.rs
@@ -459,7 +459,7 @@ impl<'tcx> NonConstOp<'tcx> for InlineAsm {
 
 #[derive(Debug)]
 pub(crate) struct LiveDrop<'tcx> {
-    pub dropped_at: Option<Span>,
+    pub dropped_at: Span,
     pub dropped_ty: Ty<'tcx>,
     pub needs_non_const_drop: bool,
 }
diff --git a/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs
index c3a9aad642129..951e19b470bab 100644
--- a/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs
+++ b/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs
@@ -5,11 +5,7 @@ use rustc_span::symbol::sym;
 use tracing::trace;
 
 use super::ConstCx;
-use super::check::Qualifs;
-use super::ops::{self};
-use super::qualifs::{NeedsNonConstDrop, Qualif};
 use crate::check_consts::check::Checker;
-use crate::check_consts::qualifs::NeedsDrop;
 use crate::check_consts::rustc_allow_const_fn_unstable;
 
 /// Returns `true` if we should use the more precise live drop checker that runs after drop
@@ -46,23 +42,16 @@ pub fn check_live_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>) {
         return;
     }
 
-    let mut visitor = CheckLiveDrops { ccx: &ccx, qualifs: Qualifs::default() };
+    // I know it's not great to be creating a new const checker, but I'd
+    // rather use it so we can deduplicate the error emitting logic that
+    // it contains.
+    let mut visitor = CheckLiveDrops { checker: Checker::new(&ccx) };
 
     visitor.visit_body(body);
 }
 
 struct CheckLiveDrops<'mir, 'tcx> {
-    ccx: &'mir ConstCx<'mir, 'tcx>,
-    qualifs: Qualifs<'mir, 'tcx>,
-}
-
-// So we can access `body` and `tcx`.
-impl<'mir, 'tcx> std::ops::Deref for CheckLiveDrops<'mir, 'tcx> {
-    type Target = ConstCx<'mir, 'tcx>;
-
-    fn deref(&self) -> &Self::Target {
-        self.ccx
-    }
+    checker: Checker<'mir, 'tcx>,
 }
 
 impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> {
@@ -82,38 +71,10 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> {
 
         match &terminator.kind {
             mir::TerminatorKind::Drop { place: dropped_place, .. } => {
-                let ty_of_dropped_place = dropped_place.ty(self.body, self.tcx).ty;
-
-                let needs_drop = if let Some(local) = dropped_place.as_local() {
-                    self.qualifs.needs_drop(self.ccx, local, location)
-                } else {
-                    NeedsDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place)
-                };
-                // If this type doesn't need a drop at all, then there's nothing to enforce.
-                if !needs_drop {
-                    return;
-                }
-
-                let mut err_span = terminator.source_info.span;
-
-                let needs_non_const_drop = if let Some(local) = dropped_place.as_local() {
-                    // Use the span where the local was declared as the span of the drop error.
-                    err_span = self.body.local_decls[local].source_info.span;
-                    self.qualifs.needs_non_const_drop(self.ccx, local, location)
-                } else {
-                    NeedsNonConstDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place)
-                };
-
-                // I know it's not great to be creating a new const checker, but I'd
-                // rather use it so we can deduplicate the error emitting logic that
-                // it contains.
-                Checker::new(self.ccx).check_op_spanned_post(
-                    ops::LiveDrop {
-                        dropped_at: Some(terminator.source_info.span),
-                        dropped_ty: ty_of_dropped_place,
-                        needs_non_const_drop,
-                    },
-                    err_span,
+                self.checker.check_drop_terminator(
+                    *dropped_place,
+                    location,
+                    terminator.source_info.span,
                 );
             }
 
diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs
index 29fd3aa7d4de2..accce883e39dd 100644
--- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs
+++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs
@@ -306,11 +306,10 @@ where
             return false;
         }
 
-        // This is currently unconditionally true for all qualifs, since we do
-        // not recurse into the pointer of a deref projection, but that may change
-        // in the future. If that changes, each qualif should be required to
-        // specify whether it operates structurally for deref projections, just like
-        // we do for `Qualif::is_structural_in_adt`.
+        // `Deref` currently unconditionally "qualifies" if `in_any_value_of_ty` returns true,
+        // i.e., we treat all qualifs as non-structural for deref projections. Generally,
+        // we can say very little about `*ptr` even if we know that `ptr` satisfies all
+        // sorts of properties.
         if matches!(elem, ProjectionElem::Deref) {
             // We have to assume that this qualifies.
             return true;
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index 604e5ed61a35b..80236ee05b7ff 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -411,7 +411,7 @@ pub struct LiveDrop<'tcx> {
     pub kind: ConstContext,
     pub dropped_ty: Ty<'tcx>,
     #[label(const_eval_dropped_at_label)]
-    pub dropped_at: Option<Span>,
+    pub dropped_at: Span,
 }
 
 #[derive(Diagnostic)]
diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs
index bd1a8a02dbf47..acbad07746bb9 100644
--- a/library/core/src/marker.rs
+++ b/library/core/src/marker.rs
@@ -953,7 +953,7 @@ marker_impls! {
 ///
 /// This should be used for `~const` bounds,
 /// as non-const bounds will always hold for every type.
-#[unstable(feature = "const_destruct", issue = "10")]
+#[unstable(feature = "const_destruct", issue = "133214")]
 #[lang = "destruct"]
 #[rustc_on_unimplemented(message = "can't drop `{Self}`", append_const_msg)]
 #[rustc_deny_explicit_impl(implement_via_object = false)]
diff --git a/tests/ui/consts/const-block-const-bound.stderr b/tests/ui/consts/const-block-const-bound.stderr
index 3b8f6b6359123..9dd6a96686f36 100644
--- a/tests/ui/consts/const-block-const-bound.stderr
+++ b/tests/ui/consts/const-block-const-bound.stderr
@@ -4,7 +4,7 @@ error[E0658]: use of unstable library feature `const_destruct`
 LL | use std::marker::Destruct;
    |     ^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
+   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
    = help: add `#![feature(const_destruct)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
@@ -14,7 +14,7 @@ error[E0658]: use of unstable library feature `const_destruct`
 LL | const fn f<T: ~const Destruct>(x: T) {}
    |                      ^^^^^^^^
    |
-   = note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
+   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
    = help: add `#![feature(const_destruct)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
diff --git a/tests/ui/consts/fn_trait_refs.stderr b/tests/ui/consts/fn_trait_refs.stderr
index 15d3ae98c7e2b..c84e604793621 100644
--- a/tests/ui/consts/fn_trait_refs.stderr
+++ b/tests/ui/consts/fn_trait_refs.stderr
@@ -4,7 +4,7 @@ error[E0658]: use of unstable library feature `const_destruct`
 LL | use std::marker::Destruct;
    |     ^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
+   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
    = help: add `#![feature(const_destruct)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
@@ -14,7 +14,7 @@ error[E0658]: use of unstable library feature `const_destruct`
 LL |     T: ~const Fn<()> + ~const Destruct,
    |                               ^^^^^^^^
    |
-   = note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
+   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
    = help: add `#![feature(const_destruct)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
@@ -24,7 +24,7 @@ error[E0658]: use of unstable library feature `const_destruct`
 LL |     T: ~const FnMut<()> + ~const Destruct,
    |                                  ^^^^^^^^
    |
-   = note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
+   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
    = help: add `#![feature(const_destruct)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
@@ -34,7 +34,7 @@ error[E0658]: use of unstable library feature `const_destruct`
 LL |     T: ~const Fn<()> + ~const Destruct,
    |                               ^^^^^^^^
    |
-   = note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
+   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
    = help: add `#![feature(const_destruct)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
@@ -44,7 +44,7 @@ error[E0658]: use of unstable library feature `const_destruct`
 LL |     T: ~const FnMut<()> + ~const Destruct,
    |                                  ^^^^^^^^
    |
-   = note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
+   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
    = help: add `#![feature(const_destruct)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
diff --git a/tests/ui/traits/const-traits/const-drop-bound.stderr b/tests/ui/traits/const-traits/const-drop-bound.stderr
index 530b23975ad00..9abfa630e4def 100644
--- a/tests/ui/traits/const-traits/const-drop-bound.stderr
+++ b/tests/ui/traits/const-traits/const-drop-bound.stderr
@@ -4,7 +4,7 @@ error[E0658]: use of unstable library feature `const_destruct`
 LL | use std::marker::Destruct;
    |     ^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
+   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
    = help: add `#![feature(const_destruct)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
@@ -14,7 +14,7 @@ error[E0658]: use of unstable library feature `const_destruct`
 LL | const fn foo<T, E>(res: Result<T, E>) -> Option<T> where E: ~const Destruct {
    |                                                                    ^^^^^^^^
    |
-   = note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
+   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
    = help: add `#![feature(const_destruct)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
@@ -24,7 +24,7 @@ error[E0658]: use of unstable library feature `const_destruct`
 LL |     T: ~const Destruct,
    |               ^^^^^^^^
    |
-   = note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
+   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
    = help: add `#![feature(const_destruct)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
@@ -34,7 +34,7 @@ error[E0658]: use of unstable library feature `const_destruct`
 LL |     E: ~const Destruct,
    |               ^^^^^^^^
    |
-   = note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
+   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
    = help: add `#![feature(const_destruct)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
diff --git a/tests/ui/traits/const-traits/const-drop-fail-2.stderr b/tests/ui/traits/const-traits/const-drop-fail-2.stderr
index 83c72efb7eb4d..d871f4d425d5d 100644
--- a/tests/ui/traits/const-traits/const-drop-fail-2.stderr
+++ b/tests/ui/traits/const-traits/const-drop-fail-2.stderr
@@ -4,7 +4,7 @@ error[E0658]: use of unstable library feature `const_destruct`
 LL | use std::marker::{Destruct, PhantomData};
    |                   ^^^^^^^^
    |
-   = note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
+   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
    = help: add `#![feature(const_destruct)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
@@ -14,7 +14,7 @@ error[E0658]: use of unstable library feature `const_destruct`
 LL | const fn check<T: ~const Destruct>(_: T) {}
    |                          ^^^^^^^^
    |
-   = note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
+   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
    = help: add `#![feature(const_destruct)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
diff --git a/tests/ui/traits/const-traits/effects/minicore-drop-without-feature-gate.no.stderr b/tests/ui/traits/const-traits/effects/minicore-drop-without-feature-gate.no.stderr
index a6dcc38816a12..218f3661e39d4 100644
--- a/tests/ui/traits/const-traits/effects/minicore-drop-without-feature-gate.no.stderr
+++ b/tests/ui/traits/const-traits/effects/minicore-drop-without-feature-gate.no.stderr
@@ -1,5 +1,5 @@
 error[E0493]: destructor of `ConstDrop` cannot be evaluated at compile-time
-  --> $DIR/minicore-drop-without-feature-gate.rs:23:13
+  --> $DIR/minicore-drop-without-feature-gate.rs:24:13
    |
 LL |     let _ = ConstDrop;
    |             ^^^^^^^^^- value is dropped here
diff --git a/tests/ui/traits/const-traits/effects/minicore-drop-without-feature-gate.rs b/tests/ui/traits/const-traits/effects/minicore-drop-without-feature-gate.rs
index 06014ad1291aa..e75bf3db007f0 100644
--- a/tests/ui/traits/const-traits/effects/minicore-drop-without-feature-gate.rs
+++ b/tests/ui/traits/const-traits/effects/minicore-drop-without-feature-gate.rs
@@ -2,6 +2,7 @@
 //@ compile-flags: --crate-type=lib -Znext-solver
 //@ revisions: yes no
 //@[yes] check-pass
+// gate-test-const_destruct
 
 #![feature(no_core, const_trait_impl)]
 #![cfg_attr(yes, feature(const_destruct))]

From 69a38de97755780a5d78c655da2c953ba58ae18f Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Thu, 21 Nov 2024 23:20:59 +0000
Subject: [PATCH 5/5] Check drop is trivial before checking ty needs drop

---
 .../src/check_consts/qualifs.rs               |   7 +-
 compiler/rustc_feature/src/unstable.rs        |   2 +-
 compiler/rustc_middle/src/ty/context.rs       |   5 +-
 tests/ui/consts/const-block-const-bound.rs    |   2 +-
 .../ui/consts/const-block-const-bound.stderr  |  25 +---
 tests/ui/consts/fn_trait_refs.rs              |   1 +
 tests/ui/consts/fn_trait_refs.stderr          | 118 +++++-------------
 .../traits/const-traits/const-drop-bound.rs   |   2 +-
 .../const-traits/const-drop-bound.stderr      |  45 +------
 .../traits/const-traits/const-drop-fail-2.rs  |   2 +-
 .../const-traits/const-drop-fail-2.stderr     |  24 +---
 .../effects/auxiliary/minicore.rs             |   4 +-
 12 files changed, 57 insertions(+), 180 deletions(-)

diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs
index accce883e39dd..39eb1a7db78ba 100644
--- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs
+++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs
@@ -175,6 +175,11 @@ impl Qualif for NeedsNonConstDrop {
             return false;
         }
 
+        // If this doesn't need drop at all, then don't select `~const Destruct`.
+        if !ty.needs_drop(cx.tcx, cx.typing_env) {
+            return false;
+        }
+
         // We check that the type is `~const Destruct` since that will verify that
         // the type is both `~const Drop` (if a drop impl exists for the adt), *and*
         // that the components of this type are also `~const Destruct`. This
@@ -203,7 +208,7 @@ impl Qualif for NeedsNonConstDrop {
         // in its value since:
         // 1. The destructor may have `~const` bounds which are not present on the type.
         //   Someone needs to check that those are satisfied.
-        //   While this could be done instead satisfied by checking that the `~const Drop`
+        //   While this could be instead satisfied by checking that the `~const Drop`
         //   impl holds (i.e. replicating part of the `in_any_value_of_ty` logic above),
         //   even in this case, we have another problem, which is,
         // 2. The destructor may *modify* the operand being dropped, so even if we
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index b9a75fedcbefd..b376b0d652fac 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -426,7 +426,7 @@ declare_features! (
     (unstable, const_async_blocks, "1.53.0", Some(85368)),
     /// Allows `const || {}` closures in const contexts.
     (incomplete, const_closures, "1.68.0", Some(106003)),
-    /// Uwu
+    /// Allows using `~const Destruct` bounds and calling drop impls in const contexts.
     (unstable, const_destruct, "CURRENT_RUSTC_VERSION", Some(133214)),
     /// Allows `for _ in _` loops in const contexts.
     (unstable, const_for, "1.56.0", Some(87575)),
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 4a0d50c153c8a..1bd19a6031a3c 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -4,7 +4,7 @@
 
 pub mod tls;
 
-use std::assert_matches::assert_matches;
+use std::assert_matches::{assert_matches, debug_assert_matches};
 use std::borrow::Borrow;
 use std::cmp::Ordering;
 use std::hash::{Hash, Hasher};
@@ -377,14 +377,17 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     }
 
     fn impl_is_const(self, def_id: DefId) -> bool {
+        debug_assert_matches!(self.def_kind(def_id), DefKind::Impl { of_trait: true });
         self.is_conditionally_const(def_id)
     }
 
     fn fn_is_const(self, def_id: DefId) -> bool {
+        debug_assert_matches!(self.def_kind(def_id), DefKind::Fn | DefKind::AssocFn);
         self.is_conditionally_const(def_id)
     }
 
     fn alias_has_const_conditions(self, def_id: DefId) -> bool {
+        debug_assert_matches!(self.def_kind(def_id), DefKind::AssocTy | DefKind::OpaqueTy);
         self.is_conditionally_const(def_id)
     }
 
diff --git a/tests/ui/consts/const-block-const-bound.rs b/tests/ui/consts/const-block-const-bound.rs
index 933eb6cfc0afb..596aac09b3114 100644
--- a/tests/ui/consts/const-block-const-bound.rs
+++ b/tests/ui/consts/const-block-const-bound.rs
@@ -1,7 +1,7 @@
 //@ known-bug: #103507
 
 #![allow(unused)]
-#![feature(const_trait_impl, negative_impls)]
+#![feature(const_trait_impl, negative_impls, const_destruct)]
 
 use std::marker::Destruct;
 
diff --git a/tests/ui/consts/const-block-const-bound.stderr b/tests/ui/consts/const-block-const-bound.stderr
index 9dd6a96686f36..5e24959146b83 100644
--- a/tests/ui/consts/const-block-const-bound.stderr
+++ b/tests/ui/consts/const-block-const-bound.stderr
@@ -1,23 +1,3 @@
-error[E0658]: use of unstable library feature `const_destruct`
-  --> $DIR/const-block-const-bound.rs:6:5
-   |
-LL | use std::marker::Destruct;
-   |     ^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
-   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: use of unstable library feature `const_destruct`
-  --> $DIR/const-block-const-bound.rs:8:22
-   |
-LL | const fn f<T: ~const Destruct>(x: T) {}
-   |                      ^^^^^^^^
-   |
-   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
-   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
 error: `~const` can only be applied to `#[const_trait]` traits
   --> $DIR/const-block-const-bound.rs:8:15
    |
@@ -40,7 +20,6 @@ LL | const fn f<T: ~const Destruct>(x: T) {}
    |                                |
    |                                the destructor for this type cannot be evaluated in constant functions
 
-error: aborting due to 5 previous errors
+error: aborting due to 3 previous errors
 
-Some errors have detailed explanations: E0493, E0658.
-For more information about an error, try `rustc --explain E0493`.
+For more information about this error, try `rustc --explain E0493`.
diff --git a/tests/ui/consts/fn_trait_refs.rs b/tests/ui/consts/fn_trait_refs.rs
index 4defe4dedc784..af233efd738a2 100644
--- a/tests/ui/consts/fn_trait_refs.rs
+++ b/tests/ui/consts/fn_trait_refs.rs
@@ -5,6 +5,7 @@
 #![feature(unboxed_closures)]
 #![feature(const_trait_impl)]
 #![feature(const_cmp)]
+#![feature(const_destruct)]
 
 use std::marker::Destruct;
 
diff --git a/tests/ui/consts/fn_trait_refs.stderr b/tests/ui/consts/fn_trait_refs.stderr
index c84e604793621..dc601fbe48897 100644
--- a/tests/ui/consts/fn_trait_refs.stderr
+++ b/tests/ui/consts/fn_trait_refs.stderr
@@ -1,53 +1,3 @@
-error[E0658]: use of unstable library feature `const_destruct`
-  --> $DIR/fn_trait_refs.rs:9:5
-   |
-LL | use std::marker::Destruct;
-   |     ^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
-   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: use of unstable library feature `const_destruct`
-  --> $DIR/fn_trait_refs.rs:13:31
-   |
-LL |     T: ~const Fn<()> + ~const Destruct,
-   |                               ^^^^^^^^
-   |
-   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
-   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: use of unstable library feature `const_destruct`
-  --> $DIR/fn_trait_refs.rs:20:34
-   |
-LL |     T: ~const FnMut<()> + ~const Destruct,
-   |                                  ^^^^^^^^
-   |
-   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
-   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: use of unstable library feature `const_destruct`
-  --> $DIR/fn_trait_refs.rs:34:31
-   |
-LL |     T: ~const Fn<()> + ~const Destruct,
-   |                               ^^^^^^^^
-   |
-   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
-   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: use of unstable library feature `const_destruct`
-  --> $DIR/fn_trait_refs.rs:48:34
-   |
-LL |     T: ~const FnMut<()> + ~const Destruct,
-   |                                  ^^^^^^^^
-   |
-   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
-   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
 error[E0635]: unknown feature `const_fn_trait_ref_impls`
   --> $DIR/fn_trait_refs.rs:3:12
    |
@@ -61,19 +11,19 @@ LL | #![feature(const_cmp)]
    |            ^^^^^^^^^
 
 error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/fn_trait_refs.rs:13:8
+  --> $DIR/fn_trait_refs.rs:14:8
    |
 LL |     T: ~const Fn<()> + ~const Destruct,
    |        ^^^^^^
 
 error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/fn_trait_refs.rs:13:24
+  --> $DIR/fn_trait_refs.rs:14:24
    |
 LL |     T: ~const Fn<()> + ~const Destruct,
    |                        ^^^^^^
 
 error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/fn_trait_refs.rs:13:8
+  --> $DIR/fn_trait_refs.rs:14:8
    |
 LL |     T: ~const Fn<()> + ~const Destruct,
    |        ^^^^^^
@@ -81,7 +31,7 @@ LL |     T: ~const Fn<()> + ~const Destruct,
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/fn_trait_refs.rs:13:8
+  --> $DIR/fn_trait_refs.rs:14:8
    |
 LL |     T: ~const Fn<()> + ~const Destruct,
    |        ^^^^^^
@@ -89,7 +39,7 @@ LL |     T: ~const Fn<()> + ~const Destruct,
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/fn_trait_refs.rs:13:24
+  --> $DIR/fn_trait_refs.rs:14:24
    |
 LL |     T: ~const Fn<()> + ~const Destruct,
    |                        ^^^^^^
@@ -97,19 +47,19 @@ LL |     T: ~const Fn<()> + ~const Destruct,
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/fn_trait_refs.rs:20:8
+  --> $DIR/fn_trait_refs.rs:21:8
    |
 LL |     T: ~const FnMut<()> + ~const Destruct,
    |        ^^^^^^
 
 error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/fn_trait_refs.rs:20:27
+  --> $DIR/fn_trait_refs.rs:21:27
    |
 LL |     T: ~const FnMut<()> + ~const Destruct,
    |                           ^^^^^^
 
 error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/fn_trait_refs.rs:20:8
+  --> $DIR/fn_trait_refs.rs:21:8
    |
 LL |     T: ~const FnMut<()> + ~const Destruct,
    |        ^^^^^^
@@ -117,7 +67,7 @@ LL |     T: ~const FnMut<()> + ~const Destruct,
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/fn_trait_refs.rs:20:8
+  --> $DIR/fn_trait_refs.rs:21:8
    |
 LL |     T: ~const FnMut<()> + ~const Destruct,
    |        ^^^^^^
@@ -125,7 +75,7 @@ LL |     T: ~const FnMut<()> + ~const Destruct,
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/fn_trait_refs.rs:20:27
+  --> $DIR/fn_trait_refs.rs:21:27
    |
 LL |     T: ~const FnMut<()> + ~const Destruct,
    |                           ^^^^^^
@@ -133,13 +83,13 @@ LL |     T: ~const FnMut<()> + ~const Destruct,
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/fn_trait_refs.rs:27:8
+  --> $DIR/fn_trait_refs.rs:28:8
    |
 LL |     T: ~const FnOnce<()>,
    |        ^^^^^^
 
 error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/fn_trait_refs.rs:27:8
+  --> $DIR/fn_trait_refs.rs:28:8
    |
 LL |     T: ~const FnOnce<()>,
    |        ^^^^^^
@@ -147,7 +97,7 @@ LL |     T: ~const FnOnce<()>,
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/fn_trait_refs.rs:27:8
+  --> $DIR/fn_trait_refs.rs:28:8
    |
 LL |     T: ~const FnOnce<()>,
    |        ^^^^^^
@@ -155,19 +105,19 @@ LL |     T: ~const FnOnce<()>,
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/fn_trait_refs.rs:34:8
+  --> $DIR/fn_trait_refs.rs:35:8
    |
 LL |     T: ~const Fn<()> + ~const Destruct,
    |        ^^^^^^
 
 error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/fn_trait_refs.rs:34:24
+  --> $DIR/fn_trait_refs.rs:35:24
    |
 LL |     T: ~const Fn<()> + ~const Destruct,
    |                        ^^^^^^
 
 error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/fn_trait_refs.rs:34:8
+  --> $DIR/fn_trait_refs.rs:35:8
    |
 LL |     T: ~const Fn<()> + ~const Destruct,
    |        ^^^^^^
@@ -175,7 +125,7 @@ LL |     T: ~const Fn<()> + ~const Destruct,
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/fn_trait_refs.rs:34:8
+  --> $DIR/fn_trait_refs.rs:35:8
    |
 LL |     T: ~const Fn<()> + ~const Destruct,
    |        ^^^^^^
@@ -183,7 +133,7 @@ LL |     T: ~const Fn<()> + ~const Destruct,
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/fn_trait_refs.rs:34:24
+  --> $DIR/fn_trait_refs.rs:35:24
    |
 LL |     T: ~const Fn<()> + ~const Destruct,
    |                        ^^^^^^
@@ -191,19 +141,19 @@ LL |     T: ~const Fn<()> + ~const Destruct,
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/fn_trait_refs.rs:48:8
+  --> $DIR/fn_trait_refs.rs:49:8
    |
 LL |     T: ~const FnMut<()> + ~const Destruct,
    |        ^^^^^^
 
 error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/fn_trait_refs.rs:48:27
+  --> $DIR/fn_trait_refs.rs:49:27
    |
 LL |     T: ~const FnMut<()> + ~const Destruct,
    |                           ^^^^^^
 
 error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/fn_trait_refs.rs:48:8
+  --> $DIR/fn_trait_refs.rs:49:8
    |
 LL |     T: ~const FnMut<()> + ~const Destruct,
    |        ^^^^^^
@@ -211,7 +161,7 @@ LL |     T: ~const FnMut<()> + ~const Destruct,
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/fn_trait_refs.rs:48:8
+  --> $DIR/fn_trait_refs.rs:49:8
    |
 LL |     T: ~const FnMut<()> + ~const Destruct,
    |        ^^^^^^
@@ -219,7 +169,7 @@ LL |     T: ~const FnMut<()> + ~const Destruct,
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/fn_trait_refs.rs:48:27
+  --> $DIR/fn_trait_refs.rs:49:27
    |
 LL |     T: ~const FnMut<()> + ~const Destruct,
    |                           ^^^^^^
@@ -227,7 +177,7 @@ LL |     T: ~const FnMut<()> + ~const Destruct,
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0015]: cannot call non-const operator in constants
-  --> $DIR/fn_trait_refs.rs:70:17
+  --> $DIR/fn_trait_refs.rs:71:17
    |
 LL |         assert!(test_one == (1, 1, 1));
    |                 ^^^^^^^^^^^^^^^^^^^^^
@@ -235,7 +185,7 @@ LL |         assert!(test_one == (1, 1, 1));
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
 
 error[E0015]: cannot call non-const operator in constants
-  --> $DIR/fn_trait_refs.rs:73:17
+  --> $DIR/fn_trait_refs.rs:74:17
    |
 LL |         assert!(test_two == (2, 2));
    |                 ^^^^^^^^^^^^^^^^^^
@@ -243,7 +193,7 @@ LL |         assert!(test_two == (2, 2));
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
 
 error[E0015]: cannot call non-const closure in constant functions
-  --> $DIR/fn_trait_refs.rs:15:5
+  --> $DIR/fn_trait_refs.rs:16:5
    |
 LL |     f()
    |     ^^^
@@ -255,7 +205,7 @@ LL |     T: ~const Fn<()> + ~const Destruct + ~const Fn(),
    |                                        +++++++++++++
 
 error[E0493]: destructor of `T` cannot be evaluated at compile-time
-  --> $DIR/fn_trait_refs.rs:11:23
+  --> $DIR/fn_trait_refs.rs:12:23
    |
 LL | const fn tester_fn<T>(f: T) -> T::Output
    |                       ^ the destructor for this type cannot be evaluated in constant functions
@@ -264,7 +214,7 @@ LL | }
    | - value is dropped here
 
 error[E0015]: cannot call non-const closure in constant functions
-  --> $DIR/fn_trait_refs.rs:22:5
+  --> $DIR/fn_trait_refs.rs:23:5
    |
 LL |     f()
    |     ^^^
@@ -276,7 +226,7 @@ LL |     T: ~const FnMut<()> + ~const Destruct + ~const FnMut(),
    |                                           ++++++++++++++++
 
 error[E0493]: destructor of `T` cannot be evaluated at compile-time
-  --> $DIR/fn_trait_refs.rs:18:27
+  --> $DIR/fn_trait_refs.rs:19:27
    |
 LL | const fn tester_fn_mut<T>(mut f: T) -> T::Output
    |                           ^^^^^ the destructor for this type cannot be evaluated in constant functions
@@ -285,7 +235,7 @@ LL | }
    | - value is dropped here
 
 error[E0015]: cannot call non-const closure in constant functions
-  --> $DIR/fn_trait_refs.rs:29:5
+  --> $DIR/fn_trait_refs.rs:30:5
    |
 LL |     f()
    |     ^^^
@@ -297,7 +247,7 @@ LL |     T: ~const FnOnce<()> + ~const FnOnce(),
    |                          +++++++++++++++++
 
 error[E0493]: destructor of `T` cannot be evaluated at compile-time
-  --> $DIR/fn_trait_refs.rs:32:21
+  --> $DIR/fn_trait_refs.rs:33:21
    |
 LL | const fn test_fn<T>(mut f: T) -> (T::Output, T::Output, T::Output)
    |                     ^^^^^ the destructor for this type cannot be evaluated in constant functions
@@ -306,7 +256,7 @@ LL | }
    | - value is dropped here
 
 error[E0493]: destructor of `T` cannot be evaluated at compile-time
-  --> $DIR/fn_trait_refs.rs:46:25
+  --> $DIR/fn_trait_refs.rs:47:25
    |
 LL | const fn test_fn_mut<T>(mut f: T) -> (T::Output, T::Output)
    |                         ^^^^^ the destructor for this type cannot be evaluated in constant functions
@@ -314,7 +264,7 @@ LL | const fn test_fn_mut<T>(mut f: T) -> (T::Output, T::Output)
 LL | }
    | - value is dropped here
 
-error: aborting due to 39 previous errors
+error: aborting due to 34 previous errors
 
-Some errors have detailed explanations: E0015, E0493, E0635, E0658.
+Some errors have detailed explanations: E0015, E0493, E0635.
 For more information about an error, try `rustc --explain E0015`.
diff --git a/tests/ui/traits/const-traits/const-drop-bound.rs b/tests/ui/traits/const-traits/const-drop-bound.rs
index b0790f86ef54f..398fb39064055 100644
--- a/tests/ui/traits/const-traits/const-drop-bound.rs
+++ b/tests/ui/traits/const-traits/const-drop-bound.rs
@@ -2,7 +2,7 @@
 // FIXME check-pass
 
 #![feature(const_trait_impl)]
-#![feature(const_precise_live_drops)]
+#![feature(const_precise_live_drops, const_destruct)]
 
 use std::marker::Destruct;
 
diff --git a/tests/ui/traits/const-traits/const-drop-bound.stderr b/tests/ui/traits/const-traits/const-drop-bound.stderr
index 9abfa630e4def..eba39f859af52 100644
--- a/tests/ui/traits/const-traits/const-drop-bound.stderr
+++ b/tests/ui/traits/const-traits/const-drop-bound.stderr
@@ -1,43 +1,3 @@
-error[E0658]: use of unstable library feature `const_destruct`
-  --> $DIR/const-drop-bound.rs:7:5
-   |
-LL | use std::marker::Destruct;
-   |     ^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
-   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: use of unstable library feature `const_destruct`
-  --> $DIR/const-drop-bound.rs:9:68
-   |
-LL | const fn foo<T, E>(res: Result<T, E>) -> Option<T> where E: ~const Destruct {
-   |                                                                    ^^^^^^^^
-   |
-   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
-   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: use of unstable library feature `const_destruct`
-  --> $DIR/const-drop-bound.rs:20:15
-   |
-LL |     T: ~const Destruct,
-   |               ^^^^^^^^
-   |
-   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
-   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: use of unstable library feature `const_destruct`
-  --> $DIR/const-drop-bound.rs:21:15
-   |
-LL |     E: ~const Destruct,
-   |               ^^^^^^^^
-   |
-   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
-   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
 error: `~const` can only be applied to `#[const_trait]` traits
   --> $DIR/const-drop-bound.rs:9:61
    |
@@ -88,7 +48,6 @@ LL |         Err(_e) => None,
    |             |
    |             the destructor for this type cannot be evaluated in constant functions
 
-error: aborting due to 11 previous errors
+error: aborting due to 7 previous errors
 
-Some errors have detailed explanations: E0493, E0658.
-For more information about an error, try `rustc --explain E0493`.
+For more information about this error, try `rustc --explain E0493`.
diff --git a/tests/ui/traits/const-traits/const-drop-fail-2.rs b/tests/ui/traits/const-traits/const-drop-fail-2.rs
index 5d7bafa38871f..ed4faa95bd049 100644
--- a/tests/ui/traits/const-traits/const-drop-fail-2.rs
+++ b/tests/ui/traits/const-traits/const-drop-fail-2.rs
@@ -1,5 +1,5 @@
 //@ known-bug: #110395
-#![feature(const_trait_impl)]
+#![feature(const_trait_impl, const_destruct)]
 // #![cfg_attr(precise, feature(const_precise_live_drops))]
 
 use std::marker::{Destruct, PhantomData};
diff --git a/tests/ui/traits/const-traits/const-drop-fail-2.stderr b/tests/ui/traits/const-traits/const-drop-fail-2.stderr
index d871f4d425d5d..fde106599c236 100644
--- a/tests/ui/traits/const-traits/const-drop-fail-2.stderr
+++ b/tests/ui/traits/const-traits/const-drop-fail-2.stderr
@@ -1,23 +1,3 @@
-error[E0658]: use of unstable library feature `const_destruct`
-  --> $DIR/const-drop-fail-2.rs:5:19
-   |
-LL | use std::marker::{Destruct, PhantomData};
-   |                   ^^^^^^^^
-   |
-   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
-   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: use of unstable library feature `const_destruct`
-  --> $DIR/const-drop-fail-2.rs:20:26
-   |
-LL | const fn check<T: ~const Destruct>(_: T) {}
-   |                          ^^^^^^^^
-   |
-   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
-   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
 error: const `impl` for trait `Drop` which is not marked with `#[const_trait]`
   --> $DIR/const-drop-fail-2.rs:39:25
    |
@@ -55,7 +35,7 @@ LL | const fn check<T: ~const Destruct>(_: T) {}
    |                                    |
    |                                    the destructor for this type cannot be evaluated in constant functions
 
-error: aborting due to 7 previous errors
+error: aborting due to 5 previous errors
 
-Some errors have detailed explanations: E0277, E0493, E0658.
+Some errors have detailed explanations: E0277, E0493.
 For more information about an error, try `rustc --explain E0277`.
diff --git a/tests/ui/traits/const-traits/effects/auxiliary/minicore.rs b/tests/ui/traits/const-traits/effects/auxiliary/minicore.rs
index 660fb43259408..aaa61e21155f4 100644
--- a/tests/ui/traits/const-traits/effects/auxiliary/minicore.rs
+++ b/tests/ui/traits/const-traits/effects/auxiliary/minicore.rs
@@ -12,7 +12,7 @@
     fundamental,
     marker_trait_attr,
     const_trait_impl,
-    const_destruct,
+    const_destruct
 )]
 #![allow(internal_features, incomplete_features)]
 #![no_std]
@@ -450,7 +450,7 @@ pub trait Clone: Sized {
     fn clone(&self) -> Self;
     fn clone_from(&mut self, source: &Self)
     where
-    Self: ~const Destruct,
+        Self: ~const Destruct,
     {
         *self = source.clone()
     }