Skip to main content

hir_ty/
drop.rs

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