1 //! Check whether a type has (potentially) non-trivial drop glue.
3 use rustc_data_structures
::fx
::FxHashSet
;
4 use rustc_hir
::def_id
::DefId
;
5 use rustc_middle
::ty
::subst
::Subst
;
6 use rustc_middle
::ty
::util
::{needs_drop_components, AlwaysRequiresDrop}
;
7 use rustc_middle
::ty
::{self, Ty, TyCtxt}
;
8 use rustc_session
::Limit
;
9 use rustc_span
::{sym, DUMMY_SP}
;
11 type NeedsDropResult
<T
> = Result
<T
, AlwaysRequiresDrop
>;
13 fn needs_drop_raw
<'tcx
>(tcx
: TyCtxt
<'tcx
>, query
: ty
::ParamEnvAnd
<'tcx
, Ty
<'tcx
>>) -> bool
{
15 move |adt_def
: &ty
::AdtDef
| tcx
.adt_drop_tys(adt_def
.did
).map(|tys
| tys
.iter());
16 // If we don't know a type doesn't need drop, for example if it's a type
17 // parameter without a `Copy` bound, then we conservatively return that it
19 let res
= NeedsDropTypes
::new(tcx
, query
.param_env
, query
.value
, adt_fields
).next().is_some();
20 debug
!("needs_drop_raw({:?}) = {:?}", query
, res
);
24 fn has_significant_drop_raw
<'tcx
>(
26 query
: ty
::ParamEnvAnd
<'tcx
, Ty
<'tcx
>>,
28 let significant_drop_fields
=
29 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
)
33 debug
!("has_significant_drop_raw({:?}) = {:?}", query
, res
);
37 struct NeedsDropTypes
<'tcx
, F
> {
39 param_env
: ty
::ParamEnv
<'tcx
>,
41 seen_tys
: FxHashSet
<Ty
<'tcx
>>,
42 /// A stack of types left to process, and the recursion depth when we
43 /// pushed that type. Each round, we pop something from the stack and check
44 /// if it needs drop. If the result depends on whether some other types
45 /// need drop we push them onto the stack.
46 unchecked_tys
: Vec
<(Ty
<'tcx
>, usize)>,
47 recursion_limit
: Limit
,
51 impl<'tcx
, F
> NeedsDropTypes
<'tcx
, F
> {
54 param_env
: ty
::ParamEnv
<'tcx
>,
58 let mut seen_tys
= FxHashSet
::default();
65 unchecked_tys
: vec
![(ty
, 0)],
66 recursion_limit
: tcx
.recursion_limit(),
72 impl<'tcx
, F
, I
> Iterator
for NeedsDropTypes
<'tcx
, F
>
74 F
: Fn(&ty
::AdtDef
) -> NeedsDropResult
<I
>,
75 I
: Iterator
<Item
= Ty
<'tcx
>>,
77 type Item
= NeedsDropResult
<Ty
<'tcx
>>;
79 fn next(&mut self) -> Option
<NeedsDropResult
<Ty
<'tcx
>>> {
82 while let Some((ty
, level
)) = self.unchecked_tys
.pop() {
83 if !self.recursion_limit
.value_within_limit(level
) {
84 // Not having a `Span` isn't great. But there's hopefully some other
85 // recursion limit error as well.
88 &format
!("overflow while checking whether `{}` requires drop", self.query_ty
),
90 return Some(Err(AlwaysRequiresDrop
));
93 let components
= match needs_drop_components(ty
, &tcx
.data_layout
) {
94 Err(e
) => return Some(Err(e
)),
95 Ok(components
) => components
,
97 debug
!("needs_drop_components({:?}) = {:?}", ty
, components
);
99 let queue_type
= move |this
: &mut Self, component
: Ty
<'tcx
>| {
100 if this
.seen_tys
.insert(component
) {
101 this
.unchecked_tys
.push((component
, level
+ 1));
105 for component
in components
{
106 match *component
.kind() {
107 _
if component
.is_copy_modulo_regions(tcx
.at(DUMMY_SP
), self.param_env
) => (),
109 ty
::Closure(_
, substs
) => {
110 queue_type(self, substs
.as_closure().tupled_upvars_ty());
113 ty
::Generator(def_id
, substs
, _
) => {
114 let substs
= substs
.as_generator();
115 queue_type(self, substs
.tupled_upvars_ty());
117 let witness
= substs
.witness();
118 let interior_tys
= match witness
.kind() {
119 &ty
::GeneratorWitness(tys
) => tcx
.erase_late_bound_regions(tys
),
121 tcx
.sess
.delay_span_bug(
122 tcx
.hir().span_if_local(def_id
).unwrap_or(DUMMY_SP
),
123 &format
!("unexpected generator witness type {:?}", witness
),
125 return Some(Err(AlwaysRequiresDrop
));
129 for interior_ty
in interior_tys
{
130 queue_type(self, interior_ty
);
134 // Check for a `Drop` impl and whether this is a union or
135 // `ManuallyDrop`. If it's a struct or enum without a `Drop`
136 // impl then check whether the field types need `Drop`.
137 ty
::Adt(adt_def
, substs
) => {
138 let tys
= match (self.adt_components
)(adt_def
) {
139 Err(e
) => return Some(Err(e
)),
142 for required_ty
in tys
{
143 let subst_ty
= tcx
.normalize_erasing_regions(
145 required_ty
.subst(tcx
, substs
),
147 queue_type(self, subst_ty
);
150 ty
::Array(..) | ty
::Opaque(..) | ty
::Projection(..) | ty
::Param(_
) => {
152 // Return the type to the caller: they may be able
153 // to normalize further than we can.
154 return Some(Ok(component
));
156 // Store the type for later. We can't return here
157 // because we would then lose any other components
159 queue_type(self, component
);
162 _
=> return Some(Err(AlwaysRequiresDrop
)),
171 // This is a helper function for `adt_drop_tys` and `adt_significant_drop_tys`.
172 // Depending on the implentation of `adt_has_dtor`, it is used to check if the
173 // ADT has a destructor or if the ADT only has a significant destructor. For
174 // understanding significant destructor look at `adt_significant_drop_tys`.
175 fn adt_drop_tys_helper(
178 adt_has_dtor
: impl Fn(&ty
::AdtDef
) -> bool
,
179 ) -> Result
<&ty
::List
<Ty
<'_
>>, AlwaysRequiresDrop
> {
180 let adt_components
= move |adt_def
: &ty
::AdtDef
| {
181 if adt_def
.is_manually_drop() {
182 debug
!("adt_drop_tys: `{:?}` is manually drop", adt_def
);
183 return Ok(Vec
::new().into_iter());
184 } else if adt_has_dtor(adt_def
) {
185 debug
!("adt_drop_tys: `{:?}` implements `Drop`", adt_def
);
186 return Err(AlwaysRequiresDrop
);
187 } else if adt_def
.is_union() {
188 debug
!("adt_drop_tys: `{:?}` is a union", adt_def
);
189 return Ok(Vec
::new().into_iter());
191 Ok(adt_def
.all_fields().map(|field
| tcx
.type_of(field
.did
)).collect
::<Vec
<_
>>().into_iter())
194 let adt_ty
= tcx
.type_of(def_id
);
195 let param_env
= tcx
.param_env(def_id
);
196 let res
: Result
<Vec
<_
>, _
> =
197 NeedsDropTypes
::new(tcx
, param_env
, adt_ty
, adt_components
).collect();
199 debug
!("adt_drop_tys(`{}`) = `{:?}`", tcx
.def_path_str(def_id
), res
);
200 res
.map(|components
| tcx
.intern_type_list(&components
))
203 fn adt_drop_tys(tcx
: TyCtxt
<'_
>, def_id
: DefId
) -> Result
<&ty
::List
<Ty
<'_
>>, AlwaysRequiresDrop
> {
204 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
)
208 fn adt_significant_drop_tys(
211 ) -> Result
<&ty
::List
<Ty
<'_
>>, AlwaysRequiresDrop
> {
212 let adt_has_dtor
= |adt_def
: &ty
::AdtDef
| {
215 .map(|dtor
| !tcx
.has_attr(dtor
.did
, sym
::rustc_insignificant_dtor
))
218 adt_drop_tys_helper(tcx
, def_id
, adt_has_dtor
)
221 pub(crate) fn provide(providers
: &mut ty
::query
::Providers
) {
222 *providers
= ty
::query
::Providers
{
224 has_significant_drop_raw
,
226 adt_significant_drop_tys
,