about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/check.rs10
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs4
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs10
-rw-r--r--compiler/rustc_const_eval/src/transform/promote_consts.rs2
-rw-r--r--compiler/rustc_middle/src/query/mod.rs12
-rw-r--r--compiler/rustc_middle/src/ty/adt.rs5
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs2
-rw-r--r--compiler/rustc_middle/src/ty/util.rs37
-rw-r--r--compiler/rustc_ty_utils/src/needs_drop.rs87
9 files changed, 141 insertions, 28 deletions
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
index d02b4286c17..189e1b043b3 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -22,7 +22,7 @@ use std::mem;
 use std::ops::Deref;
 
 use super::ops::{self, NonConstOp, Status};
-use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop};
+use super::qualifs::{self, CustomEq, HasMutInterior, NeedsNonConstDrop};
 use super::resolver::FlowSensitiveAnalysis;
 use super::{is_lang_panic_fn, ConstCx, Qualif};
 use crate::const_eval::is_unstable_const_fn;
@@ -39,7 +39,7 @@ type QualifResults<'mir, 'tcx, Q> =
 #[derive(Default)]
 pub struct Qualifs<'mir, 'tcx> {
     has_mut_interior: Option<QualifResults<'mir, 'tcx, HasMutInterior>>,
-    needs_drop: Option<QualifResults<'mir, 'tcx, NeedsDrop>>,
+    needs_drop: Option<QualifResults<'mir, 'tcx, NeedsNonConstDrop>>,
     indirectly_mutable: Option<IndirectlyMutableResults<'mir, 'tcx>>,
 }
 
