Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Calculate drop glue and show it on hover #18987

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 33 additions & 2 deletions crates/hir-ty/src/consteval/tests/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,12 +354,43 @@ fn overflowing_add() {
fn needs_drop() {
check_number(
r#"
//- minicore: copy, sized
//- minicore: drop, manually_drop, copy, sized
use core::mem::ManuallyDrop;
extern "rust-intrinsic" {
pub fn needs_drop<T: ?Sized>() -> bool;
}
struct X;
const GOAL: bool = !needs_drop::<i32>() && needs_drop::<X>();
struct NeedsDrop;
impl Drop for NeedsDrop {
fn drop(&mut self) {}
}
enum Enum<T> {
A(T),
B(X),
}
const fn val_needs_drop<T>(_v: T) -> bool { needs_drop::<T>() }
const fn closure_needs_drop() -> bool {
let a = NeedsDrop;
let b = X;
!val_needs_drop(|| &a) && val_needs_drop(move || &a) && !val_needs_drop(move || &b)
}
const fn opaque() -> impl Sized {
|| {}
}
const fn opaque_copy() -> impl Sized + Copy {
|| {}
}
trait Everything {}
impl<T> Everything for T {}
const GOAL: bool = !needs_drop::<i32>() && !needs_drop::<X>()
&& needs_drop::<NeedsDrop>() && !needs_drop::<ManuallyDrop<NeedsDrop>>()
&& needs_drop::<[NeedsDrop; 1]>() && !needs_drop::<[NeedsDrop; 0]>()
&& needs_drop::<(X, NeedsDrop)>()
&& needs_drop::<Enum<NeedsDrop>>() && !needs_drop::<Enum<X>>()
&& closure_needs_drop()
&& !val_needs_drop(opaque()) && !val_needs_drop(opaque_copy())
&& needs_drop::<[NeedsDrop]>() && needs_drop::<dyn Everything>()
&& !needs_drop::<&dyn Everything>() && !needs_drop::<str>();
"#,
1,
);
Expand Down
7 changes: 6 additions & 1 deletion crates/hir-ty/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ use hir_def::{
ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId,
LifetimeParamId, LocalFieldId, StaticId, TraitId, TypeAliasId, TypeOrConstParamId, VariantId,
};
use hir_expand::name::Name;
use la_arena::ArenaMap;
use smallvec::SmallVec;
use triomphe::Arc;

use crate::{
chalk_db,
consteval::ConstEvalError,
drop::DropGlue,
dyn_compatibility::DynCompatibilityViolation,
layout::{Layout, LayoutError},
lower::{Diagnostics, GenericDefaults, GenericPredicates},
Expand All @@ -28,7 +30,6 @@ use crate::{
Binders, ClosureId, Const, FnDefId, ImplTraitId, ImplTraits, InferenceResult, Interner,
PolyFnSig, Substitution, TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId,
};
use hir_expand::name::Name;

#[ra_salsa::query_group(HirDatabaseStorage)]
pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
Expand Down Expand Up @@ -305,6 +306,10 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
block: Option<BlockId>,
env: chalk_ir::Environment<Interner>,
) -> chalk_ir::ProgramClauses<Interner>;

#[ra_salsa::invoke(crate::drop::has_drop_glue)]
#[ra_salsa::cycle(crate::drop::has_drop_glue_recover)]
fn has_drop_glue(&self, ty: Ty, env: Arc<TraitEnvironment>) -> DropGlue {}
}

#[test]
Expand Down
209 changes: 209 additions & 0 deletions crates/hir-ty/src/drop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
//! Utilities for computing drop info about types.

use base_db::ra_salsa;
use chalk_ir::cast::Cast;
use hir_def::data::adt::StructFlags;
use hir_def::lang_item::LangItem;
use hir_def::AdtId;
use stdx::never;
use triomphe::Arc;

use crate::{
db::HirDatabase, method_resolution::TyFingerprint, AliasTy, Canonical, CanonicalVarKinds,
InEnvironment, Interner, ProjectionTy, TraitEnvironment, Ty, TyBuilder, TyKind,
};
use crate::{ConcreteConst, ConstScalar, ConstValue};

fn has_destructor(db: &dyn HirDatabase, adt: AdtId) -> bool {
let module = match adt {
AdtId::EnumId(id) => db.lookup_intern_enum(id).container,
AdtId::StructId(id) => db.lookup_intern_struct(id).container,
AdtId::UnionId(id) => db.lookup_intern_union(id).container,
};
let Some(drop_trait) =
db.lang_item(module.krate(), LangItem::Drop).and_then(|it| it.as_trait())
else {
return false;
};
let impls = match module.containing_block() {
Some(block) => match db.trait_impls_in_block(block) {
Some(it) => it,
None => return false,
},
None => db.trait_impls_in_crate(module.krate()),
};
let result = impls.for_trait_and_self_ty(drop_trait, TyFingerprint::Adt(adt)).next().is_some();
result
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum DropGlue {
// Order of variants is important.
None,
/// May have a drop glue if some type parameter has it.
///
/// For the compiler this is considered as a positive result, IDE distinguishes this from "yes".
DependOnParams,
HasDropGlue,
}

pub(crate) fn has_drop_glue(db: &dyn HirDatabase, ty: Ty, env: Arc<TraitEnvironment>) -> DropGlue {
match ty.kind(Interner) {
TyKind::Adt(adt, subst) => {
if has_destructor(db, adt.0) {
return DropGlue::HasDropGlue;
}
match adt.0 {
AdtId::StructId(id) => {
if db.struct_data(id).flags.contains(StructFlags::IS_MANUALLY_DROP) {
return DropGlue::None;
}
db.field_types(id.into())
.iter()
.map(|(_, field_ty)| {
db.has_drop_glue(
field_ty.clone().substitute(Interner, subst),
env.clone(),
)
})
.max()
.unwrap_or(DropGlue::None)
}
// Unions cannot have fields with destructors.
AdtId::UnionId(_) => DropGlue::None,
AdtId::EnumId(id) => db
.enum_data(id)
.variants
.iter()
.map(|&(variant, _)| {
db.field_types(variant.into())
.iter()
.map(|(_, field_ty)| {
db.has_drop_glue(
field_ty.clone().substitute(Interner, subst),
env.clone(),
)
})
.max()
.unwrap_or(DropGlue::None)
})
.max()
.unwrap_or(DropGlue::None),
}
}
TyKind::Tuple(_, subst) => subst
.iter(Interner)
.map(|ty| ty.assert_ty_ref(Interner))
.map(|ty| db.has_drop_glue(ty.clone(), env.clone()))
.max()
.unwrap_or(DropGlue::None),
TyKind::Array(ty, len) => {
if let ConstValue::Concrete(ConcreteConst { interned: ConstScalar::Bytes(len, _) }) =
&len.data(Interner).value
{
match (&**len).try_into() {
Ok(len) => {
let len = usize::from_le_bytes(len);
if len == 0 {
// Arrays of size 0 don't have drop glue.
return DropGlue::None;
}
}
Err(_) => {
never!("const array size with non-usize len");
}
}
}
db.has_drop_glue(ty.clone(), env)
}
TyKind::Slice(ty) => db.has_drop_glue(ty.clone(), env),
TyKind::Closure(closure_id, subst) => {
let owner = db.lookup_intern_closure((*closure_id).into()).0;
let infer = db.infer(owner);
let (captures, _) = infer.closure_info(closure_id);
let env = db.trait_environment_for_body(owner);
captures
.iter()
.map(|capture| db.has_drop_glue(capture.ty(subst), env.clone()))
.max()
.unwrap_or(DropGlue::None)
}
// FIXME: Handle coroutines.
TyKind::Coroutine(..) | TyKind::CoroutineWitness(..) => DropGlue::None,
TyKind::Ref(..)
| TyKind::Raw(..)
| TyKind::FnDef(..)
| TyKind::Str
| TyKind::Never
| TyKind::Scalar(_)
| TyKind::Function(_)
| TyKind::Foreign(_)
| TyKind::Error => DropGlue::None,
TyKind::Dyn(_) => DropGlue::HasDropGlue,
TyKind::AssociatedType(assoc_type_id, subst) => projection_has_drop_glue(
db,
env,
ProjectionTy { associated_ty_id: *assoc_type_id, substitution: subst.clone() },
ty,
),
TyKind::Alias(AliasTy::Projection(projection)) => {
projection_has_drop_glue(db, env, projection.clone(), ty)
}
TyKind::OpaqueType(..) | TyKind::Alias(AliasTy::Opaque(_)) => {
if is_copy(db, ty, env) {
DropGlue::None
} else {
DropGlue::HasDropGlue
}
}
TyKind::Placeholder(_) | TyKind::BoundVar(_) => {
if is_copy(db, ty, env) {
DropGlue::None
} else {
DropGlue::DependOnParams
}
}
TyKind::InferenceVar(..) => unreachable!("inference vars shouldn't exist out of inference"),
}
}

fn projection_has_drop_glue(
db: &dyn HirDatabase,
env: Arc<TraitEnvironment>,
projection: ProjectionTy,
ty: Ty,
) -> DropGlue {
let normalized = db.normalize_projection(projection, env.clone());
match normalized.kind(Interner) {
TyKind::Alias(AliasTy::Projection(_)) | TyKind::AssociatedType(..) => {
if is_copy(db, ty, env) {
DropGlue::None
} else {
DropGlue::DependOnParams
}
}
_ => db.has_drop_glue(normalized, env),
}
}

fn is_copy(db: &dyn HirDatabase, ty: Ty, env: Arc<TraitEnvironment>) -> bool {
let Some(copy_trait) = db.lang_item(env.krate, LangItem::Copy).and_then(|it| it.as_trait())
else {
return false;
};
let trait_ref = TyBuilder::trait_ref(db, copy_trait).push(ty).build();
let goal = Canonical {
value: InEnvironment::new(&env.env, trait_ref.cast(Interner)),
binders: CanonicalVarKinds::empty(Interner),
};
db.trait_solve(env.krate, env.block, goal).is_some()
}

pub(crate) fn has_drop_glue_recover(
_db: &dyn HirDatabase,
_cycle: &ra_salsa::Cycle,
_ty: &Ty,
_env: &Arc<TraitEnvironment>,
) -> DropGlue {
DropGlue::None
}
2 changes: 2 additions & 0 deletions crates/hir-ty/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ extern crate ra_ap_rustc_pattern_analysis as rustc_pattern_analysis;
mod builder;
mod chalk_db;
mod chalk_ext;
mod drop;
mod infer;
mod inhabitedness;
mod interner;
Expand Down Expand Up @@ -81,6 +82,7 @@ use crate::{
pub use autoderef::autoderef;
pub use builder::{ParamKind, TyBuilder};
pub use chalk_ext::*;
pub use drop::DropGlue;
pub use infer::{
cast::CastError,
closure::{CaptureKind, CapturedItem},
Expand Down
11 changes: 10 additions & 1 deletion crates/hir-ty/src/mir/eval/shim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use hir_def::{
};
use hir_expand::name::Name;
use intern::{sym, Symbol};
use stdx::never;

use crate::{
error_lifetime,
Expand All @@ -19,6 +20,7 @@ use crate::{
LangItem, Layout, Locals, Lookup, MirEvalError, MirSpan, Mutability, Result, Substitution,
Ty, TyBuilder, TyExt,
},
DropGlue,
};

mod simd;
Expand Down Expand Up @@ -852,7 +854,14 @@ impl Evaluator<'_> {
"size_of generic arg is not provided".into(),
));
};
let result = !ty.clone().is_copy(self.db, locals.body.owner);
let result = match self.db.has_drop_glue(ty.clone(), self.trait_env.clone()) {
DropGlue::HasDropGlue => true,
DropGlue::None => false,
DropGlue::DependOnParams => {
never!("should be fully monomorphized now");
true
}
};
destination.write_from_bytes(self, &[u8::from(result)])
}
"ptr_guaranteed_cmp" => {
Expand Down
Loading
Loading