rust-analyzer/crates/hir-ty/src/utils.rs

378 lines
12 KiB
Rust
Raw Normal View History

2019-11-26 14:26:08 +00:00
//! Helper functions for working with def, which don't need to be a separate
//! query, but can't be computed directly from `*Data` (ie, which need a `db`).
use std::{hash::Hash, iter};
use base_db::CrateId;
use chalk_ir::{
fold::{FallibleTypeFolder, Shift},
2024-06-21 08:49:19 +00:00
DebruijnIndex,
};
2019-11-26 13:59:24 +00:00
use hir_def::{
db::DefDatabase,
2024-06-21 08:49:19 +00:00
generics::{WherePredicate, WherePredicateTypeTarget},
2023-02-13 11:55:14 +00:00
lang_item::LangItem,
2019-11-26 13:59:24 +00:00
resolver::{HasResolver, TypeNs},
2021-06-14 12:36:56 +00:00
type_ref::{TraitBoundModifier, TypeRef},
2024-06-21 08:49:19 +00:00
EnumId, EnumVariantId, FunctionId, Lookup, OpaqueInternableThing, TraitId, TypeAliasId,
TypeOrConstParamId,
2019-11-26 13:59:24 +00:00
};
use hir_expand::name::Name;
2024-07-16 10:05:16 +00:00
use intern::sym;
2024-02-04 09:07:35 +00:00
use rustc_abi::TargetDataLayout;
use rustc_hash::FxHashSet;
2021-12-10 19:01:24 +00:00
use smallvec::{smallvec, SmallVec};
use stdx::never;
2019-11-26 13:59:24 +00:00
use crate::{
consteval::unknown_const,
db::HirDatabase,
layout::{Layout, TagEncoding},
mir::pad16,
ChalkTraitId, Const, ConstScalar, GenericArg, Interner, Substitution, TraitRef, TraitRefExt,
Ty, WhereClause,
};
2023-02-13 11:55:14 +00:00
pub(crate) fn fn_traits(
db: &dyn DefDatabase,
krate: CrateId,
) -> impl Iterator<Item = TraitId> + '_ {
[LangItem::Fn, LangItem::FnMut, LangItem::FnOnce]
.into_iter()
.filter_map(move |lang| db.lang_item(krate, lang))
.flat_map(|it| it.as_trait())
}
2023-02-13 11:55:14 +00:00
/// Returns an iterator over the whole super trait hierarchy (including the
/// trait itself).
pub fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> SmallVec<[TraitId; 4]> {
// we need to take care a bit here to avoid infinite loops in case of cycles
// (i.e. if we have `trait A: B; trait B: A;`)
let mut result = smallvec![trait_];
let mut i = 0;
while let Some(&t) = result.get(i) {
// yeah this is quadratic, but trait hierarchies should be flat
// enough that this doesn't matter
direct_super_traits(db, t, |tt| {
if !result.contains(&tt) {
result.push(tt);
}
});
i += 1;
}
result
}
/// Given a trait ref (`Self: Trait`), builds all the implied trait refs for
/// super traits. The original trait ref will be included. So the difference to
/// `all_super_traits` is that we keep track of type parameters; for example if
/// we have `Self: Trait<u32, i32>` and `Trait<T, U>: OtherTrait<U>` we'll get
/// `Self: OtherTrait<i32>`.
pub(super) fn all_super_trait_refs<T>(
db: &dyn HirDatabase,
trait_ref: TraitRef,
cb: impl FnMut(TraitRef) -> Option<T>,
) -> Option<T> {
let seen = iter::once(trait_ref.trait_id).collect();
SuperTraits { db, seen, stack: vec![trait_ref] }.find_map(cb)
2023-02-13 11:55:14 +00:00
}
struct SuperTraits<'a> {
db: &'a dyn HirDatabase,
stack: Vec<TraitRef>,
seen: FxHashSet<ChalkTraitId>,
}
impl SuperTraits<'_> {
2023-02-13 11:55:14 +00:00
fn elaborate(&mut self, trait_ref: &TraitRef) {
direct_super_trait_refs(self.db, trait_ref, |trait_ref| {
if !self.seen.contains(&trait_ref.trait_id) {
self.stack.push(trait_ref);
}
});
}
}
impl Iterator for SuperTraits<'_> {
2023-02-13 11:55:14 +00:00
type Item = TraitRef;
fn next(&mut self) -> Option<Self::Item> {
if let Some(next) = self.stack.pop() {
self.elaborate(&next);
Some(next)
} else {
None
}
}
}
pub(super) fn elaborate_clause_supertraits(
db: &dyn HirDatabase,
clauses: impl Iterator<Item = WhereClause>,
) -> ClauseElaborator<'_> {
let mut elaborator = ClauseElaborator { db, stack: Vec::new(), seen: FxHashSet::default() };
elaborator.extend_deduped(clauses);
elaborator
}
pub(super) struct ClauseElaborator<'a> {
db: &'a dyn HirDatabase,
stack: Vec<WhereClause>,
seen: FxHashSet<WhereClause>,
}
impl<'a> ClauseElaborator<'a> {
fn extend_deduped(&mut self, clauses: impl IntoIterator<Item = WhereClause>) {
self.stack.extend(clauses.into_iter().filter(|c| self.seen.insert(c.clone())))
}
fn elaborate_supertrait(&mut self, clause: &WhereClause) {
if let WhereClause::Implemented(trait_ref) = clause {
direct_super_trait_refs(self.db, trait_ref, |t| {
let clause = WhereClause::Implemented(t);
if self.seen.insert(clause.clone()) {
self.stack.push(clause);
}
});
}
}
}
impl Iterator for ClauseElaborator<'_> {
type Item = WhereClause;
fn next(&mut self) -> Option<Self::Item> {
if let Some(next) = self.stack.pop() {
self.elaborate_supertrait(&next);
Some(next)
} else {
None
}
}
}
2023-02-13 11:55:14 +00:00
fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut(TraitId)) {
2019-11-26 13:59:24 +00:00
let resolver = trait_.resolver(db);
let generic_params = db.generic_params(trait_.into());
2024-06-21 16:27:05 +00:00
let trait_self = generic_params.trait_self_param();
generic_params
.where_predicates()
2020-12-11 12:49:32 +00:00
.filter_map(|pred| match pred {
WherePredicate::ForLifetime { target, bound, .. }
2022-06-23 18:08:29 +00:00
| WherePredicate::TypeBound { target, bound } => {
let is_trait = match target {
WherePredicateTypeTarget::TypeRef(type_ref) => match &**type_ref {
TypeRef::Path(p) => p.is_self_type(),
_ => false,
},
WherePredicateTypeTarget::TypeOrConstParam(local_id) => {
Some(*local_id) == trait_self
}
};
match is_trait {
true => bound.as_path(),
false => None,
2020-12-11 12:49:32 +00:00
}
2022-06-23 18:08:29 +00:00
}
WherePredicate::Lifetime { .. } => None,
2019-11-26 13:59:24 +00:00
})
2022-06-23 18:08:29 +00:00
.filter(|(_, bound_modifier)| matches!(bound_modifier, TraitBoundModifier::None))
.filter_map(|(path, _)| match resolver.resolve_path_in_type_ns_fully(db, path) {
2019-11-26 13:59:24 +00:00
Some(TypeNs::TraitId(t)) => Some(t),
_ => None,
})
2023-02-13 11:55:14 +00:00
.for_each(cb);
2019-11-26 13:59:24 +00:00
}
2023-02-13 11:55:14 +00:00
fn direct_super_trait_refs(db: &dyn HirDatabase, trait_ref: &TraitRef, cb: impl FnMut(TraitRef)) {
2021-03-18 20:53:19 +00:00
let generic_params = db.generic_params(trait_ref.hir_trait_id().into());
2024-06-21 16:27:05 +00:00
let trait_self = match generic_params.trait_self_param() {
2021-12-29 13:35:59 +00:00
Some(p) => TypeOrConstParamId { parent: trait_ref.hir_trait_id().into(), local_id: p },
2023-02-13 11:55:14 +00:00
None => return,
};
db.generic_predicates_for_param(trait_self.parent, trait_self, None)
.iter()
.filter_map(|pred| {
pred.as_ref().filter_map(|pred| match pred.skip_binders() {
2021-03-24 22:07:54 +00:00
// FIXME: how to correctly handle higher-ranked bounds here?
WhereClause::Implemented(tr) => Some(
tr.clone()
2021-12-19 16:58:39 +00:00
.shifted_out_to(Interner, DebruijnIndex::ONE)
.expect("FIXME unexpected higher-ranked trait bound"),
),
_ => None,
})
})
2021-12-19 16:58:39 +00:00
.map(|pred| pred.substitute(Interner, &trait_ref.substitution))
2023-02-13 11:55:14 +00:00
.for_each(cb);
}
2019-11-26 15:02:50 +00:00
pub(super) fn associated_type_by_name_including_super_traits(
db: &dyn HirDatabase,
trait_ref: TraitRef,
2019-11-26 14:42:21 +00:00
name: &Name,
) -> Option<(TraitRef, TypeAliasId)> {
2023-02-13 11:55:14 +00:00
all_super_trait_refs(db, trait_ref, |t| {
2021-03-18 20:53:19 +00:00
let assoc_type = db.trait_data(t.hir_trait_id()).associated_type_by_name(name)?;
Some((t, assoc_type))
})
2019-11-26 14:42:21 +00:00
}
2019-11-27 13:25:01 +00:00
/// It is a bit different from the rustc equivalent. Currently it stores:
/// - 0: the function signature, encoded as a function pointer type
/// - 1..n: generics of the parent
///
/// and it doesn't store the closure types and fields.
///
/// Codes should not assume this ordering, and should always use methods available
/// on this struct for retrieving, and `TyBuilder::substs_for_closure` for creating.
pub(crate) struct ClosureSubst<'a>(pub(crate) &'a Substitution);
impl<'a> ClosureSubst<'a> {
pub(crate) fn parent_subst(&self) -> &'a [GenericArg] {
match self.0.as_slice(Interner) {
[_, x @ ..] => x,
_ => {
never!("Closure missing parameter");
&[]
}
}
}
pub(crate) fn sig_ty(&self) -> &'a Ty {
match self.0.as_slice(Interner) {
[x, ..] => x.assert_ty_ref(Interner),
_ => {
unreachable!("Closure missing sig_ty parameter");
}
}
}
}
pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool {
let data = db.function_data(func);
if data.is_unsafe() {
return true;
}
match func.lookup(db.upcast()).container {
hir_def::ItemContainerId::ExternBlockId(block) => {
// Function in an `extern` block are always unsafe to call, except when it has
// `"rust-intrinsic"` ABI there are a few exceptions.
let id = block.lookup(db.upcast()).id;
let is_intrinsic =
2024-07-16 10:05:16 +00:00
id.item_tree(db.upcast())[id.value].abi.as_ref() == Some(&sym::rust_dash_intrinsic);
if is_intrinsic {
// Intrinsics are unsafe unless they have the rustc_safe_intrinsic attribute
2024-07-16 10:05:16 +00:00
!data.attrs.by_key(&sym::rustc_safe_intrinsic).exists()
} else {
// Extern items are always unsafe
true
}
}
_ => false,
}
}
pub(crate) struct UnevaluatedConstEvaluatorFolder<'a> {
pub(crate) db: &'a dyn HirDatabase,
}
impl FallibleTypeFolder<Interner> for UnevaluatedConstEvaluatorFolder<'_> {
type Error = ();
fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder<Interner, Error = ()> {
self
}
fn interner(&self) -> Interner {
Interner
}
fn try_fold_const(
&mut self,
constant: Const,
_outer_binder: DebruijnIndex,
) -> Result<Const, Self::Error> {
if let chalk_ir::ConstValue::Concrete(c) = &constant.data(Interner).value {
if let ConstScalar::UnevaluatedConst(id, subst) = &c.interned {
if let Ok(eval) = self.db.const_eval(*id, subst.clone(), None) {
return Ok(eval);
} else {
return Ok(unknown_const(constant.data(Interner).ty.clone()));
}
}
}
Ok(constant)
}
}
pub(crate) fn detect_variant_from_bytes<'a>(
layout: &'a Layout,
db: &dyn HirDatabase,
target_data_layout: &TargetDataLayout,
b: &[u8],
e: EnumId,
) -> Option<(EnumVariantId, &'a Layout)> {
let (var_id, var_layout) = match &layout.variants {
hir_def::layout::Variants::Single { index } => {
(db.enum_data(e).variants[index.0].0, layout)
}
hir_def::layout::Variants::Multiple { tag, tag_encoding, variants, .. } => {
let size = tag.size(target_data_layout).bytes_usize();
let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field
let tag = i128::from_le_bytes(pad16(&b[offset..offset + size], false));
match tag_encoding {
TagEncoding::Direct => {
let (var_idx, layout) =
variants.iter_enumerated().find_map(|(var_idx, v)| {
let def = db.enum_data(e).variants[var_idx.0].0;
(db.const_eval_discriminant(def) == Ok(tag)).then_some((def, v))
})?;
(var_idx, layout)
}
TagEncoding::Niche { untagged_variant, niche_start, .. } => {
let candidate_tag = tag.wrapping_sub(*niche_start as i128) as usize;
let variant = variants
.iter_enumerated()
.map(|(x, _)| x)
.filter(|x| x != untagged_variant)
.nth(candidate_tag)
.unwrap_or(*untagged_variant);
(db.enum_data(e).variants[variant.0].0, &variants[variant])
}
}
}
};
Some((var_id, var_layout))
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct InTypeConstIdMetadata(pub(crate) Ty);
impl OpaqueInternableThing for InTypeConstIdMetadata {
fn dyn_hash(&self, mut state: &mut dyn std::hash::Hasher) {
self.hash(&mut state);
}
fn dyn_eq(&self, other: &dyn OpaqueInternableThing) -> bool {
other.as_any().downcast_ref::<Self>().map_or(false, |x| self == x)
}
fn dyn_clone(&self) -> Box<dyn OpaqueInternableThing> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn box_any(&self) -> Box<dyn std::any::Any> {
Box::new(self.clone())
}
}