1use 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 None,
42 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 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 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 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 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}