mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 11:31:15 +00:00
200 lines
7.2 KiB
Rust
200 lines
7.2 KiB
Rust
//! Utilities for computing drop info about types.
|
|
|
|
use chalk_ir::cast::Cast;
|
|
use hir_def::AdtId;
|
|
use hir_def::lang_item::LangItem;
|
|
use hir_def::signatures::StructFlags;
|
|
use stdx::never;
|
|
use triomphe::Arc;
|
|
|
|
use crate::{
|
|
AliasTy, Canonical, CanonicalVarKinds, ConcreteConst, ConstScalar, ConstValue, InEnvironment,
|
|
Interner, ProjectionTy, TraitEnvironment, Ty, TyBuilder, TyKind, db::HirDatabase,
|
|
method_resolution::TyFingerprint,
|
|
};
|
|
|
|
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) = LangItem::Drop.resolve_trait(db, module.krate()) 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()),
|
|
};
|
|
impls.for_trait_and_self_ty(drop_trait, TyFingerprint::Adt(adt)).next().is_some()
|
|
}
|
|
|
|
#[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_signature(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) => id
|
|
.enum_variants(db)
|
|
.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) = LangItem::Copy.resolve_trait(db, env.krate) 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).certain()
|
|
}
|
|
|
|
pub(crate) fn has_drop_glue_cycle_result(
|
|
_db: &dyn HirDatabase,
|
|
_ty: Ty,
|
|
_env: Arc<TraitEnvironment>,
|
|
) -> DropGlue {
|
|
DropGlue::None
|
|
}
|