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