Skip to content

Commit acfd594

Browse files
committed
Improve needs_drop query
* Handle cycles in `needs_drop` correctly * Normalize types when computing `needs_drop` * Move queries from rustc to rustc_ty
1 parent f64e2dc commit acfd594

File tree

10 files changed

+304
-153
lines changed

10 files changed

+304
-153
lines changed

src/librustc/query/mod.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -651,26 +651,27 @@ rustc_queries! {
651651
no_force
652652
desc { "computing whether `{}` is `Copy`", env.value }
653653
}
654+
/// Query backing `TyS::is_sized`.
654655
query is_sized_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
655656
no_force
656657
desc { "computing whether `{}` is `Sized`", env.value }
657658
}
659+
/// Query backing `TyS::is_freeze`.
658660
query is_freeze_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
659661
no_force
660662
desc { "computing whether `{}` is freeze", env.value }
661663
}
662-
663-
// The cycle error here should be reported as an error by `check_representable`.
664-
// We consider the type as not needing drop in the meanwhile to avoid
665-
// further errors (done in impl Value for NeedsDrop).
666-
// Use `cycle_delay_bug` to delay the cycle error here to be emitted later
667-
// in case we accidentally otherwise don't emit an error.
668-
query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> NeedsDrop {
669-
cycle_delay_bug
664+
/// Query backing `TyS::needs_drop`.
665+
query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
670666
no_force
671667
desc { "computing whether `{}` needs drop", env.value }
672668
}
673669

