diff options
| -rw-r--r-- | compiler/rustc_const_eval/src/transform/check_consts/check.rs | 10 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs | 10 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/transform/promote_consts.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/query/mod.rs | 12 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/ty/adt.rs | 5 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/ty/mod.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/ty/util.rs | 37 | ||||
| -rw-r--r-- | compiler/rustc_ty_utils/src/needs_drop.rs | 87 |
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 }; |
