Skip to content

Commit d1fb18e

Browse files
Calculate drop glue and show it on hover
Also fix the `needs_drop()` intrinsic. Unions also need this information (to err if they have a drop-needing field), but this will come in a follow-up PR.
1 parent 4273bec commit d1fb18e

13 files changed

Lines changed: 1002 additions & 6 deletions

File tree

‎src/tools/rust-analyzer/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
fnneeds_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
);

‎src/tools/rust-analyzer/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
usecrate::{
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
pubtraitHirDatabase:DefDatabase + Upcast<dynDefDatabase>{
@@ -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+
fnhas_drop_glue(&self,ty:Ty,env:Arc<TraitEnvironment>) -> DropGlue{}
308313
}
309314

310315
#[test]
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+
usecrate::{
12+
db::HirDatabase, method_resolution::TyFingerprint,AliasTy,Canonical,CanonicalVarKinds,
13+
InEnvironment,Interner,ProjectionTy,TraitEnvironment,Ty,TyBuilder,TyKind,
14+
};
15+
usecrate::{ConcreteConst,ConstScalar,ConstValue};
16+
17+
fnhas_destructor(db:&dynHirDatabase,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+
letSome(drop_trait) =
24+
db.lang_item(module.krate(),LangItem::Drop).and_then(|it| it.as_trait())
25+
else{
26+
returnfalse;
27+
};
28+
let impls = match module.containing_block(){
29+
Some(block) => match db.trait_impls_in_block(block){
30+
Some(it) => it,
31+
None => returnfalse,
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+
pubenumDropGlue{
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)fnhas_drop_glue(db:&dynHirDatabase,ty:Ty,env:Arc<TraitEnvironment>) -> DropGlue{
51+
match ty.kind(Interner){
52+
TyKind::Adt(adt, subst) => {
53+
ifhas_destructor(db, adt.0){
54+
returnDropGlue::HasDropGlue;
55+
}
56+
match adt.0{
57+
AdtId::StructId(id) => {
58+
if db.struct_data(id).flags.contains(StructFlags::IS_MANUALLY_DROP){
59+
returnDropGlue::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+
ifletConstValue::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+
returnDropGlue::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+
ifis_copy(db, ty, env){
154+
DropGlue::None
155+
}else{
156+
DropGlue::HasDropGlue
157+
}
158+
}
159+
TyKind::Placeholder(_) | TyKind::BoundVar(_) => {
160+
ifis_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+
fnprojection_has_drop_glue(
171+
db:&dynHirDatabase,
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+
ifis_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+
fnis_copy(db:&dynHirDatabase,ty:Ty,env:Arc<TraitEnvironment>) -> bool{
190+
letSome(copy_trait) = db.lang_item(env.krate,LangItem::Copy).and_then(|it| it.as_trait())
191+
else{
192+
returnfalse;
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)fnhas_drop_glue_recover(
203+
_db:&dynHirDatabase,
204+
_cycle:&ra_salsa::Cycle,
205+
_ty:&Ty,
206+
_env:&Arc<TraitEnvironment>,
207+
) -> DropGlue{
208+
DropGlue::None
209+
}

‎src/tools/rust-analyzer/crates/hir-ty/src/lib.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ extern crate ra_ap_rustc_pattern_analysis as rustc_pattern_analysis;
2424
mod builder;
2525
mod chalk_db;
2626
mod chalk_ext;
27+
mod drop;
2728
mod infer;
2829
mod inhabitedness;
2930
mod interner;
@@ -81,6 +82,7 @@ use crate::{
8182
pubuse autoderef::autoderef;
8283
pubuse builder::{ParamKind,TyBuilder};
8384
pubuse chalk_ext::*;
85+
pubuse drop::DropGlue;
8486
pubuse infer::{
8587
cast::CastError,
8688
closure::{CaptureKind,CapturedItem},

‎src/tools/rust-analyzer/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
usecrate::{
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 = matchself.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)