Skip to content

Commit 104e40f

Browse files
committed
Const dropping
1 parent d9797d2 commit 104e40f

File tree

9 files changed

+141
-28
lines changed

9 files changed

+141
-28
lines changed

compiler/rustc_const_eval/src/transform/check_consts/check.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use std::mem;
2222
use std::ops::Deref;
2323

2424
use super::ops::{self, NonConstOp, Status};
25-
use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop};
25+
use super::qualifs::{self, CustomEq, HasMutInterior, NeedsNonConstDrop};
2626
use super::resolver::FlowSensitiveAnalysis;
2727
use super::{is_lang_panic_fn, ConstCx, Qualif};
2828
use crate::const_eval::is_unstable_const_fn;
@@ -39,7 +39,7 @@ type QualifResults<'mir, 'tcx, Q> =
3939
#[derive(Default)]
4040
pub struct Qualifs<'mir, 'tcx> {
4141
has_mut_interior: Option<QualifResults<'mir, 'tcx, HasMutInterior>>,
42-
needs_drop: Option<QualifResults<'mir, 'tcx, NeedsDrop>>,
42+
needs_drop: Option<QualifResults<'mir, 'tcx, NeedsNonConstDrop>>,
4343
indirectly_mutable: Option<IndirectlyMutableResults<'mir, 'tcx>>,
4444
}
4545

