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