hir_ty/
drop.rs

1//! Utilities for computing drop info about types.
2
3use hir_def::{AdtId, signatures::StructFlags};
4use rustc_hash::FxHashSet;
5use rustc_type_ir::inherent::{AdtDef, IntoKind, SliceLike};
6use stdx::never;
7
8use crate::{
9    InferenceResult, consteval,
10    method_resolution::TraitImpls,
11    next_solver::{
12        DbInterner, ParamEnv, SimplifiedType, Ty, TyKind,
13        infer::{InferCtxt, traits::ObligationCause},
14        obligation_ctxt::ObligationCtxt,
15    },
16};
17
18fn has_destructor(interner: DbInterner<'_>, adt: AdtId) -> bool {
19    let db = interner.db;
20    let module = match adt {
21        AdtId::EnumId(id) => db.lookup_intern_enum(id).container,
22        AdtId::StructId(id) => db.lookup_intern_struct(id).container,
23        AdtId::UnionId(id) => db.lookup_intern_union(id).container,
24    };
25    let Some(drop_trait) = interner.lang_items().Drop else {
26        return false;
27    };
28    let impls = match module.block(db) {
29        Some(block) => match TraitImpls::for_block(db, block) {
30            Some(it) => &**it,
31            None => return false,
32        },
33        None => TraitImpls::for_crate(db, module.krate(db)),
34    };
35    !impls.for_trait_and_self_ty(drop_trait, &SimplifiedType::Adt(adt.into())).is_empty()
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
39pub enum DropGlue {
40    // Order of variants is important.
41    None,
42    /// May have a drop glue if some type parameter has it.
43    ///
44    /// For the compiler this is considered as a positive result, IDE distinguishes this from "yes".
45    DependOnParams,
46    HasDropGlue,
47}
48
49pub fn has_drop_glue<'db>(infcx: &InferCtxt<'db>, ty: Ty<'db>, env: ParamEnv<'db>) -> DropGlue {
50    has_drop_glue_impl(infcx, ty, env, &mut FxHashSet::default())
51}
52
53fn has_drop_glue_impl<'db>(
54    infcx: &InferCtxt<'db>,
55    ty: Ty<'db>,
56    env: ParamEnv<'db>,
57    visited: &mut FxHashSet<Ty<'db>>,
58) -> DropGlue {
59    let mut ocx = ObligationCtxt::new(infcx);
60    let ty = ocx.structurally_normalize_ty(&ObligationCause::dummy(), env, ty).unwrap_or(ty);
61
62    if !visited.insert(ty) {
63        // Recursive type.
64        return DropGlue::None;
65    }
66
67    let db = infcx.interner.db;
68    match ty.kind() {
69        TyKind::Adt(adt_def, subst) => {
70            let adt_id = adt_def.def_id().0;
71            if has_destructor(infcx.interner, adt_id) {
72                return DropGlue::HasDropGlue;
73            }
74            match adt_id {
75                AdtId::StructId(id) => {
76                    if db
77                        .struct_signature(id)
78                        .flags
79                        .intersects(StructFlags::IS_MANUALLY_DROP | StructFlags::IS_PHANTOM_DATA)
80                    {
81                        return DropGlue::None;
82                    }
83                    db.field_types(id.into())
84                        .iter()
85                        .map(|(_, field_ty)| {
86                            has_drop_glue_impl(
87                                infcx,
88                                field_ty.instantiate(infcx.interner, subst),
89                                env,
90                                visited,
91                            )
92                        })
93                        .max()
94                        .unwrap_or(DropGlue::None)
95                }
96                // Unions cannot have fields with destructors.
97                AdtId::UnionId(_) => DropGlue::None,
98                AdtId::EnumId(id) => id
99                    .enum_variants(db)
100                    .variants
101                    .iter()
102                    .map(|&(variant, _, _)| {
103                        db.field_types(variant.into())
104                            .iter()
105                            .map(|(_, field_ty)| {
106                                has_drop_glue_impl(
107                                    infcx,
108                                    field_ty.instantiate(infcx.interner, subst),
109                                    env,
110                                    visited,
111                                )
112                            })
113                            .max()
114                            .unwrap_or(DropGlue::None)
115                    })
116                    .max()
117                    .unwrap_or(DropGlue::None),
118            }
119        }
120        TyKind::Tuple(tys) => tys
121            .iter()
122            .map(|ty| has_drop_glue_impl(infcx, ty, env, visited))
123            .max()
124            .unwrap_or(DropGlue::None),
125        TyKind::Array(ty, len) => {
126            if consteval::try_const_usize(db, len) == Some(0) {
127                // Arrays of size 0 don't have drop glue.
128                return DropGlue::None;
129            }
130            has_drop_glue_impl(infcx, ty, env, visited)
131        }
132        TyKind::Slice(ty) => has_drop_glue_impl(infcx, ty, env, visited),
133        TyKind::Closure(closure_id, subst) => {
134            let owner = db.lookup_intern_closure(closure_id.0).0;
135            let infer = InferenceResult::for_body(db, owner);
136            let (captures, _) = infer.closure_info(closure_id.0);
137            let env = db.trait_environment_for_body(owner);
138            captures
139                .iter()
140                .map(|capture| has_drop_glue_impl(infcx, capture.ty(db, subst), env, visited))
141                .max()
142                .unwrap_or(DropGlue::None)
143        }
144        // FIXME: Handle coroutines.
145        TyKind::Coroutine(..) | TyKind::CoroutineWitness(..) | TyKind::CoroutineClosure(..) => {
146            DropGlue::None
147        }
148        TyKind::Ref(..)
149        | TyKind::RawPtr(..)
150        | TyKind::FnDef(..)
151        | TyKind::Str
152        | TyKind::Never
153        | TyKind::Bool
154        | TyKind::Char
155        | TyKind::Int(_)
156        | TyKind::Uint(_)
157        | TyKind::Float(_)
158        | TyKind::FnPtr(..)
159        | TyKind::Foreign(_)
160        | TyKind::Error(_)
161        | TyKind::Bound(..)
162        | TyKind::Placeholder(..) => DropGlue::None,
163        TyKind::Dynamic(..) => DropGlue::HasDropGlue,
164        TyKind::Alias(..) => {
165            if infcx.type_is_copy_modulo_regions(env, ty) {
166                DropGlue::None
167            } else {
168                DropGlue::HasDropGlue
169            }
170        }
171        TyKind::Param(_) => {
172            if infcx.type_is_copy_modulo_regions(env, ty) {
173                DropGlue::None
174            } else {
175                DropGlue::DependOnParams
176            }
177        }
178        TyKind::Infer(..) => unreachable!("inference vars shouldn't exist out of inference"),
179        TyKind::Pat(..) | TyKind::UnsafeBinder(..) => {
180            never!("we do not handle pattern and unsafe binder types");
181            DropGlue::None
182        }
183    }
184}