hir_ty/
drop.rs

1//! Utilities for computing drop info about types.
2
3use chalk_ir::cast::Cast;
4use hir_def::AdtId;
5use hir_def::lang_item::LangItem;
6use hir_def::signatures::StructFlags;
7use stdx::never;
8use triomphe::Arc;
9
10use crate::{
11    AliasTy, Canonical, CanonicalVarKinds, ConcreteConst, ConstScalar, ConstValue, InEnvironment,
12    Interner, ProjectionTy, TraitEnvironment, Ty, TyBuilder, TyKind, db::HirDatabase,
13    method_resolution::TyFingerprint,
14};
15
16fn has_destructor(db: &dyn HirDatabase, adt: AdtId) -> bool {
17    let module = match adt {
18        AdtId::EnumId(id) => db.lookup_intern_enum(id).container,
19        AdtId::StructId(id) => db.lookup_intern_struct(id).container,
20        AdtId::UnionId(id) => db.lookup_intern_union(id).container,
21    };
22    let Some(drop_trait) = LangItem::Drop.resolve_trait(db, module.krate()) else {
23        return false;
24    };
25    let impls = match module.containing_block() {
26        Some(block) => match db.trait_impls_in_block(block) {
27            Some(it) => it,
28            None => return false,
29        },
30        None => db.trait_impls_in_crate(module.krate()),
31    };
32    impls.for_trait_and_self_ty(drop_trait, TyFingerprint::Adt(adt)).next().is_some()
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
36pub enum DropGlue {
37    // Order of variants is important.
38    None,
39    /// May have a drop glue if some type parameter has it.
40    ///
41    /// For the compiler this is considered as a positive result, IDE distinguishes this from "yes".
42    DependOnParams,
43    HasDropGlue,
44}
45
46pub(crate) fn has_drop_glue(db: &dyn HirDatabase, ty: Ty, env: Arc<TraitEnvironment>) -> DropGlue {
47    match ty.kind(Interner) {
48        TyKind::Adt(adt, subst) => {
49            if has_destructor(db, adt.0) {
50                return DropGlue::HasDropGlue;
51            }
52            match adt.0 {
53                AdtId::StructId(id) => {
54                    if db.struct_signature(id).flags.contains(StructFlags::IS_MANUALLY_DROP) {
55                        return DropGlue::None;
56                    }
57                    db.field_types(id.into())
58                        .iter()
59                        .map(|(_, field_ty)| {
60                            db.has_drop_glue(
61                                field_ty.clone().substitute(Interner, subst),
62                                env.clone(),
63                            )
64                        })
65                        .max()
66                        .unwrap_or(DropGlue::None)
67                }
68                // Unions cannot have fields with destructors.
69                AdtId::UnionId(_) => DropGlue::None,
70                AdtId::EnumId(id) => id
71                    .enum_variants(db)
72                    .variants
73                    .iter()
74                    .map(|&(variant, _, _)| {
75                        db.field_types(variant.into())
76                            .iter()
77                            .map(|(_, field_ty)| {
78                                db.has_drop_glue(
79                                    field_ty.clone().substitute(Interner, subst),
80                                    env.clone(),
81                                )
82                            })
83                            .max()
84                            .unwrap_or(DropGlue::None)
85                    })
86                    .max()
87                    .unwrap_or(DropGlue::None),
88            }
89        }
90        TyKind::Tuple(_, subst) => subst
91            .iter(Interner)
92            .map(|ty| ty.assert_ty_ref(Interner))
93            .map(|ty| db.has_drop_glue(ty.clone(), env.clone()))
94            .max()
95            .unwrap_or(DropGlue::None),
96        TyKind::Array(ty, len) => {
97            if let ConstValue::Concrete(ConcreteConst { interned: ConstScalar::Bytes(len, _) }) =
98                &len.data(Interner).value
99            {
100                match (&**len).try_into() {
101                    Ok(len) => {
102                        let len = usize::from_le_bytes(len);
103                        if len == 0 {
104                            // Arrays of size 0 don't have drop glue.
105                            return DropGlue::None;
106                        }
107                    }
108                    Err(_) => {
109                        never!("const array size with non-usize len");
110                    }
111                }
112            }
113            db.has_drop_glue(ty.clone(), env)
114        }
115        TyKind::Slice(ty) => db.has_drop_glue(ty.clone(), env),
116        TyKind::Closure(closure_id, subst) => {
117            let owner = db.lookup_intern_closure((*closure_id).into()).0;
118            let infer = db.infer(owner);
119            let (captures, _) = infer.closure_info(closure_id);
120            let env = db.trait_environment_for_body(owner);
121            captures
122                .iter()
123                .map(|capture| db.has_drop_glue(capture.ty(subst), env.clone()))
124                .max()
125                .unwrap_or(DropGlue::None)
126        }
127        // FIXME: Handle coroutines.
128        TyKind::Coroutine(..) | TyKind::CoroutineWitness(..) => DropGlue::None,
129        TyKind::Ref(..)
130        | TyKind::Raw(..)
131        | TyKind::FnDef(..)
132        | TyKind::Str
133        | TyKind::Never
134        | TyKind::Scalar(_)
135        | TyKind::Function(_)
136        | TyKind::Foreign(_)
137        | TyKind::Error => DropGlue::None,
138        TyKind::Dyn(_) => DropGlue::HasDropGlue,
139        TyKind::AssociatedType(assoc_type_id, subst) => projection_has_drop_glue(
140            db,
141            env,
142            ProjectionTy { associated_ty_id: *assoc_type_id, substitution: subst.clone() },
143            ty,
144        ),
145        TyKind::Alias(AliasTy::Projection(projection)) => {
146            projection_has_drop_glue(db, env, projection.clone(), ty)
147        }
148        TyKind::OpaqueType(..) | TyKind::Alias(AliasTy::Opaque(_)) => {
149            if is_copy(db, ty, env) {
150                DropGlue::None
151            } else {
152                DropGlue::HasDropGlue
153            }
154        }
155        TyKind::Placeholder(_) | TyKind::BoundVar(_) => {
156            if is_copy(db, ty, env) {
157                DropGlue::None
158            } else {
159                DropGlue::DependOnParams
160            }
161        }
162        TyKind::InferenceVar(..) => unreachable!("inference vars shouldn't exist out of inference"),
163    }
164}
165
166fn projection_has_drop_glue(
167    db: &dyn HirDatabase,
168    env: Arc<TraitEnvironment>,
169    projection: ProjectionTy,
170    ty: Ty,
171) -> DropGlue {
172    let normalized = db.normalize_projection(projection, env.clone());
173    match normalized.kind(Interner) {
174        TyKind::Alias(AliasTy::Projection(_)) | TyKind::AssociatedType(..) => {
175            if is_copy(db, ty, env) { DropGlue::None } else { DropGlue::DependOnParams }
176        }
177        _ => db.has_drop_glue(normalized, env),
178    }
179}
180
181fn is_copy(db: &dyn HirDatabase, ty: Ty, env: Arc<TraitEnvironment>) -> bool {
182    let Some(copy_trait) = LangItem::Copy.resolve_trait(db, env.krate) else {
183        return false;
184    };
185    let trait_ref = TyBuilder::trait_ref(db, copy_trait).push(ty).build();
186    let goal = Canonical {
187        value: InEnvironment::new(&env.env, trait_ref.cast(Interner)),
188        binders: CanonicalVarKinds::empty(Interner),
189    };
190    db.trait_solve(env.krate, env.block, goal).certain()
191}
192
193pub(crate) fn has_drop_glue_cycle_result(
194    _db: &dyn HirDatabase,
195    _ty: Ty,
196    _env: Arc<TraitEnvironment>,
197) -> DropGlue {
198    DropGlue::None
199}