1use rustc_data_structures::fx::FxHashSet;
4use rustc_hir::def_id::DefId;
5use rustc_middle::bug;
6use rustc_middle::query::Providers;
7use rustc_middle::ty::util::{AlwaysRequiresDrop, needs_drop_components};
8use rustc_middle::ty::{self, EarlyBinder, GenericArgsRef, Ty, TyCtxt};
9use rustc_session::Limit;
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(tcx, query.typing_env))
46 .next()
47 .is_some();
48
49 debug!("needs_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}
69
70fn has_significant_drop_raw<'tcx>(
71 tcx: TyCtxt<'tcx>,
72 query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
73) -> bool {
74 let res = drop_tys_helper(
75 tcx,
76 query.value,
77 query.typing_env,
78 adt_consider_insignificant_dtor(tcx),
79 true,
80 false,
81 )
82 .filter(filter_array_elements(tcx, query.typing_env))
83 .next()
84 .is_some();
85 debug!("has_significant_drop_raw({:?}) = {:?}", query, res);
86 res
87}
88
89struct NeedsDropTypes<'tcx, F> {
90 tcx: TyCtxt<'tcx>,
91 typing_env: ty::TypingEnv<'tcx>,
92 reveal_coroutine_witnesses: bool,
95 query_ty: Ty<'tcx>,
96 seen_tys: FxHashSet<Ty<'tcx>>,
97 unchecked_tys: Vec<(Ty<'tcx>, usize)>,
102 recursion_limit: Limit,
103 adt_components: F,
104 exhaustive: bool,
107}
108
109impl<'tcx, F> NeedsDropTypes<'tcx, F> {
110 fn new(
111 tcx: TyCtxt<'tcx>,
112 typing_env: ty::TypingEnv<'tcx>,
113 ty: Ty<'tcx>,
114 exhaustive: bool,
115 adt_components: F,
116 ) -> Self {
117 let mut seen_tys = FxHashSet::default();
118 seen_tys.insert(ty);
119 Self {
120 tcx,
121 typing_env,
122 reveal_coroutine_witnesses: exhaustive,
123 seen_tys,
124 query_ty: ty,
125 unchecked_tys: vec![(ty, 0)],
126 recursion_limit: tcx.recursion_limit(),
127 adt_components,
128 exhaustive,
129 }
130 }
131
132 fn always_drop_component(&self, ty: Ty<'tcx>) -> NeedsDropResult<Ty<'tcx>> {
136 if self.exhaustive { Ok(ty) } else { Err(AlwaysRequiresDrop) }
137 }
138}
139
140impl<'tcx, F, I> Iterator for NeedsDropTypes<'tcx, F>
141where
142 F: Fn(ty::AdtDef<'tcx>, GenericArgsRef<'tcx>) -> NeedsDropResult<I>,
143 I: Iterator<Item = Ty<'tcx>>,
144{
145 type Item = NeedsDropResult<Ty<'tcx>>;
146
147 #[instrument(level = "debug", skip(self), ret)]
148 fn next(&mut self) -> Option<NeedsDropResult<Ty<'tcx>>> {
149 let tcx = self.tcx;
150
151 while let Some((ty, level)) = self.unchecked_tys.pop() {
152 debug!(?ty, "needs_drop_components: inspect");
153 if !self.recursion_limit.value_within_limit(level) {
154 debug!("needs_drop_components: recursion limit exceeded");
157 tcx.dcx().emit_err(NeedsDropOverflow { query_ty: self.query_ty });
158 return Some(self.always_drop_component(ty));
159 }
160
161 let components = match needs_drop_components(tcx, ty) {
162 Err(AlwaysRequiresDrop) => return Some(self.always_drop_component(ty)),
163 Ok(components) => components,
164 };
165 debug!("needs_drop_components({:?}) = {:?}", ty, components);
166
167 let queue_type = move |this: &mut Self, component: Ty<'tcx>| {
168 if this.seen_tys.insert(component) {
169 this.unchecked_tys.push((component, level + 1));
170 }
171 };
172
173 for component in components {
174 match *component.kind() {
175 ty::Coroutine(_, args) => {
187 if self.reveal_coroutine_witnesses {
188 queue_type(self, args.as_coroutine().witness());
189 } else {
190 return Some(self.always_drop_component(ty));
191 }
192 }
193 ty::CoroutineWitness(def_id, args) => {
194 if let Some(witness) = tcx.mir_coroutine_witnesses(def_id) {
195 self.reveal_coroutine_witnesses = true;
196 for field_ty in &witness.field_tys {
197 queue_type(
198 self,
199 EarlyBinder::bind(field_ty.ty).instantiate(tcx, args),
200 );
201 }
202 }
203 }
204
205 ty::UnsafeBinder(bound_ty) => {
206 let ty = self.tcx.instantiate_bound_regions_with_erased(bound_ty.into());
207 queue_type(self, ty);
208 }
209
210 _ if tcx.type_is_copy_modulo_regions(self.typing_env, component) => {}
211
212 ty::Closure(_, args) => {
213 for upvar in args.as_closure().upvar_tys() {
214 queue_type(self, upvar);
215 }
216 }
217
218 ty::CoroutineClosure(_, args) => {
219 for upvar in args.as_coroutine_closure().upvar_tys() {
220 queue_type(self, upvar);
221 }
222 }
223
224 ty::Adt(adt_def, args) => {
228 let tys = match (self.adt_components)(adt_def, args) {
229 Err(AlwaysRequiresDrop) => {
230 return Some(self.always_drop_component(ty));
231 }
232 Ok(tys) => tys,
233 };
234 for required_ty in tys {
235 let required = tcx
236 .try_normalize_erasing_regions(self.typing_env, required_ty)
237 .unwrap_or(required_ty);
238
239 queue_type(self, required);
240 }
241 }
242 ty::Alias(..) | ty::Array(..) | ty::Placeholder(_) | ty::Param(_) => {
243 if ty == component {
244 return Some(Ok(component));
247 } else {
248 queue_type(self, component);
252 }
253 }
254
255 ty::Foreign(_) | ty::Dynamic(..) => {
256 debug!("needs_drop_components: foreign or dynamic");
257 return Some(self.always_drop_component(ty));
258 }
259
260 ty::Bool
261 | ty::Char
262 | ty::Int(_)
263 | ty::Uint(_)
264 | ty::Float(_)
265 | ty::Str
266 | ty::Slice(_)
267 | ty::Ref(..)
268 | ty::RawPtr(..)
269 | ty::FnDef(..)
270 | ty::Pat(..)
271 | ty::FnPtr(..)
272 | ty::Tuple(_)
273 | ty::Bound(..)
274 | ty::Never
275 | ty::Infer(_)
276 | ty::Error(_) => {
277 bug!("unexpected type returned by `needs_drop_components`: {component}")
278 }
279 }
280 }
281 }
282
283 None
284 }
285}
286
287enum DtorType {
288 Insignificant,
292
293 Significant,
295}
296
297fn drop_tys_helper<'tcx>(
302 tcx: TyCtxt<'tcx>,
303 ty: Ty<'tcx>,
304 typing_env: ty::TypingEnv<'tcx>,
305 adt_has_dtor: impl Fn(ty::AdtDef<'tcx>) -> Option<DtorType>,
306 only_significant: bool,
307 exhaustive: bool,
308) -> impl Iterator<Item = NeedsDropResult<Ty<'tcx>>> {
309 fn with_query_cache<'tcx>(
310 tcx: TyCtxt<'tcx>,
311 iter: impl IntoIterator<Item = Ty<'tcx>>,
312 ) -> NeedsDropResult<Vec<Ty<'tcx>>> {
313 iter.into_iter().try_fold(Vec::new(), |mut vec, subty| {
314 match subty.kind() {
315 ty::Adt(adt_id, args) => {
316 for subty in tcx.adt_drop_tys(adt_id.did())? {
317 vec.push(EarlyBinder::bind(subty).instantiate(tcx, args));
318 }
319 }
320 _ => vec.push(subty),
321 };
322 Ok(vec)
323 })
324 }
325
326 let adt_components = move |adt_def: ty::AdtDef<'tcx>, args: GenericArgsRef<'tcx>| {
327 if adt_def.is_manually_drop() {
328 debug!("drop_tys_helper: `{:?}` is manually drop", adt_def);
329 Ok(Vec::new())
330 } else if let Some(dtor_info) = adt_has_dtor(adt_def) {
331 match dtor_info {
332 DtorType::Significant => {
333 debug!("drop_tys_helper: `{:?}` implements `Drop`", adt_def);
334 Err(AlwaysRequiresDrop)
335 }
336 DtorType::Insignificant => {
337 debug!("drop_tys_helper: `{:?}` drop is insignificant", adt_def);
338
339 Ok(args.types().collect())
343 }
344 }
345 } else if adt_def.is_union() {
346 debug!("drop_tys_helper: `{:?}` is a union", adt_def);
347 Ok(Vec::new())
348 } else {
349 let field_tys = adt_def.all_fields().map(|field| {
350 let r = tcx.type_of(field.did).instantiate(tcx, args);
351 debug!(
352 "drop_tys_helper: Instantiate into {:?} with {:?} getting {:?}",
353 field, args, r
354 );
355 r
356 });
357 if only_significant {
358 Ok(field_tys.collect())
360 } else {
361 with_query_cache(tcx, field_tys)
366 }
367 }
368 .map(|v| v.into_iter())
369 };
370
371 NeedsDropTypes::new(tcx, typing_env, ty, exhaustive, adt_components)
372}
373
374fn adt_consider_insignificant_dtor<'tcx>(
375 tcx: TyCtxt<'tcx>,
376) -> impl Fn(ty::AdtDef<'tcx>) -> Option<DtorType> {
377 move |adt_def: ty::AdtDef<'tcx>| {
378 let is_marked_insig = tcx.has_attr(adt_def.did(), sym::rustc_insignificant_dtor);
379 if is_marked_insig {
380 Some(DtorType::Insignificant)
385 } else if adt_def.destructor(tcx).is_some() {
386 Some(DtorType::Significant)
389 } else {
390 None
393 }
394 }
395}
396
397fn adt_drop_tys<'tcx>(
398 tcx: TyCtxt<'tcx>,
399 def_id: DefId,
400) -> Result<&'tcx ty::List<Ty<'tcx>>, AlwaysRequiresDrop> {
401 let adt_has_dtor =
404 |adt_def: ty::AdtDef<'tcx>| adt_def.destructor(tcx).map(|_| DtorType::Significant);
405 drop_tys_helper(
407 tcx,
408 tcx.type_of(def_id).instantiate_identity(),
409 ty::TypingEnv::non_body_analysis(tcx, def_id),
410 adt_has_dtor,
411 false,
412 false,
413 )
414 .collect::<Result<Vec<_>, _>>()
415 .map(|components| tcx.mk_type_list(&components))
416}
417fn adt_significant_drop_tys(
421 tcx: TyCtxt<'_>,
422 def_id: DefId,
423) -> Result<&ty::List<Ty<'_>>, AlwaysRequiresDrop> {
424 drop_tys_helper(
425 tcx,
426 tcx.type_of(def_id).instantiate_identity(), ty::TypingEnv::non_body_analysis(tcx, def_id),
428 adt_consider_insignificant_dtor(tcx),
429 true,
430 false,
431 )
432 .collect::<Result<Vec<_>, _>>()
433 .map(|components| tcx.mk_type_list(&components))
434}
435
436#[instrument(level = "debug", skip(tcx), ret)]
437fn list_significant_drop_tys<'tcx>(
438 tcx: TyCtxt<'tcx>,
439 key: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
440) -> &'tcx ty::List<Ty<'tcx>> {
441 tcx.mk_type_list(
442 &drop_tys_helper(
443 tcx,
444 key.value,
445 key.typing_env,
446 adt_consider_insignificant_dtor(tcx),
447 true,
448 true,
449 )
450 .filter_map(|res| res.ok())
451 .collect::<Vec<_>>(),
452 )
453}
454
455pub(crate) fn provide(providers: &mut Providers) {
456 *providers = Providers {
457 needs_drop_raw,
458 needs_async_drop_raw,
459 has_significant_drop_raw,
460 adt_drop_tys,
461 adt_significant_drop_tys,
462 list_significant_drop_tys,
463 ..*providers
464 };
465}