@@ -80,14 +80,14 @@ impl Qualifs<'mir, 'tcx> {
         location: Location,
     ) -> bool {
         let ty = ccx.body.local_decls[local].ty;
-        if !NeedsDrop::in_any_value_of_ty(ccx, ty) {
+        if !NeedsNonConstDrop::in_any_value_of_ty(ccx, ty) {
             return false;
         }
 
         let needs_drop = self.needs_drop.get_or_insert_with(|| {
             let ConstCx { tcx, body, .. } = *ccx;
 
-            FlowSensitiveAnalysis::new(NeedsDrop, ccx)
+            FlowSensitiveAnalysis::new(NeedsNonConstDrop, ccx)
                 .into_engine(tcx, &body)
                 .iterate_to_fixpoint()
                 .into_results_cursor(&body)
@@ -991,7 +991,7 @@ impl Visitor<'tcx> for Checker<'mir, 'tcx> {
                 // Check to see if the type of this place can ever have a drop impl. If not, this
                 // `Drop` terminator is frivolous.
                 let ty_needs_drop =
-                    dropped_place.ty(self.body, self.tcx).ty.needs_drop(self.tcx, self.param_env);
+                    dropped_place.ty(self.body, self.tcx).ty.needs_non_const_drop(self.tcx, self.param_env);
 
                 if !ty_needs_drop {
                     return;
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs
index b08ce219034..4f66e6be297 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs
@@ -5,7 +5,7 @@ use rustc_span::Span;
 
 use super::check::Qualifs;
 use super::ops::{self, NonConstOp};
-use super::qualifs::{NeedsDrop, Qualif};
+use super::qualifs::{NeedsNonConstDrop, Qualif};
 use super::ConstCx;
 
 /// Returns `true` if we should use the more precise live drop checker that runs after drop
@@ -78,7 +78,7 @@ impl Visitor<'tcx> for CheckLiveDrops<'mir, 'tcx> {
         match &terminator.kind {
             mir::TerminatorKind::Drop { place: dropped_place, .. } => {
                 let dropped_ty = dropped_place.ty(self.body, self.tcx).ty;
-                if !NeedsDrop::in_any_value_of_ty(self.ccx, dropped_ty) {
+                if !NeedsNonConstDrop::in_any_value_of_ty(self.ccx, dropped_ty) {
                     bug!(
                         "Drop elaboration left behind a Drop for a type that does not need dropping"
                     );
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
index 413a9638eb3..50b691dffe8 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
@@ -17,7 +17,7 @@ pub fn in_any_value_of_ty(
 ) -> ConstQualifs {
     ConstQualifs {
         has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty),
-        needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty),
+        needs_drop: NeedsNonConstDrop::in_any_value_of_ty(cx, ty),
         custom_eq: CustomEq::in_any_value_of_ty(cx, ty),
         error_occured,
     }
@@ -97,10 +97,10 @@ impl Qualif for HasMutInterior {
 /// This must be ruled out (a) because we cannot run `Drop` during compile-time
 /// as that might not be a `const fn`, and (b) because implicit promotion would
 /// remove side-effects that occur as part of dropping that value.
-pub struct NeedsDrop;
+pub struct NeedsNonConstDrop;
 
-impl Qualif for NeedsDrop {
-    const ANALYSIS_NAME: &'static str = "flow_needs_drop";
+impl Qualif for NeedsNonConstDrop {
+    const ANALYSIS_NAME: &'static str = "flow_needs_nonconst_drop";
     const IS_CLEARED_ON_MOVE: bool = true;
 
     fn in_qualifs(qualifs: &ConstQualifs) -> bool {
@@ -112,7 +112,7 @@ impl Qualif for NeedsDrop {
     }
 
     fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &'tcx AdtDef, _: SubstsRef<'tcx>) -> bool {
-        adt.has_dtor(cx.tcx)
+        adt.has_non_const_dtor(cx.tcx)
     }
 }
 
diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs
index 6822ad2d7b5..94c0d8db847 100644
--- a/compiler/rustc_const_eval/src/transform/promote_consts.rs
+++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs
@@ -231,7 +231,7 @@ impl<'tcx> Validator<'_, 'tcx> {
 
                         // We cannot promote things that need dropping, since the promoted value
                         // would not get dropped.
-                        if self.qualif_local::<qualifs::NeedsDrop>(place.local) {
+                        if self.qualif_local::<qualifs::NeedsNonConstDrop>(place.local) {
                             return Err(Unpromotable);
                         }
 
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index dd5753e95d0..4f13ca892dd 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1077,6 +1077,10 @@ rustc_queries! {
     query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
         desc { "computing whether `{}` needs drop", env.value }
     }
+    /// Query backing `Tys::needs_non_const_drop`.
+    query needs_non_const_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
+        desc { "computing whether `{}` needs non-const drop", env.value }
+    }
     /// Query backing `TyS::has_significant_drop_raw`.
     query has_significant_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
         desc { "computing whether `{}` has a significant drop", env.value }
@@ -1101,6 +1105,14 @@ rustc_queries! {
         cache_on_disk_if { true }
     }
 
+    /// A list of types where the ADT requires drop if and only if any of
+    /// those types require non-const drop. If the ADT is known to always need
+    /// non-const drop then `Err(AlwaysRequiresDrop)` is returned.
+    query adt_drop_tys_non_const(def_id: DefId) -> Result<&'tcx ty::List<Ty<'tcx>>, AlwaysRequiresDrop> {
+        desc { |tcx| "computing when `{}` needs non-const drop", tcx.def_path_str(def_id) }
+        cache_on_disk_if { true }
+    }
+
     /// A list of types where the ADT requires drop if and only if any of those types
     /// has significant drop. A type marked with the attribute `rustc_insignificant_dtor`
     /// is considered to not be significant. A drop is significant if it is implemented
diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs
index 27927bcca72..c32f0ea9ca5 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -7,6 +7,7 @@ use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_errors::ErrorReported;
+use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_index::vec::{Idx, IndexVec};
@@ -288,6 +289,10 @@ impl<'tcx> AdtDef {
         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) -> &VariantDef {
         assert!(self.is_struct() || self.is_union());
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index fddfd6e435c..bab223ac6d5 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -1377,6 +1377,8 @@ where
 pub struct Destructor {
     /// The `DefId` of the destructor method
     pub did: DefId,
+    /// The constness of the destructor method
+    pub constness: hir::Constness,
 }
 
 bitflags! {
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 1b8e94260b9..1d86715772d 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -336,16 +336,16 @@ impl<'tcx> TyCtxt<'tcx> {
         self.ensure().coherent_trait(drop_trait);
 
         let ty = self.type_of(adt_did);
-        let dtor_did = self.find_map_relevant_impl(drop_trait, ty, |impl_did| {
+        let (did, constness) = self.find_map_relevant_impl(drop_trait, ty, |impl_did| {
             if let Some(item) = self.associated_items(impl_did).in_definition_order().next() {
                 if validate(self, impl_did).is_ok() {
-                    return Some(item.def_id);
+                    return Some((item.def_id, self.impl_constness(impl_did)));
                 }
             }
             None
-        });
+        })?;
 
-        Some(ty::Destructor { did: dtor_did? })
+        Some(ty::Destructor { did, constness })
     }
 
     /// Returns the set of types that are required to be alive in
@@ -792,6 +792,35 @@ impl<'tcx> ty::TyS<'tcx> {
             }
         }
     }
+    /// If `ty.needs_non_const_drop(...)` returns true, then `ty` is definitely
+    /// non-copy and *might* have a non-const destructor attached; if it returns
+    /// `false`, then `ty` definitely has a const destructor or no destructor at all.
+    ///
+    /// (Note that this implies that if `ty` has a non-const destructor attached,
+    /// then `needs_non_const_drop` will definitely return `true` for `ty`.)
+    pub fn needs_non_const_drop(
+        &'tcx self,
+        tcx: TyCtxt<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> bool {
+        // Avoid querying in simple cases.
+        match needs_drop_components(self, &tcx.data_layout) {
+            Err(AlwaysRequiresDrop) => true,
+            Ok(components) => {
+                let query_ty = match *components {
+                    [] => return false,
+                    // if we've got a single component, call the query with that
+                    // to increase the chance that we hit the query cache.
+                    [component_ty] => component_ty,
+                    _ => self,
+                };
+                // This doesn't depend on regions, so try to minimize distinct
+                // query keys used.
+                let erased = tcx.normalize_erasing_regions(param_env, query_ty);
+                tcx.needs_non_const_drop_raw(param_env.and(erased))
+            }
+        }
+    }
 
     /// Checks if `ty` has has a significant drop.
     ///
diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs
index d837af85d58..d93379aa386 100644
--- a/compiler/rustc_ty_utils/src/needs_drop.rs
+++ b/compiler/rustc_ty_utils/src/needs_drop.rs
@@ -1,22 +1,41 @@
 //! Check whether a type has (potentially) non-trivial drop glue.
 
 use rustc_data_structures::fx::FxHashSet;
+use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
+use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::ty::subst::Subst;
 use rustc_middle::ty::util::{needs_drop_components, AlwaysRequiresDrop};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::Limit;
 use rustc_span::{sym, DUMMY_SP};
+use rustc_trait_selection::traits::{Obligation, ObligationCause, SelectionContext};
 
 type NeedsDropResult<T> = Result<T, AlwaysRequiresDrop>;
 
-fn needs_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
-    let adt_fields =
-        move |adt_def: &ty::AdtDef| tcx.adt_drop_tys(adt_def.did).map(|tys| tys.iter());
+fn needs_drop_raw<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
+    needs_non_const_drop: bool,
+) -> bool {
     // If we don't know a type doesn't need drop, for example if it's a type
     // parameter without a `Copy` bound, then we conservatively return that it
     // needs drop.
-    let res = NeedsDropTypes::new(tcx, query.param_env, query.value, adt_fields).next().is_some();
+    let res = if needs_non_const_drop {
+        let adt_components = move |adt_def: &ty::AdtDef| {
+            tcx.adt_drop_tys_non_const(adt_def.did).map(|tys| tys.iter())
+        };
+        NeedsDropTypes::new(tcx, query.param_env, query.value, adt_components, needs_non_const_drop)
+            .next()
+            .is_some()
+    } else {
+        let adt_components =
+            move |adt_def: &ty::AdtDef| tcx.adt_drop_tys(adt_def.did).map(|tys| tys.iter());
+        NeedsDropTypes::new(tcx, query.param_env, query.value, adt_components, needs_non_const_drop)
+            .next()
+            .is_some()
+    };
+
     debug!("needs_drop_raw({:?}) = {:?}", query, res);
     res
 }
@@ -27,9 +46,10 @@ fn has_significant_drop_raw<'tcx>(
 ) -> bool {
     let significant_drop_fields =
         move |adt_def: &ty::AdtDef| tcx.adt_significant_drop_tys(adt_def.did).map(|tys| tys.iter());
-    let res = NeedsDropTypes::new(tcx, query.param_env, query.value, significant_drop_fields)
-        .next()
-        .is_some();
+    let res =
+        NeedsDropTypes::new(tcx, query.param_env, query.value, significant_drop_fields, false)
+            .next()
+            .is_some();
     debug!("has_significant_drop_raw({:?}) = {:?}", query, res);
     res
 }
@@ -46,6 +66,7 @@ struct NeedsDropTypes<'tcx, F> {
     unchecked_tys: Vec<(Ty<'tcx>, usize)>,
     recursion_limit: Limit,
     adt_components: F,
+    needs_non_const_drop: bool,
 }
 
 impl<'tcx, F> NeedsDropTypes<'tcx, F> {
@@ -54,6 +75,7 @@ impl<'tcx, F> NeedsDropTypes<'tcx, F> {
         param_env: ty::ParamEnv<'tcx>,
         ty: Ty<'tcx>,
         adt_components: F,
+        needs_non_const_drop: bool,
     ) -> Self {
         let mut seen_tys = FxHashSet::default();
         seen_tys.insert(ty);
@@ -65,6 +87,7 @@ impl<'tcx, F> NeedsDropTypes<'tcx, F> {
             unchecked_tys: vec![(ty, 0)],
             recursion_limit: tcx.recursion_limit(),
             adt_components,
+            needs_non_const_drop,
         }
     }
 }
@@ -147,6 +170,35 @@ where
                             queue_type(self, subst_ty);
                         }
                     }
+                    ty::Param(_)
+                        if self.needs_non_const_drop && self.tcx.features().const_trait_impl =>
+                    {
+                        // Check if the param is bounded to have a `~const Drop` impl.
+                        let drop_trait = self.tcx.require_lang_item(hir::LangItem::Drop, None);
+                        let trait_ref = ty::TraitRef {
+                            def_id: drop_trait,
+                            substs: self.tcx.mk_substs_trait(component, &[]),
+                        };
+
+                        let obligation = Obligation::new(
+                            ObligationCause::dummy(),
+                            self.param_env,
+                            ty::Binder::dummy(ty::TraitPredicate {
+                                trait_ref,
+                                constness: ty::BoundConstness::ConstIfConst,
+                            }),
+                        );
+
+                        let implsrc = tcx.infer_ctxt().enter(|infcx| {
+                            let mut selcx =
+                                SelectionContext::with_constness(&infcx, hir::Constness::Const);
+                            selcx.select(&obligation)
+                        });
+
+                        if let Ok(Some(_)) = implsrc {
+                            return None;
+                        }
+                    }
                     ty::Array(..) | ty::Opaque(..) | ty::Projection(..) | ty::Param(_) => {
                         if ty == component {
                             // Return the type to the caller: they may be able
@@ -176,6 +228,7 @@ fn adt_drop_tys_helper(
     tcx: TyCtxt<'_>,
     def_id: DefId,
     adt_has_dtor: impl Fn(&ty::AdtDef) -> bool,
+    needs_non_const_drop: bool,
 ) -> Result<&ty::List<Ty<'_>>, AlwaysRequiresDrop> {
     let adt_components = move |adt_def: &ty::AdtDef| {
         if adt_def.is_manually_drop() {
@@ -194,7 +247,7 @@ fn adt_drop_tys_helper(
     let adt_ty = tcx.type_of(def_id);
     let param_env = tcx.param_env(def_id);
     let res: Result<Vec<_>, _> =
-        NeedsDropTypes::new(tcx, param_env, adt_ty, adt_components).collect();
+        NeedsDropTypes::new(tcx, param_env, adt_ty, adt_components, needs_non_const_drop).collect();
 
     debug!("adt_drop_tys(`{}`) = `{:?}`", tcx.def_path_str(def_id), res);
     res.map(|components| tcx.intern_type_list(&components))
@@ -202,7 +255,17 @@ fn adt_drop_tys_helper(
 
 fn adt_drop_tys(tcx: TyCtxt<'_>, def_id: DefId) -> Result<&ty::List<Ty<'_>>, AlwaysRequiresDrop> {
     let adt_has_dtor = |adt_def: &ty::AdtDef| adt_def.destructor(tcx).is_some();
-    adt_drop_tys_helper(tcx, def_id, adt_has_dtor)
+    adt_drop_tys_helper(tcx, def_id, adt_has_dtor, false)
+}
+
+fn adt_drop_tys_non_const(
+    tcx: TyCtxt<'_>,
+    def_id: DefId,
+) -> Result<&ty::List<Ty<'_>>, AlwaysRequiresDrop> {
+    let adt_has_dtor = |adt_def: &ty::AdtDef| {
+        adt_def.destructor(tcx).map(|d| d.constness) == Some(hir::Constness::NotConst)
+    };
+    adt_drop_tys_helper(tcx, def_id, adt_has_dtor, true)
 }
 
 fn adt_significant_drop_tys(
@@ -215,14 +278,16 @@ fn adt_significant_drop_tys(
             .map(|dtor| !tcx.has_attr(dtor.did, sym::rustc_insignificant_dtor))
             .unwrap_or(false)
     };
-    adt_drop_tys_helper(tcx, def_id, adt_has_dtor)
+    adt_drop_tys_helper(tcx, def_id, adt_has_dtor, false)
 }
 
 pub(crate) fn provide(providers: &mut ty::query::Providers) {
     *providers = ty::query::Providers {
-        needs_drop_raw,
+        needs_drop_raw: |tcx, query| needs_drop_raw(tcx, query, false),
+        needs_non_const_drop_raw: |tcx, query| needs_drop_raw(tcx, query, true),
         has_significant_drop_raw,
         adt_drop_tys,
+        adt_drop_tys_non_const,
         adt_significant_drop_tys,
         ..*providers
     };