Skip to content

Commit 93bd36d

Browse files
authored
Merge pull request #18987 from ChayimFriedman2/drop-glue
feat: Calculate drop glue and show it on hover
2 parents e2c281a + 100e166 commit 93bd36d

File tree

13 files changed

+1002
-6
lines changed

13 files changed

+1002
-6
lines changed

crates/hir-ty/src/consteval/tests/intrinsics.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,12 +354,43 @@ fn overflowing_add() {
354354
fn needs_drop() {
355355
check_number(
356356
r#"
357-
//- minicore: copy, sized
357+
//- minicore: drop, manually_drop, copy, sized
358+
use core::mem::ManuallyDrop;
358359
extern "rust-intrinsic" {
359360
pub fn needs_drop<T: ?Sized>() -> bool;
360361
}
361362
struct X;
362-
const GOAL: bool = !needs_drop::<i32>() && needs_drop::<X>();
363+
struct NeedsDrop;
364+
impl Drop for NeedsDrop {
365+
fn drop(&mut self) {}
366+
}
367+
enum Enum<T> {
368+
A(T),
369+
B(X),
370+
}
371+
const fn val_needs_drop<T>(_v: T) -> bool { needs_drop::<T>() }
372+
const fn closure_needs_drop() -> bool {
373+
let a = NeedsDrop;
374+
let b = X;
375+
!val_needs_drop(|| &a) && val_needs_drop(move || &a) && !val_needs_drop(move || &b)
376+
}
377+
const fn opaque() -> impl Sized {
378+
|| {}
379+
}
380+
const fn opaque_copy() -> impl Sized + Copy {
381+
|| {}
382+
}
383+
trait Everything {}
384+
impl<T> Everything for T {}
385+
const GOAL: bool = !needs_drop::<i32>() && !needs_drop::<X>()
386+
&& needs_drop::<NeedsDrop>() && !needs_drop::<ManuallyDrop<NeedsDrop>>()
387+
&& needs_drop::<[NeedsDrop; 1]>() && !needs_drop::<[NeedsDrop; 0]>()
388+
&& needs_drop::<(X, NeedsDrop)>()
389+
&& needs_drop::<Enum<NeedsDrop>>() && !needs_drop::<Enum<X>>()
390+
&& closure_needs_drop()
391+
&& !val_needs_drop(opaque()) && !val_needs_drop(opaque_copy())
392+
&& needs_drop::<[NeedsDrop]>() && needs_drop::<dyn Everything>()
393+
&& !needs_drop::<&dyn Everything>() && !needs_drop::<str>();
363394
"#,
364395
1,
365396
);

crates/hir-ty/src/db.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@ use hir_def::{
1313
ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId,
1414
LifetimeParamId, LocalFieldId, StaticId, TraitId, TypeAliasId, TypeOrConstParamId, VariantId,
1515
};
16+
use hir_expand::name::Name;
1617
use la_arena::ArenaMap;
1718
use smallvec::SmallVec;
1819
use triomphe::Arc;
1920

2021
use crate::{
2122
chalk_db,
2223
consteval::ConstEvalError,
24+
drop::DropGlue,
2325
dyn_compatibility::DynCompatibilityViolation,
2426
layout::{Layout, LayoutError},
2527
lower::{Diagnostics, GenericDefaults, GenericPredicates},
@@ -28,7 +30,6 @@ use crate::{
2830
Binders, ClosureId, Const, FnDefId, ImplTraitId, ImplTraits, InferenceResult, Interner,
2931
PolyFnSig, Substitution, TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId,
3032
};
31-
use hir_expand::name::Name;
3233

3334
#[ra_salsa::query_group(HirDatabaseStorage)]
3435
pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
@@ -305,6 +306,10 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
305306
block: Option<BlockId>,
306307
env: chalk_ir::Environment<Interner>,
307308
) -> chalk_ir::ProgramClauses<Interner>;
309+
310+
#[ra_salsa::invoke(crate::drop::has_drop_glue)]
311+
#[ra_salsa::cycle(crate::drop::has_drop_glue_recover)]
312+
fn has_drop_glue(&self, ty: Ty, env: Arc<TraitEnvironment>) -> DropGlue {}
308313
}
309314

310315
#[test]

crates/hir-ty/src/drop.rs

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

crates/hir-ty/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ extern crate ra_ap_rustc_hashes as rustc_hashes;
3030
mod builder;
3131
mod chalk_db;
3232
mod chalk_ext;
33+
mod drop;
3334
mod infer;
3435
mod inhabitedness;
3536
mod interner;
@@ -87,6 +88,7 @@ use crate::{
8788
pub use autoderef::autoderef;
8889
pub use builder::{ParamKind, TyBuilder};
8990
pub use chalk_ext::*;
91+
pub use drop::DropGlue;
9092
pub use infer::{
9193
cast::CastError,
9294
closure::{CaptureKind, CapturedItem},

crates/hir-ty/src/mir/eval/shim.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use hir_def::{
1111
};
1212
use hir_expand::name::Name;
1313
use intern::{sym, Symbol};
14+
use stdx::never;
1415

1516
use crate::{
1617
error_lifetime,
@@ -20,6 +21,7 @@ use crate::{
2021
LangItem, Layout, Locals, Lookup, MirEvalError, MirSpan, Mutability, Result, Substitution,
2122
Ty, TyBuilder, TyExt,
2223
},
24+
DropGlue,
2325
};
2426

2527
mod simd;
@@ -853,7 +855,14 @@ impl Evaluator<'_> {
853855
"size_of generic arg is not provided".into(),
854856
));
855857
};
856-
let result = !ty.clone().is_copy(self.db, locals.body.owner);
858+
let result = match self.db.has_drop_glue(ty.clone(), self.trait_env.clone()) {
859+
DropGlue::HasDropGlue => true,
860+
DropGlue::None => false,
861+
DropGlue::DependOnParams => {
862+
never!("should be fully monomorphized now");
863+
true
864+
}
865+
};
857866
destination.write_from_bytes(self, &[u8::from(result)])
858867
}
859868
"ptr_guaranteed_cmp" => {

0 commit comments

Comments
 (0)