@@ -80,14 +80,14 @@ impl Qualifs<'mir, 'tcx> {
8080
location: Location,
8181
) -> bool {
8282
let ty = ccx.body.local_decls[local].ty;
83-
if !NeedsDrop::in_any_value_of_ty(ccx, ty) {
83+
if !NeedsNonConstDrop::in_any_value_of_ty(ccx, ty) {
8484
return false;
8585
}
8686

8787
let needs_drop = self.needs_drop.get_or_insert_with(|| {
8888
let ConstCx { tcx, body, .. } = *ccx;
8989

90-
FlowSensitiveAnalysis::new(NeedsDrop, ccx)
90+
FlowSensitiveAnalysis::new(NeedsNonConstDrop, ccx)
9191
.into_engine(tcx, &body)
9292
.iterate_to_fixpoint()
9393
.into_results_cursor(&body)
@@ -991,7 +991,7 @@ impl Visitor<'tcx> for Checker<'mir, 'tcx> {
991991
// Check to see if the type of this place can ever have a drop impl. If not, this
992992
// `Drop` terminator is frivolous.
993993
let ty_needs_drop =
994-
dropped_place.ty(self.body, self.tcx).ty.needs_drop(self.tcx, self.param_env);
994+
dropped_place.ty(self.body, self.tcx).ty.needs_non_const_drop(self.tcx, self.param_env);
995995

996996
if !ty_needs_drop {
997997
return;

compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc_span::Span;
55

66
use super::check::Qualifs;
77
use super::ops::{self, NonConstOp};
8-
use super::qualifs::{NeedsDrop, Qualif};
8+
use super::qualifs::{NeedsNonConstDrop, Qualif};
99
use super::ConstCx;
1010

1111
/// 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> {
7878
match &terminator.kind {
7979
mir::TerminatorKind::Drop { place: dropped_place, .. } => {
8080
let dropped_ty = dropped_place.ty(self.body, self.tcx).ty;
81-
if !NeedsDrop::in_any_value_of_ty(self.ccx, dropped_ty) {
81+
if !NeedsNonConstDrop::in_any_value_of_ty(self.ccx, dropped_ty) {
8282
bug!(
8383
"Drop elaboration left behind a Drop for a type that does not need dropping"
8484
);

compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub fn in_any_value_of_ty(
1717
) -> ConstQualifs {
1818
ConstQualifs {
1919
has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty),
20-
needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty),
20+
needs_drop: NeedsNonConstDrop::in_any_value_of_ty(cx, ty),
2121
custom_eq: CustomEq::in_any_value_of_ty(cx, ty),
2222
error_occured,
2323
}
@@ -97,10 +97,10 @@ impl Qualif for HasMutInterior {
9797
/// This must be ruled out (a) because we cannot run `Drop` during compile-time
9898
/// as that might not be a `const fn`, and (b) because implicit promotion would
9999
/// remove side-effects that occur as part of dropping that value.
100-
pub struct NeedsDrop;
100+
pub struct NeedsNonConstDrop;
101101

102-
impl Qualif for NeedsDrop {
103-
const ANALYSIS_NAME: &'static str = "flow_needs_drop";
102+
impl Qualif for NeedsNonConstDrop {
103+
const ANALYSIS_NAME: &'static str = "flow_needs_nonconst_drop";
104104
const IS_CLEARED_ON_MOVE: bool = true;
105105

106106
fn in_qualifs(qualifs: &ConstQualifs) -> bool {
@@ -112,7 +112,7 @@ impl Qualif for NeedsDrop {
112112
}
113113

114114
fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &'tcx AdtDef, _: SubstsRef<'tcx>) -> bool {
115-
adt.has_dtor(cx.tcx)
115+
adt.has_non_const_dtor(cx.tcx)
116116
}
117117
}
118118

compiler/rustc_const_eval/src/transform/promote_consts.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ impl<'tcx> Validator<'_, 'tcx> {
231231

232232
// We cannot promote things that need dropping, since the promoted value
233233
// would not get dropped.
234-
if self.qualif_local::<qualifs::NeedsDrop>(place.local) {
234+
if self.qualif_local::<qualifs::NeedsNonConstDrop>(place.local) {
235235
return Err(Unpromotable);
236236
}
237237

compiler/rustc_middle/src/query/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,6 +1077,10 @@ rustc_queries! {
10771077
query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
10781078
desc { "computing whether `{}` needs drop", env.value }
10791079
}
1080+
/// Query backing `Tys::needs_non_const_drop`.
1081+
query needs_non_const_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
1082+
desc { "computing whether `{}` needs non-const drop", env.value }
1083+
}
10801084
/// Query backing `TyS::has_significant_drop_raw`.
10811085
query has_significant_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
10821086
desc { "computing whether `{}` has a significant drop", env.value }
@@ -1101,6 +1105,14 @@ rustc_queries! {
11011105
cache_on_disk_if { true }
11021106
}
11031107

1108+
/// A list of types where the ADT requires drop if and only if any of
1109+
/// those types require non-const drop. If the ADT is known to always need
1110+
/// non-const drop then `Err(AlwaysRequiresDrop)` is returned.
1111+
query adt_drop_tys_non_const(def_id: DefId) -> Result<&'tcx ty::List<Ty<'tcx>>, AlwaysRequiresDrop> {
1112+
desc { |tcx| "computing when `{}` needs non-const drop", tcx.def_path_str(def_id) }
1113+
cache_on_disk_if { true }
1114+
}
1115+
11041116
/// A list of types where the ADT requires drop if and only if any of those types
11051117
/// has significant drop. A type marked with the attribute `rustc_insignificant_dtor`
11061118
/// is considered to not be significant. A drop is significant if it is implemented

compiler/rustc_middle/src/ty/adt.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use rustc_data_structures::fingerprint::Fingerprint;
77
use rustc_data_structures::fx::FxHashMap;
88
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
99
use rustc_errors::ErrorReported;
10+
use rustc_hir as hir;
1011
use rustc_hir::def::{DefKind, Res};
1112
use rustc_hir::def_id::DefId;
1213
use rustc_index::vec::{Idx, IndexVec};
@@ -288,6 +289,10 @@ impl<'tcx> AdtDef {
288289
self.destructor(tcx).is_some()
289290
}
290291

292+
pub fn has_non_const_dtor(&self, tcx: TyCtxt<'tcx>) -> bool {
293+
matches!(self.destructor(tcx), Some(Destructor { constness: hir::Constness::NotConst, .. }))
294+
}
295+
291296
/// Asserts this is a struct or union and returns its unique variant.
292297
pub fn non_enum_variant(&self) -> &VariantDef {
293298
assert!(self.is_struct() || self.is_union());

compiler/rustc_middle/src/ty/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1377,6 +1377,8 @@ where
13771377
pub struct Destructor {
13781378
/// The `DefId` of the destructor method
13791379
pub did: DefId,
1380+
/// The constness of the destructor method
1381+
pub constness: hir::Constness,
13801382
}
13811383

13821384
bitflags! {

compiler/rustc_middle/src/ty/util.rs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -336,16 +336,16 @@ impl<'tcx> TyCtxt<'tcx> {
336336
self.ensure().coherent_trait(drop_trait);
337337

338338
let ty = self.type_of(adt_did);
339-
let dtor_did = self.find_map_relevant_impl(drop_trait, ty, |impl_did| {
339+
let (did, constness) = self.find_map_relevant_impl(drop_trait, ty, |impl_did| {
340340
if let Some(item) = self.associated_items(impl_did).in_definition_order().next() {
341341
if validate(self, impl_did).is_ok() {
342-
return Some(item.def_id);
342+
return Some((item.def_id, self.impl_constness(impl_did)));
343343
}
344344
}
345345
None
346-
});
346+
})?;
347347

348-
Some(ty::Destructor { did: dtor_did? })
348+
Some(ty::Destructor { did, constness })
349349
}
350350

351351
/// Returns the set of types that are required to be alive in
@@ -792,6 +792,35 @@ impl<'tcx> ty::TyS<'tcx> {
792792
}
793793
}
794794
}
795+
/// If `ty.needs_non_const_drop(...)` returns true, then `ty` is definitely
796+
/// non-copy and *might* have a non-const destructor attached; if it returns
797+
/// `false`, then `ty` definitely has a const destructor or no destructor at all.
798+
///
799+
/// (Note that this implies that if `ty` has a non-const destructor attached,
800+
/// then `needs_non_const_drop` will definitely return `true` for `ty`.)
801+
pub fn needs_non_const_drop(
802+
&'tcx self,
803+
tcx: TyCtxt<'tcx>,
804+
param_env: ty::ParamEnv<'tcx>,
805+
) -> bool {
806+
// Avoid querying in simple cases.
807+
match needs_drop_components(self, &tcx.data_layout) {
808+
Err(AlwaysRequiresDrop) => true,
809+
Ok(components) => {
810+
let query_ty = match *components {
811+
[] => return false,
812+
// if we've got a single component, call the query with that
813+
// to increase the chance that we hit the query cache.
814+
[component_ty] => component_ty,
815+
_ => self,
816+
};
817+
// This doesn't depend on regions, so try to minimize distinct
818+
// query keys used.
819+
let erased = tcx.normalize_erasing_regions(param_env, query_ty);
820+
tcx.needs_non_const_drop_raw(param_env.and(erased))
821+
}
822+
}
823+
}
795824

796825
/// Checks if `ty` has has a significant drop.
797826
///

compiler/rustc_ty_utils/src/needs_drop.rs

Lines changed: 76 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,41 @@
11
//! Check whether a type has (potentially) non-trivial drop glue.
22
33
use rustc_data_structures::fx::FxHashSet;
4+
use rustc_hir as hir;
45
use rustc_hir::def_id::DefId;
6+
use rustc_infer::infer::TyCtxtInferExt;
57
use rustc_middle::ty::subst::Subst;
68
use rustc_middle::ty::util::{needs_drop_components, AlwaysRequiresDrop};
79
use rustc_middle::ty::{self, Ty, TyCtxt};
810
use rustc_session::Limit;
911
use rustc_span::{sym, DUMMY_SP};
12+
use rustc_trait_selection::traits::{Obligation, ObligationCause, SelectionContext};
1013

1114
type NeedsDropResult<T> = Result<T, AlwaysRequiresDrop>;
1215

13-
fn needs_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
14-
let adt_fields =
15-
move |adt_def: &ty::AdtDef| tcx.adt_drop_tys(adt_def.did).map(|tys| tys.iter());
16+
fn needs_drop_raw<'tcx>(
17+
tcx: TyCtxt<'tcx>,
18+
query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
19+
needs_non_const_drop: bool,
20+
) -> bool {
1621
// If we don't know a type doesn't need drop, for example if it's a type
1722
// parameter without a `Copy` bound, then we conservatively return that it
1823
// needs drop.
19-
let res = NeedsDropTypes::new(tcx, query.param_env, query.value, adt_fields).next().is_some();
24+
let res = if needs_non_const_drop {
25+
let adt_components = move |adt_def: &ty::AdtDef| {
26+
tcx.adt_drop_tys_non_const(adt_def.did).map(|tys| tys.iter())
27+
};
28+
NeedsDropTypes::new(tcx, query.param_env, query.value, adt_components, needs_non_const_drop)
29+
.next()
30+
.is_some()
31+
} else {
32+
let adt_components =
33+
move |adt_def: &ty::AdtDef| tcx.adt_drop_tys(adt_def.did).map(|tys| tys.iter());
34+
NeedsDropTypes::new(tcx, query.param_env, query.value, adt_components, needs_non_const_drop)
35+
.next()
36+
.is_some()
37+
};
38+
2039
debug!("needs_drop_raw({:?}) = {:?}", query, res);
2140
res
2241
}
@@ -27,9 +46,10 @@ fn has_significant_drop_raw<'tcx>(
2746
) -> bool {
2847
let significant_drop_fields =
2948
move |adt_def: &ty::AdtDef| tcx.adt_significant_drop_tys(adt_def.did).map(|tys| tys.iter());
30-
let res = NeedsDropTypes::new(tcx, query.param_env, query.value, significant_drop_fields)
31-
.next()
32-
.is_some();
49+
let res =
50+
NeedsDropTypes::new(tcx, query.param_env, query.value, significant_drop_fields, false)
51+
.next()
52+
.is_some();
3353
debug!("has_significant_drop_raw({:?}) = {:?}", query, res);
3454
res
3555
}
@@ -46,6 +66,7 @@ struct NeedsDropTypes<'tcx, F> {
4666
unchecked_tys: Vec<(Ty<'tcx>, usize)>,
4767
recursion_limit: Limit,
4868
adt_components: F,
69+
needs_non_const_drop: bool,
4970
}
5071

5172
impl<'tcx, F> NeedsDropTypes<'tcx, F> {
@@ -54,6 +75,7 @@ impl<'tcx, F> NeedsDropTypes<'tcx, F> {
5475
param_env: ty::ParamEnv<'tcx>,
5576
ty: Ty<'tcx>,
5677
adt_components: F,
78+
needs_non_const_drop: bool,
5779
) -> Self {
5880
let mut seen_tys = FxHashSet::default();
5981
seen_tys.insert(ty);
@@ -65,6 +87,7 @@ impl<'tcx, F> NeedsDropTypes<'tcx, F> {
6587
unchecked_tys: vec![(ty, 0)],
6688
recursion_limit: tcx.recursion_limit(),
6789
adt_components,
90+
needs_non_const_drop,
6891
}
6992
}
7093
}
@@ -147,6 +170,35 @@ where
147170
queue_type(self, subst_ty);
148171
}
149172
}
173+
ty::Param(_)
174+
if self.needs_non_const_drop && self.tcx.features().const_trait_impl =>
175+
{
176+
// Check if the param is bounded to have a `~const Drop` impl.
177+
let drop_trait = self.tcx.require_lang_item(hir::LangItem::Drop, None);
178+
let trait_ref = ty::TraitRef {
179+
def_id: drop_trait,
180+
substs: self.tcx.mk_substs_trait(component, &[]),
181+
};
182+
183+
let obligation = Obligation::new(
184+
ObligationCause::dummy(),
185+
self.param_env,
186+
ty::Binder::dummy(ty::TraitPredicate {
187+
trait_ref,
188+
constness: ty::BoundConstness::ConstIfConst,
189+
}),
190+
);
191+
192+
let implsrc = tcx.infer_ctxt().enter(|infcx| {
193+
let mut selcx =
194+
SelectionContext::with_constness(&infcx, hir::Constness::Const);
195+
selcx.select(&obligation)
196+
});
197+
198+
if let Ok(Some(_)) = implsrc {
199+
return None;
200+
}
201+
}
150202
ty::Array(..) | ty::Opaque(..) | ty::Projection(..) | ty::Param(_) => {
151203
if ty == component {
152204
// Return the type to the caller: they may be able
@@ -176,6 +228,7 @@ fn adt_drop_tys_helper(
176228
tcx: TyCtxt<'_>,
177229
def_id: DefId,
178230
adt_has_dtor: impl Fn(&ty::AdtDef) -> bool,
231+
needs_non_const_drop: bool,
179232
) -> Result<&ty::List<Ty<'_>>, AlwaysRequiresDrop> {
180233
let adt_components = move |adt_def: &ty::AdtDef| {
181234
if adt_def.is_manually_drop() {
@@ -194,15 +247,25 @@ fn adt_drop_tys_helper(
194247
let adt_ty = tcx.type_of(def_id);
195248
let param_env = tcx.param_env(def_id);
196249
let res: Result<Vec<_>, _> =
197-
NeedsDropTypes::new(tcx, param_env, adt_ty, adt_components).collect();
250+
NeedsDropTypes::new(tcx, param_env, adt_ty, adt_components, needs_non_const_drop).collect();
198251

199252
debug!("adt_drop_tys(`{}`) = `{:?}`", tcx.def_path_str(def_id), res);
200253
res.map(|components| tcx.intern_type_list(&components))
201254
}
202255

203256
fn adt_drop_tys(tcx: TyCtxt<'_>, def_id: DefId) -> Result<&ty::List<Ty<'_>>, AlwaysRequiresDrop> {
204257
let adt_has_dtor = |adt_def: &ty::AdtDef| adt_def.destructor(tcx).is_some();
205-
adt_drop_tys_helper(tcx, def_id, adt_has_dtor)
258+
adt_drop_tys_helper(tcx, def_id, adt_has_dtor, false)
259+
}
260+
261+
fn adt_drop_tys_non_const(
262+
tcx: TyCtxt<'_>,
263+
def_id: DefId,
264+
) -> Result<&ty::List<Ty<'_>>, AlwaysRequiresDrop> {
265+
let adt_has_dtor = |adt_def: &ty::AdtDef| {
266+
adt_def.destructor(tcx).map(|d| d.constness) == Some(hir::Constness::NotConst)
267+
};
268+
adt_drop_tys_helper(tcx, def_id, adt_has_dtor, true)
206269
}
207270

208271
fn adt_significant_drop_tys(
@@ -215,14 +278,16 @@ fn adt_significant_drop_tys(
215278
.map(|dtor| !tcx.has_attr(dtor.did, sym::rustc_insignificant_dtor))
216279
.unwrap_or(false)
217280
};
218-
adt_drop_tys_helper(tcx, def_id, adt_has_dtor)
281+
adt_drop_tys_helper(tcx, def_id, adt_has_dtor, false)
219282
}
220283

221284
pub(crate) fn provide(providers: &mut ty::query::Providers) {
222285
*providers = ty::query::Providers {
223-
needs_drop_raw,
286+
needs_drop_raw: |tcx, query| needs_drop_raw(tcx, query, false),
287+
needs_non_const_drop_raw: |tcx, query| needs_drop_raw(tcx, query, true),
224288
has_significant_drop_raw,
225289
adt_drop_tys,
290+
adt_drop_tys_non_const,
226291
adt_significant_drop_tys,
227292
..*providers
228293
};

0 commit comments

Comments
 (0)