1use rustc_data_structures::fx::FxHashSet;
4use rustc_hir::def_id::DefId;
5use rustc_hir::limit::Limit;
6use rustc_middle::bug;
7use rustc_middle::query::Providers;
8use rustc_middle::ty::util::{AlwaysRequiresDrop, needs_drop_components};
9use rustc_middle::ty::{self, EarlyBinder, GenericArgsRef, Ty, TyCtxt};
10use rustc_span::sym;
11use tracing::{debug, instrument};
12
13use crate::errors::NeedsDropOverflow;
14
15type NeedsDropResult<T> = Result<T, AlwaysRequiresDrop>;
16
17fn needs_drop_raw<'tcx>(
18 tcx: TyCtxt<'tcx>,
19 query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
20) -> bool {
21 let adt_has_dtor =
25 |adt_def: ty::AdtDef<'tcx>| adt_def.destructor(tcx).map(|_| DtorType::Significant);
26 let res = drop_tys_helper(tcx, query.value, query.typing_env, adt_has_dtor, false, false)
27 .filter(filter_array_elements(tcx, query.typing_env))
28 .next()
29 .is_some();
30
31 debug!("needs_drop_raw({:?}) = {:?}", query, res);
32 res
33}
34
35fn needs_async_drop_raw<'tcx>(
36 tcx: TyCtxt<'tcx>,
37 query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
38) -> bool {
39 let adt_has_async_dtor =
43 |adt_def: ty::AdtDef<'tcx>| adt_def.async_destructor(tcx).map(|_| DtorType::Significant);
44 let res = drop_tys_helper(tcx, query.value, query.typing_env, adt_has_async_dtor, false, false)
45 .filter(filter_array_elements_async(tcx, query.typing_env))
46 .next()
47 .is_some();
48
49 debug!("needs_async_drop_raw({:?}) = {:?}", query, res);
50 res
51}
52
53fn filter_array_elements<'tcx>(
58 tcx: TyCtxt<'tcx>,
59 typing_env: ty::TypingEnv<'tcx>,
60) -> impl Fn(&Result<Ty<'tcx>, AlwaysRequiresDrop>) -> bool {
61 move |ty| match ty {
62 Ok(ty) => match *ty.kind() {
63 ty::Array(elem, _) => tcx.needs_drop_raw(typing_env.as_query_input(elem)),
64 _ => true,
65 },
66 Err(AlwaysRequiresDrop) => true,
67 }
68}
69fn filter_array_elements_async<'tcx>(
70 tcx: TyCtxt<'tcx>,
71 typing_env: ty::TypingEnv<'tcx>,
72) -> impl Fn(&Result<Ty<'tcx>, AlwaysRequiresDrop>) -> bool {
73 move |ty| match ty {
74 Ok(ty) => match *ty.kind() {
75 ty::Array(elem, _) => tcx.needs_async_drop_raw(typing_env.as_query_input(elem)),
76 _ => true,
77 },
78 Err(AlwaysRequiresDrop) => true,
79 }
80}
81
82fn has_significant_drop_raw<'tcx>(
83 tcx: TyCtxt<'tcx>,
84 query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
85) -> bool {
86 let res = drop_tys_helper(
87 tcx,
88 query.value,
89 query.typing_env,
90 adt_consider_insignificant_dtor(tcx),
91 true,
92 false,
93 )
94 .filter(filter_array_elements(tcx, query.typing_env))
95 .next()
96 .is_some();
97 debug!("has_significant_drop_raw({:?}) = {:?}", query, res);
98 res
99}
100
101struct NeedsDropTypes<'tcx, F> {
102 tcx: TyCtxt<'tcx>,
103 typing_env: ty::TypingEnv<'tcx>,
104 query_ty: Ty<'tcx>,
105 seen_tys: FxHashSet<Ty<'tcx>>,
106 unchecked_tys: Vec<(Ty<'tcx>, usize)>,
111 recursion_limit: Limit,
112 adt_components: F,
113 exhaustive: bool,
125}
126
127impl<'tcx, F> NeedsDropTypes<'tcx, F> {
128 fn new(
129 tcx: TyCtxt<'tcx>,
130 typing_env: ty::TypingEnv<'tcx>,
131 ty: Ty<'tcx>,
132 exhaustive: bool,
133 adt_components: F,
134 ) -> Self {
135 let mut seen_tys = FxHashSet::default();
136 seen_tys.insert(ty);
137 Self {
138 tcx,
139 typing_env,
140 seen_tys,
141 query_ty: ty,
142 unchecked_tys: vec![(ty, 0)],
143 recursion_limit: tcx.recursion_limit(),
144 adt_components,
145 exhaustive,
146 }
147 }
148
149 fn always_drop_component(&self, ty: Ty<'tcx>) -> NeedsDropResult<Ty<'tcx>> {
153 if self.exhaustive { Ok(ty) } else { Err(AlwaysRequiresDrop) }
154 }
155}
156
157impl<'tcx, F, I> Iterator for NeedsDropTypes<'tcx, F>
158where
159 F: Fn(ty::AdtDef<'tcx>, GenericArgsRef<'tcx>) -> NeedsDropResult<I>,
160 I: Iterator<Item = Ty<'tcx>>,
161{
162 type Item = NeedsDropResult<Ty<'tcx>>;
163
164 #[instrument(level = "debug", skip(self), ret)]
165 fn next(&mut self) -> Option<NeedsDropResult<Ty<'tcx>>> {
166 let tcx = self.tcx;
167
168 while let Some((ty, level)) = self.unchecked_tys.pop() {
169 debug!(?ty, "needs_drop_components: inspect");
170 if !self.recursion_limit.value_within_limit(level) {
171 debug!("needs_drop_components: recursion limit exceeded");
174 tcx.dcx().emit_err(NeedsDropOverflow { query_ty: self.query_ty });
175 return Some(self.always_drop_component(ty));
176 }
177
178 let components = match needs_drop_components(tcx, ty) {
179 Err(AlwaysRequiresDrop) => return Some(self.always_drop_component(ty)),
180 Ok(components) => components,
181 };
182 debug!("needs_drop_components({:?}) = {:?}", ty, components);
183
184 let queue_type = move |this: &mut Self, component: Ty<'tcx>| {
185 if this.seen_tys.insert(component) {
186 this.unchecked_tys.push((component, level + 1));
187 }
188 };
189
190 for component in components {
191 match *component.kind() {
192 ty::Coroutine(def_id, args) => {
204 if self.exhaustive {
206 for upvar in args.as_coroutine().upvar_tys() {
207 queue_type(self, upvar);
208 }
209 queue_type(self, args.as_coroutine().resume_ty());
210 if let Some(witness) = tcx.mir_coroutine_witnesses(def_id) {
211 for field_ty in &witness.field_tys {
212 queue_type(
213 self,
214 EarlyBinder::bind(field_ty.ty).instantiate(tcx, args),
215 );
216 }
217 }
218 } else {
219 return Some(self.always_drop_component(ty));
220 }
221 }
222 ty::CoroutineWitness(..) => {
223 unreachable!("witness should be handled in parent");
224 }
225
226 ty::UnsafeBinder(bound_ty) => {
227 let ty = self.tcx.instantiate_bound_regions_with_erased(bound_ty.into());
228 queue_type(self, ty);
229 }
230
231 _ if tcx.type_is_copy_modulo_regions(self.typing_env, component) => {}
232
233 ty::Closure(_, args) => {
234 for upvar in args.as_closure().upvar_tys() {
235 queue_type(self, upvar);
236 }
237 }
238
239 ty::CoroutineClosure(_, args) => {
240 for upvar in args.as_coroutine_closure().upvar_tys() {
241 queue_type(self, upvar);
242 }
243 }
244
245 ty::Adt(adt_def, args) => {
249 let tys = match (self.adt_components)(adt_def, args) {
250 Err(AlwaysRequiresDrop) => {
251 return Some(self.always_drop_component(ty));
252 }
253 Ok(tys) => tys,
254 };
255 for required_ty in tys {
256 let required = tcx
257 .try_normalize_erasing_regions(self.typing_env, required_ty)
258 .unwrap_or(required_ty);
259
260 queue_type(self, required);
261 }
262 }
263 ty::Alias(..) | ty::Array(..) | ty::Placeholder(_) | ty::Param(_) => {
264 if ty == component {
265 return Some(Ok(component));
268 } else {
269 queue_type(self, component);
273 }
274 }
275
276 ty::Foreign(_) | ty::Dynamic(..) => {
277 debug!("needs_drop_components: foreign or dynamic");
278 return Some(self.always_drop_component(ty));
279 }
280
281 ty::Bool
282 | ty::Char
283 | ty::Int(_)
284 | ty::Uint(_)
285 | ty::Float(_)
286 | ty::Str
287 | ty::Slice(_)
288 | ty::Ref(..)
289 | ty::RawPtr(..)
290 | ty::FnDef(..)
291 | ty::Pat(..)
292 | ty::FnPtr(..)
293 | ty::Tuple(_)
294 | ty::Bound(..)
295 | ty::Never
296 | ty::Infer(_)
297 | ty::Error(_) => {
298 bug!("unexpected type returned by `needs_drop_components`: {component}")
299 }
300 }
301 }
302 }
303
304 None
305 }
306}
307
308enum DtorType {
309 Insignificant,
313
314 Significant,
316}
317
318fn drop_tys_helper<'tcx>(
323 tcx: TyCtxt<'tcx>,
324 ty: Ty<'tcx>,
325 typing_env: ty::TypingEnv<'tcx>,
326 adt_has_dtor: impl Fn(ty::AdtDef<'tcx>) -> Option<DtorType>,
327 only_significant: bool,
328 exhaustive: bool,
329) -> impl Iterator<Item = NeedsDropResult<Ty<'tcx>>> {
330 fn with_query_cache<'tcx>(
331 tcx: TyCtxt<'tcx>,
332 iter: impl IntoIterator<Item = Ty<'tcx>>,
333 ) -> NeedsDropResult<Vec<Ty<'tcx>>> {
334 iter.into_iter().try_fold(Vec::new(), |mut vec, subty| {
335 match subty.kind() {
336 ty::Adt(adt_id, args) => {
337 for subty in tcx.adt_drop_tys(adt_id.did())? {
338 vec.push(EarlyBinder::bind(subty).instantiate(tcx, args));
339 }
340 }
341 _ => vec.push(subty),
342 };
343 Ok(vec)
344 })
345 }
346
347 let adt_components = move |adt_def: ty::AdtDef<'tcx>, args: GenericArgsRef<'tcx>| {
348 if adt_def.is_manually_drop() {
349 debug!("drop_tys_helper: `{:?}` is manually drop", adt_def);
350 Ok(Vec::new())
351 } else if let Some(dtor_info) = adt_has_dtor(adt_def) {
352 match dtor_info {
353 DtorType::Significant => {
354 debug!("drop_tys_helper: `{:?}` implements `Drop`", adt_def);
355 Err(AlwaysRequiresDrop)
356 }
357 DtorType::Insignificant => {
358 debug!("drop_tys_helper: `{:?}` drop is insignificant", adt_def);
359
360 Ok(args.types().collect())
364 }
365 }
366 } else if adt_def.is_union() {
367 debug!("drop_tys_helper: `{:?}` is a union", adt_def);
368 Ok(Vec::new())
369 } else {
370 let field_tys = adt_def.all_fields().map(|field| {
371 let r = tcx.type_of(field.did).instantiate(tcx, args);
372 debug!(
373 "drop_tys_helper: Instantiate into {:?} with {:?} getting {:?}",
374 field, args, r
375 );
376 r
377 });
378 if only_significant {
379 Ok(field_tys.collect())
381 } else {
382 with_query_cache(tcx, field_tys)
387 }
388 }
389 .map(|v| v.into_iter())
390 };
391
392 NeedsDropTypes::new(tcx, typing_env, ty, exhaustive, adt_components)
393}
394
395fn adt_consider_insignificant_dtor<'tcx>(
396 tcx: TyCtxt<'tcx>,
397) -> impl Fn(ty::AdtDef<'tcx>) -> Option<DtorType> {
398 move |adt_def: ty::AdtDef<'tcx>| {
399 let is_marked_insig = tcx.has_attr(adt_def.did(), sym::rustc_insignificant_dtor);
400 if is_marked_insig {
401 Some(DtorType::Insignificant)
406 } else if adt_def.destructor(tcx).is_some() {
407 Some(DtorType::Significant)
410 } else {
411 None
414 }
415 }
416}
417
418fn adt_drop_tys<'tcx>(
419 tcx: TyCtxt<'tcx>,
420 def_id: DefId,
421) -> Result<&'tcx ty::List<Ty<'tcx>>, AlwaysRequiresDrop> {
422 let adt_has_dtor =
425 |adt_def: ty::AdtDef<'tcx>| adt_def.destructor(tcx).map(|_| DtorType::Significant);
426 drop_tys_helper(
428 tcx,
429 tcx.type_of(def_id).instantiate_identity(),
430 ty::TypingEnv::non_body_analysis(tcx, def_id),
431 adt_has_dtor,
432 false,
433 false,
434 )
435 .collect::<Result<Vec<_>, _>>()
436 .map(|components| tcx.mk_type_list(&components))
437}
438
439fn adt_async_drop_tys<'tcx>(
440 tcx: TyCtxt<'tcx>,
441 def_id: DefId,
442) -> Result<&'tcx ty::List<Ty<'tcx>>, AlwaysRequiresDrop> {
443 let adt_has_dtor =
445 |adt_def: ty::AdtDef<'tcx>| adt_def.async_destructor(tcx).map(|_| DtorType::Significant);
446 drop_tys_helper(
448 tcx,
449 tcx.type_of(def_id).instantiate_identity(),
450 ty::TypingEnv::non_body_analysis(tcx, def_id),
451 adt_has_dtor,
452 false,
453 false,
454 )
455 .collect::<Result<Vec<_>, _>>()
456 .map(|components| tcx.mk_type_list(&components))
457}
458
459fn adt_significant_drop_tys(
463 tcx: TyCtxt<'_>,
464 def_id: DefId,
465) -> Result<&ty::List<Ty<'_>>, AlwaysRequiresDrop> {
466 drop_tys_helper(
467 tcx,
468 tcx.type_of(def_id).instantiate_identity(), ty::TypingEnv::non_body_analysis(tcx, def_id),
470 adt_consider_insignificant_dtor(tcx),
471 true,
472 false,
473 )
474 .collect::<Result<Vec<_>, _>>()
475 .map(|components| tcx.mk_type_list(&components))
476}
477
478#[instrument(level = "debug", skip(tcx), ret)]
479fn list_significant_drop_tys<'tcx>(
480 tcx: TyCtxt<'tcx>,
481 key: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
482) -> &'tcx ty::List<Ty<'tcx>> {
483 tcx.mk_type_list(
484 &drop_tys_helper(
485 tcx,
486 key.value,
487 key.typing_env,
488 adt_consider_insignificant_dtor(tcx),
489 true,
490 true,
491 )
492 .filter_map(|res| res.ok())
493 .collect::<Vec<_>>(),
494 )
495}
496
497pub(crate) fn provide(providers: &mut Providers) {
498 *providers = Providers {
499 needs_drop_raw,
500 needs_async_drop_raw,
501 has_significant_drop_raw,
502 adt_drop_tys,
503 adt_async_drop_tys,
504 adt_significant_drop_tys,
505 list_significant_drop_tys,
506 ..*providers
507 };
508}