670+
/// A list of types where the ADT requires drop if and only if any of
671+
/// those types require drop. If the ADT is known to always need drop
672+
/// then `Err(AlwaysRequiresDrop)` is returned.
673+
query adt_drop_tys(_: DefId) -> Result<&'tcx ty::List<Ty<'tcx>>, AlwaysRequiresDrop> {}
674+
674675
query layout_raw(
675676
env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>
676677
) -> Result<&'tcx ty::layout::LayoutDetails, ty::layout::LayoutError<'tcx>> {

src/librustc/traits/misc.rs

Lines changed: 0 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
//! Miscellaneous type-system utilities that are too small to deserve their own modules.
22
3-
use crate::middle::lang_items;
43
use crate::traits::{self, ObligationCause};
5-
use crate::ty::util::NeedsDrop;
64
use crate::ty::{self, Ty, TyCtxt, TypeFoldable};
75

86
use rustc_hir as hir;
9-
use rustc_span::DUMMY_SP;
107

118
#[derive(Clone)]
129
pub enum CopyImplementationError<'tcx> {
@@ -71,132 +68,3 @@ pub fn can_type_implement_copy(
7168
Ok(())
7269
})
7370
}
74-
75-
fn is_copy_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
76-
is_item_raw(tcx, query, lang_items::CopyTraitLangItem)
77-
}
78-
79-
fn is_sized_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
80-
is_item_raw(tcx, query, lang_items::SizedTraitLangItem)
81-
}
82-
83-
fn is_freeze_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
84-
is_item_raw(tcx, query, lang_items::FreezeTraitLangItem)
85-
}
86-
87-
fn is_item_raw<'tcx>(
88-
tcx: TyCtxt<'tcx>,
89-
query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
90-
item: lang_items::LangItem,
91-
) -> bool {
92-
let (param_env, ty) = query.into_parts();
93-
let trait_def_id = tcx.require_lang_item(item, None);
94-
tcx.infer_ctxt().enter(|infcx| {
95-
traits::type_known_to_meet_bound_modulo_regions(
96-
&infcx,
97-
param_env,
98-
ty,
99-
trait_def_id,
100-
DUMMY_SP,
101-
)
102-
})
103-
}
104-
105-
fn needs_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> NeedsDrop {
106-
let (param_env, ty) = query.into_parts();
107-
108-
let needs_drop = |ty: Ty<'tcx>| -> bool { tcx.needs_drop_raw(param_env.and(ty)).0 };
109-
110-
assert!(!ty.needs_infer());
111-
112-
NeedsDrop(match ty.kind {
113-
// Fast-path for primitive types
114-
ty::Infer(ty::FreshIntTy(_))
115-
| ty::Infer(ty::FreshFloatTy(_))
116-
| ty::Bool
117-
| ty::Int(_)
118-
| ty::Uint(_)
119-
| ty::Float(_)
120-
| ty::Never
121-
| ty::FnDef(..)
122-
| ty::FnPtr(_)
123-
| ty::Char
124-
| ty::GeneratorWitness(..)
125-
| ty::RawPtr(_)
126-
| ty::Ref(..)
127-
| ty::Str => false,
128-
129-
// Foreign types can never have destructors
130-
ty::Foreign(..) => false,
131-
132-
// `ManuallyDrop` doesn't have a destructor regardless of field types.
133-
ty::Adt(def, _) if Some(def.did) == tcx.lang_items().manually_drop() => false,
134-
135-
// Issue #22536: We first query `is_copy_modulo_regions`. It sees a
136-
// normalized version of the type, and therefore will definitely
137-
// know whether the type implements Copy (and thus needs no
138-
// cleanup/drop/zeroing) ...
139-
_ if ty.is_copy_modulo_regions(tcx, param_env, DUMMY_SP) => false,
140-
141-
// ... (issue #22536 continued) but as an optimization, still use
142-
// prior logic of asking for the structural "may drop".
143-
144-
// FIXME(#22815): Note that this is a conservative heuristic;
145-
// it may report that the type "may drop" when actual type does
146-
// not actually have a destructor associated with it. But since
147-
// the type absolutely did not have the `Copy` bound attached
148-
// (see above), it is sound to treat it as having a destructor.
149-
150-
// User destructors are the only way to have concrete drop types.
151-
ty::Adt(def, _) if def.has_dtor(tcx) => true,
152-
153-
// Can refer to a type which may drop.
154-
// FIXME(eddyb) check this against a ParamEnv.
155-
ty::Dynamic(..)
156-
| ty::Projection(..)
157-
| ty::Param(_)
158-
| ty::Bound(..)
159-
| ty::Placeholder(..)
160-
| ty::Opaque(..)
161-
| ty::Infer(_)
162-
| ty::Error => true,
163-
164-
ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"),
165-
166-
// Zero-length arrays never contain anything to drop.
167-
ty::Array(_, len) if len.try_eval_usize(tcx, param_env) == Some(0) => false,
168-
169-
// Structural recursion.
170-
ty::Array(ty, _) | ty::Slice(ty) => needs_drop(ty),
171-
172-
ty::Closure(def_id, ref substs) => {
173-
substs.as_closure().upvar_tys(def_id, tcx).any(needs_drop)
174-
}
175-
176-
// Pessimistically assume that all generators will require destructors
177-
// as we don't know if a destructor is a noop or not until after the MIR
178-
// state transformation pass
179-
ty::Generator(..) => true,
180-
181-
ty::Tuple(..) => ty.tuple_fields().any(needs_drop),
182-
183-
// unions don't have destructors because of the child types,
184-
// only if they manually implement `Drop` (handled above).
185-
ty::Adt(def, _) if def.is_union() => false,
186-
187-
ty::Adt(def, substs) => def
188-
.variants
189-
.iter()
190-
.any(|variant| variant.fields.iter().any(|field| needs_drop(field.ty(tcx, substs)))),
191-
})
192-
}
193-
194-
pub fn provide(providers: &mut ty::query::Providers<'_>) {
195-
*providers = ty::query::Providers {
196-
is_copy_raw,
197-
is_sized_raw,
198-
is_freeze_raw,
199-
needs_drop_raw,
200-
..*providers
201-
};
202-
}

src/librustc/traits/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1255,7 +1255,6 @@ impl<'tcx> TraitObligation<'tcx> {
12551255
}
12561256

