Const dropping
This commit is contained in:
parent
d9797d23d5
commit
104e40fb74
9 changed files with 141 additions and 28 deletions
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
);
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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! {
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue