Skip to content

refactor: De-arc lang item queries #19708

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

Merged
merged 1 commit into from
Apr 28, 2025
Merged
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
8 changes: 1 addition & 7 deletions crates/hir-def/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use crate::{
hir::generics::GenericParams,
import_map::ImportMap,
item_tree::{AttrOwner, ItemTree},
lang_item::{self, LangItem, LangItemTarget, LangItems},
lang_item::{self, LangItem},
nameres::{
DefMap, LocalDefMap,
assoc::{ImplItems, TraitItems},
Expand Down Expand Up @@ -325,9 +325,6 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + SourceDatabase {

// endregion:attrs

#[salsa::invoke(LangItems::lang_item_query)]
fn lang_item(&self, start_crate: Crate, item: LangItem) -> Option<LangItemTarget>;

#[salsa::invoke(ImportMap::import_map_query)]
fn import_map(&self, krate: Crate) -> Arc<ImportMap>;

Expand All @@ -349,9 +346,6 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + SourceDatabase {

// endregion:visibilities

#[salsa::invoke(LangItems::crate_lang_items_query)]
fn crate_lang_items(&self, krate: Crate) -> Option<Arc<LangItems>>;

#[salsa::invoke(crate::lang_item::notable_traits_in_deps)]
fn notable_traits_in_deps(&self, krate: Crate) -> Arc<[Arc<[TraitId]>]>;
#[salsa::invoke(crate::lang_item::crate_notable_traits)]
Expand Down
187 changes: 101 additions & 86 deletions crates/hir-def/src/lang_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,104 +83,99 @@ impl LangItemTarget {
}
}

#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct LangItems {
items: FxHashMap<LangItem, LangItemTarget>,
}
/// Salsa query. This will look for lang items in a specific crate.
#[salsa::tracked(return_ref)]
pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option<Box<LangItems>> {
let _p = tracing::info_span!("crate_lang_items_query").entered();

impl LangItems {
pub fn target(&self, item: LangItem) -> Option<LangItemTarget> {
self.items.get(&item).copied()
}
let mut lang_items = LangItems::default();

/// Salsa query. This will look for lang items in a specific crate.
pub(crate) fn crate_lang_items_query(
db: &dyn DefDatabase,
krate: Crate,
) -> Option<Arc<LangItems>> {
let _p = tracing::info_span!("crate_lang_items_query").entered();

let mut lang_items = LangItems::default();
let crate_def_map = db.crate_def_map(krate);

let crate_def_map = db.crate_def_map(krate);
for (_, module_data) in crate_def_map.modules() {
for impl_def in module_data.scope.impls() {
lang_items.collect_lang_item(db, impl_def, LangItemTarget::ImplDef);
for &(_, assoc) in db.impl_items(impl_def).items.iter() {
match assoc {
AssocItemId::FunctionId(f) => {
lang_items.collect_lang_item(db, f, LangItemTarget::Function)
}
AssocItemId::TypeAliasId(t) => {
lang_items.collect_lang_item(db, t, LangItemTarget::TypeAlias)
}
AssocItemId::ConstId(_) => (),
}
}
}

for (_, module_data) in crate_def_map.modules() {
for impl_def in module_data.scope.impls() {
lang_items.collect_lang_item(db, impl_def, LangItemTarget::ImplDef);
for &(_, assoc) in db.impl_items(impl_def).items.iter() {
match assoc {
for def in module_data.scope.declarations() {
match def {
ModuleDefId::TraitId(trait_) => {
lang_items.collect_lang_item(db, trait_, LangItemTarget::Trait);
db.trait_items(trait_).items.iter().for_each(|&(_, assoc_id)| match assoc_id {
AssocItemId::FunctionId(f) => {
lang_items.collect_lang_item(db, f, LangItemTarget::Function)
lang_items.collect_lang_item(db, f, LangItemTarget::Function);
}
AssocItemId::TypeAliasId(t) => {
lang_items.collect_lang_item(db, t, LangItemTarget::TypeAlias)
AssocItemId::TypeAliasId(alias) => {
lang_items.collect_lang_item(db, alias, LangItemTarget::TypeAlias)
}
AssocItemId::ConstId(_) => (),
}
AssocItemId::ConstId(_) => {}
});
}
}

for def in module_data.scope.declarations() {
match def {
ModuleDefId::TraitId(trait_) => {
lang_items.collect_lang_item(db, trait_, LangItemTarget::Trait);
db.trait_items(trait_).items.iter().for_each(
|&(_, assoc_id)| match assoc_id {
AssocItemId::FunctionId(f) => {
lang_items.collect_lang_item(db, f, LangItemTarget::Function);
}
AssocItemId::TypeAliasId(alias) => lang_items.collect_lang_item(
db,
alias,
LangItemTarget::TypeAlias,
),
AssocItemId::ConstId(_) => {}
},
);
}
ModuleDefId::AdtId(AdtId::EnumId(e)) => {
lang_items.collect_lang_item(db, e, LangItemTarget::EnumId);
db.enum_variants(e).variants.iter().for_each(|&(id, _)| {
lang_items.collect_lang_item(db, id, LangItemTarget::EnumVariant);
});
}
ModuleDefId::AdtId(AdtId::StructId(s)) => {
lang_items.collect_lang_item(db, s, LangItemTarget::Struct);
}
ModuleDefId::AdtId(AdtId::UnionId(u)) => {
lang_items.collect_lang_item(db, u, LangItemTarget::Union);
}
ModuleDefId::FunctionId(f) => {
lang_items.collect_lang_item(db, f, LangItemTarget::Function);
}
ModuleDefId::StaticId(s) => {
lang_items.collect_lang_item(db, s, LangItemTarget::Static);
}
ModuleDefId::TypeAliasId(t) => {
lang_items.collect_lang_item(db, t, LangItemTarget::TypeAlias);
}
_ => {}
ModuleDefId::AdtId(AdtId::EnumId(e)) => {
lang_items.collect_lang_item(db, e, LangItemTarget::EnumId);
db.enum_variants(e).variants.iter().for_each(|&(id, _)| {
lang_items.collect_lang_item(db, id, LangItemTarget::EnumVariant);
});
}
ModuleDefId::AdtId(AdtId::StructId(s)) => {
lang_items.collect_lang_item(db, s, LangItemTarget::Struct);
}
ModuleDefId::AdtId(AdtId::UnionId(u)) => {
lang_items.collect_lang_item(db, u, LangItemTarget::Union);
}
ModuleDefId::FunctionId(f) => {
lang_items.collect_lang_item(db, f, LangItemTarget::Function);
}
ModuleDefId::StaticId(s) => {
lang_items.collect_lang_item(db, s, LangItemTarget::Static);
}
ModuleDefId::TypeAliasId(t) => {
lang_items.collect_lang_item(db, t, LangItemTarget::TypeAlias);
}
_ => {}
}
}
}

if lang_items.items.is_empty() { None } else { Some(Arc::new(lang_items)) }
if lang_items.items.is_empty() { None } else { Some(Box::new(lang_items)) }
}

/// Salsa query. Look for a lang item, starting from the specified crate and recursively
/// traversing its dependencies.
#[salsa::tracked]
pub fn lang_item(
db: &dyn DefDatabase,
start_crate: Crate,
item: LangItem,
) -> Option<LangItemTarget> {
let _p = tracing::info_span!("lang_item_query").entered();
if let Some(target) =
crate_lang_items(db, start_crate).as_ref().and_then(|it| it.items.get(&item).copied())
{
return Some(target);
}
start_crate.data(db).dependencies.iter().find_map(|dep| lang_item(db, dep.crate_id, item))
}

/// Salsa query. Look for a lang item, starting from the specified crate and recursively
/// traversing its dependencies.
pub(crate) fn lang_item_query(
db: &dyn DefDatabase,
start_crate: Crate,
item: LangItem,
) -> Option<LangItemTarget> {
let _p = tracing::info_span!("lang_item_query").entered();
if let Some(target) =
db.crate_lang_items(start_crate).and_then(|it| it.items.get(&item).copied())
{
return Some(target);
}
start_crate.data(db).dependencies.iter().find_map(|dep| db.lang_item(dep.crate_id, item))
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct LangItems {
items: FxHashMap<LangItem, LangItemTarget>,
}

impl LangItems {
pub fn target(&self, item: LangItem) -> Option<LangItemTarget> {
self.items.get(&item).copied()
}

fn collect_lang_item<T>(
Expand Down Expand Up @@ -269,18 +264,38 @@ macro_rules! language_item_table {
}

impl LangItem {
pub fn resolve_function(self, db: &dyn DefDatabase, start_crate: Crate) -> Option<FunctionId> {
lang_item(db, start_crate, self).and_then(|t| t.as_function())
}

pub fn resolve_trait(self, db: &dyn DefDatabase, start_crate: Crate) -> Option<TraitId> {
lang_item(db, start_crate, self).and_then(|t| t.as_trait())
}

pub fn resolve_enum(self, db: &dyn DefDatabase, start_crate: Crate) -> Option<EnumId> {
lang_item(db, start_crate, self).and_then(|t| t.as_enum())
}

pub fn resolve_type_alias(
self,
db: &dyn DefDatabase,
start_crate: Crate,
) -> Option<TypeAliasId> {
lang_item(db, start_crate, self).and_then(|t| t.as_type_alias())
}

/// Opposite of [`LangItem::name`]
pub fn from_name(name: &hir_expand::name::Name) -> Option<Self> {
Self::from_symbol(name.symbol())
}

pub fn path(&self, db: &dyn DefDatabase, start_crate: Crate) -> Option<Path> {
let t = db.lang_item(start_crate, *self)?;
let t = lang_item(db, start_crate, *self)?;
Some(Path::LangItem(t, None))
}

pub fn ty_rel_path(&self, db: &dyn DefDatabase, start_crate: Crate, seg: Name) -> Option<Path> {
let t = db.lang_item(start_crate, *self)?;
let t = lang_item(db, start_crate, *self)?;
Some(Path::LangItem(t, Some(seg)))
}
}
Expand Down
6 changes: 2 additions & 4 deletions crates/hir-ty/src/autoderef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,15 +198,13 @@ pub(crate) fn deref_by_trait(
// blanked impl on `Deref`.
#[expect(clippy::overly_complex_bool_expr)]
if use_receiver_trait && false {
if let Some(receiver) =
db.lang_item(table.trait_env.krate, LangItem::Receiver).and_then(|l| l.as_trait())
{
if let Some(receiver) = LangItem::Receiver.resolve_trait(db, table.trait_env.krate) {
return Some(receiver);
}
}
// Old rustc versions might not have `Receiver` trait.
// Fallback to `Deref` if they don't
db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait())
LangItem::Deref.resolve_trait(db, table.trait_env.krate)
};
let trait_id = trait_id()?;
let target =
Expand Down
24 changes: 7 additions & 17 deletions crates/hir-ty/src/chalk_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use hir_def::{
AssocItemId, BlockId, CallableDefId, GenericDefId, HasModule, ItemContainerId, Lookup,
TypeAliasId, VariantId,
hir::Movability,
lang_item::{LangItem, LangItemTarget},
lang_item::LangItem,
signatures::{ImplFlags, StructFlags, TraitFlags},
};

Expand Down Expand Up @@ -262,10 +262,7 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
well_known_trait: rust_ir::WellKnownTrait,
) -> Option<chalk_ir::TraitId<Interner>> {
let lang_attr = lang_item_from_well_known_trait(well_known_trait);
let trait_ = match self.db.lang_item(self.krate, lang_attr) {
Some(LangItemTarget::Trait(trait_)) => trait_,
_ => return None,
};
let trait_ = lang_attr.resolve_trait(self.db, self.krate)?;
Some(to_chalk_trait_id(trait_))
}

Expand Down Expand Up @@ -306,11 +303,8 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
chalk_ir::Binders::new(binders, bound)
}
crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => {
if let Some((future_trait, future_output)) = self
.db
.lang_item(self.krate, LangItem::Future)
.and_then(|item| item.as_trait())
.and_then(|trait_| {
if let Some((future_trait, future_output)) =
LangItem::Future.resolve_trait(self.db, self.krate).and_then(|trait_| {
let alias = self
.db
.trait_items(trait_)
Expand Down Expand Up @@ -338,10 +332,7 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
});
let mut binder = vec![];
binder.push(crate::wrap_empty_binders(impl_bound));
let sized_trait = self
.db
.lang_item(self.krate, LangItem::Sized)
.and_then(|item| item.as_trait());
let sized_trait = LangItem::Sized.resolve_trait(self.db, self.krate);
if let Some(sized_trait_) = sized_trait {
let sized_bound = WhereClause::Implemented(TraitRef {
trait_id: to_chalk_trait_id(sized_trait_),
Expand Down Expand Up @@ -660,9 +651,8 @@ pub(crate) fn associated_ty_data_query(
}

if !ctx.unsized_types.contains(&self_ty) {
let sized_trait = db
.lang_item(resolver.krate(), LangItem::Sized)
.and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id));
let sized_trait =
LangItem::Sized.resolve_trait(db, resolver.krate()).map(to_chalk_trait_id);
let sized_bound = sized_trait.into_iter().map(|sized_trait| {
let trait_bound =
rust_ir::TraitBound { trait_id: sized_trait, args_no_self: Default::default() };
Expand Down
7 changes: 2 additions & 5 deletions crates/hir-ty/src/chalk_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,7 @@ impl TyExt for Ty {
match db.lookup_intern_impl_trait_id((*opaque_ty_id).into()) {
ImplTraitId::AsyncBlockTypeImplTrait(def, _expr) => {
let krate = def.module(db).krate();
if let Some(future_trait) =
db.lang_item(krate, LangItem::Future).and_then(|item| item.as_trait())
{
if let Some(future_trait) = LangItem::Future.resolve_trait(db, krate) {
// This is only used by type walking.
// Parameters will be walked outside, and projection predicate is not used.
// So just provide the Future trait.
Expand Down Expand Up @@ -364,8 +362,7 @@ impl TyExt for Ty {

fn is_copy(self, db: &dyn HirDatabase, owner: DefWithBodyId) -> bool {
let crate_id = owner.module(db).krate();
let Some(copy_trait) = db.lang_item(crate_id, LangItem::Copy).and_then(|it| it.as_trait())
else {
let Some(copy_trait) = LangItem::Copy.resolve_trait(db, crate_id) else {
return false;
};
let trait_ref = TyBuilder::trait_ref(db, copy_trait).push(self).build();
Expand Down
5 changes: 2 additions & 3 deletions crates/hir-ty/src/diagnostics/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -482,9 +482,8 @@ struct FilterMapNextChecker {
impl FilterMapNextChecker {
fn new(resolver: &hir_def::resolver::Resolver, db: &dyn HirDatabase) -> Self {
// Find and store the FunctionIds for Iterator::filter_map and Iterator::next
let (next_function_id, filter_map_function_id) = match db
.lang_item(resolver.krate(), LangItem::IteratorNext)
.and_then(|it| it.as_function())
let (next_function_id, filter_map_function_id) = match LangItem::IteratorNext
.resolve_function(db, resolver.krate())
{
Some(next_function_id) => (
Some(next_function_id),
Expand Down
Loading