12571257
pub fn provide(providers: &mut ty::query::Providers<'_>) {
1258-
misc::provide(providers);
12591258
*providers = ty::query::Providers {
12601259
is_object_safe: object_safety::is_object_safe_provider,
12611260
specialization_graph_of: specialize::specialization_graph_provider,

src/librustc/ty/query/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use crate::traits::Clauses;
3333
use crate::traits::{self, Vtable};
3434
use crate::ty::steal::Steal;
3535
use crate::ty::subst::SubstsRef;
36-
use crate::ty::util::NeedsDrop;
36+
use crate::ty::util::AlwaysRequiresDrop;
3737
use crate::ty::{self, AdtSizedConstraint, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt};
3838
use crate::util::common::ErrorReported;
3939
use rustc_data_structures::fingerprint::Fingerprint;

src/librustc/ty/query/values.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use crate::ty::util::NeedsDrop;
21
use crate::ty::{self, AdtSizedConstraint, Ty, TyCtxt};
32

43
use rustc_span::symbol::Symbol;
@@ -26,12 +25,6 @@ impl<'tcx> Value<'tcx> for ty::SymbolName {
2625
}
2726
}
2827

29-
impl<'tcx> Value<'tcx> for NeedsDrop {
30-
fn from_cycle_error(_: TyCtxt<'tcx>) -> Self {
31-
NeedsDrop(false)
32-
}
33-
}
34-
3528
impl<'tcx> Value<'tcx> for AdtSizedConstraint<'tcx> {
3629
fn from_cycle_error(tcx: TyCtxt<'tcx>) -> Self {
3730
AdtSizedConstraint(tcx.intern_type_list(&[tcx.types.err]))

src/librustc/ty/util.rs

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use rustc_hir::def::DefKind;
1717
use rustc_hir::def_id::DefId;
1818
use rustc_macros::HashStable;
1919
use rustc_span::Span;
20+
use smallvec::SmallVec;
2021
use std::{cmp, fmt};
2122
use syntax::ast;
2223
use syntax::attr::{self, SignedInt, UnsignedInt};
@@ -724,7 +725,23 @@ impl<'tcx> ty::TyS<'tcx> {
724725
/// Note that this method is used to check eligible types in unions.
725726
#[inline]
726727
pub fn needs_drop(&'tcx self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
727-
tcx.needs_drop_raw(param_env.and(self)).0
728+
// Avoid querying in simple cases.
729+
match needs_drop_components(self) {
730+
Err(AlwaysRequiresDrop) => true,
731+
Ok(components) => {
732+
let query_ty = match *components {
733+
[] => return false,
734+
// If we've got a single component, call the query with that
735+
// to increase the chance that we hit the query cache.
736+
[component_ty] => component_ty,
737+
_ => self,
738+
};
739+
// This doesn't depend on regions, so try to minimize distinct.
740+
// query keys used.
741+
let erased = tcx.normalize_erasing_regions(param_env, query_ty);
742+
tcx.needs_drop_raw(param_env.and(erased))
743+
}
744+
}
728745
}
729746

730747
pub fn same_type(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
@@ -923,9 +940,6 @@ impl<'tcx> ty::TyS<'tcx> {
923940
}
924941
}
925942

926-
#[derive(Clone, HashStable)]
927-
pub struct NeedsDrop(pub bool);
928-
929943
pub enum ExplicitSelf<'tcx> {
930944
ByValue,
931945
ByReference(ty::Region<'tcx>, hir::Mutability),
@@ -974,3 +988,62 @@ impl<'tcx> ExplicitSelf<'tcx> {
974988
}
975989
}
976990
}
991+
992+
/// Returns a list of types such that the given type needs drop if and only if
993+
/// *any* of the returned types need drop. Returns `Err(AlwaysRequiresDrop)` if
994+
/// this type always needs drop.
995+
pub fn needs_drop_components(ty: Ty<'tcx>) -> Result<SmallVec<[Ty<'tcx>; 4]>, AlwaysRequiresDrop> {
996+
match ty.kind {
997+
ty::Infer(ty::FreshIntTy(_))
998+
| ty::Infer(ty::FreshFloatTy(_))
999+
| ty::Bool
1000+
| ty::Int(_)
1001+
| ty::Uint(_)
1002+
| ty::Float(_)
1003+
| ty::Never
1004+
| ty::FnDef(..)
1005+
| ty::FnPtr(_)
1006+
| ty::Char
1007+
| ty::GeneratorWitness(..)
1008+
| ty::RawPtr(_)
1009+
| ty::Ref(..)
1010+
| ty::Str => Ok(SmallVec::new()),
1011+
1012+
// Foreign types can never have destructors
1013+
ty::Foreign(..) => Ok(SmallVec::new()),
1014+
1015+
// Pessimistically assume that all generators will require destructors
1016+
// as we don't know if a destructor is a noop or not until after the MIR
1017+
// state transformation pass
1018+
ty::Generator(..) | ty::Dynamic(..) | ty::Error => Err(AlwaysRequiresDrop),
1019+
1020+
ty::Slice(ty) => needs_drop_components(ty),
1021+
ty::Array(elem_ty, ..) => {
1022+
match needs_drop_components(elem_ty) {
1023+
Ok(v) if v.is_empty() => Ok(v),
1024+
// Arrays of size zero don't need drop, even if their element
1025+
// type does.
1026+
_ => Ok(smallvec![ty]),
1027+
}
1028+
}
1029+
// If any field needs drop, then the whole tuple does.
1030+
ty::Tuple(..) => ty.tuple_fields().try_fold(SmallVec::new(), |mut acc, elem| {
1031+
acc.extend(needs_drop_components(elem)?);
1032+
Ok(acc)
1033+
}),
1034+
1035+
// These require checking for `Copy` bounds or `Adt` destructors.
1036+
ty::Adt(..)
1037+
| ty::Projection(..)
1038+
| ty::UnnormalizedProjection(..)
1039+
| ty::Param(_)
1040+
| ty::Bound(..)
1041+
| ty::Placeholder(..)
1042+
| ty::Opaque(..)
1043+
| ty::Infer(_)
1044+
| ty::Closure(..) => Ok(smallvec![ty]),
1045+
}
1046+
}
1047+
1048+
#[derive(Copy, Clone, Debug, HashStable)]
1049+
pub struct AlwaysRequiresDrop;

src/librustc_ty/common_traits.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//! Queries for checking whether a type implements one of a few common traits.
2+
3+
use rustc::middle::lang_items;
4+
use rustc::traits;
5+
use rustc::ty::{self, Ty, TyCtxt};
6+
use rustc_span::DUMMY_SP;
7+
8+
fn is_copy_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
9+
is_item_raw(tcx, query, lang_items::CopyTraitLangItem)
10+
}
11+
12+
fn is_sized_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
13+
is_item_raw(tcx, query, lang_items::SizedTraitLangItem)
14+
}
15+
16+
fn is_freeze_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
17+
is_item_raw(tcx, query, lang_items::FreezeTraitLangItem)
18+
}
19+
20+
fn is_item_raw<'tcx>(
21+
tcx: TyCtxt<'tcx>,
22+
query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
23+
item: lang_items::LangItem,
24+
) -> bool {
25+
let (param_env, ty) = query.into_parts();
26+
let trait_def_id = tcx.require_lang_item(item, None);
27+
tcx.infer_ctxt().enter(|infcx| {
28+
traits::type_known_to_meet_bound_modulo_regions(
29+
&infcx,
30+
param_env,
31+
ty,
32+
trait_def_id,
33+
DUMMY_SP,
34+
)
35+
})
36+
}
37+
38+
pub(crate) fn provide(providers: &mut ty::query::Providers<'_>) {
39+
*providers = ty::query::Providers { is_copy_raw, is_sized_raw, is_freeze_raw, ..*providers };
40+
}

src/librustc_ty/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@ extern crate log;
1616

1717
use rustc::ty::query::Providers;
1818

19+
mod common_traits;
20+
mod needs_drop;
1921
mod ty;
2022

2123
pub fn provide(providers: &mut Providers<'_>) {
24+
common_traits::provide(providers);
25+
needs_drop::provide(providers);
2226
ty::provide(providers);
2327
}

0 commit comments

Comments
 (0)